@novely/core 0.54.0 → 0.55.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.mts +1319 -0
- package/dist/index.mjs +2623 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +12 -12
- package/dist/index.d.ts +0 -1260
- package/dist/index.js +0 -2771
- package/dist/index.js.map +0 -1
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,1319 @@
|
|
|
1
|
+
//#region src/store.d.ts
|
|
2
|
+
type Stored<T> = {
|
|
3
|
+
subscribe: (cb: (value: T) => void) => () => void;
|
|
4
|
+
update: (fn: (prev: T) => T) => void;
|
|
5
|
+
set: (val: T) => void;
|
|
6
|
+
get: () => T;
|
|
7
|
+
};
|
|
8
|
+
type Derived<T> = {
|
|
9
|
+
subscribe: (cb: (value: T) => void) => () => void;
|
|
10
|
+
get: () => T;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/translations.d.ts
|
|
14
|
+
declare const RU: {
|
|
15
|
+
NewGame: string;
|
|
16
|
+
HomeScreen: string;
|
|
17
|
+
ToTheGame: string;
|
|
18
|
+
Language: string;
|
|
19
|
+
NoSaves: string;
|
|
20
|
+
LoadSave: string;
|
|
21
|
+
Saves: string;
|
|
22
|
+
Settings: string;
|
|
23
|
+
Sumbit: string;
|
|
24
|
+
GoBack: string;
|
|
25
|
+
DoSave: string;
|
|
26
|
+
Auto: string;
|
|
27
|
+
Stop: string;
|
|
28
|
+
Exit: string;
|
|
29
|
+
Automatic: string;
|
|
30
|
+
Manual: string;
|
|
31
|
+
Remove: string;
|
|
32
|
+
LoadASaveFrom: string;
|
|
33
|
+
DeleteASaveFrom: string;
|
|
34
|
+
TextSpeed: string;
|
|
35
|
+
TextSpeedSlow: string;
|
|
36
|
+
TextSpeedMedium: string;
|
|
37
|
+
TextSpeedFast: string;
|
|
38
|
+
TextSpeedAuto: string;
|
|
39
|
+
CompleteText: string;
|
|
40
|
+
GoForward: string;
|
|
41
|
+
ExitDialogWarning: string;
|
|
42
|
+
ExitDialogExit: string;
|
|
43
|
+
ExitDialogBack: string;
|
|
44
|
+
OpenMenu: string;
|
|
45
|
+
CloseMenu: string;
|
|
46
|
+
MusicVolume: string;
|
|
47
|
+
SoundVolume: string;
|
|
48
|
+
VoiceVolume: string;
|
|
49
|
+
Close: string;
|
|
50
|
+
DialogOverview: string;
|
|
51
|
+
};
|
|
52
|
+
type BaseTranslationStrings = keyof typeof RU;
|
|
53
|
+
declare const EN: Record<BaseTranslationStrings, string>;
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/renderer.d.ts
|
|
56
|
+
type CharacterHandle = {
|
|
57
|
+
emotion: (emotion: string, render: boolean) => void;
|
|
58
|
+
append: (className?: string, style?: string, restoring?: boolean) => void;
|
|
59
|
+
remove: (className?: string, style?: string, duration?: number, restoring?: boolean) => Promise<void>;
|
|
60
|
+
animate: (classes: string[]) => void;
|
|
61
|
+
emotions: Record<string, HTMLImageElement[]>;
|
|
62
|
+
};
|
|
63
|
+
type CustomActionHandle = {
|
|
64
|
+
/**
|
|
65
|
+
* Function to remove custom action from screen (and from your state if any completely)
|
|
66
|
+
*/
|
|
67
|
+
remove: () => void;
|
|
68
|
+
/**
|
|
69
|
+
* Function that will give action root (element which you should add to the screen because custom actions rendered into that element)
|
|
70
|
+
*/
|
|
71
|
+
setMountElement: (mountElement: null | HTMLDivElement) => void;
|
|
72
|
+
};
|
|
73
|
+
type AudioHandle = {
|
|
74
|
+
stop: () => void;
|
|
75
|
+
pause: () => void;
|
|
76
|
+
play: (loop: boolean) => void;
|
|
77
|
+
};
|
|
78
|
+
type Context = {
|
|
79
|
+
id: string;
|
|
80
|
+
get root(): HTMLElement;
|
|
81
|
+
set root(value: HTMLElement);
|
|
82
|
+
character: (character: string) => CharacterHandle;
|
|
83
|
+
background: (background: Record<string, string>) => void;
|
|
84
|
+
dialog: (content: string, name: string, character: string | undefined, emotion: string | undefined, resolve: () => void) => void;
|
|
85
|
+
choices: (question: string, choices: [name: string, active: Stored<boolean>, visible: Stored<boolean>, onselect: () => void, image: string][], resolve: (selected: number) => void) => void;
|
|
86
|
+
input: (question: string, onInput: (meta: ActionInputOnInputMeta<Lang, State>) => void, setup: ActionInputSetup, resolve: () => void) => void;
|
|
87
|
+
clear: (keep: Set<keyof DefaultActionProxy>, keepCharacters: Set<string>, keepAudio: {
|
|
88
|
+
music: Set<string>;
|
|
89
|
+
sounds: Set<string>;
|
|
90
|
+
}, resolve: () => void) => void;
|
|
91
|
+
custom: (fn: CustomHandler<Lang, State>) => CustomActionHandle;
|
|
92
|
+
/**
|
|
93
|
+
* Clears all mentioned actions except for preserved one
|
|
94
|
+
* @param preserve Action that should not be cleared
|
|
95
|
+
*/
|
|
96
|
+
clearBlockingActions: (preserve: 'dialog' | 'choice' | 'input' | 'text' | undefined) => void;
|
|
97
|
+
text: (str: string, resolve: () => void) => void;
|
|
98
|
+
vibrate: (pattern: VibratePattern) => void;
|
|
99
|
+
audio: {
|
|
100
|
+
voice: (source: string, paused: Derived<boolean>) => void;
|
|
101
|
+
voiceStop: () => void;
|
|
102
|
+
music: (source: string, paused: Derived<boolean>, method: 'music' | 'sound') => AudioHandle;
|
|
103
|
+
/**
|
|
104
|
+
* Stop all sounds
|
|
105
|
+
*/
|
|
106
|
+
clear: () => void;
|
|
107
|
+
/**
|
|
108
|
+
* Destroy
|
|
109
|
+
*/
|
|
110
|
+
destroy: () => void;
|
|
111
|
+
/**
|
|
112
|
+
* Initialize audio service, attach events, etc
|
|
113
|
+
*/
|
|
114
|
+
start: () => void;
|
|
115
|
+
};
|
|
116
|
+
loading: (shown: boolean) => void;
|
|
117
|
+
meta: {
|
|
118
|
+
get restoring(): boolean;
|
|
119
|
+
set restoring(value: boolean);
|
|
120
|
+
get preview(): boolean;
|
|
121
|
+
set preview(value: boolean);
|
|
122
|
+
get goingBack(): boolean;
|
|
123
|
+
set goingBack(value: boolean);
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
type Renderer = {
|
|
127
|
+
misc: {
|
|
128
|
+
/**
|
|
129
|
+
* Function to preload image sync
|
|
130
|
+
* @param image Image URL
|
|
131
|
+
* @returns Image URL
|
|
132
|
+
*/
|
|
133
|
+
preloadImage: <T extends string>(image: T) => T;
|
|
134
|
+
/**
|
|
135
|
+
* Function to preload image async
|
|
136
|
+
* @param image Image URL
|
|
137
|
+
* @returns Promise
|
|
138
|
+
*/
|
|
139
|
+
preloadImageBlocking: (image: string) => Promise<void>;
|
|
140
|
+
/**
|
|
141
|
+
* Function to preload audio
|
|
142
|
+
* @param source <url> pointing to the audio
|
|
143
|
+
*/
|
|
144
|
+
preloadAudioBlocking: (source: string) => Promise<void>;
|
|
145
|
+
};
|
|
146
|
+
ui: {
|
|
147
|
+
/**
|
|
148
|
+
* Shows the screen
|
|
149
|
+
*/
|
|
150
|
+
showScreen(name: NovelyScreen): void;
|
|
151
|
+
/**
|
|
152
|
+
* Returns current screen
|
|
153
|
+
*/
|
|
154
|
+
getScreen(): NovelyScreen | (string & Record<never, never>);
|
|
155
|
+
/**
|
|
156
|
+
* Shows loading
|
|
157
|
+
*
|
|
158
|
+
* Unline `showScreen('loading')` does not change screen
|
|
159
|
+
*/
|
|
160
|
+
showLoading(): void;
|
|
161
|
+
/**
|
|
162
|
+
* Hides loading
|
|
163
|
+
*/
|
|
164
|
+
hideLoading(): void;
|
|
165
|
+
/**
|
|
166
|
+
* Shows prompt to exit
|
|
167
|
+
*/
|
|
168
|
+
showExitPrompt(): void;
|
|
169
|
+
/**
|
|
170
|
+
* Render the game
|
|
171
|
+
*/
|
|
172
|
+
start(): {
|
|
173
|
+
/**
|
|
174
|
+
* Unmount
|
|
175
|
+
*/
|
|
176
|
+
unmount(): void;
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
actions: Record<string, (...args: any[]) => ValidAction>;
|
|
180
|
+
getContext: (context: string) => Context;
|
|
181
|
+
removeContext: (context: string) => void;
|
|
182
|
+
};
|
|
183
|
+
type RendererInitPreviewReturn = {
|
|
184
|
+
/**
|
|
185
|
+
* Assets that was used in game preview
|
|
186
|
+
*/
|
|
187
|
+
assets: string[];
|
|
188
|
+
};
|
|
189
|
+
type RendererInit<$Language extends Lang, $Characters extends Record<string, Character<$Language>>> = {
|
|
190
|
+
characters: CharactersData<$Characters>;
|
|
191
|
+
characterAssetSizes: CharacterAssetSizes<$Characters>;
|
|
192
|
+
set: (save: Save<State>) => Promise<void>;
|
|
193
|
+
restore: (save?: Save<State>) => Promise<void>;
|
|
194
|
+
save: (type: Save<State>[2][1]) => void;
|
|
195
|
+
newGame: () => void;
|
|
196
|
+
exit: (force?: boolean) => void;
|
|
197
|
+
back: () => Promise<void>;
|
|
198
|
+
languages: $Language[];
|
|
199
|
+
/**
|
|
200
|
+
* Translation function
|
|
201
|
+
*/
|
|
202
|
+
t: (key: BaseTranslationStrings, lang: Lang) => string;
|
|
203
|
+
/**
|
|
204
|
+
* Store that tracks data updates
|
|
205
|
+
*/
|
|
206
|
+
storageData: Stored<StorageData<Lang, Data>>;
|
|
207
|
+
/**
|
|
208
|
+
* Store that used to communicate between renderer and core
|
|
209
|
+
*/
|
|
210
|
+
coreData: Stored<CoreData>;
|
|
211
|
+
/**
|
|
212
|
+
* There is different context, and the main one which is used for game
|
|
213
|
+
*/
|
|
214
|
+
mainContextKey: string;
|
|
215
|
+
preview: (save: Save<State>, name: string) => Promise<RendererInitPreviewReturn>;
|
|
216
|
+
removeContext: (name: string) => void;
|
|
217
|
+
getStateFunction: (context: string) => StateFunction<State>;
|
|
218
|
+
clearCustomActionsAtContext: (ctx: Context) => void;
|
|
219
|
+
getLanguageDisplayName: (lang: Lang) => string;
|
|
220
|
+
getCharacterColor: (character: string) => string;
|
|
221
|
+
getCharacterAssets: (character: string, emotion: string) => string[];
|
|
222
|
+
getDialogOverview: () => Promise<DialogOverview>;
|
|
223
|
+
getResourseType: (url: string) => Promise<'image' | 'audio' | 'other'>;
|
|
224
|
+
setLanguage: (language: string) => void;
|
|
225
|
+
};
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/storage.d.ts
|
|
228
|
+
type StorageAdapter = {
|
|
229
|
+
get: () => Promise<StorageData>;
|
|
230
|
+
set: (data: StorageData) => Promise<void>;
|
|
231
|
+
};
|
|
232
|
+
type StorageAdapterLocalOptions = {
|
|
233
|
+
key: string;
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* Stores data in localStorage
|
|
237
|
+
*/
|
|
238
|
+
declare const storageAdapterLocal: ({
|
|
239
|
+
key
|
|
240
|
+
}: StorageAdapterLocalOptions) => StorageAdapter;
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region src/translation.d.ts
|
|
243
|
+
type PluralType = Intl.LDMLPluralRule;
|
|
244
|
+
type Pluralization = Partial<Record<PluralType, string>>;
|
|
245
|
+
type AllowedContent = string | ((state: State | Data) => string | string[]) | string[] | (string | ((state: State | Data) => string | string[]))[];
|
|
246
|
+
type TranslationActions = Partial<Record<string, (str: string) => string>>;
|
|
247
|
+
/**
|
|
248
|
+
* Turns any allowed content into string
|
|
249
|
+
* @param c Content
|
|
250
|
+
*/
|
|
251
|
+
//#endregion
|
|
252
|
+
//#region src/utilities/internationalization.d.ts
|
|
253
|
+
declare const getLanguage$1: (languages: string[]) => string;
|
|
254
|
+
//#endregion
|
|
255
|
+
//#region src/types.d.ts
|
|
256
|
+
type NovelyAsset = {
|
|
257
|
+
readonly source: string;
|
|
258
|
+
readonly id: string;
|
|
259
|
+
readonly type: 'audio' | 'image';
|
|
260
|
+
};
|
|
261
|
+
type Thenable<T> = T | Promise<T>;
|
|
262
|
+
type PathItem = [null, number] | ['jump', string] | ['choice', number] | ['choice:exit'] | ['condition', string] | ['condition:exit'] | ['exit'] | ['block', string] | ['block:exit'];
|
|
263
|
+
type Path = PathItem[];
|
|
264
|
+
type State = Record<string, any>;
|
|
265
|
+
type Data = Record<string, any>;
|
|
266
|
+
type SaveDate = number;
|
|
267
|
+
type SaveType = 'manual' | 'auto';
|
|
268
|
+
type SaveMeta = [date: SaveDate, type: SaveType];
|
|
269
|
+
type Save<S extends State = State> = [path: Path, state: S, meta: SaveMeta, state_snapshots: S[]];
|
|
270
|
+
type Lang = string;
|
|
271
|
+
type TypewriterSpeed = 'Slow' | 'Medium' | 'Fast' | 'Auto';
|
|
272
|
+
type SoundVolume = number;
|
|
273
|
+
type StorageMeta<L extends Lang = string> = [lang: L, typewriter_speed: TypewriterSpeed, music_volume: SoundVolume, sound_volume: SoundVolume, voice_volume: SoundVolume];
|
|
274
|
+
type Migration = (save: unknown) => unknown;
|
|
275
|
+
type StorageData<L extends Lang = string, D extends Data = Data> = {
|
|
276
|
+
saves: Save[];
|
|
277
|
+
data: D;
|
|
278
|
+
meta: StorageMeta<L>;
|
|
279
|
+
};
|
|
280
|
+
type Stack = {
|
|
281
|
+
value: Save;
|
|
282
|
+
back(): void;
|
|
283
|
+
push(value: Save): void;
|
|
284
|
+
clear(): void;
|
|
285
|
+
};
|
|
286
|
+
type NovelyScreen = 'mainmenu' | 'game' | 'saves' | 'settings';
|
|
287
|
+
/**
|
|
288
|
+
* @see https://pendletonjones.com/deep-partial
|
|
289
|
+
*/
|
|
290
|
+
type DeepPartial<T> = unknown extends T ? T : T extends object ? { [P in keyof T]?: T[P] extends Array<infer U> ? Array<DeepPartial<U>> : T[P] extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : DeepPartial<T[P]> } : T;
|
|
291
|
+
/**
|
|
292
|
+
*
|
|
293
|
+
*/
|
|
294
|
+
type Assign<A extends object, B extends object> = Pick<A, Exclude<keyof A, keyof B>> & B;
|
|
295
|
+
type NonEmptyRecord<T extends Record<PropertyKey, unknown>> = keyof T extends never ? never : T;
|
|
296
|
+
type CoreData = {
|
|
297
|
+
dataLoaded: boolean;
|
|
298
|
+
paused: boolean;
|
|
299
|
+
focused: boolean;
|
|
300
|
+
};
|
|
301
|
+
type StackHolder = Save[] & {
|
|
302
|
+
previous: Save | undefined;
|
|
303
|
+
};
|
|
304
|
+
type TranslationDescription = {
|
|
305
|
+
internal: Record<BaseTranslationStrings, string>;
|
|
306
|
+
/**
|
|
307
|
+
* IETF BCP 47 language tag
|
|
308
|
+
*/
|
|
309
|
+
tag?: string;
|
|
310
|
+
/**
|
|
311
|
+
* Custom name
|
|
312
|
+
*/
|
|
313
|
+
nameOverride?: string;
|
|
314
|
+
plural?: Record<string, Pluralization>;
|
|
315
|
+
actions?: TranslationActions;
|
|
316
|
+
};
|
|
317
|
+
type DefaultEmotions<$Characters extends Record<string, Character<Lang>>> = { [Character in keyof $Characters]?: keyof $Characters[Character]['emotions'] & string };
|
|
318
|
+
type CharacterAssetSizes<$Characters extends Record<string, Character<Lang>>> = { [Character in keyof $Characters]?: {
|
|
319
|
+
width: number;
|
|
320
|
+
height: number;
|
|
321
|
+
} };
|
|
322
|
+
type CharactersData<$Characters extends Record<string, Character<Lang>>> = { [Character in keyof $Characters]: {
|
|
323
|
+
name: $Characters[Character]['name'];
|
|
324
|
+
emotions: Array<keyof $Characters[Character]['emotions']>;
|
|
325
|
+
} };
|
|
326
|
+
type AssetsPreloading = 'lazy' | 'automatic';
|
|
327
|
+
type CloneFN = <T>(value: T) => T;
|
|
328
|
+
type StoryOptionsStatic = {
|
|
329
|
+
/**
|
|
330
|
+
* Static mode means that story is static
|
|
331
|
+
*/
|
|
332
|
+
mode: 'static';
|
|
333
|
+
};
|
|
334
|
+
type StoryOptionsDynamic = {
|
|
335
|
+
/**
|
|
336
|
+
* Dynamic mode means that story parts can be loaded dynamically
|
|
337
|
+
*/
|
|
338
|
+
mode: 'dynamic';
|
|
339
|
+
/**
|
|
340
|
+
* Number of saves to preload story for
|
|
341
|
+
* @default 4
|
|
342
|
+
*/
|
|
343
|
+
preloadSaves?: number;
|
|
344
|
+
/**
|
|
345
|
+
* Function to dynamically load story parts.
|
|
346
|
+
*
|
|
347
|
+
* When engine find's unknown scene it will run this function.
|
|
348
|
+
* @example
|
|
349
|
+
* ```
|
|
350
|
+
* const load = async (scene: string): Promise<Story> => {
|
|
351
|
+
* if (['part_2__act_1', 'part_2__act_2', 'part_2__act_3'].includes(scene)) {
|
|
352
|
+
* const { getStory } = await import('./part-2.ts');
|
|
353
|
+
*
|
|
354
|
+
* return getStory(engine.action);
|
|
355
|
+
* }
|
|
356
|
+
*
|
|
357
|
+
* if (scene === 'end') {
|
|
358
|
+
* return {
|
|
359
|
+
* 'end': [
|
|
360
|
+
* engine.action.text('The End')
|
|
361
|
+
* ]
|
|
362
|
+
* }
|
|
363
|
+
* }
|
|
364
|
+
*
|
|
365
|
+
* throw new Error(`Unknown scene: ${scene}`);
|
|
366
|
+
* }
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
load: (scene: string) => Promise<Story>;
|
|
370
|
+
};
|
|
371
|
+
type StoryOptions = StoryOptionsStatic | StoryOptionsDynamic;
|
|
372
|
+
type OnLanguageChange<$Lang extends Lang> = (language: $Lang) => void;
|
|
373
|
+
interface NovelyInit<$Language extends Lang, $Characters extends Record<string, Character<NoInfer<$Language>>>, $State extends State, $Data extends Data, $Actions extends Record<string, (...args: any[]) => ValidAction>> {
|
|
374
|
+
/**
|
|
375
|
+
* An object containing the characters in the game.
|
|
376
|
+
* @example
|
|
377
|
+
* ```ts
|
|
378
|
+
* const engine = novely({
|
|
379
|
+
* characters: {
|
|
380
|
+
* // Character ID
|
|
381
|
+
* Alexei: {
|
|
382
|
+
* name: 'Alexei',
|
|
383
|
+
* color: '#f60002',
|
|
384
|
+
* emotions: {
|
|
385
|
+
* hopeful: './hopeful.png'
|
|
386
|
+
* }
|
|
387
|
+
* }
|
|
388
|
+
* }
|
|
389
|
+
* })
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
characters: $Characters;
|
|
393
|
+
/**
|
|
394
|
+
* Define default emotions for characters
|
|
395
|
+
* @example
|
|
396
|
+
* ```ts
|
|
397
|
+
* const engine = novely({
|
|
398
|
+
* characters: {
|
|
399
|
+
* Yuki: {
|
|
400
|
+
* name: 'Yuki',
|
|
401
|
+
* color: '#f595f6',
|
|
402
|
+
* emotions: {
|
|
403
|
+
* normal: './normal.png'
|
|
404
|
+
* }
|
|
405
|
+
* }
|
|
406
|
+
* },
|
|
407
|
+
* defaultEmotions: {
|
|
408
|
+
* Yuki: 'normal'
|
|
409
|
+
* }
|
|
410
|
+
* });
|
|
411
|
+
*
|
|
412
|
+
* engine.script({
|
|
413
|
+
* start: [
|
|
414
|
+
* // Without emotion!
|
|
415
|
+
* engine.action.showCharacter('Yuki')
|
|
416
|
+
* ]
|
|
417
|
+
* })
|
|
418
|
+
* ```
|
|
419
|
+
*/
|
|
420
|
+
defaultEmotions?: DefaultEmotions<NoInfer<$Characters>>;
|
|
421
|
+
/**
|
|
422
|
+
* Character asset sizes. We need width-height pair to render character, but we get it only after the assets are loaded. However, using that option we can use width-height before load.
|
|
423
|
+
* @example
|
|
424
|
+
* ```
|
|
425
|
+
* import peter_the_great from './assets/peter_the_great.png?width=800&height=1200';
|
|
426
|
+
*
|
|
427
|
+
* const engine = novely({
|
|
428
|
+
* characters: {
|
|
429
|
+
* Peter: {
|
|
430
|
+
* name: 'Peter',
|
|
431
|
+
* color: '#c04931',
|
|
432
|
+
* emotions: {
|
|
433
|
+
* normal: peter_the_great
|
|
434
|
+
* }
|
|
435
|
+
* }
|
|
436
|
+
* },
|
|
437
|
+
* characterAssetSizes: {
|
|
438
|
+
* Peter: {
|
|
439
|
+
* width: 800,
|
|
440
|
+
* height: 1200
|
|
441
|
+
* }
|
|
442
|
+
* }
|
|
443
|
+
* })
|
|
444
|
+
* ```
|
|
445
|
+
*/
|
|
446
|
+
characterAssetSizes?: CharacterAssetSizes<NoInfer<$Characters>>;
|
|
447
|
+
/**
|
|
448
|
+
* An object that provides access to the game's storage system.
|
|
449
|
+
* @default storageAdapterLocal({ key: 'novely-game-storage' })
|
|
450
|
+
*/
|
|
451
|
+
storage?: StorageAdapter;
|
|
452
|
+
/**
|
|
453
|
+
* Delay loading data until Promise is resolved
|
|
454
|
+
*/
|
|
455
|
+
storageDelay?: Promise<void>;
|
|
456
|
+
/**
|
|
457
|
+
* A function that returns a Renderer object used to display the game's content
|
|
458
|
+
*/
|
|
459
|
+
renderer: (initializationData: RendererInit<NoInfer<$Language>, NoInfer<$Characters>>) => Renderer & {
|
|
460
|
+
actions: $Actions;
|
|
461
|
+
};
|
|
462
|
+
/**
|
|
463
|
+
* An optional property that specifies the initial screen to display when the game starts
|
|
464
|
+
*/
|
|
465
|
+
initialScreen?: NovelyScreen;
|
|
466
|
+
/**
|
|
467
|
+
* An object containing the translation functions used in the game
|
|
468
|
+
* @see https://novely.pages.dev/guide/translation.html Docs
|
|
469
|
+
* @example
|
|
470
|
+
* ```ts
|
|
471
|
+
* import { novely, EN } from 'novely';
|
|
472
|
+
*
|
|
473
|
+
* const engine = novely({
|
|
474
|
+
* translation: {
|
|
475
|
+
* internal: EN,
|
|
476
|
+
* // Optional IETF BCP 47 language tag
|
|
477
|
+
* tag: 'en-US',
|
|
478
|
+
* plural: {
|
|
479
|
+
*
|
|
480
|
+
* },
|
|
481
|
+
* actions: {
|
|
482
|
+
*
|
|
483
|
+
* }
|
|
484
|
+
* }
|
|
485
|
+
* })
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
translation: Record<$Language, TranslationDescription>;
|
|
489
|
+
/**
|
|
490
|
+
* Initial state value
|
|
491
|
+
*
|
|
492
|
+
* State is a local value bound to one save
|
|
493
|
+
*/
|
|
494
|
+
state?: $State;
|
|
495
|
+
/**
|
|
496
|
+
* Initial data value
|
|
497
|
+
*
|
|
498
|
+
* Data is a global value shared between saves
|
|
499
|
+
*/
|
|
500
|
+
data?: $Data;
|
|
501
|
+
/**
|
|
502
|
+
* Enable autosaves or disable
|
|
503
|
+
* @default true
|
|
504
|
+
*/
|
|
505
|
+
autosaves?: boolean;
|
|
506
|
+
/**
|
|
507
|
+
* Migration from old saves to newer
|
|
508
|
+
*/
|
|
509
|
+
migrations?: Migration[];
|
|
510
|
+
/**
|
|
511
|
+
* For saves Novely uses `throttle` function. This might be needed if you want to control frequency of saves to the storage
|
|
512
|
+
* @default 850
|
|
513
|
+
*/
|
|
514
|
+
throttleTimeout?: number;
|
|
515
|
+
/**
|
|
516
|
+
* Limits how many assets can be downloaded parallelly
|
|
517
|
+
* @default 15
|
|
518
|
+
*/
|
|
519
|
+
parallelAssetsDownloadLimit?: number;
|
|
520
|
+
/**
|
|
521
|
+
* Custom language detector
|
|
522
|
+
* @param languages Supported languages
|
|
523
|
+
* @param original Original function that novely, could be used as fallback
|
|
524
|
+
* @example
|
|
525
|
+
* ```ts
|
|
526
|
+
* const engine = novely({
|
|
527
|
+
* getLanguage(languages, original) {
|
|
528
|
+
* if (!sdk) return original(languages);
|
|
529
|
+
*
|
|
530
|
+
* return sdk.environment.i18n.lang // i.e. custom language from some sdk
|
|
531
|
+
* }
|
|
532
|
+
* })
|
|
533
|
+
* ```
|
|
534
|
+
*/
|
|
535
|
+
getLanguage?: (languages: NoInfer<$Language>[], original: typeof getLanguage$1) => $Language | (string & Record<never, never>);
|
|
536
|
+
/**
|
|
537
|
+
* Ignores saved language, and uses `getLanguage` to get it on every engine start
|
|
538
|
+
* @default false
|
|
539
|
+
*/
|
|
540
|
+
overrideLanguage?: boolean;
|
|
541
|
+
/**
|
|
542
|
+
* Show a prompt before exiting a game
|
|
543
|
+
* @default true
|
|
544
|
+
*/
|
|
545
|
+
askBeforeExit?: boolean;
|
|
546
|
+
/**
|
|
547
|
+
* "automatic" will try to preload assets when possible
|
|
548
|
+
*
|
|
549
|
+
* "lazy" will load assets only when they are shown
|
|
550
|
+
*
|
|
551
|
+
* @default "automatic"
|
|
552
|
+
*/
|
|
553
|
+
preloadAssets?: AssetsPreloading;
|
|
554
|
+
/**
|
|
555
|
+
* Fetching function
|
|
556
|
+
*/
|
|
557
|
+
fetch?: typeof fetch;
|
|
558
|
+
/**
|
|
559
|
+
* Function for clonning operations
|
|
560
|
+
*/
|
|
561
|
+
cloneFunction?: CloneFN;
|
|
562
|
+
/**
|
|
563
|
+
* When page is going to be unloaded will call `storage.set` method
|
|
564
|
+
* If 'prod' is passed enable only in production env.
|
|
565
|
+
* @default true
|
|
566
|
+
*/
|
|
567
|
+
saveOnUnload?: boolean | 'prod';
|
|
568
|
+
/**
|
|
569
|
+
* The key that signifies the start of the game. It is not recommended to override this parameter.
|
|
570
|
+
*
|
|
571
|
+
* @default 'start'
|
|
572
|
+
* @example
|
|
573
|
+
* ```ts
|
|
574
|
+
* const engine = novely({
|
|
575
|
+
* ...,
|
|
576
|
+
* startKey: 'PART_1'
|
|
577
|
+
* })
|
|
578
|
+
*
|
|
579
|
+
* engine.script({
|
|
580
|
+
* // now game will start from here
|
|
581
|
+
* PART_1: [
|
|
582
|
+
*
|
|
583
|
+
* ]
|
|
584
|
+
* })
|
|
585
|
+
* ```
|
|
586
|
+
*/
|
|
587
|
+
startKey?: 'start' | (string & Record<never, never>);
|
|
588
|
+
/**
|
|
589
|
+
* Typewriter speed set by default
|
|
590
|
+
*/
|
|
591
|
+
defaultTypewriterSpeed?: TypewriterSpeed;
|
|
592
|
+
/**
|
|
593
|
+
* Options to control story loading behaviour
|
|
594
|
+
*/
|
|
595
|
+
storyOptions?: StoryOptions;
|
|
596
|
+
/**
|
|
597
|
+
* Will be called ONLY when language was changed by player
|
|
598
|
+
*/
|
|
599
|
+
onLanguageChange?: OnLanguageChange<NoInfer<$Language>>;
|
|
600
|
+
}
|
|
601
|
+
type StateFunction<S extends State> = {
|
|
602
|
+
(value: DeepPartial<S> | ((prev: S) => S)): void;
|
|
603
|
+
(): S;
|
|
604
|
+
};
|
|
605
|
+
type EngineTypes<$Lang extends Lang = Lang, $State extends State = State, $Data extends Data = Data, $Characters extends Record<string, Character<$Lang>> = Record<string, Character<$Lang>>> = {
|
|
606
|
+
readonly l: $Lang;
|
|
607
|
+
readonly s: $State;
|
|
608
|
+
readonly d: $Data;
|
|
609
|
+
readonly c: $Characters;
|
|
610
|
+
};
|
|
611
|
+
type DialogOverviewEntry = {
|
|
612
|
+
/**
|
|
613
|
+
* Link to character voice
|
|
614
|
+
*/
|
|
615
|
+
voice: string | undefined;
|
|
616
|
+
/**
|
|
617
|
+
* Character name
|
|
618
|
+
*/
|
|
619
|
+
name: string;
|
|
620
|
+
/**
|
|
621
|
+
* Text that character says
|
|
622
|
+
*/
|
|
623
|
+
text: string;
|
|
624
|
+
};
|
|
625
|
+
type DialogOverview = DialogOverviewEntry[];
|
|
626
|
+
//#endregion
|
|
627
|
+
//#region src/character.d.ts
|
|
628
|
+
type Name<$Lang extends Lang> = string | Record<$Lang, string>;
|
|
629
|
+
type Emotions<Emotion extends string = string> = Record<Emotion, string | NovelyAsset | (string | NovelyAsset)[]>;
|
|
630
|
+
type Character<$Lang extends Lang = string> = {
|
|
631
|
+
name: Name<$Lang>;
|
|
632
|
+
color: string;
|
|
633
|
+
emotions: Emotions;
|
|
634
|
+
};
|
|
635
|
+
//#endregion
|
|
636
|
+
//#region src/ticker.d.ts
|
|
637
|
+
type TickHandler = (ticker: Ticker) => void;
|
|
638
|
+
declare class Ticker {
|
|
639
|
+
listeners: Set<TickHandler>;
|
|
640
|
+
running: boolean;
|
|
641
|
+
private _factory;
|
|
642
|
+
constructor(factory: TickerFactory);
|
|
643
|
+
get deltaTime(): number;
|
|
644
|
+
get lastTime(): number;
|
|
645
|
+
add(cb: (ticker: Ticker) => void): () => void;
|
|
646
|
+
remove(cb: (ticker: Ticker) => void): void;
|
|
647
|
+
start: () => void;
|
|
648
|
+
stop: () => void;
|
|
649
|
+
detach: () => void;
|
|
650
|
+
}
|
|
651
|
+
declare class TickerFactory {
|
|
652
|
+
private _children;
|
|
653
|
+
private _raf;
|
|
654
|
+
private _running;
|
|
655
|
+
private _unsubscribe;
|
|
656
|
+
deltaTime: number;
|
|
657
|
+
lastTime: number;
|
|
658
|
+
constructor(paused: Derived<boolean>);
|
|
659
|
+
start(): void;
|
|
660
|
+
stop(): void;
|
|
661
|
+
fork(): Ticker;
|
|
662
|
+
check(positive: boolean): void;
|
|
663
|
+
destroy(): void;
|
|
664
|
+
detach(ticker: Ticker): void;
|
|
665
|
+
private update;
|
|
666
|
+
}
|
|
667
|
+
//#endregion
|
|
668
|
+
//#region src/action.d.ts
|
|
669
|
+
type ValidAction = ['choice', string | undefined, ...[string, unknown[], (() => boolean)?, (() => boolean)?, string?][]] | ['clear', Set<keyof DefaultActionProxy>?, Set<string>?, {
|
|
670
|
+
music: Set<string>;
|
|
671
|
+
sounds: Set<string>;
|
|
672
|
+
}?] | ['condition', (state: State) => boolean, Record<string, ValidAction[]>] | ['dialog', string | undefined, TextContent<string, State>, string | undefined] | ['end'] | ['showBackground', string | NovelyAsset | BackgroundImage] | ['playMusic', string | NovelyAsset] | ['stopMusic', string | NovelyAsset] | ['pauseMusic', string | NovelyAsset] | ['playSound', audio: string | NovelyAsset, loop?: boolean] | ['pauseSound', string | NovelyAsset] | ['stopSound', string | NovelyAsset] | ['voice', string | NovelyAsset | Record<string, string | NovelyAsset>] | ['stopVoice'] | ['jump', string] | ['showCharacter', string, keyof Character['emotions'], string?, string?] | ['hideCharacter', string, string?, string?, number?] | ['animateCharacter', string, number, ...string[]] | ['wait', number | ((state: State) => number)] | ['function', FunctionAction<string, State>] | ['input', string, (meta: ActionInputOnInputMeta<string, State>) => void, ActionInputSetup?] | ['custom', CustomHandler<string, State>] | ['vibrate', ...number[]] | ['next'] | ['text', ...TextContent<string, State>[]] | ['exit'] | ['preload', string | NovelyAsset] | ['block', string] | ValidAction[];
|
|
673
|
+
type Story = Record<string, ValidAction[]>;
|
|
674
|
+
type TextContent<L extends string, S extends State> = string | ((state: S) => string) | Record<L, string | ((state: S) => string)>;
|
|
675
|
+
type FunctionableValue<T> = T | (() => T);
|
|
676
|
+
type CustomHandlerGetResultDataFunction = <T = Record<string, unknown>>(data?: T) => T;
|
|
677
|
+
type CustomHandlerGetResult<I extends boolean> = {
|
|
678
|
+
/**
|
|
679
|
+
* Element for the custom action to be rendered into
|
|
680
|
+
*/
|
|
681
|
+
element: I extends true ? HTMLDivElement : null;
|
|
682
|
+
/**
|
|
683
|
+
* Root node
|
|
684
|
+
*/
|
|
685
|
+
root: HTMLElement;
|
|
686
|
+
};
|
|
687
|
+
type OnForwardFnParams = {
|
|
688
|
+
isUserRequiredAction: boolean;
|
|
689
|
+
isBlockingAction: boolean;
|
|
690
|
+
action: Exclude<ValidAction, ValidAction[]>;
|
|
691
|
+
};
|
|
692
|
+
type OnForwardFn = (params: OnForwardFnParams) => void;
|
|
693
|
+
type CustomHandlerFunctionGetFn = <I extends boolean = true>(insert?: I) => CustomHandlerGetResult<I>;
|
|
694
|
+
type CustomHandlerFunctionParameters<L extends string, S extends State> = {
|
|
695
|
+
/**
|
|
696
|
+
* Returns:
|
|
697
|
+
* - Root where entire novely is mounted
|
|
698
|
+
* - Element in which custom action could be mounted
|
|
699
|
+
*
|
|
700
|
+
* @example
|
|
701
|
+
* ```ts
|
|
702
|
+
* // pass `true` to insert element to the DOM
|
|
703
|
+
* const { root, element } = getDomNodes(true);
|
|
704
|
+
* ```
|
|
705
|
+
*/
|
|
706
|
+
getDomNodes: CustomHandlerFunctionGetFn;
|
|
707
|
+
/**
|
|
708
|
+
* Function to get current Save. It can be mutated. Only use it when you know what you do
|
|
709
|
+
*
|
|
710
|
+
* @deprecated
|
|
711
|
+
*/
|
|
712
|
+
getSave: () => Save<S>;
|
|
713
|
+
/**
|
|
714
|
+
* Renderer Context
|
|
715
|
+
*/
|
|
716
|
+
rendererContext: Context;
|
|
717
|
+
/**
|
|
718
|
+
* Context key in which action is running
|
|
719
|
+
*/
|
|
720
|
+
contextKey: string;
|
|
721
|
+
/**
|
|
722
|
+
* Function to work with custom action's state
|
|
723
|
+
*
|
|
724
|
+
* @example
|
|
725
|
+
* ```ts
|
|
726
|
+
* type Data = { name: string };
|
|
727
|
+
*
|
|
728
|
+
* const handler: CustomHandler = async ({ data }) => {
|
|
729
|
+
* const _data = data<Data>();
|
|
730
|
+
*
|
|
731
|
+
* if (!_data.name) {
|
|
732
|
+
* _data.name = 'Mr. Crabs'
|
|
733
|
+
* }
|
|
734
|
+
*
|
|
735
|
+
* data<Data>().name // 'Mr. Crabs'
|
|
736
|
+
*
|
|
737
|
+
* data<Data>() == _data // true
|
|
738
|
+
*
|
|
739
|
+
* // passing object will replace object
|
|
740
|
+
* data<Data>({ name: 'Mr. Crabs' })
|
|
741
|
+
* data<Data>() == _data // false
|
|
742
|
+
* }
|
|
743
|
+
* ```
|
|
744
|
+
*/
|
|
745
|
+
data: CustomHandlerGetResultDataFunction;
|
|
746
|
+
/**
|
|
747
|
+
* Function to access data stored at specific key.
|
|
748
|
+
* @example
|
|
749
|
+
* ```ts
|
|
750
|
+
* const handler: CustomHandler = async ({ dataAtKey }) => {
|
|
751
|
+
* // peek at data at action with key 'action-2'
|
|
752
|
+
* console.log(dataAtKey('action-2'))
|
|
753
|
+
* }
|
|
754
|
+
*
|
|
755
|
+
* handler.key = 'action-1'
|
|
756
|
+
* ```
|
|
757
|
+
* @deprecated
|
|
758
|
+
*/
|
|
759
|
+
dataAtKey: <T extends Record<string, unknown>>(key: string) => T | null;
|
|
760
|
+
/**
|
|
761
|
+
* Function to register cleanup callbacks (executed in reverse order of registration).
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* ```ts
|
|
765
|
+
* const handler: CustomHandler = async ({ clear, paused }) => {
|
|
766
|
+
* const unsubscribe = paused.subscribe((paused) => {
|
|
767
|
+
*
|
|
768
|
+
* })
|
|
769
|
+
*
|
|
770
|
+
* clear(paused);
|
|
771
|
+
* }
|
|
772
|
+
* ```
|
|
773
|
+
*/
|
|
774
|
+
clear: (fn: () => void) => void;
|
|
775
|
+
/**
|
|
776
|
+
* Overwrites `onBack` callback. Callback will be called after clearing some actions, but before starting others.
|
|
777
|
+
*
|
|
778
|
+
* @example
|
|
779
|
+
* ```ts
|
|
780
|
+
* const handler: CustomHandler = ({ getDomNodes, state, onBack }) => {
|
|
781
|
+
* // some code
|
|
782
|
+
* const update = () => button.textContent = `Clicks: ${state().clicks}`;
|
|
783
|
+
*
|
|
784
|
+
* onBack(() => {
|
|
785
|
+
* console.log('Backing up');
|
|
786
|
+
* // sync state with UI
|
|
787
|
+
* update();
|
|
788
|
+
* })
|
|
789
|
+
* }
|
|
790
|
+
* ```
|
|
791
|
+
*/
|
|
792
|
+
onBack: (fn: () => void) => void;
|
|
793
|
+
/**
|
|
794
|
+
* Overwrites `onForward` callback. Callback will be called before executing next action.
|
|
795
|
+
*
|
|
796
|
+
* @example
|
|
797
|
+
* ```ts
|
|
798
|
+
* const handler: CustomHandler = ({ getDomNodes, state, onForward }) => {
|
|
799
|
+
* onForward(({ action, isBlockingAction, isUserRequiredAction }) => {
|
|
800
|
+
* console.log(action)
|
|
801
|
+
* console.log({ isBlockingAction, isUserRequiredAction })
|
|
802
|
+
* })
|
|
803
|
+
* }
|
|
804
|
+
* ```
|
|
805
|
+
*/
|
|
806
|
+
onForward: (fn: OnForwardFn) => void;
|
|
807
|
+
/**
|
|
808
|
+
* It will call all clear actions and remove HTML element from `getDomNodes` function
|
|
809
|
+
*/
|
|
810
|
+
remove: () => void;
|
|
811
|
+
/**
|
|
812
|
+
* State function
|
|
813
|
+
*/
|
|
814
|
+
state: StateFunction<S>;
|
|
815
|
+
/**
|
|
816
|
+
* Game state
|
|
817
|
+
*/
|
|
818
|
+
flags: {
|
|
819
|
+
restoring: boolean;
|
|
820
|
+
goingBack: boolean;
|
|
821
|
+
preview: boolean;
|
|
822
|
+
};
|
|
823
|
+
/**
|
|
824
|
+
* Game language
|
|
825
|
+
*/
|
|
826
|
+
lang: L;
|
|
827
|
+
/**
|
|
828
|
+
* Function to replace template content
|
|
829
|
+
*
|
|
830
|
+
* @example
|
|
831
|
+
* ```ts
|
|
832
|
+
* const handler: CustomHandler = async ({ state, templateReplace }) => {
|
|
833
|
+
* const text = templateReplace({ en: '' }, state())
|
|
834
|
+
* }
|
|
835
|
+
* ```
|
|
836
|
+
*/
|
|
837
|
+
templateReplace: (content: TextContent<L, State>, values?: State) => string;
|
|
838
|
+
/**
|
|
839
|
+
* Is game currently paused
|
|
840
|
+
*
|
|
841
|
+
* @example
|
|
842
|
+
* ```ts
|
|
843
|
+
* const handler: CustomHandler = async ({ clear, paused }) => {
|
|
844
|
+
* const unsubscribe = paused.subscribe((paused) => {
|
|
845
|
+
* // Here you can pause/resume animations, sounds, etc, etc
|
|
846
|
+
* })
|
|
847
|
+
*
|
|
848
|
+
* clear(paused);
|
|
849
|
+
* }
|
|
850
|
+
*/
|
|
851
|
+
paused: Derived<boolean>;
|
|
852
|
+
/**
|
|
853
|
+
* Ticker
|
|
854
|
+
*
|
|
855
|
+
* @example
|
|
856
|
+
* ```ts
|
|
857
|
+
* const handler: CustomHandler = async ({ clear, ticker }) => {
|
|
858
|
+
* const unsubscribe = ticker.add((ticker) => {
|
|
859
|
+
* console.log(ticker.deltaTime);
|
|
860
|
+
* })
|
|
861
|
+
*
|
|
862
|
+
* ticker.start();
|
|
863
|
+
*
|
|
864
|
+
* clear(unsubscribe);
|
|
865
|
+
* }
|
|
866
|
+
* ```
|
|
867
|
+
*/
|
|
868
|
+
ticker: Ticker;
|
|
869
|
+
/**
|
|
870
|
+
* Fetching function.
|
|
871
|
+
* @default fetch
|
|
872
|
+
*/
|
|
873
|
+
request: typeof fetch;
|
|
874
|
+
};
|
|
875
|
+
type CustomHandlerFunction<L extends string, S extends State> = (parameters: CustomHandlerFunctionParameters<L, S>) => Thenable<void>;
|
|
876
|
+
type CustomHandlerCalling = {
|
|
877
|
+
/**
|
|
878
|
+
* Call only the last custom action of this type or not. Does not affect other custom actions
|
|
879
|
+
* @example
|
|
880
|
+
* ```ts
|
|
881
|
+
* ['custom', customSomething1]
|
|
882
|
+
* ['custom', customSomething1]
|
|
883
|
+
* ['custom', customSomething1] <-- Run only that
|
|
884
|
+
* ```
|
|
885
|
+
*/
|
|
886
|
+
callOnlyLatest?: boolean;
|
|
887
|
+
/**
|
|
888
|
+
* Manually check should be skipped or not during restore
|
|
889
|
+
* @param nextActions Next actions in the restoring queue
|
|
890
|
+
*/
|
|
891
|
+
skipOnRestore?: (nextActions: Exclude<ValidAction, ValidAction[]>[]) => boolean;
|
|
892
|
+
};
|
|
893
|
+
/**
|
|
894
|
+
* Array of URL or NovelyAsset
|
|
895
|
+
*/
|
|
896
|
+
type ResolvedAssets = (NovelyAsset | string)[];
|
|
897
|
+
type AssetsResolverArgs = {
|
|
898
|
+
/**
|
|
899
|
+
* Fetching function
|
|
900
|
+
*/
|
|
901
|
+
request: typeof fetch;
|
|
902
|
+
};
|
|
903
|
+
/**
|
|
904
|
+
* Function to get assets
|
|
905
|
+
*/
|
|
906
|
+
type AssetsResolver = (args: AssetsResolverArgs) => Thenable<ResolvedAssets>;
|
|
907
|
+
type CustomHandlerInfo = CustomHandlerCalling & {
|
|
908
|
+
/**
|
|
909
|
+
* Assets used by action. When preload is "automatic", will be preloaded before action runs.
|
|
910
|
+
*
|
|
911
|
+
* In case function is provided, execution time is limited to 250ms,
|
|
912
|
+
* then returned assets or empty array (when limited)
|
|
913
|
+
* will always be used with that action
|
|
914
|
+
*
|
|
915
|
+
* @example
|
|
916
|
+
* ```ts
|
|
917
|
+
* handler.assets = [url]
|
|
918
|
+
* handler.assets = async ({ request }) => {
|
|
919
|
+
* return [url]
|
|
920
|
+
* }
|
|
921
|
+
* ```
|
|
922
|
+
*/
|
|
923
|
+
assets?: ResolvedAssets | AssetsResolver;
|
|
924
|
+
/**
|
|
925
|
+
* When true interacting with it will be saved in history
|
|
926
|
+
*/
|
|
927
|
+
requireUserAction?: boolean;
|
|
928
|
+
/**
|
|
929
|
+
* Id by which we will determine what action is which
|
|
930
|
+
*/
|
|
931
|
+
id: string | symbol;
|
|
932
|
+
/**
|
|
933
|
+
* Key by which we will save the custom action's data. It includes cleanup function's provided by `clear` and data in `data` function
|
|
934
|
+
*
|
|
935
|
+
* It can be a name of action or more specific thing. In example for custom `showCharacter` it may be `show-character-${character}
|
|
936
|
+
*/
|
|
937
|
+
key: string;
|
|
938
|
+
};
|
|
939
|
+
type CustomHandler<L extends string = string, S extends State = State> = CustomHandlerFunction<L, S> & CustomHandlerInfo;
|
|
940
|
+
interface ActionInputOnInputMeta<L extends string, S extends State> {
|
|
941
|
+
/**
|
|
942
|
+
* Input Element itself
|
|
943
|
+
*/
|
|
944
|
+
input: HTMLInputElement;
|
|
945
|
+
/**
|
|
946
|
+
* Function to show error message or hide it
|
|
947
|
+
* @param error Error message or empty string to remove it
|
|
948
|
+
*/
|
|
949
|
+
error: (error: string) => void;
|
|
950
|
+
/**
|
|
951
|
+
* Input Event
|
|
952
|
+
*/
|
|
953
|
+
event: InputEvent & {
|
|
954
|
+
currentTarget: HTMLInputElement;
|
|
955
|
+
};
|
|
956
|
+
/**
|
|
957
|
+
* Sanitized `input.value`
|
|
958
|
+
*/
|
|
959
|
+
value: string;
|
|
960
|
+
/**
|
|
961
|
+
* Language
|
|
962
|
+
*/
|
|
963
|
+
lang: L;
|
|
964
|
+
/**
|
|
965
|
+
* State function
|
|
966
|
+
*/
|
|
967
|
+
state: StateFunction<S>;
|
|
968
|
+
}
|
|
969
|
+
type FunctionActionProps<L extends Lang, S extends State> = {
|
|
970
|
+
restoring: boolean;
|
|
971
|
+
goingBack: boolean;
|
|
972
|
+
preview: boolean;
|
|
973
|
+
/**
|
|
974
|
+
* Language
|
|
975
|
+
*/
|
|
976
|
+
lang: L;
|
|
977
|
+
/**
|
|
978
|
+
* State function
|
|
979
|
+
*/
|
|
980
|
+
state: StateFunction<S>;
|
|
981
|
+
};
|
|
982
|
+
type ChoiceCheckFunctionProps<L extends Lang, S extends State> = {
|
|
983
|
+
/**
|
|
984
|
+
* Language
|
|
985
|
+
*/
|
|
986
|
+
lang: L;
|
|
987
|
+
/**
|
|
988
|
+
* State
|
|
989
|
+
*/
|
|
990
|
+
state: S;
|
|
991
|
+
};
|
|
992
|
+
type ChoiceOnSelectFunctionProps = {
|
|
993
|
+
/**
|
|
994
|
+
* Triggers `active` and `visible` properties computation
|
|
995
|
+
*/
|
|
996
|
+
recompute: () => void;
|
|
997
|
+
};
|
|
998
|
+
type ChoiceCheckFunction<L extends Lang, S extends State> = (props: ChoiceCheckFunctionProps<L, S>) => boolean;
|
|
999
|
+
type ChoiceOnSelectFunction = (props: ChoiceOnSelectFunctionProps) => void;
|
|
1000
|
+
type ConditionCheckFunction<S extends State, R extends string | true | false> = (state: S) => R;
|
|
1001
|
+
type FunctionAction<L extends string, S extends State> = (props: FunctionActionProps<L, S>) => Thenable<void>;
|
|
1002
|
+
type ActionInputSetupCleanup = () => void;
|
|
1003
|
+
type ActionInputSetup = (input: HTMLInputElement) => ActionInputSetupCleanup | void;
|
|
1004
|
+
type BackgroundImage = Record<string, string | NovelyAsset>;
|
|
1005
|
+
type VoiceAction<L extends Lang> = (params: string | NovelyAsset | Partial<Record<L, string | NovelyAsset>>) => ValidAction;
|
|
1006
|
+
type ActionChoiceChoiceObject<L extends Lang, S extends State> = {
|
|
1007
|
+
title: TextContent<L, S>;
|
|
1008
|
+
children: ValidAction[];
|
|
1009
|
+
active?: ChoiceCheckFunction<L, S>;
|
|
1010
|
+
visible?: ChoiceCheckFunction<L, S>;
|
|
1011
|
+
onSelect?: ChoiceOnSelectFunction;
|
|
1012
|
+
image?: string | NovelyAsset;
|
|
1013
|
+
};
|
|
1014
|
+
type ActionChoiceChoice<L extends Lang, S extends State> = [title: TextContent<L, S>, actions: ValidAction[], active?: ChoiceCheckFunction<L, S>, visible?: ChoiceCheckFunction<L, S>, onSelect?: ChoiceOnSelectFunction, image?: string | NovelyAsset];
|
|
1015
|
+
type ActionProxy<Characters extends Record<string, Character>, Languages extends Lang, S extends State> = {
|
|
1016
|
+
choice: {
|
|
1017
|
+
(...choices: ActionChoiceChoice<Languages, S>[]): ValidAction;
|
|
1018
|
+
(question: TextContent<Languages, S>, ...choices: ActionChoiceChoice<Languages, S>[]): ValidAction;
|
|
1019
|
+
};
|
|
1020
|
+
clear: (keep?: Set<keyof DefaultActionProxy>, keepCharacters?: Set<string>, keepAudio?: {
|
|
1021
|
+
music: Set<string>;
|
|
1022
|
+
sounds: Set<string>;
|
|
1023
|
+
}) => ValidAction;
|
|
1024
|
+
condition: <T extends string | true | false>(condition: ConditionCheckFunction<S, T>, variants: Record<T extends true ? 'true' : T extends false ? 'false' : T, ValidAction[]>) => ValidAction;
|
|
1025
|
+
exit: () => ValidAction;
|
|
1026
|
+
dialog: {
|
|
1027
|
+
<C extends keyof Characters>(character: C, content: TextContent<Languages, S>, emotion?: keyof Characters[C]['emotions']): ValidAction;
|
|
1028
|
+
(character: undefined, content: TextContent<Languages, S>, emotion?: undefined): ValidAction;
|
|
1029
|
+
(character: string, content: TextContent<Languages, S>, emotion?: undefined): ValidAction;
|
|
1030
|
+
};
|
|
1031
|
+
end: () => ValidAction;
|
|
1032
|
+
showBackground: {
|
|
1033
|
+
(background: string | NovelyAsset): ValidAction;
|
|
1034
|
+
<T extends Record<string, string | NovelyAsset>>(background: NonEmptyRecord<T>): ValidAction;
|
|
1035
|
+
};
|
|
1036
|
+
playMusic: (audio: string | NovelyAsset) => ValidAction;
|
|
1037
|
+
pauseMusic: (audio: string | NovelyAsset) => ValidAction;
|
|
1038
|
+
stopMusic: (audio: string | NovelyAsset) => ValidAction;
|
|
1039
|
+
playSound: (audio: string | NovelyAsset, loop?: boolean) => ValidAction;
|
|
1040
|
+
pauseSound: (audio: string | NovelyAsset) => ValidAction;
|
|
1041
|
+
stopSound: (audio: string | NovelyAsset) => ValidAction;
|
|
1042
|
+
/**
|
|
1043
|
+
* Plays voice
|
|
1044
|
+
*
|
|
1045
|
+
* @example
|
|
1046
|
+
* ```
|
|
1047
|
+
* engine.script({
|
|
1048
|
+
* start: [
|
|
1049
|
+
* engine.action.voice('./rick-astley-never-gonna-give-you-up.mp3'),
|
|
1050
|
+
* engine.action.say('Rick', 'Never gonna give you up'),
|
|
1051
|
+
* ]
|
|
1052
|
+
* })
|
|
1053
|
+
* ```
|
|
1054
|
+
*/
|
|
1055
|
+
voice: VoiceAction<Languages>;
|
|
1056
|
+
/**
|
|
1057
|
+
* Stops currently playing voice
|
|
1058
|
+
*/
|
|
1059
|
+
stopVoice: () => ValidAction;
|
|
1060
|
+
jump: (scene: string) => ValidAction;
|
|
1061
|
+
showCharacter: <C extends keyof Characters>(character: C, emotion?: keyof Characters[C]['emotions'], className?: string, style?: string) => ValidAction;
|
|
1062
|
+
hideCharacter: (character: keyof Characters, className?: string, style?: string, duration?: number) => ValidAction;
|
|
1063
|
+
animateCharacter: (character: keyof Characters, classes: string) => ValidAction;
|
|
1064
|
+
wait: (time: number | ((state: State) => number)) => ValidAction;
|
|
1065
|
+
function: (fn: FunctionAction<Languages, S>) => ValidAction;
|
|
1066
|
+
input: (question: TextContent<Languages, S>, onInput: (meta: ActionInputOnInputMeta<Languages, S>) => void, setup?: ActionInputSetup) => ValidAction;
|
|
1067
|
+
custom: (handler: CustomHandler<Languages, S> | CustomHandler) => ValidAction;
|
|
1068
|
+
vibrate: (...pattern: number[]) => ValidAction;
|
|
1069
|
+
next: () => ValidAction;
|
|
1070
|
+
text: (...text: TextContent<Languages, S>[]) => ValidAction;
|
|
1071
|
+
preload: (source: string | NovelyAsset) => ValidAction;
|
|
1072
|
+
block: (scene: string) => ValidAction;
|
|
1073
|
+
};
|
|
1074
|
+
type DefaultActionProxy = ActionProxy<Record<string, Character>, Lang, State>;
|
|
1075
|
+
type GetActionParameters<T extends Capitalize<keyof DefaultActionProxy>> = Parameters<DefaultActionProxy[Uncapitalize<T>]>;
|
|
1076
|
+
type VirtualActions<$Characters extends Record<string, Character>, $Lang extends Lang, $State extends State> = {
|
|
1077
|
+
choice: (question: TextContent<$Lang, $State>, ...choices: ActionChoiceChoiceObject<$Lang, $State>[]) => ValidAction;
|
|
1078
|
+
say: (character: keyof $Characters, content: TextContent<$Lang, $State>) => ValidAction;
|
|
1079
|
+
};
|
|
1080
|
+
//#endregion
|
|
1081
|
+
//#region src/type-utils.d.ts
|
|
1082
|
+
type ConditionParams<T> = T extends EngineTypes<any, infer $State, any, any> ? $State : never;
|
|
1083
|
+
type ChoiceParams<T> = T extends EngineTypes<infer $Lang, infer $State, any, any> ? ChoiceCheckFunctionProps<$Lang, $State> : never;
|
|
1084
|
+
type FunctionParams<T> = T extends EngineTypes<infer $Lang, infer $State, any, any> ? FunctionActionProps<$Lang, $State> : never;
|
|
1085
|
+
type InputHandler<T> = T extends EngineTypes<infer $Lang, infer $State, any, any> ? ActionInputOnInputMeta<$Lang, $State> : never;
|
|
1086
|
+
/**
|
|
1087
|
+
* @example
|
|
1088
|
+
* ```ts
|
|
1089
|
+
* type Types = TypesFromEngine<typeof engine>;
|
|
1090
|
+
* ```
|
|
1091
|
+
*/
|
|
1092
|
+
type TypesFromEngine<T> = T extends {
|
|
1093
|
+
types: EngineTypes<infer $Lang, infer $State, infer $Data, infer $Characters> | null;
|
|
1094
|
+
} ? EngineTypes<$Lang, $State, $Data, $Characters> : never;
|
|
1095
|
+
//#endregion
|
|
1096
|
+
//#region src/novely.d.ts
|
|
1097
|
+
declare const novely: <$Language extends string, $Characters extends Record<string, Character<$Language>>, $State extends State, $Data extends Data, $Actions extends Record<string, (...args: any[]) => ValidAction>>({
|
|
1098
|
+
characters,
|
|
1099
|
+
characterAssetSizes,
|
|
1100
|
+
defaultEmotions,
|
|
1101
|
+
storage,
|
|
1102
|
+
storageDelay,
|
|
1103
|
+
renderer: createRenderer,
|
|
1104
|
+
initialScreen,
|
|
1105
|
+
translation,
|
|
1106
|
+
state: defaultState,
|
|
1107
|
+
data: defaultData,
|
|
1108
|
+
autosaves,
|
|
1109
|
+
migrations,
|
|
1110
|
+
throttleTimeout,
|
|
1111
|
+
getLanguage,
|
|
1112
|
+
overrideLanguage,
|
|
1113
|
+
askBeforeExit,
|
|
1114
|
+
preloadAssets,
|
|
1115
|
+
parallelAssetsDownloadLimit,
|
|
1116
|
+
fetch: request,
|
|
1117
|
+
cloneFunction: clone,
|
|
1118
|
+
saveOnUnload,
|
|
1119
|
+
startKey,
|
|
1120
|
+
defaultTypewriterSpeed,
|
|
1121
|
+
storyOptions,
|
|
1122
|
+
onLanguageChange
|
|
1123
|
+
}: NovelyInit<$Language, $Characters, $State, $Data, $Actions>) => {
|
|
1124
|
+
/**
|
|
1125
|
+
* Function to set game script
|
|
1126
|
+
*
|
|
1127
|
+
* @example
|
|
1128
|
+
* ```ts
|
|
1129
|
+
* engine.script({
|
|
1130
|
+
* start: [
|
|
1131
|
+
* action.function(() => {})
|
|
1132
|
+
* ]
|
|
1133
|
+
* })
|
|
1134
|
+
* ```
|
|
1135
|
+
*/
|
|
1136
|
+
script: (part: Story) => Promise<void>;
|
|
1137
|
+
/**
|
|
1138
|
+
* Get actions
|
|
1139
|
+
*
|
|
1140
|
+
* @example
|
|
1141
|
+
* ```ts
|
|
1142
|
+
* engine.script({
|
|
1143
|
+
* start: [
|
|
1144
|
+
* action.function(() => {})
|
|
1145
|
+
* ]
|
|
1146
|
+
* })
|
|
1147
|
+
* ```
|
|
1148
|
+
*/
|
|
1149
|
+
action: $Actions & ActionProxy<$Characters, $Language, $State> & VirtualActions<$Characters, $Language, $State>;
|
|
1150
|
+
/**
|
|
1151
|
+
* State bound to `$MAIN` game context
|
|
1152
|
+
* @deprecated Use `state` function provided from action arguments
|
|
1153
|
+
*/
|
|
1154
|
+
state: StateFunction<State>;
|
|
1155
|
+
/**
|
|
1156
|
+
* Store data between games
|
|
1157
|
+
*
|
|
1158
|
+
* @example
|
|
1159
|
+
* ```ts
|
|
1160
|
+
* engine.script({
|
|
1161
|
+
* start: [
|
|
1162
|
+
* action.function(() => {
|
|
1163
|
+
* // Paid content should be purchased only once
|
|
1164
|
+
* // So it will be available in any save
|
|
1165
|
+
* data({ paid_content_purchased: true })
|
|
1166
|
+
* })
|
|
1167
|
+
* ]
|
|
1168
|
+
* })
|
|
1169
|
+
* ```
|
|
1170
|
+
*/
|
|
1171
|
+
data: StateFunction<$Data>;
|
|
1172
|
+
/**
|
|
1173
|
+
* Used in combination with type utilities
|
|
1174
|
+
* @example
|
|
1175
|
+
* ```ts
|
|
1176
|
+
* import type { TypesFromEngine, ConditionParams, StateFunction } from '@novely/core';
|
|
1177
|
+
*
|
|
1178
|
+
* type Types = TypesFromEngine<typeof engine>;
|
|
1179
|
+
*
|
|
1180
|
+
* const conditionCheck = (state: StateFunction<ConditionParams<Types>>) => {
|
|
1181
|
+
* return state.age >= 18;
|
|
1182
|
+
* }
|
|
1183
|
+
* ```
|
|
1184
|
+
*/
|
|
1185
|
+
types: EngineTypes<$Language, $State, $Data, $Characters> | null;
|
|
1186
|
+
/**
|
|
1187
|
+
* Replaces content inside {{braces}} using global data
|
|
1188
|
+
* @example
|
|
1189
|
+
* ```ts
|
|
1190
|
+
* data({ name: 'Alexei' })
|
|
1191
|
+
*
|
|
1192
|
+
* templateReplace('{{name}} is our hero')
|
|
1193
|
+
* templateReplace({
|
|
1194
|
+
* en: (data) => 'Hello, ' + data.name
|
|
1195
|
+
* })
|
|
1196
|
+
* ```
|
|
1197
|
+
*/
|
|
1198
|
+
templateReplace(content: TextContent<$Language, $Data>): string;
|
|
1199
|
+
/**
|
|
1200
|
+
* Same as `templateReplace` but uses state and requires explicitly providing it
|
|
1201
|
+
*/
|
|
1202
|
+
templateReplaceState(content: TextContent<$Language, $State>, state: State): string;
|
|
1203
|
+
/**
|
|
1204
|
+
* Cancel data loading, hide UI, ignore page change events
|
|
1205
|
+
* Data updates still will work in case Novely already was loaded
|
|
1206
|
+
*/
|
|
1207
|
+
destroy(): void;
|
|
1208
|
+
/**
|
|
1209
|
+
* Funtion to get current storage data
|
|
1210
|
+
*
|
|
1211
|
+
* @example
|
|
1212
|
+
* ```ts
|
|
1213
|
+
* const currentStorageData = engine.getCurrentStorageData();
|
|
1214
|
+
* ```
|
|
1215
|
+
*/
|
|
1216
|
+
getCurrentStorageData: () => StorageData<$Language, $Data> | null;
|
|
1217
|
+
/**
|
|
1218
|
+
* Function to set storage data. Using this function is not recommended.
|
|
1219
|
+
*
|
|
1220
|
+
* @deprecated
|
|
1221
|
+
* @example
|
|
1222
|
+
* ```ts
|
|
1223
|
+
* const currentStorageData = engine.getCurrentStorageData();
|
|
1224
|
+
*
|
|
1225
|
+
* if (currentStorageData) {
|
|
1226
|
+
* // update music volume
|
|
1227
|
+
* currentStorageData.meta[2] = 1;
|
|
1228
|
+
*
|
|
1229
|
+
* setStorageData(currentStorageData)
|
|
1230
|
+
* }
|
|
1231
|
+
* ```
|
|
1232
|
+
*/
|
|
1233
|
+
setStorageData: (data: StorageData<$Language, $Data>) => void;
|
|
1234
|
+
/**
|
|
1235
|
+
* Function to control paused state. Custom Actions are provided with `paused` store they can subscribe to.
|
|
1236
|
+
* This function will notify Custom Actions. Pause state can be used when showing ads.
|
|
1237
|
+
* @example
|
|
1238
|
+
* ```ts
|
|
1239
|
+
* sdk.on('pause' () => engine.setPaused(true));
|
|
1240
|
+
* sdk.on('resume', () => engine.setPaused(false));
|
|
1241
|
+
* ```
|
|
1242
|
+
*/
|
|
1243
|
+
setPaused: (paused: boolean) => void;
|
|
1244
|
+
/**
|
|
1245
|
+
* Function to control focused state. It will affect `paused` store passed to Custom Actions.
|
|
1246
|
+
* This function can be used to pause game when it's not focused.
|
|
1247
|
+
* @example
|
|
1248
|
+
* ```ts
|
|
1249
|
+
* import { pauseOnBlur } from '@novely/core';
|
|
1250
|
+
*
|
|
1251
|
+
* // Will subscribe to blur/focus events and call `setFocused`
|
|
1252
|
+
* pauseOnBlur(engine);
|
|
1253
|
+
*
|
|
1254
|
+
* // OR
|
|
1255
|
+
*
|
|
1256
|
+
* sdk.on('focus' () => engine.setFocused(true));
|
|
1257
|
+
* sdk.on('blur', () => engine.setFocused(false));
|
|
1258
|
+
* ```
|
|
1259
|
+
*/
|
|
1260
|
+
setFocused: (focused: boolean) => void;
|
|
1261
|
+
};
|
|
1262
|
+
//#endregion
|
|
1263
|
+
//#region src/extend-actions.d.ts
|
|
1264
|
+
type Part = Record<string, (...args: any[]) => ValidAction>;
|
|
1265
|
+
/**
|
|
1266
|
+
* Extens core action with custom actions
|
|
1267
|
+
* @param base Actions object you will extend, `engine.action`
|
|
1268
|
+
* @param extension Actions object you will extend with
|
|
1269
|
+
* @example
|
|
1270
|
+
* ```ts
|
|
1271
|
+
* const action = extendAction(engine.action, {
|
|
1272
|
+
* particles: (options: Parameters<typeof particles>[0]) => {
|
|
1273
|
+
* return ['custom', particles(options)]
|
|
1274
|
+
* }
|
|
1275
|
+
* })
|
|
1276
|
+
* ```
|
|
1277
|
+
*/
|
|
1278
|
+
declare const extendAction: <Part0 extends Part, Part1 extends Part>(base: Part0, extension: Part1) => Readonly<Assign<Part0, Part1>>;
|
|
1279
|
+
//#endregion
|
|
1280
|
+
//#region src/asset.d.ts
|
|
1281
|
+
/**
|
|
1282
|
+
* Memoizes and returns an asset selection object based on provided file variants.
|
|
1283
|
+
* The selected asset depends on the client's support for various formats.
|
|
1284
|
+
*
|
|
1285
|
+
* @param {...string} variants - A variable number of strings, each representing a potential asset file URL.
|
|
1286
|
+
* @returns {NovelyAsset} An object representing the selected asset with `source` and `type` properties.
|
|
1287
|
+
*
|
|
1288
|
+
* @throws {Error} If in DEV mode and no arguments are provided.
|
|
1289
|
+
* @example
|
|
1290
|
+
* ```
|
|
1291
|
+
* import { asset } from 'novely';
|
|
1292
|
+
*
|
|
1293
|
+
* // Passed first have higher priority
|
|
1294
|
+
* const classroom = asset(
|
|
1295
|
+
* 'classroom.avif',
|
|
1296
|
+
* 'classroom.webp',
|
|
1297
|
+
* 'classroom.jpeg'
|
|
1298
|
+
* );
|
|
1299
|
+
*
|
|
1300
|
+
* setTimeout(() => {
|
|
1301
|
+
* console.log(classroom.source);
|
|
1302
|
+
* }, 100);
|
|
1303
|
+
* ```
|
|
1304
|
+
*/
|
|
1305
|
+
declare const asset: {
|
|
1306
|
+
(...variants: string[]): NovelyAsset;
|
|
1307
|
+
image(source: string): NovelyAsset;
|
|
1308
|
+
audio(source: string): NovelyAsset;
|
|
1309
|
+
};
|
|
1310
|
+
//#endregion
|
|
1311
|
+
//#region src/browser-events.d.ts
|
|
1312
|
+
declare const pauseOnBlur: (engine: {
|
|
1313
|
+
setFocused: (focused: boolean) => void;
|
|
1314
|
+
}) => {
|
|
1315
|
+
unsubscribe: () => void;
|
|
1316
|
+
};
|
|
1317
|
+
//#endregion
|
|
1318
|
+
export { type ActionChoiceChoice, type ActionChoiceChoiceObject, type ActionInputOnInputMeta, type ActionInputSetup, type ActionInputSetupCleanup, type ActionProxy, type AllowedContent, type AudioHandle, type BackgroundImage, type BaseTranslationStrings, type Character, type CharacterAssetSizes, type CharacterHandle, type CharactersData, type ChoiceCheckFunction, type ChoiceCheckFunctionProps, type ChoiceOnSelectFunction, type ChoiceOnSelectFunctionProps, type ChoiceParams, type ConditionCheckFunction, type ConditionParams, type Context, type CoreData, type CustomActionHandle, type CustomHandler, type CustomHandlerFunction, type CustomHandlerFunctionGetFn, type CustomHandlerFunctionParameters, type CustomHandlerGetResult, type CustomHandlerGetResultDataFunction, type CustomHandlerInfo, type Data, type DeepPartial, type DefaultActionProxy, type Derived, EN, type Emotions, type EngineTypes, type FunctionParams, type FunctionableValue, type GetActionParameters, type InputHandler, type Lang, type NovelyAsset, type NovelyInit, type NovelyScreen, type Path, type PathItem, type PluralType, type Pluralization, RU, type Renderer, type RendererInit, type RendererInitPreviewReturn, type Save, type Stack, type StackHolder, type State, type StateFunction, type StorageAdapter, type StorageData, type StorageMeta, type Stored, type Story, type TextContent, type Thenable, type Ticker, type TranslationActions, type TypesFromEngine, type TypewriterSpeed, type ValidAction, asset, extendAction, novely, pauseOnBlur, storageAdapterLocal };
|
|
1319
|
+
//# sourceMappingURL=index.d.mts.map
|