@novely/core 0.0.0-beta.2 → 0.0.0-beta.4

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.js CHANGED
@@ -1,2 +1,480 @@
1
- var j=a=>(n,d)=>a[n](d),I=a=>typeof a=="number",G=a=>a===null,F=a=>typeof a=="string";var U=a=>String(a),K=(a,n)=>a==="custom"&&n[0]&&n[0].requireUserAction,k=(a={})=>[[[null,"start"],[null,0]],a,[Date.now(),"auto"]],N=(a,n=navigator.language)=>a.includes(n)||a.includes(n=n.substring(0,2))||(n=a.find(d=>navigator.languages.includes(d)))?n:a[0],B=(a,n)=>{let d=!1,h,C;function S(){if(d){h=arguments,C=this;return}a.apply(this,arguments),d=!1}return setTimeout(function(){d=!1,h&&(S.apply(C,h),h=C=null)},n),S};var J=(a,n=new Set)=>{let d=f=>(n.add(f),f(a),()=>{n.delete(f)}),h=f=>{n.forEach(w=>w(f))};return{subscribe:d,update:f=>{h(a=f(a))},get:()=>a}};import{all as ne}from"deepmerge";import{klona as D}from"klona/json";var W=new Set(["dialog","input","vibrate","text"]);import{replace as oe}from"@novely/t9n";var se=({characters:a,storage:n,renderer:d,initialScreen:h="mainmenu",t9n:C,languages:S,state:f})=>{let w,Q=e=>{w=Object.fromEntries(Object.entries(e).map(([t,r])=>{let c=m=>m.flatMap(p=>{let A=p[0];return Array.isArray(A)?c(p):[p]});return[t,c(r)]})),h!=="game"&&s.ui.showScreen(h)},$=new Proxy({},{get(e,t){return(...r)=>[t,...r]}});function _(e){if(!e)return o.value[1];let t=o.value[1],r=typeof e=="function"?e(t):ne([t,e]);o.value[1]=r}let z=(e,t=[e])=>({get value(){return console.log(t),t.at(-1)},set value(r){t[t.length-1]=r},back(){t.length>1&&(t.pop(),P=!0)},push(r){t.push(r)},clear(){t=[k(D(f))]}}),X={saves:[],meta:[N(S)]},v=J(X),R=!1,Y=B(e=>{R&&n.set(e)},120);v.subscribe(Y),n.get().then(e=>{e.meta[0]||=N(S),R=!0,v.update(()=>e),h==="game"&&M()});let O=k(D(f)),o=z(O),q=(e=!1,t=e?"auto":"manual")=>{R&&v.update(r=>{let c=o.value[2][0],m=r.saves.findIndex(p=>p[2][0]===c)===r.saves.length-1;return o.value[2][0]=Date.now(),o.value[2][1]=t,e&&m?r.saves[r.saves.length-1]=o.value:r.saves.push(o.value),r})},Z=()=>{if(!R)return;let e=k(D(f));v.update(t=>(t.saves.push(e),M(e),t))},ee=e=>(o.value=e,M(e)),g=!1,P=!1,M=async e=>{if(!R)return;let t=e||v.get().saves.at(-1);t||(v.update(()=>({saves:[O],meta:[N(S)]})),t=D(O)),g=!0,o.value=t,s.ui.showScreen("game"),b("clear",[P]);let r=w,c=0,m=o.value[0].reduce((u,[i,y])=>G(i)&&I(y)?u+1:u,0),p=[];for(let[u,i]of o.value[0])if(u===null){if(F(i))r=r[i];else if(I(i)){c++;for(let y=0;y<i;y++){let[x,...L]=r[y];if(W.has(x)||K(x,L))if(c===m&&y===i)p.push([x,L]);else continue;p.push([x,L])}r=r[i]}}else u==="choice"?r=r[i+1][1]:u==="condition"&&(r=r[2][i]);let A=p.map((u,i)=>u.concat(i));for await(let[u,i,y]of A)if(u==="function"||u==="custom"){if(u==="custom"&&i[0].callOnlyLatest&&!!A.slice(y+1).some(([le,ae])=>U(ae[0])===U(i[0])))continue;let x=b(u,i);x&&"then"in x&&await x}else b(u,i);g=!1,P=!1,T()},te=()=>{let e=w;for(let[t,r]of o.value[0])t===null?e=e[r]:t==="choice"?e=e[r+1][1]:t==="condition"&&(e=e[2][r]);return e},s=d({characters:a,set:ee,restore:M,save:q,newGame:Z,stack:o,languages:S,t:C.i,$:v}),b=j({wait([e]){g||setTimeout(l,e)},showBackground([e]){s.background(e),l()},playMusic([e]){s.music(e,"music").play(),l()},stopMusic([e]){s.music(e,"music").stop(),l()},showCharacter([e,t,r,c]){let m=s.character(e);m.append(r,c),m.withEmotion(t)(),l()},hideCharacter([e,t,r,c]){s.character(e).remove(t,r,c)(l)},dialog([e,t,r]){s.dialog(V(t),e,r)(H)},function([e]){let t=e();return g||(t?t.then(l):l()),t},choice(e){let t=e.map(([r,c,m])=>[V(r),c,m]);s.choices(t)(r=>{E(),o.value[0].push(["choice",r],[null,0]),T()})},jump([e]){o.value[0]=[[null,e],[null,0]],s.clear(!1)(()=>{g||T()})},clear(){try{navigator.vibrate(0)}finally{s.clear(P)(l)}},condition([e]){let t=e();g||(o.value[0].push(["condition",t],[null,0]),T())},end(){q(!1,"auto"),b("clear",[]),s.ui.showScreen("mainmenu")},input([e,t,r]){s.input(e,t,r)(H)},custom([e]){return s.custom(e,P,()=>{!g&&e.requireUserAction&&E(),g||l()})},vibrate(e){try{navigator.vibrate(e)}finally{l()}},next(){l()},animateCharacter([e,t,...r]){let c=()=>{let m=s.store.characters[e];if(!m)return;let p=m.canvas;if(!p)return;let A=r.filter(u=>!p.classList.contains(u));p.classList.add(...A),setTimeout(()=>{p.classList.remove(...A)},t)};return c.callOnlyLatest=!0,s.custom(c,P,()=>{}),l()},text(e){s.text(e.map(V).join(" "),H)}}),E=()=>{if(g)return;let e=D(o.value);e[0]=D(o.value[0]),e[2][0]=new Date().valueOf(),e[2][1]="auto",o.push(e)},re=()=>{let e=o.value[0][o.value[0].length-1];if(G(e[0])&&I(e[1])){e[1]=e[1]+1;return}o.value[0].push([null,0])},H=()=>{E(),l()},T=()=>{let e=te();if(e){let[t,...r]=e;b(t,r)}},l=()=>{g||(re(),T())},V=e=>{let t=v.get().meta[0],r=_();return oe(typeof e=="function"?e(t,r):e,r)};return{withStory:Q,action:$,render:T,state:_,t:C.t}};var ie=a=>({async get(){let n=localStorage.getItem(a.key);return n?JSON.parse(n):{saves:[],meta:[]}},async set(n){localStorage.setItem(a.key,JSON.stringify(n))}});export{ie as localStorageStorage,se as novely};
1
+ // src/utils.ts
2
+ var matchAction = (values) => {
3
+ return (action, props) => {
4
+ return values[action](props);
5
+ };
6
+ };
7
+ var isNumber = (val) => {
8
+ return typeof val === "number";
9
+ };
10
+ var isNull = (val) => {
11
+ return val === null;
12
+ };
13
+ var isString = (val) => {
14
+ return typeof val === "string";
15
+ };
16
+ var str = (value) => {
17
+ return String(value);
18
+ };
19
+ var isUserRequiredAction = (action, meta) => {
20
+ return action === "custom" && meta[0] && meta[0].requireUserAction;
21
+ };
22
+ var getDefaultSave = (state = {}) => {
23
+ return [[[null, "start"], [null, 0]], state, [Date.now(), "auto"]];
24
+ };
25
+ var getLanguage = (languages, language = navigator.language) => {
26
+ if (languages.includes(language)) {
27
+ return language;
28
+ } else if (languages.includes(language = language.substring(0, 2))) {
29
+ return language;
30
+ } else if (language = languages.find((value) => navigator.languages.includes(value))) {
31
+ return language;
32
+ }
33
+ return languages[0];
34
+ };
35
+ var throttle = (fn, ms) => {
36
+ let throttled = false, savedArgs, savedThis;
37
+ function wrapper() {
38
+ if (throttled) {
39
+ savedArgs = arguments;
40
+ savedThis = this;
41
+ return;
42
+ }
43
+ fn.apply(this, arguments);
44
+ throttled = false;
45
+ }
46
+ setTimeout(function() {
47
+ throttled = false;
48
+ if (savedArgs) {
49
+ wrapper.apply(savedThis, savedArgs);
50
+ savedArgs = savedThis = null;
51
+ }
52
+ }, ms);
53
+ return wrapper;
54
+ };
55
+
56
+ // src/store.ts
57
+ var store = (current, subscribers = /* @__PURE__ */ new Set()) => {
58
+ const subscribe = (cb) => {
59
+ subscribers.add(cb), cb(current);
60
+ return () => {
61
+ subscribers.delete(cb);
62
+ };
63
+ };
64
+ const push = (value) => {
65
+ subscribers.forEach((cb) => cb(value));
66
+ };
67
+ const update = (fn) => {
68
+ push(current = fn(current));
69
+ };
70
+ const get = () => {
71
+ return current;
72
+ };
73
+ return { subscribe, update, get };
74
+ };
75
+
76
+ // src/novely.ts
77
+ import { all as deepmerge } from "deepmerge";
78
+ import { klona } from "klona/json";
79
+
80
+ // src/constants.ts
81
+ var SKIPPED_DURING_RESTORE = /* @__PURE__ */ new Set([
82
+ "dialog",
83
+ "input",
84
+ "vibrate",
85
+ "text"
86
+ ]);
87
+
88
+ // src/novely.ts
89
+ import { replace as replaceT9N } from "@novely/t9n";
90
+ var novely = ({ characters, storage, renderer: createRenderer, initialScreen = "mainmenu", t9n, languages, state: defaultState }) => {
91
+ let story;
92
+ const withStory = (s) => {
93
+ story = Object.fromEntries(Object.entries(s).map(([name, items]) => {
94
+ const flat = (item) => {
95
+ return item.flatMap((data) => {
96
+ const type = data[0];
97
+ if (Array.isArray(type))
98
+ return flat(data);
99
+ return [data];
100
+ });
101
+ };
102
+ return [name, flat(items)];
103
+ }));
104
+ if (initialScreen !== "game")
105
+ renderer.ui.showScreen(initialScreen);
106
+ };
107
+ const action = new Proxy({}, {
108
+ get(_, prop) {
109
+ return (...props) => {
110
+ return [prop, ...props];
111
+ };
112
+ }
113
+ });
114
+ function state(value) {
115
+ if (!value)
116
+ return stack.value[1];
117
+ const prev = stack.value[1];
118
+ const val = typeof value === "function" ? value(prev) : deepmerge([prev, value]);
119
+ stack.value[1] = val;
120
+ }
121
+ const createStack = (current, stack2 = [current]) => {
122
+ return {
123
+ get value() {
124
+ return stack2.at(-1);
125
+ },
126
+ set value(value) {
127
+ stack2[stack2.length - 1] = value;
128
+ },
129
+ back() {
130
+ if (stack2.length > 1)
131
+ stack2.pop(), goingBack = true;
132
+ },
133
+ push(value) {
134
+ stack2.push(value);
135
+ },
136
+ clear() {
137
+ stack2 = [getDefaultSave(klona(defaultState))];
138
+ }
139
+ };
140
+ };
141
+ const initialData = {
142
+ saves: [],
143
+ meta: [getLanguage(languages)]
144
+ };
145
+ const $ = store(initialData);
146
+ let initialDataLoaded = false;
147
+ const onStorageDataChange = (value) => {
148
+ if (initialDataLoaded)
149
+ storage.set(value);
150
+ };
151
+ const throttledOnStorageDataChange = throttle(onStorageDataChange, 120);
152
+ $.subscribe(throttledOnStorageDataChange);
153
+ storage.get().then((stored) => {
154
+ stored.meta[0] ||= getLanguage(languages);
155
+ initialDataLoaded = true;
156
+ $.update(() => stored);
157
+ if (initialScreen === "game")
158
+ restore();
159
+ });
160
+ const initial = getDefaultSave(klona(defaultState));
161
+ const stack = createStack(initial);
162
+ const save = (override = false, type = override ? "auto" : "manual") => {
163
+ if (!initialDataLoaded)
164
+ return;
165
+ $.update((prev) => {
166
+ const date = stack.value[2][0];
167
+ const isLatest = prev.saves.findIndex((value) => value[2][0] === date) === prev.saves.length - 1;
168
+ stack.value[2][0] = Date.now();
169
+ stack.value[2][1] = type;
170
+ if (override) {
171
+ if (isLatest) {
172
+ prev.saves[prev.saves.length - 1] = stack.value;
173
+ } else {
174
+ prev.saves.push(stack.value);
175
+ }
176
+ } else {
177
+ prev.saves.push(stack.value);
178
+ }
179
+ return prev;
180
+ });
181
+ };
182
+ const newGame = () => {
183
+ if (!initialDataLoaded)
184
+ return;
185
+ const save2 = getDefaultSave(klona(defaultState));
186
+ $.update((prev) => {
187
+ prev.saves.push(save2), restore(save2);
188
+ return prev;
189
+ });
190
+ };
191
+ const set = (save2) => {
192
+ stack.value = save2;
193
+ return restore(save2);
194
+ };
195
+ let restoring = false;
196
+ let goingBack = false;
197
+ const restore = async (save2) => {
198
+ if (!initialDataLoaded)
199
+ return;
200
+ let latest = save2 ? save2 : $.get().saves.at(-1);
201
+ if (!latest) {
202
+ $.update(() => ({ saves: [initial], meta: [getLanguage(languages)] }));
203
+ latest = klona(initial);
204
+ }
205
+ restoring = true, stack.value = latest;
206
+ renderer.ui.showScreen("game");
207
+ match("clear", [goingBack]);
208
+ let current = story;
209
+ let index = 0;
210
+ const max = stack.value[0].reduce((acc, [type, val]) => {
211
+ if (isNull(type) && isNumber(val))
212
+ return acc + 1;
213
+ return acc;
214
+ }, 0);
215
+ const queue = [];
216
+ for (const [type, val] of stack.value[0]) {
217
+ if (type === null) {
218
+ if (isString(val)) {
219
+ current = current[val];
220
+ } else if (isNumber(val)) {
221
+ index++;
222
+ for (let i = 0; i < val; i++) {
223
+ const [action2, ...meta] = current[i];
224
+ if (SKIPPED_DURING_RESTORE.has(action2) || isUserRequiredAction(action2, meta)) {
225
+ if (index === max && i === val) {
226
+ queue.push([action2, meta]);
227
+ } else {
228
+ continue;
229
+ }
230
+ }
231
+ queue.push([action2, meta]);
232
+ }
233
+ current = current[val];
234
+ }
235
+ } else if (type === "choice") {
236
+ current = current[val + 1][1];
237
+ } else if (type === "condition") {
238
+ current = current[2][val];
239
+ }
240
+ }
241
+ const indexedQueue = queue.map((value, index2) => value.concat(index2));
242
+ for await (const [action2, meta, i] of indexedQueue) {
243
+ if (action2 === "function" || action2 === "custom") {
244
+ if (action2 === "custom" && meta[0].callOnlyLatest) {
245
+ const next2 = indexedQueue.slice(i + 1);
246
+ const latest2 = !next2.some(([_action, _meta]) => str(_meta[0]) === str(meta[0]));
247
+ if (!latest2)
248
+ continue;
249
+ }
250
+ const result = match(action2, meta);
251
+ if (result && "then" in result)
252
+ await result;
253
+ } else {
254
+ match(action2, meta);
255
+ }
256
+ }
257
+ restoring = false, goingBack = false, render();
258
+ };
259
+ const refer = () => {
260
+ let current = story;
261
+ for (const [type, val] of stack.value[0]) {
262
+ if (type === null) {
263
+ current = current[val];
264
+ } else if (type === "choice") {
265
+ current = current[val + 1][1];
266
+ } else if (type === "condition") {
267
+ current = current[2][val];
268
+ }
269
+ }
270
+ return current;
271
+ };
272
+ const renderer = createRenderer({
273
+ characters,
274
+ set,
275
+ restore,
276
+ save,
277
+ newGame,
278
+ stack,
279
+ languages,
280
+ t: t9n.i,
281
+ $
282
+ });
283
+ const match = matchAction({
284
+ wait([time]) {
285
+ if (!restoring)
286
+ setTimeout(push, time);
287
+ },
288
+ showBackground([background]) {
289
+ renderer.background(background);
290
+ push();
291
+ },
292
+ playMusic([source]) {
293
+ renderer.music(source, "music").play();
294
+ push();
295
+ },
296
+ stopMusic([source]) {
297
+ renderer.music(source, "music").stop();
298
+ push();
299
+ },
300
+ showCharacter([character, emotion, className, style]) {
301
+ const handle = renderer.character(character);
302
+ handle.append(className, style);
303
+ handle.withEmotion(emotion)();
304
+ push();
305
+ },
306
+ hideCharacter([character, className, style, duration]) {
307
+ const handle = renderer.character(character);
308
+ handle.remove(className, style, duration)(push);
309
+ },
310
+ dialog([character, content, emotion]) {
311
+ const name = (() => {
312
+ const c = character, cs = characters;
313
+ const lang = $.get().meta[0];
314
+ return c ? c in cs ? typeof cs[c].name === "string" ? cs[c].name : cs[c].name[lang] : c : "";
315
+ })();
316
+ renderer.dialog(unwrap(content), unwrap(name), character, emotion)(forward);
317
+ },
318
+ function([fn]) {
319
+ const result = fn();
320
+ if (!restoring)
321
+ result ? result.then(push) : push();
322
+ return result;
323
+ },
324
+ choice([question, ...choices]) {
325
+ const isWithoutQuestion = Array.isArray(question);
326
+ if (isWithoutQuestion) {
327
+ choices.unshift(question);
328
+ question = "";
329
+ }
330
+ const unwrapped = choices.map(([content, action2, visible]) => {
331
+ return [unwrap(content), action2, visible];
332
+ });
333
+ renderer.choices(question, unwrapped)((selected) => {
334
+ enmemory();
335
+ stack.value[0].push(["choice", isWithoutQuestion ? selected : selected + 1], [null, 0]), render();
336
+ });
337
+ },
338
+ jump([scene]) {
339
+ stack.value[0] = [[null, scene], [null, 0]];
340
+ renderer.clear(false)(() => {
341
+ if (!restoring)
342
+ render();
343
+ });
344
+ },
345
+ clear() {
346
+ try {
347
+ navigator.vibrate(0);
348
+ } finally {
349
+ renderer.clear(goingBack)(push);
350
+ }
351
+ },
352
+ condition([condition]) {
353
+ const value = condition();
354
+ if (!restoring)
355
+ stack.value[0].push(["condition", value], [null, 0]), render();
356
+ },
357
+ end() {
358
+ save(false, "auto");
359
+ match("clear", []);
360
+ renderer.ui.showScreen("mainmenu");
361
+ },
362
+ input([question, onInput, setup]) {
363
+ renderer.input(question, onInput, setup)(forward);
364
+ },
365
+ custom([handler]) {
366
+ const result = renderer.custom(handler, goingBack, () => {
367
+ if (!restoring && handler.requireUserAction)
368
+ enmemory();
369
+ if (!restoring)
370
+ push();
371
+ });
372
+ return result;
373
+ },
374
+ vibrate(pattern) {
375
+ try {
376
+ navigator.vibrate(pattern);
377
+ } finally {
378
+ push();
379
+ }
380
+ },
381
+ next() {
382
+ push();
383
+ },
384
+ animateCharacter([character, timeout, ...classes]) {
385
+ const handler = () => {
386
+ const char = renderer.store.characters[character];
387
+ if (!char)
388
+ return;
389
+ const target = char.canvas;
390
+ if (!target)
391
+ return;
392
+ const classNames = classes.filter((className) => !target.classList.contains(className));
393
+ target.classList.add(...classNames);
394
+ setTimeout(() => {
395
+ target.classList.remove(...classNames);
396
+ }, timeout);
397
+ };
398
+ handler.callOnlyLatest = true;
399
+ return renderer.custom(handler, goingBack, () => {
400
+ }), push();
401
+ },
402
+ text(text) {
403
+ renderer.text(text.map(unwrap).join(" "), forward);
404
+ },
405
+ exit() {
406
+ const path = stack.value[0];
407
+ for (let i = path.length - 1; i > 0; i--) {
408
+ if (path[i][0] !== "choice" && path[i][0] !== "condition")
409
+ continue;
410
+ stack.value[0] = path.slice(0, i);
411
+ next();
412
+ break;
413
+ }
414
+ render();
415
+ }
416
+ });
417
+ const enmemory = () => {
418
+ if (restoring)
419
+ return;
420
+ const current = klona(stack.value);
421
+ current[0] = klona(stack.value[0]);
422
+ current[2][0] = (/* @__PURE__ */ new Date()).valueOf();
423
+ current[2][1] = "auto";
424
+ stack.push(current);
425
+ };
426
+ const next = () => {
427
+ const path = stack.value[0];
428
+ const last = path[path.length - 1];
429
+ if (isNull(last[0]) && isNumber(last[1])) {
430
+ last[1] = last[1] + 1;
431
+ return;
432
+ }
433
+ path.push([null, 0]);
434
+ };
435
+ const render = () => {
436
+ const referred = refer();
437
+ if (!(referred && Array.isArray(referred)))
438
+ return;
439
+ const [action2, ...props] = referred;
440
+ match(action2, props);
441
+ };
442
+ const push = () => {
443
+ if (!restoring)
444
+ next(), render();
445
+ };
446
+ const forward = () => {
447
+ enmemory();
448
+ push();
449
+ };
450
+ const unwrap = (content) => {
451
+ const lang = $.get().meta[0];
452
+ const data = state();
453
+ return replaceT9N(typeof content === "function" ? content(lang, data) : content, data);
454
+ };
455
+ return {
456
+ withStory,
457
+ action,
458
+ render,
459
+ state,
460
+ t: t9n.t
461
+ };
462
+ };
463
+
464
+ // src/storage.ts
465
+ var localStorageStorage = (options) => {
466
+ return {
467
+ async get() {
468
+ const value = localStorage.getItem(options.key);
469
+ return value ? JSON.parse(value) : { saves: [], meta: [] };
470
+ },
471
+ async set(data) {
472
+ localStorage.setItem(options.key, JSON.stringify(data));
473
+ }
474
+ };
475
+ };
476
+ export {
477
+ localStorageStorage,
478
+ novely
479
+ };
2
480
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/store.ts","../src/novely.ts","../src/constants.ts","../src/storage.ts"],"sourcesContent":["import type { ActionProxyProvider, CustomHandler } from './action'\nimport type { Character } from './character'\nimport type { Save, Thenable } from './types'\n\ntype MatchActionMap = {\n [Key in keyof ActionProxyProvider<Record<string, Character>>]: (data: Parameters<ActionProxyProvider<Record<string, Character>>[Key]>) => void;\n}\n\ntype MatchActionMapComplete = Omit<MatchActionMap, 'custom'> & {\n custom: (value: [handler: CustomHandler]) => Thenable<void>;\n}\n\nconst matchAction = <M extends MatchActionMapComplete>(values: M) => {\n return (action: keyof MatchActionMap, props: any) => {\n return values[action](props);\n }\n}\n\nconst isNumber = (val: unknown): val is number => {\n return typeof val === 'number';\n}\n\nconst isNull = (val: unknown): val is null => {\n return val === null;\n}\n\nconst isString = (val: unknown): val is string => {\n return typeof val === 'string';\n}\n\nconst isCSSImage = (str: string) => {\n const startsWith = String.prototype.startsWith.bind(str);\n\n return startsWith('http') || startsWith('/') || startsWith('.') || startsWith('data');\n}\n\nconst str = (value: unknown) => {\n return String(value);\n}\n\nconst isUserRequiredAction = (action: keyof MatchActionMapComplete, meta: Parameters<MatchActionMapComplete[keyof MatchActionMapComplete]>) => {\n return action === 'custom' && meta[0] && (meta[0] as unknown as CustomHandler).requireUserAction;\n}\n\nconst getDefaultSave = (state = {}) => {\n return [[[null, 'start'], [null, 0]], state, [Date.now(), 'auto']] as Save;\n}\n\nconst getLanguage = (languages: string[], language = navigator.language) => {\n if (languages.includes(language)) {\n return language;\n } else if (languages.includes((language = language.substring(0, 2)))) {\n return language;\n } else if ((language = languages.find((value) => navigator.languages.includes(value))!)) {\n return language\n }\n\n /**\n * We'v checked the `en-GB` format, `en` format, and maybe any second languages, but there were no matches\n */\n return languages[0];\n}\n\n/**\n * @copyright Techlead LLC\n * @see https://learn.javascript.ru/task/throttle\n */\nconst throttle = <Fn extends ((...args: any[]) => any)>(fn: Fn, ms: number) => {\n let throttled = false, savedArgs: any, savedThis: any;\n\n function wrapper() {\n if (throttled) {\n savedArgs = arguments;\n // @ts-ignore\n savedThis = this;\n\n return;\n }\n\n // @ts-ignore\n fn.apply(this, arguments);\n\n throttled = false;\n }\n\n setTimeout(function () {\n throttled = false;\n\n if (savedArgs) {\n wrapper.apply(savedThis, savedArgs);\n savedArgs = savedThis = null;\n }\n }, ms);\n\n return wrapper as unknown as (...args: Parameters<Fn>) => void;\n}\n\nexport { matchAction, isNumber, isNull, isString, isCSSImage, str, isUserRequiredAction, getDefaultSave, getLanguage, throttle }","type Stored<T> = {\n subscribe: (cb: (value: T) => void) => () => void;\n update: (fn: (prev: T) => T) => void;\n get: () => T;\n}\n\nconst store = <T>(current: T, subscribers = new Set<(value: T) => void>()): Stored<T> => {\n const subscribe = (cb: (value: T) => void) => {\n subscribers.add(cb), cb(current);\n\n return () => {\n subscribers.delete(cb);\n }\n };\n\n const push = (value: T) => {\n subscribers.forEach((cb) => cb(value))\n };\n\n const update = (fn: (prev: T) => T) => {\n push((current = fn(current)));\n };\n\n const get = () => {\n return current;\n };\n\n return { subscribe, update, get } as const;\n};\n\nexport { store }\nexport type { Stored }","import type { Character } from './character';\nimport type { ActionProxyProvider, GetActionParameters, Story, ValidAction, DialogContent, ChoiceContent, CustomHandler } from './action';\nimport type { Storage } from './storage';\nimport type { Save, State, StorageData, DeepPartial } from './types'\nimport type { Renderer, RendererInit } from './renderer'\nimport type { SetupT9N } from '@novely/t9n'\nimport { matchAction, isNumber, isNull, isString, str, isUserRequiredAction, getDefaultSave, getLanguage, throttle } from './utils';\nimport { store } from './store';\nimport { all as deepmerge } from 'deepmerge'\nimport { klona } from 'klona/json';\nimport { SKIPPED_DURING_RESTORE } from './constants';\nimport { replace as replaceT9N } from '@novely/t9n';\n\ninterface NovelyInit<Languages extends string, Characters extends Record<string, Character<Languages>>, Inter extends ReturnType<SetupT9N<Languages>>, StateScheme extends State> {\n /**\n * An array of languages supported by the game.\n */\n languages: Languages[];\n /**\n * An object containing the characters in the game.\n */\n characters: Characters;\n /**\n * An object that provides access to the game's storage system.\n */\n storage: Storage;\n /**\n * A function that returns a Renderer object used to display the game's content\n */\n renderer: (characters: RendererInit) => Renderer;\n /**\n * An optional property that specifies the initial screen to display when the game starts\n */\n initialScreen?: \"mainmenu\" | \"game\" | \"saves\" | \"settings\";\n /**\n * An object containing the translation functions used in the game\n */\n t9n: Inter;\n /**\n * Initial state value\n */\n state?: StateScheme;\n}\n\nconst novely = <Languages extends string, Characters extends Record<string, Character<Languages>>, Inter extends ReturnType<SetupT9N<Languages>>, StateScheme extends State>({ characters, storage, renderer: createRenderer, initialScreen = \"mainmenu\", t9n, languages, state: defaultState }: NovelyInit<Languages, Characters, Inter, StateScheme>) => {\n let story: Story;\n\n // @todo: find bug here\n const withStory = (s: Story) => {\n /**\n * Transforms `(ValidAction | ValidAction[])[]` to `ValidAction[]`\n */\n story = Object.fromEntries(Object.entries(s).map(([name, items]) => {\n const flat = (item: (ValidAction | ValidAction[])[]): ValidAction[] => {\n return item.flatMap((data) => {\n const type = data[0];\n\n /**\n * This is not just an action like `['name', ...arguments]`, but an array of actions\n */\n if (Array.isArray(type)) return flat(data as ValidAction[]);\n\n return [data as ValidAction];\n });\n };\n\n return [name, flat(items)];\n }));\n\n /**\n * When `initialScreen` is not a game, we can safely show it\n */\n if (initialScreen !== 'game') renderer.ui.showScreen(initialScreen);\n }\n\n const action = new Proxy({} as ActionProxyProvider<Characters>, {\n get(_, prop) {\n return (...props: Parameters<ActionProxyProvider<Record<string, Character>>[keyof ActionProxyProvider<Record<string, Character>>]>) => {\n return [prop, ...props];\n }\n }\n });\n\n function state(value: DeepPartial<StateScheme> | ((prev: StateScheme) => StateScheme)): void;\n function state(): StateScheme;\n function state(value?: DeepPartial<StateScheme> | ((prev: StateScheme) => StateScheme)): StateScheme | void {\n if (!value) return stack.value[1] as StateScheme | void;\n\n const prev = stack.value[1];\n const val = typeof value === 'function' ? value(prev as StateScheme) : deepmerge([prev, value]);\n\n stack.value[1] = val as StateScheme;\n }\n\n const createStack = (current: Save, stack = [current]) => {\n return {\n get value() {\n console.log(stack);\n\n return stack.at(-1)!;\n },\n set value(value: Save) {\n stack[stack.length - 1] = value;\n },\n back() {\n if (stack.length > 1) stack.pop(), goingBack = true;\n },\n push(value: Save) {\n stack.push(value);\n },\n clear() {\n stack = [getDefaultSave(klona(defaultState))];\n }\n }\n }\n\n /**\n * 1) Novely rendered using the `initialData`, you can still start new game or `load` an empty one - this is scary, imagine losing your progress\n * 2) Actual stored data is loaded, language and etc is changed \n */\n const initialData: StorageData = {\n saves: [],\n meta: [getLanguage(languages)]\n };\n\n const $ = store(initialData);\n\n let initialDataLoaded = false;\n\n const onStorageDataChange = (value: StorageData) => {\n if (initialDataLoaded) storage.set(value);\n };\n\n const throttledOnStorageDataChange = throttle(onStorageDataChange, 120);\n\n $.subscribe(throttledOnStorageDataChange);\n\n storage.get().then(stored => {\n /**\n * Default `localStorageStorage` cannot determine preferred language, and returns empty array\n */\n stored.meta[0] ||= getLanguage(languages);\n\n /**\n * Now the next store updates will entail saving via storage.set\n */\n initialDataLoaded = true;\n\n $.update(() => stored);\n\n /**\n * When initialScreen is game, then we will load it, but after the data is loaded\n */\n if (initialScreen === 'game') restore();\n });\n\n const initial = getDefaultSave(klona(defaultState));\n const stack = createStack(initial);\n\n const save = (override = false, type: Save[2][1] = override ? 'auto' : 'manual') => {\n if (!initialDataLoaded) return;\n\n $.update(prev => {\n const date = stack.value[2][0];\n const isLatest = prev.saves.findIndex(value => value[2][0] === date) === prev.saves.length - 1;\n\n /**\n * Обновим дату и тип\n */\n stack.value[2][0] = Date.now();\n stack.value[2][1] = type;\n\n if (override) {\n /**\n * Перезапишем\n */\n if (isLatest) {\n /**\n * Сохранения хранятся в массиве. Нельзя перезаписать любое последнее\n * \n * Если перезаписывать старое сохранение, то они не будут идти в хронологическом порядке\n */\n prev.saves[prev.saves.length - 1] = stack.value;\n } else {\n prev.saves.push(stack.value);\n }\n } else {\n /**\n * Добавляем текущее сохранение\n */\n prev.saves.push(stack.value);\n }\n\n /**\n * Устанавливаем новое значение\n */\n return prev;\n });\n }\n\n const newGame = () => {\n if (!initialDataLoaded) return;\n\n const save = getDefaultSave(klona(defaultState));\n\n $.update(prev => {\n prev.saves.push(save), restore(save);\n\n return prev;\n });\n }\n\n /**\n * Устанавливает сохранение\n */\n const set = (save: Save) => {\n stack.value = save;\n\n return restore(save);\n }\n\n let restoring = false;\n let goingBack = false;\n\n /**\n * Визуально восстанавливает историю\n */\n const restore = async (save?: Save) => {\n if (!initialDataLoaded) return;\n\n let latest = save ? save : $.get().saves.at(-1);\n\n /**\n * Если нет сохранённой игры, то запустим ту, которая уже есть\n */\n if (!latest) {\n $.update(() => ({ saves: [initial], meta: [getLanguage(languages)] }));\n\n latest = klona(initial);\n }\n\n restoring = true, stack.value = latest;\n\n /**\n * Показать экран игры\n */\n renderer.ui.showScreen('game');\n\n match('clear', [goingBack]);\n\n /**\n * Текущий элемент в истории\n */\n let current: any = story;\n /**\n * Текущий элемент `[null, int]`\n */\n let index = 0;\n\n /**\n * Число элементов `[null, int]`\n */\n const max = stack.value[0].reduce((acc, [type, val]) => {\n if (isNull(type) && isNumber(val)) return acc + 1;\n\n return acc;\n }, 0);\n\n const queue = [] as [any, any][];\n\n for (const [type, val] of stack.value[0]) {\n if (type === null) {\n if (isString(val)) {\n current = current[val];\n } else if (isNumber(val)) {\n index++;\n\n /**\n * Запустим все экшены которые идут в `[null, int]` от `0` до `int`\n */\n for (let i = 0; i < val; i++) {\n const [action, ...meta] = current[i];\n\n /**\n * Экшены, для закрытия которых пользователь должен с ними взаимодействовать\n * Также в эту группу входят экшены, которые не должны быть вызваны при восстановлении\n */\n if (SKIPPED_DURING_RESTORE.has(action) || isUserRequiredAction(action, meta)) {\n if (index === max && i === val) {\n queue.push([action, meta]);\n } else {\n continue;\n }\n }\n\n queue.push([action, meta]);\n }\n\n current = current[val];\n }\n } else if (type === 'choice') {\n current = current[val as number + 1][1];\n } else if (type === 'condition') {\n current = current[2][val];\n }\n }\n\n const indexedQueue = queue.map((value, index) => value.concat(index) as [ValidAction[0], ValidAction[1], number]);\n\n for await (const [action, meta, i] of indexedQueue) {\n if (action === 'function' || action === 'custom') {\n /**\n * Если `callOnlyLatest` - `true`\n */\n if (action === 'custom' && (meta as GetActionParameters<'Custom'>)[0].callOnlyLatest) {\n /**\n * Вычислим `latest` или нет\n */\n const next = indexedQueue.slice(i + 1);\n const latest = !next.some(([_action, _meta]) => str(_meta[0]) === str(meta[0]));\n\n if (!latest) continue;\n }\n\n /**\n * Action может возвращать Promise. Нужно подожать его `resolve`\n */\n const result = match(action, meta);\n\n /**\n * Дождёмся окончания\n */\n if (result && 'then' in result) await result;\n } else {\n match(action as keyof ActionProxyProvider<Record<string, Character<string>>>, meta);\n }\n }\n\n restoring = false, goingBack = false, render();\n }\n\n const refer = () => {\n let current: any = story;\n\n for (const [type, val] of stack.value[0]) {\n if (type === null) {\n current = current[val];\n } else if (type === 'choice') {\n current = current[val as number + 1][1];\n } else if (type === 'condition') {\n current = current[2][val];\n }\n }\n\n return current;\n }\n\n const renderer = createRenderer({\n characters,\n set,\n restore,\n save,\n newGame,\n stack,\n languages,\n t: t9n.i,\n $\n });\n\n const match = matchAction({\n wait([time]) {\n /**\n * `restoring` может поменяться на `true` перед тем как запуститься `push` из `setTimeout`\n */\n if (!restoring) setTimeout(push, time);\n },\n showBackground([background]) {\n renderer.background(background);\n push()\n },\n playMusic([source]) {\n renderer.music(source, 'music').play();\n push()\n },\n stopMusic([source]) {\n renderer.music(source, 'music').stop();\n push()\n },\n showCharacter([character, emotion, className, style]) {\n const handle = renderer.character(character);\n\n handle.append(className, style);\n handle.withEmotion(emotion)();\n\n push()\n },\n hideCharacter([character, className, style, duration]) {\n const handle = renderer.character(character);\n\n handle.remove(className, style, duration)(push);\n },\n dialog([person, content, emotion]) {\n renderer.dialog(unwrap(content), person, emotion)(forward);\n },\n function([fn]) {\n const result = fn();\n\n if (!restoring) result ? result.then(push) : push();\n\n return result;\n },\n choice(choices) {\n const unwrapped = choices.map(([content, action, visible]) => {\n return [unwrap(content), action, visible] as [string, ValidAction[], () => boolean];\n });\n\n renderer.choices(unwrapped)((selected) => {\n enmemory();\n\n stack.value[0].push(['choice', selected], [null, 0]), render();\n });\n },\n jump([scene]) {\n stack.value[0] = [[null, scene], [null, 0]];\n\n renderer.clear(false)(() => {\n if (!restoring) render();\n })\n },\n clear() {\n try {\n navigator.vibrate(0)\n } finally {\n renderer.clear(goingBack)(push);\n }\n },\n condition([condition]) {\n const value = condition();\n\n if (!restoring) stack.value[0].push(['condition', value], [null, 0]), render();\n },\n end() {\n /**\n * Save Current Game\n */\n save(false, 'auto');\n /**\n * Clear the Scene\n */\n match('clear', []);\n /**\n * Go to the main menu\n */\n renderer.ui.showScreen('mainmenu');\n },\n input([question, onInput, setup]) {\n renderer.input(question, onInput, setup)(forward);\n },\n custom([handler]) {\n const result = renderer.custom(handler, goingBack, () => {\n if (!restoring && handler.requireUserAction) enmemory();\n if (!restoring) push();\n });\n\n return result;\n },\n vibrate(pattern) {\n try {\n navigator.vibrate(pattern)\n } finally {\n push()\n }\n },\n next() {\n push();\n },\n animateCharacter([character, timeout, ...classes]) {\n const handler: CustomHandler = () => {\n const char = renderer.store.characters[character];\n\n /**\n * Character is not defined, maybe, `animateCharacter` was called before `showCharacter`\n */\n if (!char) return;\n\n const target = char.canvas;\n\n /**\n * Character is not found\n */\n if (!target) return;\n\n const classNames = classes.filter(className => !target.classList.contains(className));\n\n target.classList.add(...classNames);\n\n setTimeout(() => {\n target.classList.remove(...classNames);\n }, timeout);\n }\n\n handler.callOnlyLatest = true;\n\n return renderer.custom(handler, goingBack, () => { }), push();\n },\n text(text) {\n renderer.text(text.map(unwrap).join(' '), forward);\n }\n });\n\n const enmemory = () => {\n if (restoring) return;\n\n const current = klona(stack.value);\n\n current[0] = klona(stack.value[0]);\n\n current[2][0] = new Date().valueOf();\n current[2][1] = 'auto';\n\n stack.push(current);\n }\n\n const next = () => {\n /**\n * Последний элемент пути\n */\n const last = stack.value[0][stack.value[0].length - 1]!;\n\n /**\n * Если он вида `[null, int]` - увеличивает `int`\n */\n if (isNull(last[0]) && isNumber(last[1])) {\n last[1] = last[1] + 1;\n return;\n }\n\n /**\n * Иначе добавляет новое `[null int]`\n */\n stack.value[0].push([null, 0]);\n }\n\n const forward = () => {\n enmemory();\n push();\n }\n\n const render = () => {\n const referred = refer();\n\n if (referred) {\n const [action, ...props] = referred;\n\n match(action, props);\n }\n }\n\n const push = () => {\n if (!restoring) next(), render();\n }\n\n const unwrap = (content: DialogContent | ChoiceContent) => {\n const lang = $.get().meta[0];\n const data = state();\n\n return replaceT9N(typeof content === 'function' ? content(lang, data) : content, data);\n }\n\n return {\n withStory,\n action,\n render,\n state,\n t: t9n.t as Inter['t'],\n }\n}\n\nexport { novely }\n","const SKIPPED_DURING_RESTORE = new Set([\n 'dialog',\n 'input',\n 'vibrate',\n 'text'\n] as const);\n\nexport { SKIPPED_DURING_RESTORE }","import type { StorageData } from './types'\n\ninterface LocalStorageStorageSettings {\n key: string\n}\n\ninterface Storage {\n get: () => Promise<StorageData>;\n set: (data: StorageData) => Promise<void>;\n}\n\nconst localStorageStorage = (options: LocalStorageStorageSettings): Storage => {\n return {\n async get() {\n const value = localStorage.getItem(options.key);\n\n return value ? JSON.parse(value) : { saves: [], meta: [] };\n },\n async set(data) {\n localStorage.setItem(options.key, JSON.stringify(data));\n }\n }\n}\n\nexport type { Storage }\nexport { localStorageStorage }"],"mappings":"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","names":["matchAction","values","action","props","isNumber","val","isNull","isString","str","value","isUserRequiredAction","action","meta","getDefaultSave","state","getLanguage","languages","language","throttle","fn","ms","throttled","savedArgs","savedThis","wrapper","store","current","subscribers","subscribe","cb","push","value","fn","deepmerge","klona","SKIPPED_DURING_RESTORE","replaceT9N","novely","characters","storage","createRenderer","initialScreen","t9n","languages","defaultState","story","withStory","s","name","items","flat","item","data","type","renderer","action","_","prop","props","state","value","stack","prev","val","deepmerge","createStack","current","goingBack","getDefaultSave","klona","initialData","getLanguage","$","store","initialDataLoaded","throttledOnStorageDataChange","throttle","stored","restore","initial","save","override","date","isLatest","newGame","set","restoring","latest","match","index","max","acc","isNull","isNumber","queue","isString","i","meta","SKIPPED_DURING_RESTORE","isUserRequiredAction","indexedQueue","_action","_meta","str","result","render","refer","matchAction","time","push","background","source","character","emotion","className","style","handle","duration","person","content","unwrap","forward","fn","choices","unwrapped","visible","selected","enmemory","scene","condition","question","onInput","setup","handler","pattern","timeout","classes","char","target","classNames","text","next","last","referred","lang","localStorageStorage","options","value","data"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/store.ts","../src/novely.ts","../src/constants.ts","../src/storage.ts"],"sourcesContent":["import type { ActionProxyProvider, CustomHandler } from './action'\nimport type { Character } from './character'\nimport type { Save, Thenable } from './types'\n\ntype MatchActionMap = {\n [Key in keyof ActionProxyProvider<Record<string, Character>>]: (data: Parameters<ActionProxyProvider<Record<string, Character>>[Key]>) => void;\n}\n\ntype MatchActionMapComplete = Omit<MatchActionMap, 'custom'> & {\n custom: (value: [handler: CustomHandler]) => Thenable<void>;\n}\n\nconst matchAction = <M extends MatchActionMapComplete>(values: M) => {\n return (action: keyof MatchActionMap, props: any) => {\n return values[action](props);\n }\n}\n\nconst isNumber = (val: unknown): val is number => {\n return typeof val === 'number';\n}\n\nconst isNull = (val: unknown): val is null => {\n return val === null;\n}\n\nconst isString = (val: unknown): val is string => {\n return typeof val === 'string';\n}\n\nconst isCSSImage = (str: string) => {\n const startsWith = String.prototype.startsWith.bind(str);\n\n return startsWith('http') || startsWith('/') || startsWith('.') || startsWith('data');\n}\n\nconst str = (value: unknown) => {\n return String(value);\n}\n\nconst isUserRequiredAction = (action: keyof MatchActionMapComplete, meta: Parameters<MatchActionMapComplete[keyof MatchActionMapComplete]>) => {\n return action === 'custom' && meta[0] && (meta[0] as unknown as CustomHandler).requireUserAction;\n}\n\nconst getDefaultSave = (state = {}) => {\n return [[[null, 'start'], [null, 0]], state, [Date.now(), 'auto']] as Save;\n}\n\nconst getLanguage = (languages: string[], language = navigator.language) => {\n if (languages.includes(language)) {\n return language;\n } else if (languages.includes((language = language.substring(0, 2)))) {\n return language;\n } else if ((language = languages.find((value) => navigator.languages.includes(value))!)) {\n return language\n }\n\n /**\n * We'v checked the `en-GB` format, `en` format, and maybe any second languages, but there were no matches\n */\n return languages[0];\n}\n\n/**\n * @copyright Techlead LLC\n * @see https://learn.javascript.ru/task/throttle\n */\nconst throttle = <Fn extends ((...args: any[]) => any)>(fn: Fn, ms: number) => {\n let throttled = false, savedArgs: any, savedThis: any;\n\n function wrapper() {\n if (throttled) {\n savedArgs = arguments;\n // @ts-ignore\n savedThis = this;\n\n return;\n }\n\n // @ts-ignore\n fn.apply(this, arguments);\n\n throttled = false;\n }\n\n setTimeout(function () {\n throttled = false;\n\n if (savedArgs) {\n wrapper.apply(savedThis, savedArgs);\n savedArgs = savedThis = null;\n }\n }, ms);\n\n return wrapper as unknown as (...args: Parameters<Fn>) => void;\n}\n\nexport { matchAction, isNumber, isNull, isString, isCSSImage, str, isUserRequiredAction, getDefaultSave, getLanguage, throttle }","type Stored<T> = {\n subscribe: (cb: (value: T) => void) => () => void;\n update: (fn: (prev: T) => T) => void;\n get: () => T;\n}\n\nconst store = <T>(current: T, subscribers = new Set<(value: T) => void>()): Stored<T> => {\n const subscribe = (cb: (value: T) => void) => {\n subscribers.add(cb), cb(current);\n\n return () => {\n subscribers.delete(cb);\n }\n };\n\n const push = (value: T) => {\n subscribers.forEach((cb) => cb(value))\n };\n\n const update = (fn: (prev: T) => T) => {\n push((current = fn(current)));\n };\n\n const get = () => {\n return current;\n };\n\n return { subscribe, update, get } as const;\n};\n\nexport { store }\nexport type { Stored }","import type { Character } from './character';\nimport type { ActionProxyProvider, GetActionParameters, Story, ValidAction, DialogContent, ChoiceContent, CustomHandler } from './action';\nimport type { Storage } from './storage';\nimport type { Save, State, StorageData, DeepPartial } from './types'\nimport type { Renderer, RendererInit } from './renderer'\nimport type { SetupT9N } from '@novely/t9n'\nimport { matchAction, isNumber, isNull, isString, str, isUserRequiredAction, getDefaultSave, getLanguage, throttle } from './utils';\nimport { store } from './store';\nimport { all as deepmerge } from 'deepmerge'\nimport { klona } from 'klona/json';\nimport { SKIPPED_DURING_RESTORE } from './constants';\nimport { replace as replaceT9N } from '@novely/t9n';\n\ninterface NovelyInit<Languages extends string, Characters extends Record<string, Character<Languages>>, Inter extends ReturnType<SetupT9N<Languages>>, StateScheme extends State> {\n /**\n * An array of languages supported by the game.\n */\n languages: Languages[];\n /**\n * An object containing the characters in the game.\n */\n characters: Characters;\n /**\n * An object that provides access to the game's storage system.\n */\n storage: Storage;\n /**\n * A function that returns a Renderer object used to display the game's content\n */\n renderer: (characters: RendererInit) => Renderer;\n /**\n * An optional property that specifies the initial screen to display when the game starts\n */\n initialScreen?: \"mainmenu\" | \"game\" | \"saves\" | \"settings\";\n /**\n * An object containing the translation functions used in the game\n */\n t9n: Inter;\n /**\n * Initial state value\n */\n state?: StateScheme;\n}\n\nconst novely = <Languages extends string, Characters extends Record<string, Character<Languages>>, Inter extends ReturnType<SetupT9N<Languages>>, StateScheme extends State>({ characters, storage, renderer: createRenderer, initialScreen = \"mainmenu\", t9n, languages, state: defaultState }: NovelyInit<Languages, Characters, Inter, StateScheme>) => {\n let story: Story;\n\n // @todo: find bug here\n const withStory = (s: Story) => {\n /**\n * Transforms `(ValidAction | ValidAction[])[]` to `ValidAction[]`\n */\n story = Object.fromEntries(Object.entries(s).map(([name, items]) => {\n const flat = (item: (ValidAction | ValidAction[])[]): ValidAction[] => {\n return item.flatMap((data) => {\n const type = data[0];\n\n /**\n * This is not just an action like `['name', ...arguments]`, but an array of actions\n */\n if (Array.isArray(type)) return flat(data as ValidAction[]);\n\n return [data as ValidAction];\n });\n };\n\n return [name, flat(items)];\n }));\n\n /**\n * When `initialScreen` is not a game, we can safely show it\n */\n if (initialScreen !== 'game') renderer.ui.showScreen(initialScreen);\n }\n\n const action = new Proxy({} as ActionProxyProvider<Characters>, {\n get(_, prop) {\n return (...props: Parameters<ActionProxyProvider<Record<string, Character>>[keyof ActionProxyProvider<Record<string, Character>>]>) => {\n return [prop, ...props];\n }\n }\n });\n\n function state(value: DeepPartial<StateScheme> | ((prev: StateScheme) => StateScheme)): void;\n function state(): StateScheme;\n function state(value?: DeepPartial<StateScheme> | ((prev: StateScheme) => StateScheme)): StateScheme | void {\n if (!value) return stack.value[1] as StateScheme | void;\n\n const prev = stack.value[1];\n const val = typeof value === 'function' ? value(prev as StateScheme) : deepmerge([prev, value]);\n\n stack.value[1] = val as StateScheme;\n }\n\n const createStack = (current: Save, stack = [current]) => {\n return {\n get value() {\n return stack.at(-1)!;\n },\n set value(value: Save) {\n stack[stack.length - 1] = value;\n },\n back() {\n if (stack.length > 1) stack.pop(), goingBack = true;\n },\n push(value: Save) {\n stack.push(value);\n },\n clear() {\n stack = [getDefaultSave(klona(defaultState))];\n }\n }\n }\n\n /**\n * 1) Novely rendered using the `initialData`, you can still start new game or `load` an empty one - this is scary, imagine losing your progress\n * 2) Actual stored data is loaded, language and etc is changed \n */\n const initialData: StorageData = {\n saves: [],\n meta: [getLanguage(languages)]\n };\n\n const $ = store(initialData);\n\n let initialDataLoaded = false;\n\n const onStorageDataChange = (value: StorageData) => {\n if (initialDataLoaded) storage.set(value);\n };\n\n const throttledOnStorageDataChange = throttle(onStorageDataChange, 120);\n\n $.subscribe(throttledOnStorageDataChange);\n\n storage.get().then(stored => {\n /**\n * Default `localStorageStorage` cannot determine preferred language, and returns empty array\n */\n stored.meta[0] ||= getLanguage(languages);\n\n /**\n * Now the next store updates will entail saving via storage.set\n */\n initialDataLoaded = true;\n\n $.update(() => stored);\n\n /**\n * When initialScreen is game, then we will load it, but after the data is loaded\n */\n if (initialScreen === 'game') restore();\n });\n\n const initial = getDefaultSave(klona(defaultState));\n const stack = createStack(initial);\n\n const save = (override = false, type: Save[2][1] = override ? 'auto' : 'manual') => {\n if (!initialDataLoaded) return;\n\n $.update(prev => {\n const date = stack.value[2][0];\n const isLatest = prev.saves.findIndex(value => value[2][0] === date) === prev.saves.length - 1;\n\n /**\n * Обновим дату и тип\n */\n stack.value[2][0] = Date.now();\n stack.value[2][1] = type;\n\n if (override) {\n /**\n * Перезапишем\n */\n if (isLatest) {\n /**\n * Сохранения хранятся в массиве. Нельзя перезаписать любое последнее\n * \n * Если перезаписывать старое сохранение, то они не будут идти в хронологическом порядке\n */\n prev.saves[prev.saves.length - 1] = stack.value;\n } else {\n prev.saves.push(stack.value);\n }\n } else {\n /**\n * Добавляем текущее сохранение\n */\n prev.saves.push(stack.value);\n }\n\n /**\n * Устанавливаем новое значение\n */\n return prev;\n });\n }\n\n const newGame = () => {\n if (!initialDataLoaded) return;\n\n const save = getDefaultSave(klona(defaultState));\n\n $.update(prev => {\n prev.saves.push(save), restore(save);\n\n return prev;\n });\n }\n\n /**\n * Устанавливает сохранение\n */\n const set = (save: Save) => {\n stack.value = save;\n\n return restore(save);\n }\n\n let restoring = false;\n let goingBack = false;\n\n /**\n * Визуально восстанавливает историю\n */\n const restore = async (save?: Save) => {\n if (!initialDataLoaded) return;\n\n let latest = save ? save : $.get().saves.at(-1);\n\n /**\n * Если нет сохранённой игры, то запустим ту, которая уже есть\n */\n if (!latest) {\n $.update(() => ({ saves: [initial], meta: [getLanguage(languages)] }));\n\n latest = klona(initial);\n }\n\n restoring = true, stack.value = latest;\n\n /**\n * Показать экран игры\n */\n renderer.ui.showScreen('game');\n\n match('clear', [goingBack]);\n\n /**\n * Текущий элемент в истории\n */\n let current: any = story;\n /**\n * Текущий элемент `[null, int]`\n */\n let index = 0;\n\n /**\n * Число элементов `[null, int]`\n */\n const max = stack.value[0].reduce((acc, [type, val]) => {\n if (isNull(type) && isNumber(val)) return acc + 1;\n\n return acc;\n }, 0);\n\n const queue = [] as [any, any][];\n\n for (const [type, val] of stack.value[0]) {\n if (type === null) {\n if (isString(val)) {\n current = current[val];\n } else if (isNumber(val)) {\n index++;\n\n /**\n * Запустим все экшены которые идут в `[null, int]` от `0` до `int`\n */\n for (let i = 0; i < val; i++) {\n const [action, ...meta] = current[i];\n\n /**\n * Экшены, для закрытия которых пользователь должен с ними взаимодействовать\n * Также в эту группу входят экшены, которые не должны быть вызваны при восстановлении\n */\n if (SKIPPED_DURING_RESTORE.has(action) || isUserRequiredAction(action, meta)) {\n if (index === max && i === val) {\n queue.push([action, meta]);\n } else {\n continue;\n }\n }\n\n queue.push([action, meta]);\n }\n\n current = current[val];\n }\n } else if (type === 'choice') {\n current = current[val as number + 1][1];\n } else if (type === 'condition') {\n current = current[2][val];\n }\n }\n\n const indexedQueue = queue.map((value, index) => value.concat(index) as [ValidAction[0], ValidAction[1], number]);\n\n for await (const [action, meta, i] of indexedQueue) {\n if (action === 'function' || action === 'custom') {\n /**\n * Если `callOnlyLatest` - `true`\n */\n if (action === 'custom' && (meta as GetActionParameters<'Custom'>)[0].callOnlyLatest) {\n /**\n * Вычислим `latest` или нет\n */\n const next = indexedQueue.slice(i + 1);\n const latest = !next.some(([_action, _meta]) => str(_meta[0]) === str(meta[0]));\n\n if (!latest) continue;\n }\n\n /**\n * Action может возвращать Promise. Нужно подожать его `resolve`\n */\n const result = match(action, meta);\n\n /**\n * Дождёмся окончания\n */\n if (result && 'then' in result) await result;\n } else {\n match(action as keyof ActionProxyProvider<Record<string, Character<string>>>, meta);\n }\n }\n\n restoring = false, goingBack = false, render();\n }\n\n const refer = () => {\n let current: any = story;\n\n for (const [type, val] of stack.value[0]) {\n if (type === null) {\n current = current[val];\n } else if (type === 'choice') {\n current = current[val as number + 1][1];\n } else if (type === 'condition') {\n current = current[2][val];\n }\n }\n\n return current;\n }\n\n const renderer = createRenderer({\n characters,\n set,\n restore,\n save,\n newGame,\n stack,\n languages,\n t: t9n.i,\n $\n });\n\n const match = matchAction({\n wait([time]) {\n /**\n * `restoring` может поменяться на `true` перед тем как запуститься `push` из `setTimeout`\n */\n if (!restoring) setTimeout(push, time);\n },\n showBackground([background]) {\n renderer.background(background);\n push()\n },\n playMusic([source]) {\n renderer.music(source, 'music').play();\n push()\n },\n stopMusic([source]) {\n renderer.music(source, 'music').stop();\n push()\n },\n showCharacter([character, emotion, className, style]) {\n const handle = renderer.character(character);\n\n handle.append(className, style);\n handle.withEmotion(emotion)();\n\n push()\n },\n hideCharacter([character, className, style, duration]) {\n const handle = renderer.character(character);\n\n handle.remove(className, style, duration)(push);\n },\n dialog([character, content, emotion]) {\n /**\n * Имя персонажа, с учетом выбранного языка\n */\n const name = (() => {\n const c = character, cs = characters;\n const lang = $.get().meta[0];\n\n return c ? c in cs ? typeof cs[c].name === 'string' ? cs[c].name as string : (cs[c].name as Record<string, string>)[lang] : c : '';\n })();\n\n renderer.dialog(unwrap(content), unwrap(name), character, emotion)(forward);\n },\n function([fn]) {\n const result = fn();\n\n if (!restoring) result ? result.then(push) : push();\n\n return result;\n },\n choice([question, ...choices]) {\n const isWithoutQuestion = Array.isArray(question);\n\n if (isWithoutQuestion) {\n /**\n * Первый элемент может быть как строкой, так и элементов выбора\n */\n choices.unshift(question as unknown as [ChoiceContent, ValidAction[], () => boolean]);\n /**\n * Значит, текст не требуется\n */\n question = '';\n }\n\n const unwrapped = choices.map(([content, action, visible]) => {\n return [unwrap(content), action, visible] as [string, ValidAction[], () => boolean];\n });\n\n renderer.choices(question, unwrapped)((selected) => {\n enmemory();\n\n /**\n * Если был вопрос, то `index` смещается на единицу назад, поэтому нужно добавить единицу\n */\n stack.value[0].push(['choice', isWithoutQuestion ? selected : selected + 1], [null, 0]), render();\n });\n },\n jump([scene]) {\n stack.value[0] = [[null, scene], [null, 0]];\n\n renderer.clear(false)(() => {\n if (!restoring) render();\n })\n },\n clear() {\n try {\n navigator.vibrate(0)\n } finally {\n renderer.clear(goingBack)(push);\n }\n },\n condition([condition]) {\n const value = condition();\n\n if (!restoring) stack.value[0].push(['condition', value], [null, 0]), render();\n },\n end() {\n /**\n * Save Current Game\n */\n save(false, 'auto');\n /**\n * Clear the Scene\n */\n match('clear', []);\n /**\n * Go to the main menu\n */\n renderer.ui.showScreen('mainmenu');\n },\n input([question, onInput, setup]) {\n renderer.input(question, onInput, setup)(forward);\n },\n custom([handler]) {\n const result = renderer.custom(handler, goingBack, () => {\n if (!restoring && handler.requireUserAction) enmemory();\n if (!restoring) push();\n });\n\n return result;\n },\n vibrate(pattern) {\n try {\n navigator.vibrate(pattern)\n } finally {\n push()\n }\n },\n next() {\n push();\n },\n animateCharacter([character, timeout, ...classes]) {\n const handler: CustomHandler = () => {\n const char = renderer.store.characters[character];\n\n /**\n * Character is not defined, maybe, `animateCharacter` was called before `showCharacter`\n */\n if (!char) return;\n\n const target = char.canvas;\n\n /**\n * Character is not found\n */\n if (!target) return;\n\n const classNames = classes.filter(className => !target.classList.contains(className));\n\n target.classList.add(...classNames);\n\n setTimeout(() => {\n target.classList.remove(...classNames);\n }, timeout);\n }\n\n handler.callOnlyLatest = true;\n\n return renderer.custom(handler, goingBack, () => { }), push();\n },\n text(text) {\n renderer.text(text.map(unwrap).join(' '), forward);\n },\n exit() {\n const path = stack.value[0];\n\n for (let i = path.length - 1; i > 0; i--) {\n if (path[i][0] !== 'choice' && path[i][0] !== 'condition') continue;\n\n stack.value[0] = path.slice(0, i);\n next();\n\n break;\n }\n\n render();\n }\n });\n\n const enmemory = () => {\n if (restoring) return;\n\n const current = klona(stack.value);\n\n current[0] = klona(stack.value[0]);\n\n current[2][0] = new Date().valueOf();\n current[2][1] = 'auto';\n\n stack.push(current);\n }\n\n const next = () => {\n const path = stack.value[0];\n /**\n * Последний элемент пути\n */\n const last = path[path.length - 1]!;\n\n /**\n * Если он вида `[null, int]` - увеличивает `int`\n */\n if (isNull(last[0]) && isNumber(last[1])) {\n last[1] = last[1] + 1;\n return;\n }\n\n /**\n * Иначе добавляет новое `[null int]`\n */\n path.push([null, 0]);\n }\n\n const render = () => {\n const referred = refer();\n\n if (!(referred && Array.isArray(referred))) return;\n\n const [action, ...props] = referred;\n\n match(action, props);\n }\n\n const push = () => {\n if (!restoring) next(), render();\n }\n\n const forward = () => {\n enmemory();\n push();\n }\n\n const unwrap = (content: DialogContent | ChoiceContent) => {\n const lang = $.get().meta[0];\n const data = state();\n\n return replaceT9N(typeof content === 'function' ? content(lang, data) : content, data);\n }\n\n return {\n withStory,\n action,\n render,\n state,\n t: t9n.t as Inter['t'],\n }\n}\n\nexport { novely }\n","const SKIPPED_DURING_RESTORE = new Set([\n 'dialog',\n 'input',\n 'vibrate',\n 'text'\n] as const);\n\nexport { SKIPPED_DURING_RESTORE }","import type { StorageData } from './types'\n\ninterface LocalStorageStorageSettings {\n key: string\n}\n\ninterface Storage {\n get: () => Promise<StorageData>;\n set: (data: StorageData) => Promise<void>;\n}\n\nconst localStorageStorage = (options: LocalStorageStorageSettings): Storage => {\n return {\n async get() {\n const value = localStorage.getItem(options.key);\n\n return value ? JSON.parse(value) : { saves: [], meta: [] };\n },\n async set(data) {\n localStorage.setItem(options.key, JSON.stringify(data));\n }\n }\n}\n\nexport type { Storage }\nexport { localStorageStorage }"],"mappings":"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","names":["stack","save","action","index","next","latest"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@novely/core",
3
3
  "description": "Novely - powerful visual novel engine for creating interactive stories and games with branching narratives and rich multimedia content.",
4
- "version": "0.0.0-beta.2",
4
+ "version": "0.0.0-beta.4",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "types": "./dist/index.d.ts",