State and settings
State follows the reader through the story. It’s saved in bookmarks, and it rewinds and replays with the reader.
Settings aren’t tied to the reader’s place in the story. They aren’t saved in bookmarks, and they don’t change when the reader steps backward or forward. Use them for things like preferences, unlocked galleries, and achievements.
Scripts loaded from your project.json’s main script share one state object and one settings object. Screenplay expressions can read that state too.
Each plugin has its own private state and settings. To share data with other plugins, a plugin provides selectors.
State and settings are plain objects. Both start out as an empty object, {}, unless you set starting values with defineInitialState or defineInitialSettings.
getState
Section titled “getState”getState returns the current state.
getState<T>(): TExample
const trust = getState().trust;setState
Section titled “setState”setState replaces the current state.
setState<T>(newState: T | ((currentState: T) => T)): TPass the new state, or a function that takes the current state and returns the new one. Use the function form when the new state depends on the current state.
setState replaces the whole object. To change only part of it, use updateState.
Example
setState({ trust: 1, achievements: ["first-blood"],});
setState((current) => ({ ...current, trust: current.trust + 1,}));updateState
Section titled “updateState”updateState merges a value into the current state.
updateState<T>(newState: Partial<T> | ((currentState: T) => Partial<T>)): TPass the values to merge, or a function that takes the current state and returns them.
updateState only merges the top level. Each top-level key you pass replaces the existing value, so spread nested objects to keep their other values.
Example
updateState({ trust: 8 });
updateState((current) => ({ trust: current.trust + 1 }));Settings
Section titled “Settings”getSettings
Section titled “getSettings”getSettings returns the current settings.
getSettings<T>(): TExample
const { volume } = getSettings();setSettings
Section titled “setSettings”setSettings replaces the current settings.
setSettings<T>(newSettings: T | ((currentSettings: T) => T)): TPass the new settings, or a function that takes the current settings and returns the new ones. Use the function form when the new settings depend on the current settings.
Example
setSettings({ volume: 0.8, unlocked: { "prologue-gallery": true },});
setSettings((current) => ({ ...current, volume: 0.8,}));updateSettings
Section titled “updateSettings”updateSettings merges a value into the current settings.
updateSettings<T>( newSettings: Partial<T> | ((currentSettings: T) => Partial<T>),): TPass the values to merge, or a function that takes the current settings and returns them.
updateSettings only merges the top level. Each top-level key you pass replaces the existing value, so spread nested objects to keep their other values.
Example
updateSettings({ volume: 0.8 });
updateSettings((current) => ({ unlocked: { ...current.unlocked, "act-1-gallery": true },}));Starting values and migrations
Section titled “Starting values and migrations”defineInitialState
Section titled “defineInitialState”defineInitialState sets the state your plugin has when the reader starts a new story.
defineInitialState<T>(initialState: T): voidIf you don’t call defineInitialState, your state starts as {}. If you call it twice, Inkweaver throws an error.
When Inkweaver loads saved state, anything missing from the top level comes from your initial state. That only goes one level deep, so to add a value inside a nested object, use a migration.
Example
defineInitialState({ reputation: 0 });defineStateMigration
Section titled “defineStateMigration”defineStateMigration adds a migration that upgrades state saved by an older version of your plugin.
defineStateMigration<T>(version: string, migrate: StateMigration<T>): void
type StateMigration<T> = (oldState: unknown) => T;| Parameter | Value |
|---|---|
version | The version that introduced the change, like "2.0.0". It must be valid semver. |
migrate | A function that takes state saved by the previous version and returns it in this version’s shape. |
When Inkweaver loads state, it runs the migrations for every version after the one that saved the state, oldest first. That’s whatever version your plugin.json said at the time, or project.json for your novel’s own scripts.
If you define the same version twice, Inkweaver throws an error. If the state was saved by a newer version of your plugin than the one that’s running, Inkweaver throws an error and doesn’t load it.
Example
// Before v2, `reputation` was called `trust`.defineStateMigration("2.0.0", (oldState) => { const { trust, ...rest } = oldState; return { ...rest, reputation: trust };});defineInitialSettings
Section titled “defineInitialSettings”defineInitialSettings sets your plugin’s starting settings.
defineInitialSettings<T>(initialSettings: T): voidIt works the same way defineInitialState does.
Example
defineInitialSettings({ masterVolume: 1, musicVolume: 1, voiceVolume: 1,});defineSettingsMigration
Section titled “defineSettingsMigration”defineSettingsMigration adds a migration that upgrades settings saved by an older version of your plugin.
defineSettingsMigration<T>( version: string, migrate: SettingsMigration<T>,): void
type SettingsMigration<T> = (oldSettings: unknown) => T;It works the same way defineStateMigration does, except that Inkweaver loads settings only once, at startup. So if the settings were saved by a newer version of your plugin, the novel doesn’t start.
Example
// V2 splits volume into three channels.defineSettingsMigration("2.0.0", (oldSettings) => { const { volume, ...rest } = oldSettings; return { ...rest, masterVolume: volume, musicVolume: volume, voiceVolume: volume, };});