Expand description
Partial implementation of the Ink markup language for game dialogue.
Ink is a creation of Inkle. For more information about the language, see their website.
§Why inkling?
- Simple interface for walking through branching stories or dialog trees
- Designed to slot into an external framework: like Inkle’s implementation this is not a stand alone game engine, just a processor that will feed the story text and choices to the user
- Rust native, no wrestling with Unity or C# integration
- Support for non-latin alphabets in identifiers
- Few dependencies: currently only
serdeas an optional dependency to de/serialize stories, probablyrandas maybe-optional in the future.
§Why not inkling?
- Fewer features than Inkle’s implementation of the language
- Untested in serious work loads and large scripts
- Not even alpha status, what is this???
§Usage example
§A short story
This example shows the basics of using inkling.
§Parsing the story
use inkling::read_story_from_string;
// Imagine that this is a complete `Ink` story!
let story_content = "\
Hello, World!
* Hello[ back!] right back at you!
";
let mut story = read_story_from_string(story_content).unwrap();§Starting the story processor
// We will supply a buffer for the story to read content into
let mut line_buffer = Vec::new();
// Mark the story as being prepared by calling `start`
story.start().unwrap();
// Begin the story processing: it will return once it encounters the branching choice
let result = story.resume(&mut line_buffer).unwrap();
assert_eq!(&line_buffer[0].text, "Hello, World!\n");§Accessing the content of a presented choice
use inkling::Prompt;
// The story provided us with the choice in the result
let choice = match result {
Prompt::Choice(choices) => choices[0].clone(),
_ => unreachable!(),
};
assert_eq!(&choice.text, "Hello back!");§Resuming with a selected choice
// Resume by supplying a selected choice index and calling `resume`
story.make_choice(0).unwrap();
match story.resume(&mut line_buffer).unwrap() {
Prompt::Done => (),
_ => unreachable!(),
}
// The choice text has now been added to the buffer
assert_eq!(&line_buffer[1].text, "Hello right back at you!\n");§Story loop
The idea is that story processing should be as simple as this loop:
while let Ok(Prompt::Choice(choices)) = story.resume(&mut line_buffer) {
// Present story text to user, then have them select a choice
}An example of a simple command line story player is provided in the examples.
Run cargo run --example player to try it out and browse the source code to see the
implementation.
§Features
A complete recreation of all features in the Ink language is beyond the scope of this project (and my capabilities as a programmer).
Currently the processor supports:
- Structure: Knots, stitches, nested branching choices, gathers, diverts, tags for knots and story
- Choices: Non-sticky, sticky, fallback, line variations, conditions
- Lines: Plain text, diverts, tags, conditions, alternative sequences (all except shuffle)
- Conditions: Nested,
and/orlinking, can check against variables - Reading: Address validation for diverts and conditions. Conditions and expressions are validated after parsing the story.
- Variables: Used as text in sentences and in conditions, can modify from calling program
- Mathematics: In line text, using numbers, parenthesis and variables for all numerical
calculations. Strings can be concatenated using the
+operator.
Hopefully coming:
- Structure: Multi-line blocks, labels
- Variables: Modify in the Ink script
- Reading: Include statements in files
Unlikely features:
- Structure: Threads (maybe?) and tunnels
- Program: Defining functions in the Ink story file, “advanced state tracking,” calling Rust functions from the script to get variables
§De/serializing stories
Enable the serde_support feature to derive Deserialize and Serialize for all
required objects. If you are unfamiliar with serde, this corresponds to reading
and writing finished story files in their current state. In game terms: saving
and loading.
For more information about serde see their website.
§Contributions
I am a complete novice at designing frameworks which will fit into larger schemes. As such I have no real idea of best practices for interacting with an engine like this. If you have a suggestion for how to make it easier for a user to run the processor I would be very glad to hear it!
Likewise, contributions are welcome. Please open an issue on Github to discuss improvements or submit a pull request.
Re-exports§
pub use error::InklingError;
Modules§
- error
- Errors from creating or walking through stories.
Structs§
- Choice
- Choice presented to the user.
- Line
- Single line of text in a story, ready to display.
- Story
- Story with knots, diverts, choices and possibly lots of text.
Enums§
Functions§
- copy_
lines_ into_ string - Read all text from lines in a buffer into a single string and return it.
- read_
story_ from_ string - Read a
Storyby parsing an input string.
Type Aliases§
- Line
Buffer - Convenience type to indicate when a buffer of
Lineobjects is being manipulated.