inkle / ink Goto Github PK
View Code? Open in Web Editor NEWinkle's open source scripting language for writing interactive narrative.
Home Page: http://www.inklestudios.com/ink
License: MIT License
inkle's open source scripting language for writing interactive narrative.
Home Page: http://www.inklestudios.com/ink
License: MIT License
I encounter a problem which I believe is caused by the use of a reserved word, hidden variable, or token value as knot parameter.
Both knots in the following snippet are functionally identical. Despite this, one prints out the correct value of 0, and the other, an incorrect value of 1. The only difference appears to be the name of the parameter.
Would it be possible to have the compiler catch this, whatever the root cause is?
== Begin ==
Fun with params
+ Toggle Lamp (working) -> ToggleLamp(false)
+ Toggle Lamp (broken) -> ToggleLamp2(false)
* I'm Done -> DONE
== ToggleLamp(isOn) ==
the lamp is on? {isOn}
-> Begin
== ToggleLamp2(on)
the lamp is on? {on}
-> Begin
Hello,
I got this exception running my build game on MacOs when a ink script is executed.
TypeLoadException: Could not load type 'Newtonsoft.Json.Linq.JArray' from assembly 'Newtonsoft.Json'
The strange thing is that all run well on the Unity3d editor. Checking the build I say that all dlls were in place and the ink scripts were ok...
This is for version 0.2.3 alpha.
I had not tested on device for some time, so not quite sure when this started happening. I fully realize that this is more an issue with the NewtonSoft.JSON dll than it is with the Inkle library, but seeing as how you publish on iOS as well I thought it worth a mention :-)
I can provide the JSON string for the story, or a more complete example if you want, but I think this will be easily reproducible.
running tests...InkTests
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 37)
FAILED: LoadTestFile
(Filename: /Users/builduser/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineDebugBindings.gen.cpp Line: 37)
reason: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.TypeInitializationException: An exception was thrown by the type initializer for Newtonsoft.Json.Utilities.ConvertUtils ---> System.TypeInitializationException: An exception was thrown by the type initializer for System.Collections.Generic.EqualityComparer`1 ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.ExecutionEngineException: Attempting to JIT compile method 'System.Collections.Generic.GenericEqualityComparer`1<Newtonsoft.Json.Utilities.ConvertUtils/TypeConvertKey>:.ctor ()' while running with --aot-only.
at System.Reflection.MonoCMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at System.Reflection.MonoCMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
at System.Reflection.MonoCMethod.Invoke (BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
at System.Reflection.ConstructorInfo.Invoke (System.Object[] parameters) [0x00000] in <filename un known>:0
at System.Activator.CreateInstance (System.Type type, Boolean nonPublic) [0x00000] in <filename unknown>:0
at System.Activator.CreateInstance (System.Type type) [0x00000] in <filename unknown>:0
at System.Collections.Generic.EqualityComparer`1[Newtonsoft.Json.Utilities.ConvertUtils+TypeConvertKey]..cctor () [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at System.Collections.Generic.Dictionary`2[Newtonsoft.Json.Utilities.ConvertUtils+TypeConvertKey,Newtonsoft.Json.Serialization.Func`2[System.Object,System.Object]].Init (Int32 capacity, IEqualityComparer`1 hcp) [0x00000] in <filename unknown>:0
at System.Collections.Generic.Dictionary`2[Newtonsoft.Json.Utilities.ConvertUtils+TypeConvertKey,Newtonsoft.Json.Serialization.Func`2[System.Object,System.Object]]..ctor () [0x00000] in <filename unknown>:0
at Newtonsoft.Json.Utilities.ThreadSafeStore`2[Newtonsoft.Json.Utilities.ConvertUtils+TypeConvertKey,Newtonsoft.Json.Serialization.Func`2[System.Object,System.Object]]..ctor (Newtonsoft.Json.Serialization.Func`2 creator) [0x00000] in <filename unknown>:0
at Newtonsoft.Json.Utilities.ConvertUtils..cctor () [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at Newtonsoft.Json.Linq.JToken.ToObject (System.Type objectType) [0x00000] in <filename unknown>:0
at Newtonsoft.Json.Linq.JToken.ToObject[Int32] () [0x00000] in <filename unknown>:0
at Ink.Runtime.Story..ctor (System.String jsonString) [0x00000] in <filename unknown>:0
at LiveSequenceManager.LoadStory (System.String storyName) [0x00016] in /Users/jonathanculp/Documents/Guildlings/Assets/Scripts/Ink/LiveSequenceManager.cs:104
at InkTests.LoadTestFile () [0x00000] in /Users/jonathanculp/Documents/Guildlings/Assets/Scripts/Testing/Tests/Ink/InkTests.cs:62
at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
--- End of inner exception stack trace ---
at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0
at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in <filename unknown>:0
at UnitTestHarness.RunTest (System.Reflection.MethodInfo info) [0x0000f] in /Users/jonathanculp/Documents/Guildlings/Assets/Scripts/Testing/Framework/UnitTestHarness.cs:38
Some of that trace - specific to my project obviously, but I believe I've properly isolated the cause to just the provided Inkle libraries. I'm definitely not ruling out the possibility that the problem is on my end.
I don't have the best understanding of when or how the JIT compiler is invoked. But I do remember this having worked on previous versions. I'll start reviewing the changes.
Out of curiosity whats the source of your Newtonsoft.Json.dll? I remember that the mono version of this assembly had many compatibility issues with iOS. Many unity users will be using a ported version of this assembly from the asset store that was written specifically to be more compatible with iOS's AOT restrictions. It forked quite a long time ago, so it's more than a few versions behind the official Newtonsoft project. Despite that, for many of us, its a better choice.
-Jonathan
I did not see this mentioned in the tutorial anywhere so I don't know if this feature exists, but it would be nice to be able to have a blank line between paragraphs or between prompt and selections. For instance, I'm trying to make a quit game prompt:
=== quit_prompt(-> return_to) ===
Done so soon?
But I would want it to display a space between the prompt and the choice, as a stylistic decision, and there are many cases where you are writing longer paragraphs where whitespace would be helpful.
I can get 0.1.2 inklecate to successfully run an .ink file in the Mac OS Terminal window, but when I try to integrate a created json file and dll into Unity 5.3.1f1 following the RunningYourInk doc guide, Unity throws an error at the "Story _inkStory;" line of the example wrapper script the doc suggests. Unity is complaining that 'Newtonsoft.Json' is not present. I am using the provided mac inklecate and ink-engine.dll files from https://github.com/inkle/ink/releases, I did not compile on my own.
Error:
Unhandled Exception: Mono.CSharp.InternalErrorException: Assets/Ink/Script.cs(7,14): Script ---> Mono.CSharp.InternalErrorException: Assets/Ink/Script.cs(13,15): Script._inkStory ---> System.IO.FileNotFoundException: Could not load file or assembly 'Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies.
File name: 'Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'
at (wrapper managed-to-native) System.MonoCustomAttrs:GetCustomAttributesInternal (System.Reflection.ICustomAttributeProvider,System.Type,bool)
at System.MonoCustomAttrs.GetCustomAttributesBase (ICustomAttributeProvider obj, System.Type attributeType) [0x00000] in :0
at System.MonoCustomAttrs.GetCustomAttributes (ICustomAttributeProvider obj, System.Type attributeType, Boolean inherit) [0x00000] in :0
at System.MonoType.GetCustomAttributes (System.Type attributeType, Boolean inherit) [0x00000] in :0
at Mono.CSharp.AttributeTester.GetObsoleteAttribute (System.Type type) [0x00000] in :0
at Mono.CSharp.Expression.ResolveAsTypeTerminal (IMemberContext ec, Boolean silent) [0x00000] in :0
at Mono.CSharp.MemberBase.ResolveMemberType () [0x00000] in :0
at Mono.CSharp.MemberBase.Define () [0x00000] in :0
at Mono.CSharp.Field.Define () [0x00000] in :0
at Mono.CSharp.TypeContainer+MemberCoreArrayList.DefineContainerMembers () [0x00000] in :0
--- End of inner exception stack trace ---
at Mono.CSharp.TypeContainer+MemberCoreArrayList.DefineContainerMembers () [0x00000] in :0
at Mono.CSharp.TypeContainer.DefineContainerMembers (Mono.CSharp.MemberCoreArrayList mcal) [0x00000] in :0
at Mono.CSharp.Class.DefineContainerMembers (Mono.CSharp.MemberCoreArrayList list) [0x00000] in :0
at Mono.CSharp.TypeContainer.DoDefineMembers () [0x00000] in :0
at Mono.CSharp.Class.DoDefineMembers () [0x00000] in :0
at Mono.CSharp.TypeContainer.Define () [0x00000] in :0
at Mono.CSharp.ClassOrStruct.Define () [0x00000] in :0
at Mono.CSharp.Class.Define () [0x00000] in :0
at Mono.CSharp.RootContext.PopulateTypes () [0x00000] in :0
--- End of inner exception stack trace ---
at Mono.CSharp.RootContext.PopulateTypes () [0x00000] in :0
at Mono.CSharp.Driver.Compile () [0x00000] in :0
at Mono.CSharp.Driver.Main (System.String[] args) [0x00000] in :0
The following assembly referenced from /Users/joshualawrence/Dropbox/Unity/Hypertext2/Assets/Ink/ink-engine.dll could not be loaded:
Assembly: Newtonsoft.Json (assemblyref_index=2)
Version: 7.0.0.0
Public Key: 30ad4fe6b2a6aeed
The assembly was not found in the Global Assembly Cache, a path listed in the MONO_PATH environment variable, or in the location of the executing assembly (/Users/joshualawrence/Dropbox/Unity/Hypertext2/Assets/Ink/).
Could not load file or assembly 'Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies.
Missing method .ctor in assembly /Users/joshualawrence/Dropbox/Unity/Hypertext2/Assets/Ink/ink-engine.dll, type Newtonsoft.Json.JsonObjectAttribute
Can't find custom attr constructor image: /Users/joshualawrence/Dropbox/Unity/Hypertext2/Assets/Ink/ink-engine.dll mtoken: 0x0a00002d
Script:
using UnityEngine;
using System.Collections;
using Ink.Runtime;
public class Script : MonoBehaviour {
// Set this file to your compiled json asset
public TextAsset inkAsset;
// The ink story that we're wrapping
Story _inkStory;
void Awake()
{
_inkStory = Story.CreateWithJson(inkAsset.text);
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
while (story.canContinue) {
Console.WriteLine (story.Continue ());
}
if( story.currentChoices.Count > 0 )
{
for (int i = 0; i < story.currentChoices.Count; ++i) {
ChoiceInstance choice = story.currentChoices [i];
Console.WriteLine ("CHOICE {0}: {1}", (i + 1), choice.choiceText);
}
story.ChooseChoiceIndex (index);
}
}
}
I noticed you have the NewtonSoft dll in a source folder and you have the NUnit package uploaded. These should be listed in your packages.config and any developer who forks or clones the repo will "restore" those locally. No need to track them in source.
Hello,
Maybe I didn't find it in tutorials, but what is the best way to make saving/loading in game?
Am I right, we can't do anything about internal variables, that count how many times player diverted to some knot, stitch, or chose option with label on it? It seems that we can't get any value from them in C# and we can't give these values to global variables. And it seems that there is no reset for them besides restarting the story too.
Am I missing something big here, something like states we can address to and get all variables on that moment correctly? Or should I create my own system with global variables that will duplicate the work of knots, stitches, labels for everything I want (it is still a good question how to work with conditions that depend on "entrance count", not on variables), and with every load I should restart the story? Or work only with variables if I want to successfully save and load it?
I don't believe that saving is a complicated thing, when everything in Ink is so convenient. I hope you can give a hint :)
Stumbled across this by foolish accident.
It appears that Inklecate will throw a stack overflow exception and crash when trying to compile a script that INCLUDEs a script which INCLUDEs the original script, if that makes sense.
Obviously, this is not a recommended approach, but shouldn't cause a compiler crash. :D
This example :
{
- x == 0:
~ y = 0
- x > 0:
~ y = x - 1:
- else:
~ y = x + 1;
}
Seems to have a superfluous ':' in the second switch (it outputs that character in inklecate). This is also the case with the if-then example above it. However, the ';' is not outputted.
Some older Ink version syntax left-overs? You have other examples that include neither the end-of-line colon nor the semi-colon, and the parser handles those fine, so it doesn't seem as if either character serves any purpose any longer.
In file Documentation/ink_JSON_runtime_format.md it is stated that a string value must be prepended with ^ but in the examples string values are prepended with $
Given that the runtime looks for ^ at the beginning of string objects I guess that the examples using $ are wrong?
Regards,
Martin
Using version 0.33 of ink, I ran into a problem with an ink script trying to use ink's ternary style operator on a variable to alter some text in-line.
I was hoping to use the following example from the WritingWithInk.md page to do this:
But when changing 'met_blofeld.learned_his_name' to a different conditional statement, the compile failed and crashed out. I've attached an ink script that (at least for me) demonstrates the crash, and a change I can make that makes the compile successful. Neither one runs as I would expect (i get no output from this script at all and it seems like I should be getting something? Not even the question at the beginning?) I'm assuming that part is just something silly I've done wrong.
I don't yet think I understand the parser/compiler code enough to try to take this on myself, but figured I would just post up here since I found a repeatable issue.
Doesn't seem possible? E.g.:
Welcome to my Lair:
https://github.com/inkle/ink/blob/master/Documentation/WritingWithInk.md#advanced-multiline-lists
i was not able to find any section or documentation on multiline lists
While building a text-only adventure game, I've been using Play mode for testing. My combat subsystem, a tunnel named fight
crashes when it's invoked, for some reason. Here's the stack trace:
Unhandled Exception:
System.Exception: Shouldn't ever have a null value where it couldn't be found at all
at Ink.Runtime.VariablesState.GetRawVariableWithName (System.String name, Int32 contextIndex) <0x13c6a40 + 0x000af> in <filename unknown>:0
at Ink.Runtime.VariablesState.GetVariableWithName (System.String name, Int32 contextIndex) <0x13c69a8 + 0x00023> in <filename unknown>:0
at Ink.Runtime.VariablesState.GetVariableWithName (System.String name) <0x13c6978 + 0x0001f> in <filename unknown>:0
at Ink.Runtime.Story.PerformLogicAndFlowControl (Ink.Runtime.Object contentObj) <0x13bb608 + 0x0120b> in <filename unknown>:0
at Ink.Runtime.Story.Step () <0x13baa20 + 0x00167> in <filename unknown>:0
at Ink.Runtime.Story.ContinueInternal () <0x13ba250 + 0x00083> in <filename unknown>:0
at Ink.Runtime.Story.Continue () <0x13c6398 + 0x00027> in <filename unknown>:0
at Ink.CommandLinePlayer.EvaluateStory () <0x13c6238 + 0x0003f> in <filename unknown>:0
at Ink.CommandLinePlayer.Begin () <0x13c5c08 + 0x00567> in <filename unknown>:0
at Ink.CommandLineTool..ctor (System.String[] args) <0x128b1c0 + 0x0080f> in <filename unknown>:0
at Ink.CommandLineTool.Main (System.String[] args) <0x128af30 + 0x00023> in <filename unknown>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.Exception: Shouldn't ever have a null value where it couldn't be found at all
at Ink.Runtime.VariablesState.GetRawVariableWithName (System.String name, Int32 contextIndex) <0x13c6a40 + 0x000af> in <filename unknown>:0
at Ink.Runtime.VariablesState.GetVariableWithName (System.String name, Int32 contextIndex) <0x13c69a8 + 0x00023> in <filename unknown>:0
at Ink.Runtime.VariablesState.GetVariableWithName (System.String name) <0x13c6978 + 0x0001f> in <filename unknown>:0
at Ink.Runtime.Story.PerformLogicAndFlowControl (Ink.Runtime.Object contentObj) <0x13bb608 + 0x0120b> in <filename unknown>:0
at Ink.Runtime.Story.Step () <0x13baa20 + 0x00167> in <filename unknown>:0
at Ink.Runtime.Story.ContinueInternal () <0x13ba250 + 0x00083> in <filename unknown>:0
at Ink.Runtime.Story.Continue () <0x13c6398 + 0x00027> in <filename unknown>:0
at Ink.CommandLinePlayer.EvaluateStory () <0x13c6238 + 0x0003f> in <filename unknown>:0
at Ink.CommandLinePlayer.Begin () <0x13c5c08 + 0x00567> in <filename unknown>:0
at Ink.CommandLineTool..ctor (System.String[] args) <0x128b1c0 + 0x0080f> in <filename unknown>:0
at Ink.CommandLineTool.Main (System.String[] args) <0x128af30 + 0x00023> in <filename unknown>:0
Back again.
Before I get down to the nuts and bolts here, let me just reiterate that I really love what you guys have done here. I've looked at a lot of IF scripting languages over the past few years while working on my own games scripting language v1 and v2, and Ink is - IMO - the best combination of power and simplicity I've encountered so far. Ink is awesome, and you guys are awesome for sharing it with the rest of us.
That said, nothing is perfect. Most of the nits I have a pretty minor things, but I am really not keen on your "single new line -> paragraph break" convention. I commented on this earlier in the chat, but I'd like to bring this up again now that I've worked some on a bare-bones implementation in Java and have a bit more perspective, because I do feel that this is a weakness in Ink, and perhaps something you should (re)consider.
My initial disquiet with this mechanism was due to my experiences collaborating across various technical platforms. Since I work mostly in Linux and the people I collaborate with in Windows or Max, I'd often have someone writing in one editor, and me then having to edit the text in another. That did cause issues from time to time - esp. if you're working with editors that do hard-wrapping (such as many IDE's). But while annoying, this is not a big deal (just requires some editing discipline).
I think this convention has two other effects, though, which IMO are much more serious:
My suggestion would be to go with "hard-wrapping" by default, similar to Markdown syntax and Mediawiki syntax. Basically:
Feel free to correct me if I'm off, but as far as I can see, the necessity for "Glue" (<>) is entirely the consequence of following the "single new line -> paragraph break" convention.
Without that convention, the core glue example:
=== hurry_home ===
We hurried home <>
-> to_savile_row
=== to_savile_row ===
to Savile Row
-> as_fast_as_we_could
=== as_fast_as_we_could ===
<> as fast as we could.
Would just be:
=== hurry_home ===
We hurried home
-> to_savile_row
=== to_savile_row ===
to Savile Row
-> as_fast_as_we_could
=== as_fast_as_we_could ===
as fast as we could.
Your glue test case:
"Some <>
content<>
with glue."
Would just be:
"Some
content
with glue."
A more complex example:
- 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
Could be written as follows (note the use of diverts to "glue" together across break syntax, as well as line breaks to keep the code nicely formatted):
- 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. -> after_that
* * 'Ah[.'],' I replied, uncertain what I thought.
- - (after_that) After that, -> we_passed
* ... but I said nothing[] and -> we_passed
- (we_passed) we passed the day in silence.
- -> END
Note that in the above, I refer to "break syntax" as those syntax elements that break up the script: knots, stitches, choices, gathers, etc.
TL;DR - my impression at this point is that glue is only required because of the choice of "single new line -> paragraph break". At the moment, I can't really see any obvious or common use-cases that makes it worthwhile to litter the code with a lot of <>
syntax, as opposed to simply using the double newline convention (+ the occasional divert - something that already works that way in the current Ink syntax).
The thing that really cements my unease with the glue mechanic, is that it seems to make the Ink runtime/parser a lot more complex than it needs to be.
Based on my experience implementing a markdown style parser vs the work I've done on a Java Ink runtime so far, the former just seems a lot more straightforward.
In the markdown style, you simply parse text/lines until you hit break syntax or a double newline (which is a break syntax itself). Because paragraph breaks are explicit, you always parse/compute until you hit one. It's pretty simple with no exceptions.
I can't say the same for dealing with this in current Ink script. Every time you process a line, you need to check for glue either at the beginning or the end of the line, requiring alternative back-tracking or look-ahead. The Glue syntax is an exception to the "new line -> paragraph break' rule, and because Ink is as powerful as it is, this becomes a really important exception.
The result is a lot of complicated code to deal with this exception.
I was rather puzzled by Joseph's comments earlier about whitespace handling being a problem. No longer, after implementing glue, and having to compensate for all the potential white-space issues that this syntax adds to the code. I haven't checked the C# code in detail, but I found myself having to add a lot of extra whitespace checking because of glue.
I smiled a bit when I read your comment in the runtime that goes "This code is slightly fragile" when discussing lookahead due to glue. Indeed.
In short, I think that there are good reasons to go with the markdown syntax for paragraph breaks both from the POV of the writer and the POV of the developer.
I'm not interested in forking or creating a variant of Ink, and I do realize that you may have other priorities, so if you want to kill this discussion for now, I'll accept that.
I think you should consider the issue, though. I may be overlooking something, but I feel pretty sure that the markdown style paragraph formatting would result in simpler (and by extension - less brittle) library code and it definitely makes the syntax simpler. For a project that will - hopefully - pick up a lot of fans and live for many years to come, such things are better fixed early than late.
I think I'm going to make a few basic UI templates for displaying ink stories. One of the things I'm trying to decide is whether to rely on the TextMeshPro plugin, which I believe is essential for anyone trying to make a Unity game with text (in-line styling and animation and everything you could possibly want to do with text is just so simple, not to mention actual accurate word-wrapping and resize to fit and rendering and and and). But it's also $65USD and I'd hate to leave out people who can't afford that. Maybe a separate example that shows specifically what can be accomplished with Ink and TMP working together? Thoughts?
Ideally the Ink stories could serve as practical examples for everything covered in the "Writing With Ink" documentation, but my primary focus is on demonstrating how to display Ink in Unity UI. Here are the examples/templates I'm planning:
Ink has some pretty powerful mechanisms, but I'm missing one construct that I found very useful for writing texts with variables in my own scripting language. An example:
He brought me [# number | nothing! The cad|one calling bird|two french hens|three turtle doves|an aviary#].
This simple returns the first element in the construct if number is <= 0 (or false), the last element in the construct if number >= maximum elements, and the appropriate value if number is a valid index in the sequence.
E.g.,, for number = 0, you get:
He brought me nothing! The cad.
For number = 2, you get:
He brought me two french hens.
For numbers >= 4 you get:
He brought me an aviary.
You have something a little similar in your inline if-then { val : text1 | text0 }
, but that's of course not as powerful ([#ย #]
started out as a similar construct, before I decided to flip the order and generalize it), and I haven't noticed anything that provides the same functionality.
Perhaps I missed something? How do you handle that kind of variable-dependent writing in-line today?
I have two Ink files:
Test.Ink:
and Global.Ink:
I get this error when the plugin compiles:
I'd have a better analysis of the offending code but I don't have a working debugger at the moment. Here's the GetIncludePaths code which has some weirdness that I've highlighted:
"index+"?
And then I really don't understand this. If I just comment out the include path parsing, everything works.
When I write an ink file, text wraps on the line instead of on the word. This makes long blocks of text difficult to read/write.
I've attached an example showing that the issue is ink-specific. Is anyone else getting this behavior? Is there a simple setting I've missed that would allow me to restore word wrapping?
Thanks!
Hey guys!
I was investigating some possible options for hosted forums etc, and the more time I spent doing that, the more I realised that github issues itself really isn't far off from a discussion forum anyway.
So I propose the following: For discussions, let's just use the newly-created discussion
label here. As far as when to close a discussion issue it's a bit ambiguous, but let's try to tidy up after ourselves.
On the one hand, issues is meant to be a bug tracker, but I think it could work well for discussions as well, as we've already been using it.
The other nice thing is that with the label you essentially have a forum permalink too.
As I've mentioned, I've been doodling around an Ink implementation in Java during the weekend. I've put the results up here in case anyone else is interested:
https://github.com/micabytes/jink
It essentially implements Part 1 of the Ink documentation, with a (fairly) comprehensive set of tests based on the examples in the docs. Tests are written using Spec2 (Scala), but should be easily readable (minimal Scala syntax involved).
I'm likely to be a bit busy during the coming month, so probably won't do very much more work on it between now and late April. My first impression of working with this has been positive though. I use a similar IF parser in my current (Android - hence the need for Java) game engine, and I am thinking there'd be some advantages to using Ink instead of my home-grown solution. So unless something happens to change my mind, I'll probably pick up work on this again later with a view to making it "ink-complete".
Anyway, it's available if anyone wants to play around with it. Haven't looked into a license yet, though I expect I'll MIT it to be compatible with this. If anyone wants to contribute/help, that would certainly be appreciated.
Hi guys:
I wonder if Ink is capable of doing cycling links like in twine without further work on the engine part. Or for example, has whole paragraphs disappearing and changing upon a click, like you, Inkle, did in First Draft of the Revolution.
If so, how?
Regards.
Should be public. Can't currently catch an exception of type StoryException.
Use case: I'm writing a series of unit tests for an ink story. Would like to test that attempting to call an unbound external function throws a StoryException. Since StoryException is internal, I have to catch its parent class, Exception. But I do not want an unrelated exception to cause this test to pass when it shouldn't.
I'm seeing a behavior with InkEngine.dll where a sticky choice disappears from the choices list after a call to Story.Continue. Choice is properly sticky in play mode with inklecate. Curious as to why play-mode and engine dll differ. Will update this issue with minimal example soon. On version 0.2.0 alpha.
I will test with 0.2.1 alpha and will also provide minimal example soon.
A report from @Horatio-Blackwood, via HipChat.
VAR char_name = "Joe"
* \ {char_name}: This is the choice
The end.
-> DONE
(We use the backslash in this situation, somewhat awkwardly, to escape the following space and allow "Joe" to appear in the choice text, rather than turning {char_name}
into a conditional for the choice.)
This appears to work:
But not:
Need to reproduce, and debug.
Hello!
Studying the Ink now, testing examples that are in tutorial. And stumbled on some issue with labels, that exist in your own example, maybe it will be helpful:
- (opts)
- 'Can I get a uniform from somewhere?'[] you ask the cheerful guard.
'Sure. In the locker.' He grins. 'Don't think it'll fit you, thought.'- 'Tell me about the security system.'
'It's ancient,' the guard assures you. 'Old as coal.'- 'Are there dogs?'
'Hundreds,' the guard answers, with a toothy grin. 'Hungry devils, too.'
// We require the player to ask at least one question- {loop} [Enough talking]
-> done- (loop)
// loop a few times before the guard gets bored
{ -> opts | -> opts | }
He scratches his head.
'Well, can't stand around talking all day,' he declares.- (done)
You thank the guard, and move away.
What happens here, entering the "loop" label doesn't increase count (how many times we entered this gather), so choice [Enough talking] never appear - Ink thinks that loop == 0. Problem disappears, if we add symbol before list of diverts. So I think issue is with immediate divert in gather - without at least one symbol Ink doesn't increase count for label.
It is not critical one, but if this is really an issue, maybe you can warn others about it till it'll be fixed.
By the way, very good project. Waited for it since the announcing and it totally worth the waiting, thanks for making it available.
I've met a problem after building the application in Unity, Log showed this type of error:
TypeLoadException: Could not load type 'Newtonsoft.Json.Linq.JArray' from assembly 'Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'.
If it's not so obvious and I just didn't know anything about it, maybe it's worth to add a solution for such cases in RunningWithInk documentation: In "Edit -> Project settings -> Player -> Other settings" you should set API compatibility level from .NET 2.0 subset (default one) to .NET 2.0.
If you want ink to succeed it'd be better if you provided either new backends or a spec to create them. At least a specification or json schema would be appreciated.
I am getting these compiler errors only when building in MonoDevelop (which prevents me from debugging inside MonoDevelop):
Anyone have any ideas or experience with the Ink integration + MonoDevelop? Here are my project options for Assembly-CSharp-Editor-firstpass (disclaimer: I have no idea what this stuff means):
Hi. Forgive me if this is the wrong place to ask this question.
I'm getting a story exception when testing an Ink script in the player window because I have external functions defined. Is there a good way to assign them dummy bindings in the editor only? I see there is an allowExternalFunctionFallbacks flag but what does that do exactly?
I don't know how to get the Story object from the InkPlayerWindow script. Perhaps an event could be added that fires when the window loads a story?
I've been taking a look at ink this weekend (great work btw, and kudos for open sourcing), with an eye to pitching in towards a javascript runtime (i'm a web guy and would love to play ink games in the browser, it seems such a natural fit with the json output!).
My main stumbling point so far has been the json files themselves, they seems very complex and verbose, even when compiling the smallest hello world example.
I know you have said that there are changes on the way to simplifying the format, so i'm wary of diving too deep or making any suggestions, but I guess i'm wondering if you have either of:
In case you are interested, the bit that confuses me the most is:
[
{
"cmd": "EvalStart"
},
{
"cmd": "BeginString"
},
{
"div": ".^.s",
"push": "func"
},
{
"cmd": "EndString"
},
{
"cmd": "BeginString"
},
".\"",
{
"cmd": "EndString"
},
{
"cmd": "EvalEnd"
}
]
Which seems to be repeated almost once for every line of text. I think I get what it's doing, but it seems like a hell of a lot of boilerplate for what I assume is basically printing a string? (i'm assuming some part of the more advanced features of the language require the verbosity?).
Anyway, thanks for reading, and sorry for the long non-issue (and the slight whiff of entitlement), i'd just REALLY love to get involved on the js side, but don't want to waste time if it's all going to change under me in the immediate future.
Having some trouble with external function binding. I was able to get a function of type Action to work, but another one of type Action<string,int> is throwing exceptions when called. Specifically "failed to cast string to int32". There may be function signatures that will trigger this but I have not explored any other combinations.
Minimal example provided below
Cheers,
-Jonathan
Ink File:
EXTERNAL grow_apples(amount)
EXTERNAL give_apples_to(id, amount)
-> Begin
== Begin ==
{grow_apples(4)}
{give_apples_to("beth", 4)}
-> DONE
MinInkExample.cs with compiled story inlined
using System;
using Ink.Runtime;
public class MinInkExample : object
{
private string storyText = @"{""inkVersion"":11,""root"":[[{""->"":""Begin""},null],""done"",{""Begin"":[[[""G>"",[""ev"",[4,{""x()"":""grow_apples"",""exArgs"":1},null],""out"",""/ev"",null],""G<"",null],""\n"",[""G>"",[""ev"",[""str"",""^beth"",""/str"",4,{""x()"":""give_apples_to"",""exArgs"":2},null],""out"",""/ev"",null],""G<"",null],""\n"",""done"",null],null]}]}";
public MinInkExample()
{
Story s = new Story(storyText);
s.BindExternalFunction("grow_apples", new Action<int>(GrowApples));
s.BindExternalFunction("give_apples_to", new Action<string, int>(GiveApplesTo));
s.ContinueMaximally();
}
// OK
private void GrowApples(int howMany)
{
string msg = "you grow " + howMany + " apples.";
}
// Throws: System.Exception: Failed to cast String to Int32
private void GiveApplesTo(string who, int howMany)
{
string msg = who + " has " + howMany + " apples.";
}
}
It would be nice to have some form of randomization within functions. I was hoping something like
VAR temp_pos = {~1|2|3|4|5}
would work, but it doesn't seem to be possible using the randomization options used by the printing features.
Hello.
Found an issue today with read count (at least, I think that it shouldn't work like this)
If you are making a loop, read count doesn't increase number.
Small example to reproduce this:
=== Beginning ===
How many times he started this story again: {Beginning}
Once upon a time...
+ Where was I?
-> Beginning
* Ah, nevermind. ->END
Beginning always would be 1. And with this small workaround
=== Beginning ===
Once upon a time...
+ Where was I?
-> Times
* Ah, nevermind. ->END
=== Times ===
How many times he started this story again: {Beginning}
-> Beginning
Read count increases correctly. I guess, read count can increase its value only if it comes from another knot, right?
As mentioned, I'm tinkering with an Ink implementation in Java, and it sounds like someone else might do something in Javascript (though perhaps it might make sense to join forces for that - depending on the implementation, a JS client could use the Java code).
Anyway, if it's OK, it might be useful to have a thread for questions/clarifications about the script intents. I've already run into a few things which are not covered in the docs.
I run the following test story via the terminal: (inklecate -p myStory.ink).
===once===
Once upon a time there was a story.
= options
* Read it
* Ignore it
- The end.
-> END
I get the following output:
Once upon a time there was a story.
Runtime error in myStory.ink line 2: ran out of content. Do you need a '-> DONE' or '-> END'?
If i comment out the '= options' stitch, then it works as expected. It looks like including the stitch is causing a problem.
(OSX 10.11.3 i case it matters)
Writing here, for lack of any better persistent forum for discussion (chat has the problem that there is no history for guests, thus limiting the discussion to only those who happen to be present at the time of the discussion).
I have to say that the more I study Ink, the more I like it. I have some preferences that are at odds with what you've done (already mentioned), but I really like the expressiveness and succinctness of the script. I am particularly enthused by Weaves and the Knot = Function approach. These solve issues I've struggled with myself, and my own solutions are nowhere near as elegant.
Tempted to take my existing script engine, and making it Ink compatible. Probably keeping it in Java, although I'm rather keen on Kotlin and Scala at the moment. But Java is native to Android, and will also work for a web app (e.g., using the awesome Play Framework). Have other things I need to finish up first, but it's definitely something that I could see a use for myself. We'll see how much time I have.
Some thoughts:
For instance, is:
{ - x > 0: ~ y = x - 1 - else: ~ y = x + 1 }
The same as:
{
- x > 0:
~ y = x - 1
- else:
~ y = x + 1
}
Or for that matter:
{ - x > 0:
~ y = x - 1
- else:
~ y = x + 1 }
Haven't had the time to get this up and running on my Win PC yet, but whether it's only the middle one that is valid or all three are valid, I'd suggest that this is clarified in the docs. These kind of errors suck having to debug (one of my collaborators ran into something like this not long ago).
Just my 25 cents.
Best of luck at GDC, btw. Hope the Ink talk will be made available; would love to watch it.
Hi guys,
I've tested the thing in Windows and Inklecast does not support accentuated characters. They are always substituted by a ? character.
In the beginning I though it could be just a thing of the inklecast build in windows, but simply, if I export the source to JSON, the output still does not support that special characters.
So this basically means we could not create games with Ink in any language different than English. That is, no Spanish, no French, no Russian, etc.
That's all, good work, I'm enjoying studying Ink a lot. Regards.
Okay so I've created a barebones project where I am seeing this issue:
https://drive.google.com/open?id=0BxMWw45YG-83d1Z0ejlBc2VGWnM (hosted on my google drive because github wouldn't let me upload it)
The scene is Scenes/Main; the script is Scripts/MainStory; the Ink is Resources/Ink/Ink. The plugin folder is included along with the unitypackage which was downloaded from here: https://github.com/inkle/ink-unity-integration/releases.
The issue is like the title says. @joethephish said he had seen it before. I should also mention I am on Windows and Unity version 5.3.4f1. Let me know if there is any other relevant info to know about my machine if you cannot reproduce this!
Tom's Minimalist Language is super simple, requiring way less text than json, but still as powerful and flexible: https://github.com/toml-lang/toml/blob/master/README.md
Reproducable on version 0.2.3 alpha, using windows version of inklecate. Haven't tried with the mac version.
This is the story I used to reproduce:
EXTERNAL bring_the_pain
== CrashMe ==
This is gonna hurt...
+ DONE
Stack trace as follows.
Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
at Ink.InkParser.ExternalDeclaration()
at Ink.StringParser.ParseObject(ParseRule rule)
at Ink.StringParser.OneOf(ParseRule[] array)
at Ink.InkParser.StatementAtLevel(StatementLevel level)
at Ink.InkParser.<StatementsAtLevel>c__AnonStorey6.<>m__0()
at Ink.StringParser.ParseObject(ParseRule rule)
at Ink.StringParser.Interleave[T](ParseRule ruleA, ParseRule ruleB, ParseRule untilTerminator, Boolean flatten)
at Ink.InkParser.StatementsAtLevel(StatementLevel level)
at Ink.InkParser.Parse()
at Ink.CommandLineTool.<CommandLineTool>c__AnonStorey1.<>m__1()
at Ink.CommandLineTool.TimeOperation(String opDescription, Action op)
at Ink.CommandLineTool..ctor(String[] args)
at Ink.CommandLineTool.Main(String[] args)
Hello, first of all, love this project. Very easy to use!
I'm creating a game that is very moldable, and I was trying to get all variables that were created on Ink and pass to a callback method when the -> END is reach.
I'm having difficult because story.variablesState hides his keys and I can't iterate to get all variables.
Just wan't to know if this has some reason or just that none one had this necessity before. If you're ok, i can modify this, exposing this class's keys.
I am currently noodling around with an alternative to the JSON compiled ink format. My current goals are to have small file sizes, faster loading, and no third-party dependencies. However, I'd be keen to know what the original requirements and goals were for the current format so that I can see if my approach could cover those too. For example, the current format is obviously JSON as opposed to a binary format; so the files are easy to parse in alternative languages, are arguably human readable, etc. But if these are not actually requirements of the compiled format then more improvements are possible.
In any case I am still at the experimentation stage so nothing may come of this, but the above info would be handy to document regardless.
Before I jump in and accept this code comment pull request from @MattConrad, I was just wondering if anyone can help out with the source of the problem?
I'm not a Windows expert, though I understand the differences in line termination between platforms, broadly speaking. To be honest I was relatively surprised that me using \n
everywhere worked okay, even when editing a story in Notepad, of all text editors.
Is the .gitattributes
file the way to go to ensure Windows doesn't autoconvert, or is there something else we should be aware of? Should we simply being more line-ending agnostic in the compiler itself...? What's the "modern" way of handling this stuff?
As mentioned in #25.
Will need to revise protection level of various public methods/properties in StoryState
too.
Any chance of a linux build?
Sublime's autocomplete seems to be disabled in .ink files as of the latest build. Manually setting autocomplete to true does not restore the functionality.
If it helps, before build 7222bb2 I was using autocomplete regularly.
Thanks for your help!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.