@novely/core 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +63 -64
- package/dist/index.global.js +18 -18
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +18 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -88,6 +88,49 @@ type AudioHandle = {
|
|
|
88
88
|
pause: () => void;
|
|
89
89
|
play: () => void;
|
|
90
90
|
};
|
|
91
|
+
type Context = {
|
|
92
|
+
id: string;
|
|
93
|
+
get root(): HTMLElement;
|
|
94
|
+
set root(value: HTMLElement);
|
|
95
|
+
character: (character: string) => CharacterHandle;
|
|
96
|
+
background: (background: string | BackgroundImage) => void;
|
|
97
|
+
dialog: (content: string, name: string, character: string | undefined, emotion: string | undefined, resolve: () => void) => void;
|
|
98
|
+
choices: (question: string, choices: [name: string, active: boolean][], resolve: (selected: number) => void) => void;
|
|
99
|
+
input: (question: string, onInput: (meta: ActionInputOnInputMeta<Lang, State>) => void, setup: ActionInputSetup, resolve: () => void) => void;
|
|
100
|
+
clear: (keep: Set<keyof DefaultActionProxy>, keepCharacters: Set<string>, keepAudio: {
|
|
101
|
+
music: Set<string>;
|
|
102
|
+
sounds: Set<string>;
|
|
103
|
+
}, resolve: () => void) => void;
|
|
104
|
+
custom: (fn: CustomHandler<Lang, State>, push: () => void) => Thenable<void>;
|
|
105
|
+
clearCustom: (fn: CustomHandler<Lang, State>) => void;
|
|
106
|
+
text: (str: string, resolve: () => void) => void;
|
|
107
|
+
vibrate: (pattern: VibratePattern) => void;
|
|
108
|
+
audio: {
|
|
109
|
+
voice: (source: string) => void;
|
|
110
|
+
voiceStop: () => void;
|
|
111
|
+
music: (source: string, method: 'music' | 'sound', loop?: boolean) => AudioHandle;
|
|
112
|
+
/**
|
|
113
|
+
* Stop all sounds
|
|
114
|
+
*/
|
|
115
|
+
clear: () => void;
|
|
116
|
+
/**
|
|
117
|
+
* Destroy
|
|
118
|
+
*/
|
|
119
|
+
destroy: () => void;
|
|
120
|
+
/**
|
|
121
|
+
* Initialize audio service, attach events, etc
|
|
122
|
+
*/
|
|
123
|
+
start: () => void;
|
|
124
|
+
};
|
|
125
|
+
meta: {
|
|
126
|
+
get restoring(): boolean;
|
|
127
|
+
set restoring(value: boolean);
|
|
128
|
+
get preview(): boolean;
|
|
129
|
+
set preview(value: boolean);
|
|
130
|
+
get goingBack(): boolean;
|
|
131
|
+
set goingBack(value: boolean);
|
|
132
|
+
};
|
|
133
|
+
};
|
|
91
134
|
type Renderer = {
|
|
92
135
|
misc: {
|
|
93
136
|
/**
|
|
@@ -112,11 +155,11 @@ type Renderer = {
|
|
|
112
155
|
/**
|
|
113
156
|
* Shows the screen
|
|
114
157
|
*/
|
|
115
|
-
showScreen(name: NovelyScreen
|
|
158
|
+
showScreen(name: NovelyScreen): void;
|
|
116
159
|
/**
|
|
117
160
|
* Returns current screen
|
|
118
161
|
*/
|
|
119
|
-
getScreen(): NovelyScreen |
|
|
162
|
+
getScreen(): NovelyScreen | (string & Record<never, never>);
|
|
120
163
|
/**
|
|
121
164
|
* Shows prompt to exit
|
|
122
165
|
*/
|
|
@@ -131,80 +174,35 @@ type Renderer = {
|
|
|
131
174
|
unmount(): void;
|
|
132
175
|
};
|
|
133
176
|
};
|
|
134
|
-
getContext: (context: string) =>
|
|
135
|
-
id: string;
|
|
136
|
-
get root(): HTMLElement;
|
|
137
|
-
set root(value: HTMLElement);
|
|
138
|
-
character: (character: string) => CharacterHandle;
|
|
139
|
-
background: (background: string | BackgroundImage) => void;
|
|
140
|
-
dialog: (content: string, name: string, character: string | undefined, emotion: string | undefined, resolve: () => void) => void;
|
|
141
|
-
choices: (question: string, choices: [name: string, actions: ValidAction[], active?: boolean][], resolve: (selected: number) => void) => void;
|
|
142
|
-
input: (question: string, onInput: (meta: ActionInputOnInputMeta<string, State>) => void, setup: ActionInputSetup, resolve: () => void) => void;
|
|
143
|
-
clear: (keep: Set<keyof DefaultActionProxy>, keepCharacters: Set<string>, keepAudio: {
|
|
144
|
-
music: Set<string>;
|
|
145
|
-
sounds: Set<string>;
|
|
146
|
-
}, resolve: () => void) => void;
|
|
147
|
-
custom: (fn: Parameters<DefaultActionProxy['custom']>[0], push: () => void) => Thenable<void>;
|
|
148
|
-
clearCustom: (fn: Parameters<DefaultActionProxy['custom']>[0]) => void;
|
|
149
|
-
text: (str: string, resolve: () => void) => void;
|
|
150
|
-
vibrate: (pattern: VibratePattern) => void;
|
|
151
|
-
audio: {
|
|
152
|
-
voice: (source: string) => void;
|
|
153
|
-
voiceStop: () => void;
|
|
154
|
-
music: (source: string, method: 'music' | 'sound', loop?: boolean) => AudioHandle;
|
|
155
|
-
/**
|
|
156
|
-
* Stop all sounds
|
|
157
|
-
*/
|
|
158
|
-
clear: () => void;
|
|
159
|
-
/**
|
|
160
|
-
* Destroy
|
|
161
|
-
*/
|
|
162
|
-
destroy: () => void;
|
|
163
|
-
/**
|
|
164
|
-
* Initialize audio service, attach events, etc
|
|
165
|
-
*/
|
|
166
|
-
start: () => void;
|
|
167
|
-
};
|
|
168
|
-
meta: {
|
|
169
|
-
get restoring(): boolean;
|
|
170
|
-
set restoring(value: boolean);
|
|
171
|
-
get preview(): boolean;
|
|
172
|
-
set preview(value: boolean);
|
|
173
|
-
get goingBack(): boolean;
|
|
174
|
-
set goingBack(value: boolean);
|
|
175
|
-
};
|
|
176
|
-
store: unknown;
|
|
177
|
-
setStore: unknown;
|
|
178
|
-
};
|
|
177
|
+
getContext: (context: string) => Context;
|
|
179
178
|
removeContext: (context: string) => void;
|
|
180
179
|
};
|
|
181
|
-
type Context = ReturnType<Renderer['getContext']>;
|
|
182
180
|
type RendererInit = {
|
|
183
|
-
characters: Record<string, Character
|
|
184
|
-
set: (save: Save) => Promise<void>;
|
|
185
|
-
restore: (save?: Save) => Promise<void>;
|
|
186
|
-
save: (override?: boolean, type?: Save[2][1]) => void;
|
|
181
|
+
characters: Record<string, Character<Lang>>;
|
|
182
|
+
set: (save: Save<State>) => Promise<void>;
|
|
183
|
+
restore: (save?: Save<State>) => Promise<void>;
|
|
184
|
+
save: (override?: boolean, type?: Save<State>[2][1]) => void;
|
|
187
185
|
newGame: () => void;
|
|
188
186
|
exit: (force?: boolean) => void;
|
|
189
187
|
back: () => Promise<void>;
|
|
190
|
-
languages:
|
|
188
|
+
languages: Lang[];
|
|
191
189
|
/**
|
|
192
190
|
* Translation function
|
|
193
191
|
*/
|
|
194
|
-
t: (key: BaseTranslationStrings, lang:
|
|
192
|
+
t: (key: BaseTranslationStrings, lang: Lang) => string;
|
|
195
193
|
/**
|
|
196
194
|
* Store that tracks data updates
|
|
197
195
|
*/
|
|
198
|
-
|
|
196
|
+
storageData: Stored<StorageData<Lang, Data>>;
|
|
199
197
|
/**
|
|
200
198
|
* Store that used to communicate between renderer and core
|
|
201
199
|
*/
|
|
202
|
-
|
|
200
|
+
coreData: Stored<CoreData>;
|
|
203
201
|
/**
|
|
204
202
|
* There is different context, and the main one which is used for game
|
|
205
203
|
*/
|
|
206
204
|
mainContextKey: string;
|
|
207
|
-
preview: (save: Save
|
|
205
|
+
preview: (save: Save<State>, name: string) => Promise<void>;
|
|
208
206
|
removeContext: (name: string) => void;
|
|
209
207
|
getStateFunction: (context: string) => StateFunction<State>;
|
|
210
208
|
};
|
|
@@ -212,6 +210,7 @@ type RendererInit = {
|
|
|
212
210
|
declare const getLanguage: (languages: string[]) => string;
|
|
213
211
|
|
|
214
212
|
type Thenable<T> = T | Promise<T>;
|
|
213
|
+
type NoInfer<T> = [T][T extends any ? 0 : never];
|
|
215
214
|
type PathItem = [null, number | string] | ['jump', string] | ['choice', number] | ['choice:exit'] | ['condition', string] | ['condition:exit'] | ['exit'] | ['block', string] | ['block:exit'];
|
|
216
215
|
type Path = PathItem[];
|
|
217
216
|
type State = Record<string, any>;
|
|
@@ -225,7 +224,7 @@ type Save<S extends State = State> = [
|
|
|
225
224
|
meta: SaveMeta
|
|
226
225
|
];
|
|
227
226
|
type Lang = string;
|
|
228
|
-
type TypewriterSpeed = 'Slow' | 'Medium' | 'Fast' | 'Auto'
|
|
227
|
+
type TypewriterSpeed = 'Slow' | 'Medium' | 'Fast' | 'Auto';
|
|
229
228
|
type SoundVolume = number;
|
|
230
229
|
type StorageMeta<L extends string = string> = [
|
|
231
230
|
lang: L,
|
|
@@ -246,7 +245,7 @@ type Stack = {
|
|
|
246
245
|
push(value: Save): void;
|
|
247
246
|
clear(): void;
|
|
248
247
|
};
|
|
249
|
-
type NovelyScreen = 'mainmenu' | 'game' | 'saves' | 'settings';
|
|
248
|
+
type NovelyScreen = 'mainmenu' | 'game' | 'saves' | 'settings' | 'loading';
|
|
250
249
|
/**
|
|
251
250
|
* @see https://pendletonjones.com/deep-partial
|
|
252
251
|
*/
|
|
@@ -269,7 +268,7 @@ type TranslationDescription = {
|
|
|
269
268
|
plural?: Record<string, Pluralization>;
|
|
270
269
|
actions?: TranslationActions;
|
|
271
270
|
};
|
|
272
|
-
interface NovelyInit
|
|
271
|
+
interface NovelyInit<$Language extends Lang, Characters extends Record<string, Character<NoInfer<$Language>>>, StateScheme extends State, DataScheme extends Data> {
|
|
273
272
|
/**
|
|
274
273
|
* An object containing the characters in the game.
|
|
275
274
|
* @example
|
|
@@ -301,7 +300,7 @@ interface NovelyInit<Languages extends string, Characters extends Record<string,
|
|
|
301
300
|
/**
|
|
302
301
|
* A function that returns a Renderer object used to display the game's content
|
|
303
302
|
*/
|
|
304
|
-
renderer: (
|
|
303
|
+
renderer: (initializationData: RendererInit) => Renderer;
|
|
305
304
|
/**
|
|
306
305
|
* An optional property that specifies the initial screen to display when the game starts
|
|
307
306
|
*/
|
|
@@ -328,7 +327,7 @@ interface NovelyInit<Languages extends string, Characters extends Record<string,
|
|
|
328
327
|
* })
|
|
329
328
|
* ```
|
|
330
329
|
*/
|
|
331
|
-
translation: Record
|
|
330
|
+
translation: Record<$Language, TranslationDescription>;
|
|
332
331
|
/**
|
|
333
332
|
* Initial state value
|
|
334
333
|
*
|
|
@@ -375,7 +374,7 @@ interface NovelyInit<Languages extends string, Characters extends Record<string,
|
|
|
375
374
|
* })
|
|
376
375
|
* ```
|
|
377
376
|
*/
|
|
378
|
-
getLanguage?: (languages:
|
|
377
|
+
getLanguage?: (languages: NoInfer<$Language>[], original: typeof getLanguage) => $Language | (string & Record<never, never>);
|
|
379
378
|
/**
|
|
380
379
|
* Ignores saved language, and uses `getLanguage` to get it on every engine start
|
|
381
380
|
* @default false
|
|
@@ -631,4 +630,4 @@ declare const novely: <Languages extends string, Characters extends Record<strin
|
|
|
631
630
|
destroy(): void;
|
|
632
631
|
};
|
|
633
632
|
|
|
634
|
-
export { type ActionProxy, type AllowedContent, type AudioHandle, type BaseTranslationStrings, type Character, type CharacterHandle, type Context, type CoreData, type CustomHandler, type CustomHandlerFunctionGetFn, type CustomHandlerFunctionParameters, type CustomHandlerGetResult, type CustomHandlerGetResultDataFunction, type DefaultActionProxy, EN, type Emotions, type FunctionableValue, type GetActionParameters, JP, KK, type Lang, type NovelyInit, type NovelyScreen, type Path, type PluralType, type Pluralization, RU, type Renderer, type RendererInit, type Save, type Stack, type StackHolder, type Storage, type StorageData, type StorageMeta, type Stored, type Story, type TextContent, type Thenable, type TranslationActions, type TypewriterSpeed, type ValidAction, localStorageStorage, novely };
|
|
633
|
+
export { type ActionInputOnInputMeta, type ActionInputSetup, type ActionProxy, type AllowedContent, type AudioHandle, type BackgroundImage, type BaseTranslationStrings, type Character, type CharacterHandle, type Context, type CoreData, type CustomHandler, type CustomHandlerFunctionGetFn, type CustomHandlerFunctionParameters, type CustomHandlerGetResult, type CustomHandlerGetResultDataFunction, type Data, type DeepPartial, type DefaultActionProxy, EN, type Emotions, type FunctionableValue, type GetActionParameters, JP, KK, type Lang, type NovelyInit, type NovelyScreen, type Path, type PluralType, type Pluralization, RU, type Renderer, type RendererInit, type Save, type Stack, type StackHolder, type State, type StateFunction, type Storage, type StorageData, type StorageMeta, type Stored, type Story, type TextContent, type Thenable, type TranslationActions, type TypewriterSpeed, type ValidAction, localStorageStorage, novely };
|
package/dist/index.global.js
CHANGED
|
@@ -953,7 +953,7 @@ var Novely = (() => {
|
|
|
953
953
|
data: klona(defaultData),
|
|
954
954
|
meta: [getLanguageWithoutParameters(), DEFAULT_TYPEWRITER_SPEED, 1, 1, 1]
|
|
955
955
|
};
|
|
956
|
-
const
|
|
956
|
+
const storageData = store(initialData);
|
|
957
957
|
const coreData = store({
|
|
958
958
|
dataLoaded: false
|
|
959
959
|
});
|
|
@@ -975,10 +975,10 @@ var Novely = (() => {
|
|
|
975
975
|
const throttledOnStorageDataChange = throttle(onStorageDataChange, throttleTimeout);
|
|
976
976
|
const throttledEmergencyOnStorageDataChange = throttle(() => {
|
|
977
977
|
if (saveOnUnload === true || saveOnUnload === "prod" && !DEV) {
|
|
978
|
-
onStorageDataChange(
|
|
978
|
+
onStorageDataChange(storageData.get());
|
|
979
979
|
}
|
|
980
980
|
}, 10);
|
|
981
|
-
|
|
981
|
+
storageData.subscribe(throttledOnStorageDataChange);
|
|
982
982
|
const getStoredData = async () => {
|
|
983
983
|
let stored = await storage.get();
|
|
984
984
|
for (const migration of migrations) {
|
|
@@ -995,7 +995,7 @@ var Novely = (() => {
|
|
|
995
995
|
stored.data = defaultData;
|
|
996
996
|
}
|
|
997
997
|
dataLoaded.resolve();
|
|
998
|
-
|
|
998
|
+
storageData.set(stored);
|
|
999
999
|
};
|
|
1000
1000
|
storageDelay.then(getStoredData);
|
|
1001
1001
|
const initial = getDefaultSave(klona(defaultState));
|
|
@@ -1013,7 +1013,7 @@ var Novely = (() => {
|
|
|
1013
1013
|
return;
|
|
1014
1014
|
const stack = useStack(MAIN_CONTEXT_KEY);
|
|
1015
1015
|
const current = klona(stack.value);
|
|
1016
|
-
|
|
1016
|
+
storageData.update((prev) => {
|
|
1017
1017
|
const isLatest = findLastIndex(prev.saves, (value) => times.has(value[2][0])) === prev.saves.length - 1;
|
|
1018
1018
|
current[2][0] = intime(Date.now());
|
|
1019
1019
|
current[2][1] = type;
|
|
@@ -1035,7 +1035,7 @@ var Novely = (() => {
|
|
|
1035
1035
|
return;
|
|
1036
1036
|
const save2 = getDefaultSave(klona(defaultState));
|
|
1037
1037
|
if (autosaves) {
|
|
1038
|
-
|
|
1038
|
+
storageData.update((prev) => {
|
|
1039
1039
|
return prev.saves.push(save2), prev;
|
|
1040
1040
|
});
|
|
1041
1041
|
}
|
|
@@ -1050,10 +1050,10 @@ var Novely = (() => {
|
|
|
1050
1050
|
const restore = async (save2) => {
|
|
1051
1051
|
if (!coreData.get().dataLoaded)
|
|
1052
1052
|
return;
|
|
1053
|
-
let latest = save2 ||
|
|
1053
|
+
let latest = save2 || storageData.get().saves.at(-1);
|
|
1054
1054
|
if (!latest) {
|
|
1055
1055
|
latest = klona(initial);
|
|
1056
|
-
|
|
1056
|
+
storageData.update((prev) => {
|
|
1057
1057
|
prev.saves.push(latest);
|
|
1058
1058
|
return prev;
|
|
1059
1059
|
});
|
|
@@ -1137,7 +1137,7 @@ var Novely = (() => {
|
|
|
1137
1137
|
ctx.audio.destroy();
|
|
1138
1138
|
const [time, type] = current[2];
|
|
1139
1139
|
if (type === "auto" && interacted <= 1 && times.has(time)) {
|
|
1140
|
-
|
|
1140
|
+
storageData.update((prev) => {
|
|
1141
1141
|
prev.saves = prev.saves.filter((save2) => save2 !== current);
|
|
1142
1142
|
return prev;
|
|
1143
1143
|
});
|
|
@@ -1208,8 +1208,8 @@ var Novely = (() => {
|
|
|
1208
1208
|
removeContext,
|
|
1209
1209
|
getStateFunction,
|
|
1210
1210
|
languages,
|
|
1211
|
-
|
|
1212
|
-
|
|
1211
|
+
storageData,
|
|
1212
|
+
coreData
|
|
1213
1213
|
});
|
|
1214
1214
|
const useStack = createUseStackFunction(renderer);
|
|
1215
1215
|
useStack(MAIN_CONTEXT_KEY).push(initial);
|
|
@@ -1303,7 +1303,7 @@ var Novely = (() => {
|
|
|
1303
1303
|
const name = (() => {
|
|
1304
1304
|
const c = character;
|
|
1305
1305
|
const cs = characters;
|
|
1306
|
-
const [lang] =
|
|
1306
|
+
const [lang] = storageData.get().meta;
|
|
1307
1307
|
if (c && c in cs) {
|
|
1308
1308
|
const block = cs[c].name;
|
|
1309
1309
|
if (typeof block === "string") {
|
|
@@ -1335,7 +1335,7 @@ var Novely = (() => {
|
|
|
1335
1335
|
function({ ctx, push }, [fn]) {
|
|
1336
1336
|
const { restoring, goingBack, preview: preview2 } = ctx.meta;
|
|
1337
1337
|
const result = fn({
|
|
1338
|
-
lang:
|
|
1338
|
+
lang: storageData.get().meta[0],
|
|
1339
1339
|
goingBack,
|
|
1340
1340
|
restoring,
|
|
1341
1341
|
preview: preview2,
|
|
@@ -1354,13 +1354,13 @@ var Novely = (() => {
|
|
|
1354
1354
|
}
|
|
1355
1355
|
const transformedChoices = choices.map(([content, action2, visible]) => {
|
|
1356
1356
|
const shown = !visible || visible({
|
|
1357
|
-
lang:
|
|
1357
|
+
lang: storageData.get().meta[0],
|
|
1358
1358
|
state: getStateAtCtx(ctx)
|
|
1359
1359
|
});
|
|
1360
1360
|
if (DEV && action2.length === 0 && !shown) {
|
|
1361
1361
|
console.warn(`Choice children should not be empty, either add content there or make item not selectable`);
|
|
1362
1362
|
}
|
|
1363
|
-
return [templateReplace(content, data2),
|
|
1363
|
+
return [templateReplace(content, data2), shown];
|
|
1364
1364
|
});
|
|
1365
1365
|
if (DEV && transformedChoices.length === 0) {
|
|
1366
1366
|
throw new Error(`Running choice without variants to choose from, look at how to use Choice action properly [https://novely.pages.dev/guide/actions/choice#usage]`);
|
|
@@ -1567,7 +1567,7 @@ var Novely = (() => {
|
|
|
1567
1567
|
const {
|
|
1568
1568
|
data: data2,
|
|
1569
1569
|
meta: [lang]
|
|
1570
|
-
} =
|
|
1570
|
+
} = storageData.get();
|
|
1571
1571
|
const obj = values || data2;
|
|
1572
1572
|
const cnt = isFunction(content) ? content(obj) : typeof content === "string" ? content : content[lang];
|
|
1573
1573
|
const str2 = flattenAllowedContent(
|
|
@@ -1585,11 +1585,11 @@ var Novely = (() => {
|
|
|
1585
1585
|
);
|
|
1586
1586
|
};
|
|
1587
1587
|
const data = (value) => {
|
|
1588
|
-
const _data =
|
|
1588
|
+
const _data = storageData.get().data;
|
|
1589
1589
|
if (!value)
|
|
1590
1590
|
return _data;
|
|
1591
1591
|
const val = isFunction(value) ? value(_data) : deepmerge(_data, value);
|
|
1592
|
-
|
|
1592
|
+
storageData.update((prev) => {
|
|
1593
1593
|
prev.data = val;
|
|
1594
1594
|
return prev;
|
|
1595
1595
|
});
|