[Ink](https://www.inklestudios.com/ink/) is a domain specific language for writing interactive fiction. Mostly used for visual novels and similar games, but in theory could be used as the dialogue system for any game. - [Website](https://www.inklestudios.com/ink/) - [Documentation](https://github.com/inkle/ink/blob/master/Documentation/WritingWithInk.md) # Notability Cool DSL, maybe useful for games, or at least inspiration for markup. Integrates with [[Unity Engine]], [[Godot]], [[Unreal Engine]], and others. It is open source, and has an optional custom editor. Used in commercial games made by Ink's own developers, which tend to be heavy on choose-your-own-adventure and light on gameplay. - [Ink Unity Integration](https://github.com/inkle/ink-unity-integration) - [UnrealInk](https://github.com/DavidColson/UnrealInk) (outdated Unreal support, uses [[Mono]]) - [GodotInk](https://github.com/paulloz/godot-ink) # Example ```ink LONDON, 1872 Residence of Monsieur Phileas Fogg. -> london === london === Monsieur Phileas Fogg returned home early from the Reform Club, and in a new-fangled steam-carriage, besides! "Passepartout," said he. "We are going around the world!" + "Around the world, Monsieur?" I was utterly astonished. -> astonished + [Nod curtly.] -> nod === astonished === "You are in jest!" I told him in dignified affront. "You make mock of me, Monsieur." "I am quite serious." + "But of course" -> ending === nod === I nodded curtly, not believing a word of it. -> ending === ending "We shall circumnavigate the globe within eighty days." He was quite calm as he proposed this wild scheme. "We leave for Paris on the 8:25. In an hour." -> END ``` # Markup ## Choices Input is offered to the player via text choices. A text choice is indicated by an `*` character. If no other flow instructions are given, once made, the choice will flow into the next line of text. ``` Hello world! * Hello back! Nice to hear from you! ``` ### Sticky Choices > By default, every choice in the game can only be chosen once. If you don't have loops in your story, you'll never notice this behaviour. But if you do use loops, you'll quickly notice your options disappearing... A sticky choice is simply one that doesn't get used up, and is marked by a `+` bullet. ### Fallback Choices When there are no choices left, choices with no text become live instead. As there is no text, it cannot be displayed. Instead it just jumps directly to whatever divert is specified. Or if you leave off the divert, you can supply response text immediately instead on the next line before continuing as you please. ``` * -> Mulder never could explain how he got out of that burning box car. -> season_2 ``` ### Conditional Choices Choices can be displayed or hidden based on certain criteria. In particular whether or not a knot or stitch has been visited yet. Each time a knot or stitch is visited increments a counter variable for it with the same name as it. Conditions are wrapped in curly braces and require no other keywords. All conditions on a line must be true for that option to be visible ``` * { not visit_paris } [Go to Paris] -> visit_paris + { visit_paris } { not bored_of_paris } [Return to Paris] -> visit_paris ``` Boolean operators supported are: `and`, `or`, and `not`. ### Conditional Text ``` {met_blofeld: "I saw him. Only for a moment. His real name was {met_blofeld.learned_his_name: Franz|kept a secret}." | "I missed him. Was he particularly evil?" } ``` Conceptually this is pretty neat and compact. ### Gathers Below a set of choices if you begin a line with a dash `-` then all immediately above choices ultimately are redirected to this line. ``` === escape === I ran through the forest, the dogs snapping at my heels. * I checked the jewels[] were still in my pocket, and the feel of them brought a spring to my step. <> * I did not pause for breath[] but kept on running. <> * I cheered with joy. <> - The road could not be much further! Mackie would have the engine running, and then I'd be safe. * I reached the road and looked about[]. And would you believe it? * I should interrupt to say Mackie is normally very reliable[]. He's never once let me down. Or rather, never once, previously to that night. - The road was empty. Mackie was nowhere to be seen. ``` This is neat and all, but it seems more intuitive to just implicitly fall through from the above choices instead. Perhaps by making it explicit, there is less chance of the narrative running away by accident and erroring out instead. ### Nested Weaves Using a gather with a set of choices, the document calls "a weave". This is slightly useful when speaking of "nested weaves". Basically indented choices flowing hierarchically with a gather. To indicate that an option is within a nest, just double the leading indicator. Really, this should just be an artifact of the call and response structure. But it seems that it is not whitespace aware so the double indicator is needed instead. ``` - I looked at Monsieur Fogg * ... and I could contain myself no longer. 'What is the purpose of our journey, Monsieur?' 'A wager,' he replied. * * 'A wager!'[] I returned. He nodded. * * * 'But surely that is foolishness!' * * * 'A most serious matter then!' - - - He nodded again. * * * 'But can we win?' 'That is what we will endeavour to find out,' he answered. * * * 'A modest wager, I trust?' 'Twenty thousand pounds,' he replied, quite flatly. * * * I asked nothing further of him then[.], and after a final, polite cough, he offered nothing more to me. <> * * 'Ah[.'],' I replied, uncertain what I thought. - - After that, <> * ... but I said nothing[] and <> - we passed the day in silence. - -> END ``` ### Labels A choice or gather can be labeled with parenthesis after the indicator. ``` * (foo) a foo - (bar) lets all go to the bar ``` These act like knots/stitches and can be diverted to or used in conditionals. For gathers it simply picks up where the gather does, but for options it picks up as if the player had chosen that option. So throwing in a gather even when there's only 1 option can be used as a generic label with a response that any option can jump to. ## Output Replacements Ink has this "clever" quirk. It is for a particular style of writing where you select an answer and then what your character says is slightly different. Everything in the bracket is suffixed to everything before in the option, but once selected, the contents of the brackets are deleted and everything after them is suffixed instead. Ink doesn't have a name for this other than "mixing choice and output text". Example: ``` "What's that?" my master asked. * "I am somewhat tired[."]," I repeated. "Really," he responded. "How deleterious." ``` Produces: ``` "What's that?" my master asked. 1. "I am somewhat tired." > 1 "I am somewhat tired," I repeated. "Really," he responded. "How deleterious." ``` I don't hate it, it's just very specific, and I think doesn't suit my purposes. ### Alternative Text Similar but different to the above. These are actually generators. They use the same curly bracket syntax as conditionals but with pipes between each element. Each variation is stepped through in turn. - Elements in the list can be blank. - Alternatives (brackets) can be nested. - Alternatives can divert to other sections. - Can be used in option text as well as responses, but cannot lead an option text because of the syntax collision with conditionals. #### Sequences When the final variation is displayed, it becomes permanent. ``` The radio hissed into life. {"Three!"|"Two!"|"One!"|There was the white noise racket of an explosion.|But it was just static.} {I bought a coffee with my five-pound note.|I bought a second coffee for my friend.|I didn't have enough money to buy any more coffee.} ``` #### Cycles Cycles are like sequences but they continue to loop through from the beginning once they reach the end of the list. Indicated with an ampersand `&`. ``` It was {&Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday} today. ``` #### Once Only Like sequences except once the end is reached, next time instead of re-displaying the last element, it displays nothing. Indicated with an exclamation point `!`. ``` He told me a joke. {!I laughed politely.|I smiled.|I grimaced.|I promised myself to not react again.} ``` #### Shuffles Produces a random item from the list each time. Indicated with a tilde `~`. ``` I tossed the coin. {~Heads|Tails}. ``` ## Sections Ink calls these "*knots*" because it hates me personally. ``` === top_knot === Hello world! -> END ``` ### Subsections Called "*stitches*", these are just below knots in the hierarchy, making it easier to organize things. ``` === the_orient_express === = in_first_class ... = in_third_class ... = in_the_guards_van ... = missed_the_train ... ``` ``` * [Travel in third class] -> the_orient_express.in_third_class * [Travel in the guard's van] -> the_orient_express.in_the_guards_van ``` If a knot is specified, but a stitch within it isn't, then flow picks up with whatever is after the knot heading. If there is nothing except a stitch, then the stitch will be executed. If there is some text below the knot, then it will be executed instead. This is fine, but oddly, flow doesn't continue through stitches, so you need to explicitly pass control at the end of every section or subsection - including in the initial block beneath a knot before the first stitch. So if you don't include a divert, the engine will just crash. This is intentional. ## Links Called "*diverts*", of course. > You can tell the story to move from one knot to another using `->`, a "divert arrow". Diverts happen immediately without any user input. Example: ``` === hurry_home === We hurried home to Savile Row -> as_fast_as_we_could === as_fast_as_we_could === as fast as we could. ``` Produces: ``` We hurried home to Savile Row as fast as we could. ``` ### Local Jumps Within a knot, all stitches are within context and their knot namespace doesn't need to be repeated. I think this is pretty neat. ``` -> the_orient_express === the_orient_express === = in_first_class I settled my master. * [Move to third class] -> in_third_class = in_third_class I put myself in third. ``` ## Emergent Complexity > Knots and diverts combine to create the basic story flow of the game. This flow is "flat" - there's no call-stack, and diverts aren't "returned" from. > > The very loose structure means writers can get on and write, branching and rejoining without worrying about the structure that they're creating as they go. There's no boiler-plate to creating new branches or diversions, and no need to track any state. With the above 4 components we can do clever things like: ``` === back_in_london === We arrived into London at 9.45pm exactly. * "There is not a moment to lose!"[] I declared. -> hurry_outside * "Monsieur, let us savour this moment!"[] I declared. My master clouted me firmly around the head and dragged me out of the door. -> dragged_outside * [We hurried home] -> hurry_outside === hurry_outside === We hurried home to Savile Row -> as_fast_as_we_could === dragged_outside === He insisted that we hurried home to Savile Row -> as_fast_as_we_could === as_fast_as_we_could === <> as fast as we could. ``` Or even build a whole game of whack-a-mole in just a few lines: ``` === whack_a_mole === {I heft the hammer.|{~Missed!|Nothing!|No good. Where is he?|Ah-ha! Got him! -> END}} The {&mole|{&nasty|blasted|foul} {&creature|rodent}} is {in here somewhere|hiding somewhere|still at large|laughing at me|still unwhacked|doomed}. <> {!I'll show him!|But this time he won't escape!} * [{&Hit|Smash|Try} top-left] -> whack_a_mole * [{&Whallop|Splat|Whack} top-right] -> whack_a_mole * [{&Blast|Hammer} middle] -> whack_a_mole * [{&Clobber|Bosh} bottom-left] -> whack_a_mole * [{&Nail|Thump} bottom-right] -> whack_a_mole * -> Then you collapse from hunger. The mole has defeated you! -> END ``` The above game is entirely random, but there is no need for the writer to save state. Depending on the hidden randomness between the alternate text states between the first alternate and the fallback option, you will either win or lose. ## Inclusion Ink uses the `INCLUDE` keyword (all caps) to include the contents of other files explicitly in the current one. They must always be at the to of the file though. ## Variables > Global variables can be defined anywhere, via a `VAR` statement. They should be given an initial value, which defines what type of variable they are - integer, floating point (decimal), content, or a story address. ```ink VAR foo = 1 * {foo} I am one or more foo. * {not foo} I pity the foo. - I have {foo} foos. ``` ## Functions > Functions can only be called when used within code in ink. This means they either appear within opening and closing curly brackets or on lines starting with the tilde (~) as part of variable reassignment. ### Internal ```ink > {function(params)} ~ function(params) ``` ### External External functions can be defined in an external library, bound to the Ink library with some C# code, and then Ink can hook into it with a directive. ```ink EXTERNAL functionName(params) ``` Then it can be used like an internal function: ```ink ~ functionName("parameter") ``` ## Spooky Action Ink also provides 2 additional features which allow the engine to take actions based on Ink's state: variable observers and custom parsers. ### Variable Observers Ink can run a callback when it changes a variable. This is set up in C#. ```c# _inkStory.ObserveVariable ("health", (string varName, object newValue) => { SetHealthInUI((int)newValue); }); ``` ### Parsers Much like [[Aurora Dialogue System]], there is a capability to pass the text to the engine for processing first, and the Ink developers use this to add extended features. For example they used plain text commands embedded in the Ink script to tell the engine to change camera angles. ```ink >>> SHOT: view_over_bridge ``` # Code ## Lists Lists create enums with a set of values. # See Also Other interactive fiction languages: [[Twine]], [[Inform]], [[Ink (programming language)]] # Resources ```cardlink url: https://github.com/inkle/ink-library title: "GitHub - inkle/ink-library: A collection of ink samples, tools and a list of projects that use ink" description: "A collection of ink samples, tools and a list of projects that use ink - GitHub - inkle/ink-library: A collection of ink samples, tools and a list of projects that use ink" host: github.com favicon: https://github.githubassets.com/favicons/favicon.svg image: https://opengraph.githubassets.com/16f01e4e28316688874652c480a2350a74c1e681b18aaa3818bb7d87cd562261/inkle/ink-library ``` - https://subscription.packtpub.com/book/game-development/9781801819329/5/ch05lvl1sec28/calling-functions # References - https://cohost.org/exodrifter/post/5659898-ink-lists-are-hell