inkle/ink

Ink is inkle's scripting language for writing interactive narrative, both for text-centric games as well...

inkle's scripting language for writing interactive narrative, both for text-centric games as well as more graphical games that contain highly branching stories

Ink is inkle's scripting language for writing interactive narrative, both for text-centric games as well...

ink

Ink is inkle's scripting language for writing interactive narrative, both for text-centric games as well as more graphical games that contain highly branching stories. It's designed to be easy to learn, but with powerful enough features to allow an advanced level of structuring.

Here's a taster from the tutorial.

- 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

Getting started

Download Inky, our ink editor, and the follow either:

  • The basics tutorial if you're non-technical and/or if you'd like to use ink to make a web-based interactive fiction game
  • The full tutorial if you want to see everything that ink has to offer.

For those who are very technically-minded, you can also use inklecate directly, our ink command line compiler (and player).

To keep up to date with the latest news about ink sign up for the mailing list.

Writing with Unity

  • Download the latest ink-unity-integration package, or grab it from the Unity AssetStore, and place in your project.
  • Create a .ink text file such as myStory.ink, containing the text Hello, world!.
  • Select the file in Unity, and you should see a Play button in the file's inspector.
  • Click it, and you should get an Editor window that lets you play (preview) your story.
  • Follow the tutorial: Writing with Ink.

Ink, Inky, ink-unity-integration, inkjs, inklecate, inkle oh my!

  • Ink is the core narrative engine itself, written in C#. It includes the code for the compiler. If you're not technical, you don't need to worry about this.
  • Inky is our ink editor, which is a text editor with support for playing as you write. If you're just starting out with ink, this is all you need.
  • ink-unity-integration is a plugin to allow you integrate the ink engine with a Unity game. It includes the full Ink engine source.
  • inklecate is a command-line compiler for ink. Inky uses it behind the scenes.
  • inkjs is a JavaScript port of the ink engine, useful for powering web-based game. This is included when you export a story for web within Inky.
  • inkle is the game development studio that created ink
  • inklewriter is an unrelated interactive story creation tool that designed to be easy to use, but is far less powerful. It's possible to export inklewriter stories to ink, but not vice versa.

What you need if you are a:

  • Writer: Inky
  • Unity game developer: ink-unity-integration plugin. Optionally, Inky if you're reading/writing the ink too.
  • Web-game author: Inky

Versioning

The intention is the following:

  • Each latest ink/inky/ink-unity-integration release on each Github release page should work together. Ink and Inky version numbering are separate though. You can see which version of the ink engine Inky has in the About box.
  • ink / ink-unity-integration should effectively have the same version of the same engine, except that the integration might have additional Unity-specific extra minor releases. Their X.0.0 and 0.Y.0 version numbers should match. The 0.0.Z version number in ink-unity-integration may diverge to reflect Unity-specific changes.
  • inkjs is maintained by the community (primarily @y-lohse and @ephread). It's usually one major version behind the main ink engine, but they work hard to catch up after each release!
  • The ink engine also has story-format and save-format versions that are internal to the code (see Story.cs and StoryState.cs).

Advanced: Using inklecate on the command line

  • Download the latest version of inklecate (or build it yourself, see below.)

  • Create a text file called myStory.ink, containing the text Hello, world!.

  • On the command line, run the following:

    Mac: ./inklecate -p myStory.ink

    Windows: inklecate.exe -p myStory.ink

    Linux: mono inklecate.exe -p myStory.ink

    • To run on Linux, you need the Mono runtime and the Mono System.Core library (for CLI 4.0). If you have access to the debian repository, you can install these using:
      sudo apt install mono-complete

    The -p option uses play mode so that you can see the result immediately. If you want to get a compiled .json file, just remove the -p option from the examples above.

  • Follow the tutorial: Writing with Ink.

Integrating into your game

Full article: see Running Your Ink.

For a sample Unity project, see The Intercept.

Ink comes with a C#-based (or JavaScript-based runtime engine that can load and run a compiled ink story in JSON format.

To compile the ink, either export from Inky (File -> Export to JSON). Or if you're using Unity, you can use the ink-unity-integration package which will automatically compile your ink for you whenever you edit it either in Inky or in an editor of your choice.

Advanced: You can also use the inklecate command line tool to compile ink stories, or you can call the compiler from C# code yourself.

ink isn't designed as an end-to-end narrative game engine. Rather, it's designed to be flexible, so that it can slot into your own game and UI with ease. Here's a taster of the code you need to get started:

using Ink.Runtime;

// 1) Load story
_story = new Story(sourceJsonString);

// 2) Game content, line by line
while(_story.canContinue)
    Debug.Log(story.Continue());

// 3) Display story.currentChoices list, allow player to choose one
Debug.Log(_story.currentChoices[0].choiceText);
_story.ChooseChoiceIndex(0);

// 4) Back to 2
...

The development of ink

Build Requirements

All Environments:

  • .NET Core SDK 3.1 or newer
  • Optionally Visual Studio Code

Windows (Optional):

  • Visual Studio (e.g. Community edition); required to build nuget package with multi-targeting of .NET Framework 3.5
  • Xamarin, or Unity's own version of MonoDevelop

Mac (Optional):

  • Visual Studio for Mac
  • Xamarin, or Unity's own version of MonoDevelop

Building with Visual Studio

  1. Load up the solution file - ink.sln.
  2. Select the Release configuration and choose Build -> Build All (or Build Solution in Visual Studio).
  3. The compiler binary should be built in inklecate/bin/Release (or x86), while the runtime engine DLL will be built in ink-engine-dll/bin/Release/ink-engine.dll

Building with command-line

  1. cd to the project you want to build (e.g., cd inklecate)
  2. Build using dotnet: dotnet build -c Release
  3. To run console apps: dotnet run -c Release
    • To produce self-contained executable: dotnet publish -r win-x64 -c Release --self-contained false
    • Recommended RIDs for the platform (-r) are: win-x64, linux-x64, and osx-x64

To run the binaries, you need to install .NET Core Runtime 2.2 or newer (included in SDK).

Need help?

  • Discord - we have an active community of ink users who would be happy to help you out. Discord is probably the best place to find the answer to your question.
  • GitHub Discussions - Or, you can ask a question here on GitHub. (Note: we used to use Issues for general Q&A, but we have now migrated.)

How to contribute

We’d of course appreciate any bug fixes you might find - feel free to submit a pull request. However, usually we're actively working on a game, so it might take a little while for us to take a look at a non-trivial pull request. Apologies in advance if it takes a while to get a response!

Architectural overview

See the architectural overview documentation for information about the pipeline of the ink engine, and a birds-eye view of the project's code structure.

License

ink is released under the MIT license. Although we don't require attribution, we'd love to know if you decide to use ink a project! Let us know on Twitter or by email.

Support us!

ink is free forever, but represents multiple years of thought, design, development and testing. Please consider supporting us via Patreon. Thank you, and have fun!

Issues

Quick list of the latest Issues we found

Shinigami072

Shinigami072

Icon For Comments0

It seems that since version v1.0.0 launching on linux does NOT require to be run by mono inklectate like in the Readme file - instead as suggested by SirMetathyst — 07/03/2022 on discord the program should be run directly inklecate, in addition the current rreadme file still contains the inklecate.exe filename - from when the linux and windows were packaged in the same file.

To reproduce:

Ubuntu 20.04 (With mono-complete installed)

Try to run mono inklectate Result:

julioydidingsda

julioydidingsda

Icon For Comments3

Hello, hopefully someone can help me with this issue:

The contents of all my Ink files have been erased! When I open the file everything is blank, not a line of code. And the file size is also 0KB everywhere. I have several inky files in which I've written dialog for my Unity game. From one day to the next, the files were empty!

Has anyone had this error and gotten it fixed? How can something like that happen? What can I do?

I can't have written everything for nothing :(

LudoNarraBrit64

LudoNarraBrit64

Icon For Comments0

Hi all, Games student here making a visual novel. It has been a blast using Ink and I want to carry on learning it for future projects. Just a couple of questions:

How would I tell my game to run a credits sequence once my ink file hits ->END? I don't know what the exact syntax would be but is there a way to say if(CurrentLine == "->END") or something to that effect?

Secondly, I've got a section at the end of the game where the two main characters have a massive blowout argument and I want the player to make two quick choices in rapid succession. At the moment, the player makes the first choice (Henry's choice), the line prints in the text box, but then clicking to play the next line won't do anything. This is what the flow looks like in Inky. What am I missing here? image

Thanks, Charles

russellquinn

russellquinn

Icon For Comments0

When loading a saved state for an Ink story that has changed significantly (common during development), I'm getting frequent crashes in Callstack.cs's warning about the story location being approximated, because pointer.container — referenced in the warning message — is null.

The warning in Callstack.cs that is causing the problems:

If I just take out the warning, everything works. The location is approximated correctly elsewhere in the Ink runtime and I can use my saved state.

I'll submit a pull request that lets this warning be more flexible and not crash.

Malyglut

Malyglut

Icon For Comments0

I'd like to display certain information next to a dialogue line in my UI depending on whether the dialogue line leads to an external function call.

In order to do that I need to flag these responses on the game side as responses that lead to an external function call.

I've been trying to find a way to retrieve this information at runtime but it seems that the answers themselves have no direct connection to the call but rather point to a different object in the content that contains the information regarding the external function call.

My question is: is it possible to retrieve this information and if so, what would be the best way to approach it?

(I am aware that I can just tag these lines, I'm looking for a solution that doesn't involve tags)

tomkail

tomkail

Icon For Comments1

We've got an ink list that looks like this: LIST valuesList = Family, Happiness, Love

The docs show how to create a new ink list from C#, but not how to manipulate an existing one. What is the correct way of toggling the items in that list from C#?

Our attempts to get this working have worked, except that for some reason the list isn't saved when exporting story state to json. We're assuming that the reason is that we're referencing the wrong instance of list items, or something.

What we're doing is:

  1. Getting the full list of InkListItem from the InkList.origins
  2. Getting the InkList from variableState["valuesList"]
  3. Using AddItem/Remove to toggle the items in the list.

Is this the correct way to do this?

KDmasterLOL

KDmasterLOL

Icon For Comments0

When i try to load knot table more than two times i get error VAR PlayerName = "Player" VAR ReadTableCount = 0 === table

  • {table.text==0}-> table.text
  • ->END = text Table: Welcome to this world {PlayerName}: What is the place Table: you have question, but they don't matter, practice do impact Table: you better go through green road. ->END === Hint_how_to_cook I know how to cook but you peace off. ->DONE Could not divert to table! Only Knots and Stitches can be diverted to. Is this a function? Alternatively, the path might lead to an error, which we prevent from occuring in this tool to safeguard the state. Ink.Runtime.StoryException: Ink had 1 error. It is strongly suggested that you assign an error handler to story.onError. The first issue was: RUNTIME ERROR: ran out of content. Do you need a '-> DONE' or '-> END'?
iCrazyBlaze

iCrazyBlaze

Icon For Comments0

Are you able to create functions which have optional parameters? My external function for playing sounds has only the clip required, but it also has options for volume, pitch etc. that I might not need/want to change.

neekocy

neekocy

Icon For Comments1

I want that after my character stops talking, unity changes to another scene. I think it should be made with tags but i can't seem to get it right.

pixitales

pixitales

Icon For Comments1

on ink file I wrote this on the very top: EXTERNAL PlaySound(name)

My fallback ===function PlaySound(name)=== ~ return

to call the function I wrote {PlaySound("SlipFall")}

and Unity gives me an error: ERROR: Missing function binding for external: 'PlaySound' , and no fallback ink function found. Im using Unity 2021 LTS. Anyone know how to fix?

tustin2121

tustin2121

Icon For Comments0

The "Advanced feature" mentioned in Writing With Ink that says that you can return from a tunnel but divert elsewhere using the syntax ->-> target will crash the ink compiler with a NullReferenceException if that target is a variable. See the following story file:

In Inky this results in the following exception presented in the story pane:

Fortunately, this can be worked around at the moment with the following edit:

russellquinn

russellquinn

Icon For Comments2

If I have a global LIST, e.g.:

I can get this list from within Unity with:

And when I do unityList.origins, it is populated and usable. I can also use .all and other properties that depend on origins being populated. All good so far.

If I then do SaveJson() and LoadJson() I can get the list from variablesState again, and origins is populated just like before. Still… all good.

However, if the Ink script has changed the value of some_list before the save, e.g.:

And then I SaveJson() and LoadJson(), when I get the list from variablesState this time, .origins is null.

.origins remains null until the Ink script encounters some code that evaluates this list. (It can be assigning a new value or just checking its contents.) Then origins gets populated once again.

From stepping through the Ink-runtime code, it looks like StoryState::PushEvaluationStack(Runtime.Object obj) is responsible for populating origins and I think this is where the problem lies.

When an Ink Story is first loaded from C#, PushEvaluationStack gets called on every global var, thus populating origins in all the lists. If you then do a LoadJson(), and the List value hasn't changed, this initial init is still valid and everything works. But, if the value has changed, it looks like the list is re-created(?) and this time, PushEvaluationStack does not get called (until it is used in some Ink code that is run later).

In summary, I think the problem in the Ink runtime might be:

  1. PushEvaluationStack is called on all global Ink vars at the start.
  2. After a LoadJson(), it looks like LISTs that no longer have their default value are re-created in memory.
  3. But, PushEvaluationStack is not called on these, leaving them not fully initialized.

UPDATE: I have opened a PR with a fix, which solves the problem I was having with this. But, I'm pretty sure it should be fixed in a more comprehensive way. Hopefully this PR is a useful start though: https://github.com/inkle/ink/pull/764

Codexehow

Codexehow

Icon For Comments2

Sorry if this is the wrong place to ask.

I'm wanting to program a simple Oregon Trail type of game in Ink. But I can't find a simple way to have a central hub from which to select random events (knots).

The central gameplay loop is a knot that displays variables as you go (the camp/wagon, I guess). Then the player would select from random things to do and would have to make choices in those knots. Then they come back.

The issue I'm having is that I can't find a simple way to have random knots presented to the player.

I came across this code on a GetHub thread, but I can't make it work:

{

  • RANDOM (1, 3): -> a
  • RANDOM(1,2): -> b
  • else: -> c }

I'm not a programmer, so I can't do much :P.

Any help would be greatly appreciated. Particularly if there is a way to make images come up randomly too. That would be amazing. I know Ink was not designed with images in mind, but that would be swell.

tcconway

tcconway

Icon For Comments0

I see that the various choices have . Would it be possible to have the chosen choice have ? That way we could stylize the chosen choice in CSS :) thanks!

tustin2121

tustin2121

Icon For Comments0

I'm in the process of making a port of the Ink Runtime Engine, so of course I'm basically auditing the code for everything while I'm figuring out how to port it. I'm in the last stages, porting the insanely long Story class, and I noticed something really odd:

https://github.com/inkle/ink/blob/e10a231eecd94f9155e9cf527e3712a6b43e9083/ink-engine-runtime/Story.cs#L2221-L2231

The _hasValidatedExternals internal variable is never set to false anywhere in the code base. In fact, it's set to true twice in a row in the snippet of code linked above. This seems like a bug, because _hasValidatedExternals either means that validation of external bindings has occurred or that said validation was successful, and this code seems to imply it means both? And I would think in either case, this variable should be getting set to false in BindExternalFunctionBase, to indicate that it needs to re-validate the external bindings. But it doesn't.

I imagine this is usually not a problem because the most common use case will bind functions on initialization of the game and never need to create additional bindings or change existing ones. Still seems like a bug, though, so I wanted to write it up here for future reference (and so I can reference something when my port deviates from upstream to fix this).

tcconway

tcconway

Icon For Comments1

Would love to see a "table of contents" feature, showing all knots as a list.

I imagine clicking on the 3-bars (the hamburger menu) at the top left of the app and see a top-tabbed interface.

A tab could be "Knots", where it lists all the knots in the current file, based on the order they are written. Clicking one of them would jump the editor to that specific knot. This would be extremely helpful in working on large .ink files.

Another tab could be "files" as currently shown in the menu.

Thanks.

marianaloa

marianaloa

Icon For Comments1

Hi! im trying to have something like if(knotName = “Final1”) { button.gameObject.SetActive(true) } else { button2.gameObject.SetActive(true) }

idk if it’s possible that depending on the knot you are in it’s going to let me appear those buttons

i think my question is how do i call the knot to do that

tcannonfodder

tcannonfodder

Icon For Comments13

This issue is to kick off a project discussed in the Inkle discord with @ephread: writing a specification for Ink runtimes.

Reason

Right now, building an ink runtime requires reverse-engineering the C# implementation, which causes a few issues:

  • Increased engine developer burden
  • (Likely) differences between runtimes, making ports harder
  • Code that will feel like the C# implementation, rather than writing a language/platform-specific implementation that matches the existing ecosystem

How we can help

If runtime developers collaborate & write a specification for:

  • The high-level architecture of an Ink runtime
  • A comprehensive spec for the compiled JSON bytecode
  • How to parse & prepare the JSON bytecode from Ink exports
  • How state saving & restoration should work
  • How external function calls work in Ink
  • Accompanying tests, including a defined "call and response" test suite that plays through a complete, compiled Ink story over various paths to verify the behavior ( https://github.com/inkle/the-intercept feels like a great candidate for this)

We can dramatically improve Ink's tooling, and pave the way for better adoption across the board; both for current & future tools.

lulamorashi

lulamorashi

Icon For Comments0

Hi! I'm writing an escape room game in Ink and will be uploading it to itch.io directly afterwards, so limited just to what is available in the css and main.js files. I have about seven to eight choices at some points in the game, so I would like to be able to divide the choices into two columns (every time there are 5+ options). I've tried just going into the css file and adding "columns: 2;" but all this does is move the existing list of choices over to the left, and never divides them. Please help!

mcbaya

mcbaya

Icon For Comments2

Hello! I have noticed that the way Inky applies colour to logic statements in the editor is different than I would expect.

Consider this screenshot:

inky logic screencap

The code works perfectly, in that it displays "the window" whether the player is a human or a cyborg.

However, the text "human || cyborg:" is not entirely green. I would expect it to be entirely green, like other logical and conditional statements.

This happens with "OR" statements, but if I switch to "AND" it displays as I would expect:

image

I'm using Inky 0.12.0 on Windows 11.

Thank you!

Milo-Mesdag

Milo-Mesdag

Icon For Comments1

I'm having an issue in which some choices in my story cause the page to jump to the top of the story and then stop responding to scrolling (scroll wheel and dragging the scroll bar). The problem is not consistent and does not happen every time the same options are picked. The problem is only occurring in web based versions of my game, I have not been able to reproduce it in Inky. I've encountered it in both Firefox and Google Chrome.

The problem can be worked around by saving the story (after it has jumped to the top and stopped scrolling) and then refreshing the page. Saving and loading again without refreshing makes the most recent part of the story appear at top, but still leaves me unable to scroll.

My story does contain elements of randomness, and after each choice has been selected I check a die roll against a stat to see which knot or stitch should be jumped to. I don't know if this is relevant, as the issue only happens on certain choices and even then only on certain occasions, sometimes working for both successes and failures, and sometimes not, again for both successes and failures.

I imagine that the issue lies not in my .ink file but in some of the scripts that make it run online (as I have not found any such issue within Inky's play-as-you-write system itself), but I know nothing about how those scripts work so I couldn't say any more.

lordolives

lordolives

Icon For Comments0

I'm currently running Ink version 1.0 on Windows 10 20H2. This seems like a small thing, but I can't seem to find a way to save the current project as another file. Most programs tend to have Save and Save As but I'm not seeing a Save As in Ink.

I guess as a workaround I can open a new project window, perform a copy and paste, and then select Save Project which will prompt to save. This works but it would be really nice to be able to just Save As from a current project.

calummackervoy

calummackervoy

Icon For Comments0

I've been working on a browser-based text adventure game using React. I've been using Windups and JavaScript to animate parts of the text. I started modelling the dialogue with Ink and I love the syntax, and it's so much more readable than hundreds of lines of JavaScript code!

I saw that in Heaven's Vault the devs extended Ink with some custom syntax, and if it's possible I'd like to extend it myself too

## Instructing text animation

Parts of the animations I'm using will require custom code to achieve it, but it would be great if other parts of this could be written into the Ink files, for example when to pause and controlling the pacing of the typewriter animation

Including information about who is doing the speaking

The picture and name of the performer who is speaking. Currently represented as a JSON object

## Querying semantic markup for decentralised games

Essentially the idea to achieve this is as follows:

  • the game state is modelled using linked data (a protocol allowing apps to share schema and become interoperable)
  • Inside of the Ink code the most simple extension I can think of is to write a condition with an endpoint ({ https://my-endpoint..}), which will trigger the client to make a GET request to that endpoint, expecting to receive a file which it can compare with the game state (this file is known as a Shape, and if the shape passes, then the Ink condition passes)
  • Including writing to linked data, updating the game state as I can using Ink "locally". I had in mind a similar solution, something like ~ https://my-endpoint... which would return a suggested JSON object containing requested game-state changes for the client to make. If I could achieve all of this then I think the feature set would be pretty comprehensive

What is my question?

I'm wondering if you're able to comment on if you think it is realistic to achieve this without being "internal" to this project and if there are any initial pointers that you can give me to help me on my way?

The previous solution is using JavaScript code for the "local" stories, and it is also able to render linked data stories, but the latter is not accessible for developers and the feature set is primitive compared to what can be done with Ink

Versions

Quick list of the latest released versions

v1.0.0 - Feb 22, 2021

ink has finally reached version 1.0!

We realise that it took us so long to get to this point, and in particular that it's been such a long since our last release. Game development can be very intense, so simultaneously maintaining a project such as this one at the same time can be a little tricky.

However, the great news that happened in November 2020 is that we become a proud recipient of an Epic MegaGrant, which will hugely help in the support of the development of ink.

Here are the release notes for version 1.0. There are major new features, though they're mostly on the game side of things rather than the format or writing side of things:

Major new feature: Multiple parallel flows (BETA)

It's now possible to have multiple parallel "flows" - allowing the following examples:

  • A background conversation between two characters while the protagonist talks to someone else. The protagonist could then leave and interject in the background conversation.
  • Non-blocking interactions: you could interact with an object with generates a bunch of choices, but "pause" them, and go and interact with something else. The original object's choices won't block you from interacting with something new, and you can resume them later.

The API is relatively simple:

  • story.SwitchFlow("Your flow name") -- create a new Flow context, or switch to an existing one. The name can be anything you like, though you may choose to use the same name as an entry knot that you would go on to choose with story.ChoosePathString("knotName").
  • story.SwitchToDefaultFlow() -- before you start switching Flows there's an implicit default Flow. To return to it, call this method.
  • story.RemoveFlow("Your flow name") -- Destroy a previously created Flow. If the Flow is already active, it returns to the default flow.

For now it should be considered a beta feature, since it's brand new and relatively untested. We will also be keeping an eye on how people make use of it to see if API improvements can be made.

ink now has a proper bool type

  • Booleans are now supported natively in-engine, but they also implicitly coerce to integers to support old behaviour of true + 1 == 2. This is useful when you want to convert a flag (have you done X) to a counter (how many times have you done X).

Significant technical improvements:

These are significant improvements, however they may cause breaking changes to your existing setup:

  • Better EXTERNALs behaviour: Many people were having issues where their EXTERNAL functions were unexpectedly called twice. This was because of the way the story may sometimes look ahead to see what's coming so that glue can function properly. We have now changed the behaviour so that by default it will do exactly what you expect and only ever be called once. However, if you need your functions to be compatible with glue functionality, you can pass the new lookaheadSafe parameter when you bind a function. For more on this, see the documentation, and original discussion of the issue.
  • Better error handling: Redesign the way error handling works, so that it's not possible to accidentally ignore errors by default. Previously the Story would silently generate errors that you were supposed to check from story.hadError and inside story.state.currentErrors but it was far too easy to forget to do this when starting a new integration into a game. The new behaviour is that you need to bind a callback to story.onError. If you don’t, it will throw an exception on error, so you’ll definitely see it.

Other improvements

The rest of the changes are primarily technical and bug fixes:

  • New: InkList.FromString(shortName, story) function
  • New: Events in runtime story: onDidContinue, onMakeChoice, onEvaluateFunction, onCompleteEvaluateFunction, onChoosePathString. These aren’t recommended as the general approach, but in some cases they are necessary - for example ink-unity-integration’s player window
  • New: ink engine and inklecate now uses dotnet core (Thanks @13xforever!)
  • Support for work-in-progress ink language server by @ephread: Support for character ranges in debug line metadata. Use identifier type rather than plain strings internally in compiler so that you can find a location where a symbol was defined.
  • All internal members have now been marked public. Although this is a little risky and I liked the smaller API footprint, I’d rather have the engine more easily hackable/dangerous than blindly prevent people from using certain functionality.
  • Stats switch on command line compiler: “-s”. Designed for use in Inky’s “word count and more” option.
  • New JSON communication protocol in inklecate for more robust integration with Inky.
  • Added overload of BindExternalFunction that takes 4 parameters
  • Allow passing ink list as function argument
  • Unicode character ranges: support for Korean (thanks @jungeunjin-dev), Latin1Supplement (thanks @landryyrdnal)
  • Fix for broken not-equals operator on divert targets. (thanks @russellquinn)
  • Fix for rolling back state after having followed a default choice 😱
  • Fix for integer overflow when calling RANDOM
  • Fix for error in the profiler when current pointer is null
  • Prevent super large integers from being successfully parsed yet coming out as zero
  • Fix for visit counts of choices/gathers being wrong when they're in a container nested as the first container of another container
  • Prevent storing a divert target to a knot/function that takes by-reference arguments since it’s not possible to call correctly at runtime
  • Ensure keys are removed from _variableObservers if they are empty. (thanks @mdorr)
  • Observer param in RemoveVariableObserver now optional (thanks @michael-badrobotgames)
  • Improved some error messages

0.9.0 - Jun 10, 2019

First post-Heaven's Vault release! And as such, this mainly brings in features that we developed while working on Heaven's Vault.

  • Shuffle-once and shuffle-stopping: New variants for shuffle sequences that allow you to change what happens at the end of a shuffled sequence. Adding 'once' does nothing when the sequence is finished (can be written ink {shuffle once: ...} or abbreviated {~!: ...}. Meanwhile, adding 'stopping' repeats the final branch of a sequence when the shuffle is done: {shuffle stopping: ...} or abbreviated: {~$: ...}.
  • Background saving of state: If you have a lot of state to save, it's now possible to save on a separate thread (preventing frame drops), and while continuing to evaluate the story on the main thread. It can do this safely by making the main state read-only while saving, and writing any state changes to a temporary "diff patch", which is applied when the save thread is complete. Call var stateToSave = story.CopyStateForBackgroundThreadSave() and then when you're done call story.BackgroundSaveComplete().
  • More efficient/faster main runtime (when calling Continue). When the engine looks ahead for whitespace it previously had to do a lot of copying/restoring the entire state. Now there's a patching system so that only small "diff patches" get created during evaluation - much faster!
  • Saving story state faster and memory efficient (generating less garbage). Faster when variable values are still set to their initial defaults - it only saves out those that have changed.

Unity integration package available here

0.8.3 - Jun 05, 2019

Bug fixes! See the full list of changes by browsing the changes on github.

0.8.2 - Aug 16, 2018

Ink unity integration package coming soon!

  • Added LIST_RANDOM(list): return a random item from a list
  • Added TURNS(): Which turn number are you on? i.e. how many choices have you taken? (starts at zero)
  • Added POW(x, y): Mathematical function to calculate "x to the power of y".
  • Added FLOOR(x): Round down to the nearest whole number, returning a float (or doing nothing if int is passed).
  • Added CEILING(x): Round up to the nearest whole number, returning a float (or doing nothing if int is passed).
  • Added INT(x): Cast float to an int (truncating).
  • Added FLOAT(x): Cast int to float.
  • Repurpose LIST_RANGE(list, min, max) to use a variable list rather than a list definition. To get the previous behaviour, use LIST_RANGE(LIST_ALL(list), min, max). Min and max can either be the int value of list items, or they can be the list items themselves - e.g. LIST_RANGE(cakes, FrenchFondant, ChocolateGateaux) or LIST_RANGE(cakes, 3, 6).
  • {myList:...} now means "if myList contains any items" instead of "if myList contains a non-zero valued item".
  • Better naming collision detection (e.g. between stitch names, checking for reserved words and built in functions)
  • Add string “not contains” operator: “hasnt” and “!?” for checking existence of substrings.
  • Add support for != operator on divert targets.
  • Compiler code tweaks so that it can be included in Unity code (so you can compile ink stories live in your game for user contributed stories etc). Thanks @KumoKairo!
  • Support for pasting a runtime path in the "Go to anything..." box in inky, useful when you have a bug in Unity and only have a runtime path: Play mode in compiler can now take a query of the form DebugPath hello.world.0.1 for looking up runtime paths and returning line numbers from original source files.
  • Fix for major long-standing read count bug, where read counts sometimes weren't correctly incremented when diverting to a gather or choice label if it was already close to the label at the point it diverted.
  • Fix for choices having broken threads when saved/loaded
  • Fix for excessive inline whitespace.
  • Fix for functions being called on tilda logic lines not being appended with a newline when they need one.
  • Fix for RemoveVariableObserver.
  • Fix for a divert target being used as a function parameter incorrectly being seen a normal divert, and therefore being incorrectly ignored as a loose end: e.g. + choice {f(-> g)}
  • Fix for compiler crash when target not found during tunnel onwards (e.g. ->-> onwardPathNotFound)
  • Warn, don’t crash, when there’s a duplicate list item in a list. Error when there's a duplicate item in a list definition.
  • Give sensible error when attempting to pass a knot to a function that takes a variable reference, instead of crashing
  • Clearer error when reporting missing variable - tell us which variable was missing!

0.8.1 - Apr 24, 2018

Version 0.8.0 is a major ink update, including lots of improvements and fixes under the hood! This is version 0.8.1 which includes a few bonus extra fixes. Combined 0.8.0 and 0.8.1 release notes:

ink language changes

  • "Loose end" detection is now far more robust. Previously it wouldn't detect loose ends in even in simple choices, only in a sequence of "flat" content. (https://github.com/inkle/ink/issues/416)

  • White-space handling is a bit different since handling was re-written in the engine (see below). Most obvious difference the following: previously it would be one line, whereas now there will be a newline inserted:

    Hello {true: ...world. }

    Version 0.8.1 also includes this fix.

  • Temporary values may not be accesssed by stitches from their parent knot, or across stitches. For example, this used to be possible, but is no longer allowed:

    == knot == ~ temp x = 5 -> stitch = stitch {x} -> END
  • Writing not A ? B will show a warning, since you almost certainly want not (A ? B) rather than (not A) ? B, but order of operations makes that impossible.

  • Using the increment and decrement operators (x++ and x--) is now much stricter - you're only allowed to use them on their own, and not inline, so only ~ x++ is allowed.

  • Version 0.8.1 also fixes threaded choices being broken in the global/top level flow.

  • Version 0.8.1 does type checking for divert targets being accidentally passed as read counts (see https://github.com/inkle/ink/issues/425).

And, as always, there's a smattering of bug fixes and smaller improvements!

Game- and engine-side major changes

  • Big improvements in how ink can cope with saving/loading a save state when the ink story has been changed. It will never be possible to make this perfect, so you should always exercise caution, especially when editing content for a game that's in production. But it shouldn't be quite so fragile anymore! (https://github.com/inkle/ink/issues/195)
    • If a variable is added, it will no longer crash when it accesses the value from a save state. It will try to use the original declared value. If you try to access a temporary variable that doesn't exist (yet) it will return the default value of 0 (false).
    • If you try to call ChoosePathString on a path that doesn't precisely exist, or make a choice that diverts to a target that no longer exists in a new version of the ink content, the engine will do its best to work its way up the path and find content that does exist. If it has to do this, it will create a runtime warning (see next point).
  • Story objects now have a currentWarnings list as well as currentErrors. It's very important that you pay attention to (logging) both of these lists whenever you call Continue, or you could be missing out on vital debugging information.
  • story.ContinueAsync(millisecs) - major game-side feature. New version of Continue() that allows you to specify a budget of a maximum amount of time you want to allow the engine to execute for before it pauses and allows you to finish another time. Great if your story sometimes goes into some complex logic, and you only want to allow it to execute for a few milliseconds per game frame. You can use the story.asyncContinueComplete property to detect when it's finished. Obviously this feature needs to be used with caution, since the story will be in an unpredictable state in the middle of execution. Although we have tried to guard against certain operations, it may still possible to accidentally get into sticky situations if you're not careful!
  • The compiler has now been moved out into a separate DLL (ink_compiler.dll), making inklecate a very lightweight wrapper around it. This should make it much easier for you to compile ink scripts from your own C# code (e.g. for game modding.)
  • story.ChoosePathString now takes a resetCallstack parameter that defaults to true. This means that if you were in a tunnel (or multiple) when calling ChoosePathString, then by default it'll take you back out again. If you pass false to resetCallstack then it'll preserve the state of the tunnel(s). It defaults to true since the assumption is that usually you use ChoosePathString to entirely change scene / go to an entirely new section.
  • Re-write the way EvaluateFunction calls from game code work in the engine to be more robust, and allow them to make multiple nested calls from Game -> ink -> game -> ink for very complex logic!
  • Re-write the way that whitespace is handled - see ink language changes above for an example of how this has subtly affected the language. Previously the compiler inserted "single-direction implicit glue" at the start and end of anything enclosed in { and } (inline logic). This wasn't robust since it was possible to divert out of the logic, which would "leak glue" since the left/right portions didn't match. Instead, newlines are now automatically consumed at the start of function calls, which makes the story lighter weight, and the whole system more robust.
  • Optimisation to the way the engine steps through the content.

Unity users: What should you download?

If you're using Unity, the UnityInkIntegration package is all you need!

Inky download

For Inky, the ink editor, download the latest version (0.9.1) here.

0.8.0 - Apr 19, 2018

A major ink update, including lots of improvements and fixes under the hood!

ink language changes

  • "Loose end" detection is now far more robust. Previously it wouldn't detect loose ends in even in simple choices, only in a sequence of "flat" content. (https://github.com/inkle/ink/issues/416)

  • White-space handling is a bit different since handling was re-written in the engine (see below). Most obvious difference the following: previously it would be one line, whereas now there will be a newline inserted:

    Hello {true: ...world. }
  • Temporary values may not be accesssed by stitches from their parent knot, or across stitches. For example, this used to be possible, but is no longer allowed:

    == knot == ~ temp x = 5 -> stitch = stitch {x} -> END
  • Writing not A ? B will show a warning, since you almost certainly want not (A ? B) rather than (not A) ? B, but order of operations makes that impossible.

  • Using the increment and decrement operators (x++ and x--) is now much stricter - you're only allowed to use them on their own, and not inline, so only ~ x++ is allowed.

And, as always, there's a smattering of bug fixes and smaller improvements!

Game- and engine-side major changes

  • Big improvements in how ink can cope with saving/loading a save state when the ink story has been changed. It will never be possible to make this perfect, so you should always exercise caution, especially when editing content for a game that's in production. But it shouldn't be quite so fragile anymore! (https://github.com/inkle/ink/issues/195)
    • If a variable is added, it will no longer crash when it accesses the value from a save state. It will try to use the original declared value. If you try to access a temporary variable that doesn't exist (yet) it will return the default value of 0 (false).
    • If you try to call ChoosePathString on a path that doesn't precisely exist, or make a choice that diverts to a target that no longer exists in a new version of the ink content, the engine will do its best to work its way up the path and find content that does exist. If it has to do this, it will create a runtime warning (see next point).
  • Story objects now have a currentWarnings list as well as currentErrors. It's very important that you pay attention to (logging) both of these lists whenever you call Continue, or you could be missing out on vital debugging information.
  • story.ContinueAsync(millisecs) - major game-side feature. New version of Continue() that allows you to specify a budget of a maximum amount of time you want to allow the engine to execute for before it pauses and allows you to finish another time. Great if your story sometimes goes into some complex logic, and you only want to allow it to execute for a few milliseconds per game frame. You can use the story.asyncContinueComplete property to detect when it's finished. Obviously this feature needs to be used with caution, since the story will be in an unpredictable state in the middle of execution. Although we have tried to guard against certain operations, it may still possible to accidentally get into sticky situations if you're not careful!
  • The compiler has now been moved out into a separate DLL (ink_compiler.dll), making inklecate a very lightweight wrapper around it. This should make it much easier for you to compile ink scripts from your own C# code (e.g. for game modding.)
  • story.ChoosePathString now takes a resetCallstack parameter that defaults to true. This means that if you were in a tunnel (or multiple) when calling ChoosePathString, then by default it'll take you back out again. If you pass false to resetCallstack then it'll preserve the state of the tunnel(s). It defaults to true since the assumption is that usually you use ChoosePathString to entirely change scene / go to an entirely new section.
  • Re-write the way EvaluateFunction calls from game code work in the engine to be more robust, and allow them to make multiple nested calls from Game -> ink -> game -> ink for very complex logic!
  • Re-write the way that whitespace is handled - see ink language changes above for an example of how this has subtly affected the language. Previously the compiler inserted "single-direction implicit glue" at the start and end of anything enclosed in { and } (inline logic). This wasn't robust since it was possible to divert out of the logic, which would "leak glue" since the left/right portions didn't match. Instead, newlines are now automatically consumed at the start of function calls, which makes the story lighter weight, and the whole system more robust.
  • Optimisation to the way the engine steps through the content.

0.7.4 - Jun 28, 2017

Small release with a couple of fixes:

  • Fix for using tags on choices, so that the tag doesn't get added to the line after.
  • When cloning state, ensure the _originalCallstack value is copied, for when evaluating functions from C# code.

0.7.3 - Jun 06, 2017

A solid batches of fixes and improvements.

  • Far more robust detection of naming collisions between various symbol types. You might notice some new errors in your ink that weren't being detected before!
  • Much improved behaviour for default choices (* ->), and disallowed usage of -> outside of this.
  • More divert types supported on choices, so you can do for example * out of the tunnel we go! ->->.
  • The divert taken after a tunnel return can now take parameters, useful for this pattern ->-> where_next(-> egypt).
  • The &&/and and ||/or operators on lists now check whether they contain anything, then do a logical and/or on the results. Previously and used to do the union operation on lists (e.g. cloak and dagger). Instead, you need to do cloak + dagger.
  • Fix for using x++ and x += y within functions, and general increased compiler robustness for them.
  • EXTERNALs can now be defined anywhere. Previously there were errors with using them below knots.
  • New READ_COUNT() internal function that can be used on variable divert targets, which wasn't possible before.
  • Can now store a variable divert target that points to a knot that takes parameters, and therefore get the READ_COUNT / TURNS_SINCE of it. Warning, there's no checking for the number of parameters that a parameterised knot takes when calling it from a variable divert target. If you get it wrong, weird stuff will happen!
  • Ability to query the current stack depth from runtime state (if you use source rather than the DLL, since it's currently internal), useful for debugging tunnels.

0.7.2 - Jun 05, 2017

  • New constructor for InkList to make it easy to construct a new list from a particular origin at runtime.
  • Compiler checks weaves for weave point (gather/choice) naming collisions.

0.7.1 - Mar 09, 2017

In rough order of coolness, from "um?" to "yawn":

  • Ability to pass arguments to knots (or functions) from the game.
  • Exposed list functionality to game so that you can work with InkListItem objects that the engine uses internally.
  • Multi-line sequence elements can now contain weaves (with choices) - previously they could only contain simple content.
  • Made != operator work on strings.
  • ink-engine-runtime.csproj converted to a PCL (Portable Class Library) (thanks @iainmerrick!)
  • Unity integration: Auto-generated InkLibrary.asset has been split up into InkLibrary and InkSettings.
  • Fixes for calling ink functions from game code
  • Fix for native functions that take arguments that need to have references resolved
  • Fix for compiler crash when there's an empty thread
  • Fix for thread IDs not being correct, especially for very first created thread - affected save/load of game state.
  • Fix for crash when assigning a list from another list, where neither have origins
  • More miscellaneous compiler fixes

0.7.0 - Jan 10, 2017

This version brings support for a brand new powerful ink feature - Lists - see the documentation.

Unity users: What should you download?

If you're using Unity, the UnityInkIntegration package is all you need!

0.6.4 - Dec 20, 2016

  • The ink runtime engine is no longer compiled directly into inklecate (the ink compiler), and is instead a separate DLL. This required some tweaks to the structure of how the Unity plugin is structured slightly, though hopefully shouldn't affect anything noticeably!
  • Big optimisation for when you do a large contentless calculation after a line of content (ending in a newline).
  • Force choices to always come after content - this was possible to get around previously with threads.

0.6.3 - Oct 28, 2016

Various bug fixes, including several from @Stratege, and one to fix left/right glue matching.

0.6.2 - Oct 16, 2016

This version fixes the issue with getting tags: https://github.com/inkle/ink/issues/209

0.6.1 - Oct 11, 2016

  • Ability to divert elsewhere immediately after a tunnel return ->-> like.this. See #199.
  • Optimisation and improvements for how runtime code gets generated, mainly to flatten down containers into their parents, aka "inlining".
  • You can now get the sourcePath from a Choice, which tells you exactly where in the story it was generated.

Unity users: What should you download?

If you're using Unity, the UnityInkIntegration package is all you need!

0.6.0 - Oct 07, 2016

  • Tags! As described in #122:

    Allows you to include metadata in your story. It can be used statically, so that you can grab data from your story without actually running it, or in the content itself, so that you can add markers on a line-by-line basis. For example, you can include this at the top of your story, and then grab them as a list of strings using the story.globalTags property

Or on a knot, which you can get using story.TagsForContentAtPath("myKnot"):

Or on a line by line basis, which you can get after calling story.Continue() using story.currentTags:

A line in my story. # audio: a_line.wav

or:

Joe: This is great! # smiling

or:

A line in my story. #loc:12345 // localisation code for other languages

  • BREAKING CHANGE: ink no longer steps directly into a knot when no other content is defined above it. Although this seemed like useful behaviour, it's extremely confusing if you simply add a variable and then are confused at why your ink no longer works.
  • Allow CONST values to be redefined, so long as they're given the same value. Useful for putting the same value in multiple files to reduce dependencies and make them visible to the writer.
  • Throw an error if you try to set a variable from the game that hasn't been declared in ink.

0.5.1 - Sep 20, 2016

  • When diverting to specific knots repeatedly from game code, the visit count now gets updated correctly, so long as your knot has fully completed with an -> END. If it hasn't, you can also now call myStory.state.ForceEnd() to mark the flow complete. Thanks for the report @AdamNashGames!
  • A call to -> DONE now ensures that flow actually stops, rather than marking it "safe to exit". (oops!)
  • RANDOM(min, max) and SEED_RANDOM(intSeed) now available! Random's min and max bounds are inclusive, e.g. I rolled a {RANDOM(1,6)}..
  • Warning for using else: rather than - else:.
  • Show error when passing -> myVar as a divert target rather than just myVar. (Slightly confusing)

Unity users: What should you download?

If you're using Unity, the UnityInkIntegration package is all you need!

0.5 - Sep 08, 2016

  • API to evaluate a function in ink from C#: EvaluateFunction(...).
  • API to check whether an ink function exists from C#: FunctionExists(...)
  • Fix to threads that wasn't cloning temporary variables by simply copying references, causing subtle bugs (thanks @cduquesne)
  • Fix for type coercion (thanks @cduquesne)
  • Fix for float parsing in non-English locales (or at least, those that use a comma natively rather than a dot) - wow, you're on a roll, thanks again @cduquesne!

Unity users: What should you download?

If you're using Unity, the UnityInkIntegration package is all you need!

0.4.1 - Jul 28, 2016

  • Fixed bug that prevented save/loading before Continue had first been run.
  • Fix for using temp variables within choices
  • Improved support for evaluating arbitrary ink on the inklecate command line (will be useful for watching variables etc in inky)

Unity users: What should you download?

If you're using Unity, the UnityInkIntegration package is all you need!

0.4.0 - Jul 08, 2016

Biggest change: We've removed ink's dependency on Newtonsoft.Json.dll. When you install this latest of ink (in Unity), make sure that that the dll has been removed (unless you're using it for something else of course!) This should make Unity integration a lot more stable, and remove the compatibility bugs we've been having (especially on Windows) once and for all.

It also means that you can use .NET 2.0 Subset rather than .NET 2.0, making it possible to create WebGL builds without hundreds of megabytes of JavaScript!

The build includes extra functionality for the play mode of the command line inklecate compiler. This was mainly to support inky, but you may find it useful if you use play mode too:

  • As well as typing in choice numbers, you can also type ink expressions, such as myVar to print the value of a variable, myVar = 6 to perform an assignment, or -> myKnot to jump straight to another place entirely in your story.
  • Launch the play mode with the additional -k argument to keep inklecate running even after the story as finished. This allows you to continue to evaluate expressions like -> differentKnot, to try different sections of your story, especially if it's designed to be played in fragments within your game engine.
  • Ability to get the source line and file from a particular point in the full output text, by using DebugSource(<character offset>). This isn't very human-friendly, and it's really designed for inky to be able to alt-click output text in the player window and jump to the source in the editor.

Also included are a few small bug fixes including:

  • The ability to use threads within inline logic. For example {first|second|<- pullLogicFromThread}.
  • Fix for compiler crash if you attempt to use an empty sequence, e.g. {!}.

Unity users: What should you download?

If you're using Unity, the UnityInkIntegration package is all you need!

0.3.5 - Jun 20, 2016

  • Ink-unity-integration package improvements:
    • Improved warning/error logging
    • Added some new icons
    • Added the ability to change which files are compiled automatically
    • Massively reduced the file size footprint of the ink library, and removed a call that was creating unintended effects on the Unity Editor.
  • In runtime format, Branch is no longer used, in favour of using a simple condition boolean on Diverts.
  • Command line player now signifies when player input is required with prompt characters ?>, so that integrations into automated pipelines are easier (e.g. for web or app interfaces).
  • Project settings to allow pushing to nuget (thanks @ncthbrt)
  • Bug fixes
    • Diverts no longer break when they reference a target they're currently in
    • Shuffle no longer leaves rubbish on evaluation stack, which sometimes caused functions to return the wrong result.
    • Shouldn't attempt to continue to evaluate after story's state is broken
    • Exiting inklecate with CTRL-C returns command line text colour to normal (thanks @bncastle)
    • Fix for crash in Divert.ToString()
    • Fix for visit counts when diverting as a result of a choice

Unity users: What should you download?

If you're using Unity, the UnityInkIntegration package is all you need!

0.3.4 - May 06, 2016

  • Lots of increased robustness for this release, both in the ink-unity-integration package and in ink itself. In particular, the compiler should cope much better with Windows and mixed line endings, since it does an initial cleansing/standardisation step now.
  • Removed the command line option to specify a working directory. Instead, INCLUDE files will always be relative to the root ink file.
  • Much of the ink-unity-integration plugin has been overhauled and re-written. Changes include:
    • Track ink errors, warnings and todos via the inspector
    • The name, location and existence of a story JSON file is tied to its Ink file
    • Shows relationship between INCLUDE files
    • Make compilation process visible via a loading bar
    • Ability to disable automatic compilation
    • Massive performance gains for larger stories

Unity users: What should you download?

If you're using Unity, the UnityInkIntegration package is all you need!

0.3.3 - Apr 12, 2016

  • Open up access to VisitCountAtPathString
  • Open up access to jsonToken on StoryState, useful for JSON serialisation as part of a wider game.
  • Open up access to StoryException
  • Standardised error message format for easier parsing in newly released Unity integration package.
  • Fix for JSON serialisation of save state when threads are used
  • Fix for reversed arguments in external function binding
  • Revised documentation to reflect that the ink Unity integration package is the best route into getting started
  • Bug fixes, documentation typo fixes

v0.2.3-alpha - Mar 30, 2016

Breaking changes:

  • ChoiceInstance is now Choice, while the old (internally used) Choice is now ChoicePoint, making the public-facing API more friendly.
  • choiceText and choiceIndex members are now just text and index.

Other improvements:

  • Include XML documentation in Windows release for use in Visual Studio.
  • Fix for incorrect naming conflicts for labels (named knots, stitches, gathers, choices) in entirely different scopes.
  • Parse errors in include files no longer cause a incorrect "file not found" error.
  • Removed silly synonyms for true and false which were indocumented and causing problems, and added error message for when you name a knot parameter using a reserved word.
  • A couple of smaller bug fixes

v0.2.2-alpha - Mar 26, 2016

  • Fix for sticky choices not getting serialised properly.
  • Function calls on tilda lines now clean up the evaluation stack after themselves properly.
  • Streamlining runtime implementation: Renaming Literal to Value, since it's more accurate. There's now no separate Runtime.Text, it's all just StringValue. Callstack pops are now just types of ControlCommand.
  • Improvements to Sublime syntax

v0.2.1-alpha - Mar 24, 2016

  • Public method to get the visit/read count of a knot or stitch: story.state.VisitCountAtPathString("yourKnot.yourStitch")
  • Rewrote system that counts visits to Containers. Simpler, faster, more robust.
  • story.state protection level fixed, so you can actually access it!
  • Various bug fixes

v0.2.0-alpha - Mar 23, 2016

  • Save state now available. Call story.state.ToJson() to get a JSON string representation of your game state. Call story.state.LoadJson(yourString) to load it up again.
  • Intermediate compiled JSON format for stories themselves re-worked from scratch. File size is now roughly 50% smaller. Further improvements may be made in future but they're likely to be harder work and less dramatic!
  • VariablesState now implements IEnumerable for easy walking over your global variables from game code. Thanks @bateramos!
  • API Change: Story now has a normal constructor that takes a JSON string rather than calling a static CreateWithJson factory method.
  • Small bug fixes

v0.1.3-alpha - Mar 12, 2016

Nothing changed, other than to the Mac release zip, since it needs to include Newtonsoft.Json.dll (oops).

v0.1.2-alpha - Mar 11, 2016

  • Syntax for includes has changed to INCLUDE blah.ink from ~ include blah.ink
  • Bug fixes

v0.1.1-alpha - Mar 10, 2016

  • Lots of optimisation for speed. It's not the fastest language in the world, but it's no longer quite so awfully slow.
  • Verified various features on Windows. In particular, console colouring works properly now.
  • Better error message handling
  • -> END now causes any generated choices to be cleared, effectively ending the flow completely.
  • Fallback ink functions can be defined for unbound externals. Useful to test a game script in play mode.

Library Stats (Sep 22, 2022)

Subscribers: 122
Stars: 3.3K
Forks: 424
Issues: 297

To build NServiceBus just open NServiceBus

You'll find the built assemblies in /binaries

To build NServiceBus just open NServiceBus

Use Razor to build templates from Files / EmbeddedResources / Strings / Database or your...

Use Razor to build templates from Files / EmbeddedResources / Strings / Database or your custom source outside of ASP

Use Razor to build templates from Files / EmbeddedResources / Strings / Database or your...

The AKEless Build System for C#/

NUKE is the best open-source build automation system for C#/

The AKEless Build System for C#/

Build on Windows

Many users entered valuable bug reports as well, thanks

Build on Windows

Public Build Status

[![public_build_icon]][public_build_status]

Public Build Status

Use xubot-core to build with

Confirmed working on: Windows 10 x64, Ubuntu LTS, Debian (published as linux-x64)

Use xubot-core to build with

Mod Assistant is a PC mod installer for Beat Saber

Pull Request to add your theme as a built-in theme

Mod Assistant is a PC mod installer for Beat Saber

C# script tool for

TeamCity build runner or installed as a

C# script tool for

Step 1:- download project and build

Step 3:- Docker open and running

Step 1:- download project and build
Learning Blazor: Build Single-Page Apps with WebAssembly and C#

Authentication enables an application to support Swedish BankID (svenskt BankID) authentication in

Built on NET Standard and packaged as NuGet-packages they are easy to install and use on multiple platforms

Authentication enables an application to support Swedish BankID (svenskt BankID) authentication in