atrament-core
is a framework for choice-based games, built around inkjs
.
If you need a ready-to-use library for web applications, check out atrament-web.
If you are looking for an example of a web application based on Atrament, check out atrament-web-ui.
- Implements game flow: loading Ink story, getting text content, making choices
- Manages global application settings
- Parses tags, and handles some of them (mostly compatible with Inky)
- Auto-observe variables defined with 'observe' global tag
- Manages sound and music via knot tags
- Manages autosaves, checkpoints, and named saves for every game
- Music state is saved and restored along with game state
- All changes affect the internal state
npm install @atrament/core
Tag | Description |
---|---|
# observe: varName |
Register variable observer for varName Ink variable. The variable value is available in the vars section of Atrament state. |
# persist: varName |
Save this variable value to persistent storage, and restore it before the game starts. |
# autosave: false |
Disables autosaves. |
# single_scene |
Store only the last scene in the Atrament state. |
# continue_maximally: false |
Pause story after each line. In this mode the scene object contains the canContinue field, which is set to true if the story can be continued. |
Tag | Description |
---|---|
# CLEAR |
Clear the scenes list before saving the current scene to Atrament state. |
# AUDIO: sound.mp3 |
Play sound (once). |
# AUDIOLOOP: music.mp3 |
Play background music (looped). There can be only one background music track. |
# AUDIOLOOP: false |
Stop playing all background music. |
# PLAY_SOUND: sound.mp3 |
Play sound (once). |
# STOP_SOUND: sound.mp3 |
Stop playing specific sound. |
# STOP_SOUND |
Stop playing all sounds. |
# PLAY_MUSIC: music.mp3 |
Play background music (looped). There can be multiple background music tracks, played simultaneously. |
# STOP_MUSIC: music.mp3 |
Stop playing specific background music. |
# STOP_MUSIC |
Stop playing all background music. |
# CHECKPOINT |
Save the game to the default checkpoint. |
# CHECKPOINT: checkpointName |
Save the game to checkpoint checkpointName . |
# SAVEGAME: saveslot |
Save the game to saveslot . |
# RESTART |
Start game from beginning. |
# RESTART_FROM_CHECKPOINT |
Restart the game from the latest checkpoint. |
# RESTART_FROM_CHECKPOINT: checkpointName |
Restart game from named checkpoint. |
# IMAGE: picture.jpg |
Adds specified image to the images attribute of the scene and paragraph. It can be used to preload image files for the scene. |
Note: For sound effects, please use either AUDIO/AUDIOLOOP or PLAY_SOUND/PLAY_MUSIC/STOP_SOUND/STOP_MUSIC tags. Combining them may lead to unexpected side effects.
Tag | Description |
---|---|
# UNCLICKABLE |
Alternative names: #DISABLED , #INACTIVE .Sets disabled: true attribute to the choice. |
Atrament version string. Read-only.
Defines interface modules for:
- loader: ink file loader
- persistent: persistent storage
- sound: sound control (optional)
- state: state management
Interfaces should be defined before calling any other methods.
atrament.defineInterfaces({
loader: interfaceLoader,
persistent: persistentInterface,
sound: soundInterface,
state: stateInterface
});
Initialize the game engine. Takes two parameters:
- Story is an inkjs constructor, imported directly from inkjs
- configuration is a configuration object:
- applicationID should be a unique string. It is used to distinguish persistent storage of your application.
- settings is a default settings object. These settings are immediately applied.
import {Story} from 'inkjs';
const config = {
applicationID: 'your-application-id',
settings: {
mute: true,
volume: 10,
fullscreen: true
}
}
atrament.init(Story, config);
Subscribe to specific Atrament events. The listener function is called with a single argument containing event parameters.
You can subscribe to all Atrament events:
atrament.on('*', (event, args) => { ... });
Unsubscribe specified listener from the Atrament event.
Returns Atrament state interface. Can be used to operate state directly:
atrament.state.setSubkey('game', 'checkpoint', true);
Return raw store object. It can be used in hooks, for example:
const gamestate = useStore(atrament.store);
Returns raw interface objects. It can be used to operate with them directly.
const { state, persistent } = atrament.interfaces;
Initialize game object. It is required to perform operations with saves. Parameters:
- path: path to Ink file
- file: Ink file name
- gameID: optional. If provided, Atrament will use the given ID for save management. Otherwise, it will be generated based on path and filename.
Event: 'game/init', { pathToInkFile: path, inkFile: file }
Load Ink file and initialize Ink Story object. Then it updates game metadata and initializes variable observers.
Event: 'game/initInkStory'
Returns save slot identifier for given save name and type.
Possible save types: atrament.game.SAVE_GAME
, atrament.game.SAVE_CHECKPOINT
, atrament.game.SAVE_AUTOSAVE
. For autosaves, the name
parameter should be omitted.
The returned value can be used as a saveslot
parameter.
If the game is started for the first time, or the initialized game is not the same as the current one - call initInkStory
first.
Clears game state, and gets initial data for variable observers.
If saveslot
is defined, load state from specified save.
Event: 'game/start', { saveSlot: saveslot }
Resume saved game:
- if autosave exists, resume from autosave
- if checkpoints exist, resume from the newest checkpoint
- otherwise, start a new game
Event: 'game/resume', { saveSlot: saveslot }
Returns save slot identifier if game can be resumed.
Event: 'game/canResume', { saveSlot: saveslot }
Restart the game from the specified save slot (if saveslot
is not defined, start a new game).
Event: 'game/restart', { saveSlot: saveslot }
Run atrament.game.restart
, then run atrament.game.continueStory()
to regenerate game content.
Load game state from specified save slot.
Event: 'game/load', saveslot
Save game state to save slot.
Event: 'game/save', { type: 'game', name }
Save the game state to the checkpoint.
Event: 'game/save', { type: 'checkpoint', name }
Save the game state to autosave slot.
Event: 'game/save', { type: 'autosave' }
Returns array of all existing saves for active game.
Event: 'game/listSaves', savesListArray
Removes specified game save slot.
Event: 'game/removeSave', saveslot
Returns true
if specified save slot exists.
- gets Ink scene content
- run scene processors
- process tags
- updates Atrament state with a scene content
Event: 'game/continueStory'
Event for tag handling: 'game/handleTag', { [tagName]: tagValue }
Make a choice in Ink. Wrapper for atrament.ink.makeChoice
.
Register processorFunction
for scene post-processing. It takes the scene
object as an argument by reference:
function processCheckpoint(scene) {
if (scene.tags.CHECKPOINT) {
scene.is_checkpoint = true;
}
}
atrament.game.defineSceneProcessor(processCheckpoint);
Returns the full path to asset file (image, sound, music).
Method to call at the game end. It stops music, and clears scenes
and vars
in the Atrament state.
Event: 'game/clear'
Method to call at the game end. It calls atrament.game.clear()
, then clears metadata
and game
in Atrament state.
Event: 'game/reset'
Returns current game session.
Sets current game session. If set to empty value, reset session ID to default.
Event: 'game/setSession', sessionID
Returns list of existing sessions in a { sessionName: numberOfSaves, ... }
format.
Event: 'game/getSessions', sessionList
Delete all saves for a given session.
Event: 'game/deleteSession', sessionID
Initializes Ink story with loaded content.
Event: 'ink/initStory'
Returns current Story instance.
Load Ink state from JSON.
Returns current Ink state as JSON object.
Wrapper for Story.ChooseChoiceIndex
.
Event: 'ink/makeChoice', { id: choiceId }
Wrapper for Story.VisitCountAtPathString
.
Event: 'ink/getVisitCount', { ref: ref, visitCount: value }
Evaluates Ink function, then returns the result of the evaluation. Wrapper for Story.EvaluateFunction
.
Event: 'ink/evaluateFunction', { function: functionName, args: argsArray, result: functionReturnValue }
Returns parsed Ink global tags.
Event: 'ink/getGlobalTags', { globalTags: globalTagsObject }
Returns value of specified Ink variable.
Event: 'ink/getVariable', { name: variableName }
Returns all variables and their values as a key-value object.
Event: 'ink/getVariables', inkVariablesObject
Sets value of specified Ink variable.
Event: 'ink/setVariable', { name: variableName, value: value }
Registers observer for a specified variable. Wrapper for Story.ObserveVariable
.
Go to the specified Ink knot or stitch. Wrapper for Story.ChoosePathString
.
Event: 'ink/goTo', { knot: ref }
When an Ink error occurs, it emits ink/onError
event and calls the errorCallback
function with the error event object as an argument.
Event: 'ink/onError', errorEvent
Returns Scene object.
Event: 'ink/getScene', { scene: sceneObject }
Application settings for your application. Loading, saving, and setting values changes the settings
section of the Atrament state.
However, if you need to perform additional actions when the setting is changed, you can define a handler for it - see below. By default, Atrament handles mute
and volume
settings this way, muting and setting sound volume respectively.
Load settings from persistent storage to Atrament state.
Event: 'settings/load'
Save settings to persistent storage.
Event: 'settings/save'
Returns value of the setting.
Event: 'settings/get', { name: parameter }
Sets value of setting.
Event: 'settings/set', { name: parameter, value: value }
Toggles setting (sets true
to false
and vice versa).
Defines a settings handler.
For example, you have to run some JavaScript code to toggle fullscreen mode in your app.
const fullscreenHandler = (oldValue, newValue) => {
// do some actions
}
atrament.settings.defineHandler('fullscreen', fullscreenHandler);
// later...
atrament.toggle('fullscreen');
// or
atrament.set('fullscreen', true);
// both these methods will change the setting and run the corresponding handler
{
content: [],
text: [],
tags: {},
choices: [],
images: [],
sounds: [],
music: [],
uuid: Number
}
Key | Description |
---|---|
content |
Array of Ink paragraphs: {text: '', tags: {}, images: [], sounds: [], music: []} |
text |
Array of all story text from all paragraphs of this scene |
tags |
Array of all tags from all paragraphs of this scene |
choices |
Array of choice objects: { id: 0, choice: 'Choice Text', tags: {}} |
images |
Array of all images from all paragraphs of this scene |
sound |
Array of all sounds from all paragraphs of this scene |
music |
Array of all music tracks from all paragraphs of this scene |
uuid |
Unique ID of the scene (Date.now() ) |
{
settings: {},
game: {},
metadata: {},
scenes: [],
vars: {}
}
Key | Description |
---|---|
settings |
Single-level key-value store for application settings |
game |
Single-level game-specific data. Atrament populates the following keys: $pathToInkFile, $inkFile, $gameUUID |
metadata |
Data loaded from Ink file global tags |
scenes |
Array of game scenes |
vars |
Names and values of auto-observed variables |
{ id, date, state, game, scenes }
Key | Description |
---|---|
id |
Save slot ID |
date |
Save timestamp |
state |
JSON structure of Ink state |
game |
Content of game from Atrament state |
scenes |
Content of scenes from Atrament state |
Please note that metadata
and vars
from the Atrament state are not included in the save. However, they are automatically populated from the Ink state after loading from a save.
atrament-core
uses dependency injection. It uses inkjs Story
constructor 'as-is', and uses custom interfaces for other libraries.
There are four interfaces in atrament-core
. Their implementation is not included, so developers can use atrament-core
with the libraries they like.
Interface to file operations. The function init
will be called first, taking the path to the game as a parameter. The function getAssetPath
should return the full path of a given file. The async function loadInk
should return the content of a given Ink file, located in the folder defined at the initialization time.
{
async init(path)
getAssetPath(filename)
async loadInk(filename)
}
Interface to persistent storage library.
{
init()
async exists(key)
async get()
async set(key)
async remove(key)
async keys()
}
Interface to state management library.
{
store()
get()
setKey(key, value)
toggleKey(key)
appendKey(key, value)
setSubkey(key, subkey, value)
toggleSubkey(key, subkey)
appendSubkey(key, subkey, value)
}
Interface to sound management library.
{
init(defaultSettings)
mute(flag)
isMuted()
setVolume(volume)
getVolume()
playSound(soundFile)
stopSound(soundFile|undefined)
playMusic(musicFile)
stopMusic(musicFile|undefined)
}
Atrament is distributed under MIT license.
Copyright (c) 2023 Serhii "techniX" Mozhaiskyi