diff --git a/Documentation/RunningYourInk.md b/Documentation/RunningYourInk.md index e6d22e36..7b92fc18 100644 --- a/Documentation/RunningYourInk.md +++ b/Documentation/RunningYourInk.md @@ -13,9 +13,7 @@ Ink uses an intermediate `.json` format, which is compiled from the original `.ink` files. ink's Unity integration package automatically compiles ink files for you, but you can also compile them on the command line. See **Using inklecate on the command line** in the [README](http://www.github.com/inkle/ink) for more information. -The main runtime code is included in the `ink-engine.dll`, and we also have a dependency on the `Newtonsoft.Json.dll` library, which is also included in the integration package. - -You may need to change API compatibility for .NET: Go into `Edit -> Project settings -> Player -> Other settings` and change API compatibility level from .NET 2.0 subset (the default) to .NET 2.0. (If you get the error `TypeLoadException: Could not load type 'Newtonsoft.Json.Linq.JArray' from assembly 'Newtonsoft.Json`..., this is what's wrong.) +The main runtime code is included in the `ink-engine.dll`. We recommend that you create a wrapper MonoBehaviour component for the **ink** `Story`. Here, we'll call the component "Script" - in the "film script" sense, rather than the "Unity script" sense! @@ -172,7 +170,17 @@ You can define game-side functions in C# that can be called directly from **ink* The types you can use as parameters and return values are int, float, bool (automatically converted from **ink**’s internal ints) and string. - +### Fallbacks for external functions + +When testing your story, either in [Inky](https://github.com/inkle/inky) or in the [ink-unity integration](https://github.com/inkle/ink-unity-integration/) player window, you don't get an opportunity to bind a game function before running the story. To get around this, you can define a *fallback function* within ink, which is run if the `EXTERNAL` function can't be found. To do so, simply create an ink function with the same name and parameters. For example, for the above `multiply` example, create the ink function: + +``` +=== function multiply(x,y) === +// Usually external functions can only return placeholder +// results, otherwise they'd be defined in ink! +~ return 1 +``` + ## Debugging ink engine issues The **ink** engine is still in a nascent stage (alpha!), and you may well encounter bugs, or unhelpful error messages and exceptions. diff --git a/README.md b/README.md index 2e9f0017..3dc82bf9 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![HipChat](http://www.inklestudios.com/img/ink-github-HipChat-widget.svg)](https://www.hipchat.com/gkq2pSLqU) [![CI Status](http://img.shields.io/travis/inkle/ink.svg?style=flat)](https://travis-ci.org/inkle/ink) -Ink is [inkle](http://www.inklestudios.com/)'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. +[Ink](http://www.inklestudios.com/ink) is [inkle](http://www.inklestudios.com/)'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](https://github.com/inkle/ink/blob/master/Documentation/WritingWithInk.md). @@ -26,6 +26,8 @@ Here's a taster [from the tutorial](https://github.com/inkle/ink/blob/master/Doc * ... but I said nothing[] and <> - we passed the day in silence. - -> END + +We'd recommend downloading [Inky, our ink editor](https://github.com/inkle/inky), and the follow [the tutorial](https://github.com/inkle/ink/blob/master/Documentation/WritingWithInk.md), if you want to give ink a try. Broadly, the engine is made up of two components: diff --git a/ink-engine-runtime/VariablesState.cs b/ink-engine-runtime/VariablesState.cs index 6f158746..2ef67e22 100644 --- a/ink-engine-runtime/VariablesState.cs +++ b/ink-engine-runtime/VariablesState.cs @@ -56,6 +56,12 @@ public object this[string variableName] return null; } set { + + // This is the main + if (!_globalVariables.ContainsKey (variableName)) { + throw new StoryException ("Variable '" + variableName + "' doesn't exist, so can't be set."); + } + var val = Runtime.Value.Create(value); if (val == null) { if (value == null) { diff --git a/inklecate/ParsedHierarchy/FlowBase.cs b/inklecate/ParsedHierarchy/FlowBase.cs index cae2d1d1..9026149b 100644 --- a/inklecate/ParsedHierarchy/FlowBase.cs +++ b/inklecate/ParsedHierarchy/FlowBase.cs @@ -190,8 +190,10 @@ public override Runtime.Object GenerateRuntimeObject () var childFlowRuntime = childFlow.runtimeObject; - // First inner knot/stitch - automatically step into it - if (contentIdx == 0 && !childFlow.hasParameters) { + // First inner stitch - automatically step into it + // 20/09/2016 - let's not auto step into knots + if (contentIdx == 0 && !childFlow.hasParameters + && this.flowLevel == FlowLevel.Knot) { _startingSubFlowDivert = new Runtime.Divert (); container.AddContent(_startingSubFlowDivert); _startingSubFlowRuntime = childFlowRuntime; diff --git a/tests/Tests.cs b/tests/Tests.cs index 26f7bdec..7bdeb4fb 100644 --- a/tests/Tests.cs +++ b/tests/Tests.cs @@ -181,7 +181,6 @@ public void TestCallStackEvaluation() { var storyStr = @" - === eight { six() + two() } -> END @@ -336,7 +335,6 @@ public void TestConditionalChoiceInWeave() { var storyStr = @" -== test == - start { - true: * [go to a stitch] -> a_stitch @@ -1016,6 +1014,7 @@ public void TestIncrement() public void TestKnotDotGather() { var story = CompileString(@" +-> knot === knot -> knot.gather - (gather) g @@ -1045,6 +1044,7 @@ public void TestKnotTerminationSkipsGlobalObjects() public void TestKnotThreadInteraction() { Story story = CompileString(@" +-> knot === knot <- threadB -> tunnel -> @@ -1078,6 +1078,7 @@ THE END public void TestKnotThreadInteraction2() { Story story = CompileString(@" +-> knot === knot <- threadA When should this get printed? @@ -1168,6 +1169,7 @@ public void TestMultipleConstantReferences() public void TestMultiThread() { Story story = CompileString(@" +-> start == start == -> tunnel -> The end @@ -1243,6 +1245,7 @@ public void TestNonTextInChoiceInnerContent() { var storyStr = @" +-> knot == knot * option text[]. {true: Conditional bit.} -> next -> DONE @@ -1264,6 +1267,7 @@ public void TestNonTextInChoiceInnerContent() public void TestOnceOnlyChoicesCanLinkBackToSelf() { var story = CompileString(@" +-> opts = opts * (firstOpt) [First choice] -> opts * {firstOpt} [Second choice] -> opts @@ -1480,7 +1484,6 @@ In second. public void TestReadCountAcrossThreads() { var story = CompileString(@" -=== empty_world === -> top = top @@ -1550,6 +1553,7 @@ public void TestReturnTextWarning() public void TestSameLineDivertIsInline() { var story = CompileString(@" +-> hurry_home === hurry_home === We hurried home to Savile Row -> as_fast_as_we_could @@ -1615,6 +1619,7 @@ public void TestSimpleGlue() public void TestStickyChoicesStaySticky() { var story = CompileString(@" +-> test == test == First line. Second line. @@ -1847,6 +1852,7 @@ public void TestTurnsSince() public void TestTurnsSinceNested() { var story = CompileString(@" +-> empty_world === empty_world === {TURNS_SINCE(-> then)} = -1 * (then) stuff @@ -2151,6 +2157,7 @@ public void TestWeaveOptions() { var storyStr = @" + -> test === test * Hello[.], world. -> END @@ -2170,6 +2177,7 @@ public void TestWhitespace() { var storyStr = @" +-> firstKnot === firstKnot Hello! -> anotherKnot @@ -2232,6 +2240,7 @@ public void TestTempGlobalConflict() // Test bug where temp was being treated as a global var storyStr = @" +-> outer === outer ~ temp x = 0 ~ f(x) @@ -2425,6 +2434,22 @@ Another line. Assert.AreEqual ("A line.\nAnother line.\n", story.ContinueMaximally ()); } + [Test ()] + public void TestSetNonExistantVariable () + { + var storyStr = + @" +VAR x = ""world"" +Hello {x}. +"; + var story = CompileString (storyStr); + + Assert.AreEqual ("Hello world.\n", story.Continue()); + + Assert.Throws(() => { + story.variablesState ["y"] = "earth"; + }); + } // Helper compile function protected Story CompileString(string str, bool countAllVisits = false, bool testingErrors = false)