From 40d3218c54d68d86a56092d060bd903443163b25 Mon Sep 17 00:00:00 2001 From: Jon Ingold Date: Tue, 6 Jun 2017 22:49:21 +0100 Subject: [PATCH 01/12] Implemented the READ_COUNT function runtime ("readc") as introduced in ink 0.7.3 --- engine/ControlCommand.js | 4 ++++ engine/JsonSerialisation.js | 1 + engine/Story.js | 15 +++++++++++---- engine/StoryState.js | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/engine/ControlCommand.js b/engine/ControlCommand.js index bdc7846e..65fc71ed 100644 --- a/engine/ControlCommand.js +++ b/engine/ControlCommand.js @@ -50,6 +50,9 @@ export class ControlCommand extends InkObject{ static TurnsSince(){ return new ControlCommand(CommandType.TurnsSince); } + static ReadCount(){ + return new ControlCommand(CommandType.ReadCount); + } static Random(){ return new ControlCommand(CommandType.Random); } @@ -102,6 +105,7 @@ var CommandType = { End: 18, ListFromInt: 19, ListRange: 20, + ReadCount: 21 } CommandType.TOTAL_VALUES = Object.keys(CommandType).length - 1;//-1 because NotSet shoudn't count ControlCommand.CommandType = CommandType; \ No newline at end of file diff --git a/engine/JsonSerialisation.js b/engine/JsonSerialisation.js index d36e40dc..95a0f341 100644 --- a/engine/JsonSerialisation.js +++ b/engine/JsonSerialisation.js @@ -615,6 +615,7 @@ _controlCommandNames[ControlCommand.CommandType.EndString] = "/str"; _controlCommandNames[ControlCommand.CommandType.NoOp] = "nop"; _controlCommandNames[ControlCommand.CommandType.ChoiceCount] = "choiceCnt"; _controlCommandNames[ControlCommand.CommandType.TurnsSince] = "turns"; +_controlCommandNames[ControlCommand.CommandType.ReadCount] = "readc"; _controlCommandNames[ControlCommand.CommandType.Random] = "rnd"; _controlCommandNames[ControlCommand.CommandType.SeedRandom] = "srnd"; _controlCommandNames[ControlCommand.CommandType.VisitIndex] = "visit"; diff --git a/engine/Story.js b/engine/Story.js index d724d78a..c5e53df3 100644 --- a/engine/Story.js +++ b/engine/Story.js @@ -31,7 +31,7 @@ export class Story extends InkObject{ lists = lists || null; - this.inkVersionCurrent = 16; + this.inkVersionCurrent = 17; this.inkVersionMinimumCompatible = 16; this._variableObservers = null; @@ -748,12 +748,13 @@ export class Story extends InkObject{ break; case ControlCommand.CommandType.TurnsSince: + case ControlCommand.CommandType.ReadCount: var target = this.state.PopEvaluationStack(); if( !(target instanceof DivertTargetValue) ) { var extraNote = ""; if( target instanceof IntValue ) extraNote = ". Did you accidentally pass a read count ('knot_name') instead of a target ('-> knot_name')?"; - this.Error("TURNS_SINCE expected a divert target (knot, stitch, label name), but saw "+target+extraNote); + this.Error("TURNS_SINCE / READ_COUNT expected a divert target (knot, stitch, label name), but saw "+target+extraNote); break; } @@ -761,8 +762,14 @@ export class Story extends InkObject{ var divertTarget = target; // var container = ContentAtPath (divertTarget.targetPath) as Container; var container = this.ContentAtPath(divertTarget.targetPath); - var turnCount = this.TurnsSinceForContainer(container); - this.state.PushEvaluationStack(new IntValue(turnCount)); + + var eitherCount; + if (evalCommand.commandType == ControlCommand.CommandType.TurnsSince) + eitherCount = TurnsSinceForContainer(container); + else + eitherCount = VisitCountForContainer(container); + + this.state.PushEvaluationStack(new IntValue(eitherCount)); break; case ControlCommand.CommandType.Random: diff --git a/engine/StoryState.js b/engine/StoryState.js index 32cb4adb..7cc282a5 100644 --- a/engine/StoryState.js +++ b/engine/StoryState.js @@ -773,5 +773,5 @@ export class StoryState{ } } -StoryState.kInkSaveStateVersion = 6; +StoryState.kInkSaveStateVersion = 7; StoryState.kMinCompatibleLoadVersion = 6; From 2636efb616f103de3ae3c531511dced56ecc9263 Mon Sep 17 00:00:00 2001 From: Jon Ingold Date: Tue, 6 Jun 2017 22:58:23 +0100 Subject: [PATCH 02/12] Implements && and || for two lists, which treats each as a simple boolean: "am I non-empty"? --- engine/NativeFunctionCall.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/NativeFunctionCall.js b/engine/NativeFunctionCall.js index b5079a87..647ddb13 100644 --- a/engine/NativeFunctionCall.js +++ b/engine/NativeFunctionCall.js @@ -298,7 +298,6 @@ export class NativeFunctionCall extends InkObject{ this.AddStringBinaryOp(this.NotEquals,(x, y) => {return !(x === y) ? 1 : 0}); this.AddListBinaryOp(this.Add, (x, y) => {return x.Union(y)}); - this.AddListBinaryOp(this.And, (x, y) => {return x.Union(y)}); this.AddListBinaryOp(this.Subtract, (x, y) => {return x.Without(y)}); this.AddListBinaryOp(this.Has, (x, y) => {return x.Contains(y) ? 1 : 0}); this.AddListBinaryOp(this.Hasnt, (x, y) => {return x.Contains(y) ? 0 : 1}); @@ -310,6 +309,9 @@ export class NativeFunctionCall extends InkObject{ this.AddListBinaryOp(this.GreaterThanOrEquals, (x, y) => {return x.GreaterThanOrEquals(y) ? 1 : 0}); this.AddListBinaryOp(this.LessThanOrEquals, (x, y) => {return x.LessThanOrEquals(y) ? 1 : 0}); this.AddListBinaryOp(this.NotEquals, (x, y) => {return !x.Equals(y) ? 1 : 0}); + + this.AddListBinaryOp (this.And, (x, y) => {return x.Count > 0 && y.Count > 0 ? 1 : 0}); + this.AddListBinaryOp (this.Or, (x, y) => {return x.Count > 0 || y.Count > 0 ? 1 : 0}); this.AddListUnaryOp(this.Not, (x) => {return x.Count == 0 ? 1 : 0}); From 34919224e921b521ef1f3bfaf77b4abc1d83309f Mon Sep 17 00:00:00 2001 From: Jon Ingold Date: Wed, 7 Jun 2017 10:19:37 +0100 Subject: [PATCH 03/12] Missing "this." in ported code --- engine/Story.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/Story.js b/engine/Story.js index c5e53df3..6dc9b00e 100644 --- a/engine/Story.js +++ b/engine/Story.js @@ -765,9 +765,9 @@ export class Story extends InkObject{ var eitherCount; if (evalCommand.commandType == ControlCommand.CommandType.TurnsSince) - eitherCount = TurnsSinceForContainer(container); + eitherCount = this.TurnsSinceForContainer(container); else - eitherCount = VisitCountForContainer(container); + eitherCount = this.VisitCountForContainer(container); this.state.PushEvaluationStack(new IntValue(eitherCount)); break; From 9567c0135ae9170bd97266c51ac8b0780d94e3e3 Mon Sep 17 00:00:00 2001 From: Jon Ingold Date: Mon, 12 Jun 2017 15:04:52 +0100 Subject: [PATCH 04/12] Fix: logic returned int not IntValue This bug's been here for a while, but I think this code path wasn't being used (much?) until 0.7.3 introduced "normal" && and || for lists. --- engine/NativeFunctionCall.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/NativeFunctionCall.js b/engine/NativeFunctionCall.js index b5079a87..33df0819 100644 --- a/engine/NativeFunctionCall.js +++ b/engine/NativeFunctionCall.js @@ -147,7 +147,7 @@ export class NativeFunctionCall extends InkObject{ // var op = _operationFuncs [ValueType.Int] as BinaryOp; var op = this._operationFuncs[ValueType.Int]; var result = op(v1.isTruthy ? 1 : 0, v2.isTruthy ? 1 : 0); - return parseInt(result); + return new IntValue (result); } // Normal (list • list) operation @@ -406,4 +406,4 @@ NativeFunctionCall.Count = "LIST_COUNT"; NativeFunctionCall.ValueOfList = "LIST_VALUE"; NativeFunctionCall.Invert = "LIST_INVERT"; -NativeFunctionCall._nativeFunctions = null; \ No newline at end of file +NativeFunctionCall._nativeFunctions = null; From 9ce3c78096b8de3685e4d15dd357de9fd066053f Mon Sep 17 00:00:00 2001 From: Jon Ingold Date: Wed, 14 Jun 2017 14:03:34 +0100 Subject: [PATCH 05/12] StoryState._originalCallstack needs to be copied This is a bug that's only just appeared in our tests; I guess we're using an external function in a slightly more complex way than previously. --- engine/StoryState.js | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/StoryState.js b/engine/StoryState.js index 32cb4adb..4b34e17a 100644 --- a/engine/StoryState.js +++ b/engine/StoryState.js @@ -734,6 +734,7 @@ export class StoryState{ } copy.callStack = new CallStack(this.callStack); + copy._originalCallstack = this._originalCallstack; copy._variablesState = new VariablesState(copy.callStack, this.story.listDefinitions); copy.variablesState.CopyFrom(this.variablesState); From 64e1f775d45d6ffbc88a050309e49240f36c3cfd Mon Sep 17 00:00:00 2001 From: Jon Ingold Date: Wed, 14 Jun 2017 16:49:15 +0100 Subject: [PATCH 06/12] OriginalEvaluationStackHeight needs to be copied --- engine/StoryState.js | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/StoryState.js b/engine/StoryState.js index 4b34e17a..5d1f62cf 100644 --- a/engine/StoryState.js +++ b/engine/StoryState.js @@ -740,6 +740,7 @@ export class StoryState{ copy.variablesState.CopyFrom(this.variablesState); copy.evaluationStack.push.apply(copy.evaluationStack, this.evaluationStack); + copy._originalEvaluationStackHeight = this._originalEvaluationStackHeight; if (this.divertedTargetObject != null) copy.divertedTargetObject = this.divertedTargetObject; From 4ee83b43477afefb54b210e6b7d5198bc744e8b3 Mon Sep 17 00:00:00 2001 From: Yannick Lohse Date: Wed, 14 Jun 2017 23:05:52 +0200 Subject: [PATCH 07/12] Removed extra whitespace --- engine/NativeFunctionCall.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/NativeFunctionCall.js b/engine/NativeFunctionCall.js index 514fdb56..e88d2a60 100644 --- a/engine/NativeFunctionCall.js +++ b/engine/NativeFunctionCall.js @@ -147,7 +147,7 @@ export class NativeFunctionCall extends InkObject{ // var op = _operationFuncs [ValueType.Int] as BinaryOp; var op = this._operationFuncs[ValueType.Int]; var result = op(v1.isTruthy ? 1 : 0, v2.isTruthy ? 1 : 0); - return new IntValue (result); + return new IntValue(result); } // Normal (list • list) operation From df492dc2dbe7403e466f0658c096868ad7ccc8b4 Mon Sep 17 00:00:00 2001 From: Yannick Lohse Date: Sun, 18 Jun 2017 19:39:30 +0200 Subject: [PATCH 08/12] port changees from 7.2 --- engine/CallStack.js | 3 +++ engine/InkList.js | 24 ++++++++++++++++++------ engine/ListDefinition.js | 2 +- engine/NativeFunctionCall.js | 2 +- engine/Story.js | 9 +++++++-- engine/StoryState.js | 3 +++ 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/engine/CallStack.js b/engine/CallStack.js index 97cd6656..d7bdf377 100644 --- a/engine/CallStack.js +++ b/engine/CallStack.js @@ -162,6 +162,9 @@ export class CallStack{ get elements(){ return this.callStack; } + get depth(){ + return this.elements.length; + } get currentElement(){ return this.callStack[this.callStack.length - 1]; } diff --git a/engine/InkList.js b/engine/InkList.js index 9c930bcd..c0b3b418 100644 --- a/engine/InkList.js +++ b/engine/InkList.js @@ -48,25 +48,37 @@ export class InkListItem{ //in C#, rawlists are based on dictionnary; the equivalent of a dictionnary in js is Object, but we can't use that or it will conflate dictionnary items and InkList class properties. //instead InkList-js has a special _values property wich contains the actual "Dictionnary", and a few Dictionnary methods are re-implemented on InkList. This also means directly iterating over the InkList won't work as expected. Maybe we can return a proxy if that's required. +//@TODO: actually we could use a Map for this. export class InkList { - constructor(otherListOrSingleElement){ + constructor(polymorphicArgument, originStory){ this._keys = {}; this._values = {}; this.origins = null; this._originNames = null; //polymorphioc constructor - if (otherListOrSingleElement){ - if (otherListOrSingleElement instanceof InkList){ - var otherList = otherListOrSingleElement; + if (polymorphicArgument){ + if (polymorphicArgument instanceof InkList){ + var otherList = polymorphicArgument; otherList.forEach((kv)=>{ this.Add(kv.Key, kv.Value); }); this._originNames = otherList._originNames; } - else if (otherListOrSingleElement.hasOwnProperty('Key') && otherListOrSingleElement.hasOwnProperty('Value')){ - var singleElement = otherListOrSingleElement; + else if (typeof polymorphicArgument === 'string'){ + this.SetInitialOriginName(polymorphicArgument); + + var def = null; + if (def = originStory.listDefinitions.TryGetDefinition(polymorphicArgument, def)){ + this.origins = [def]; + } + else{ + throw new Error("InkList origin could not be found in story when constructing new list: " + singleOriginListName); + } + } + else if (polymorphicArgument.hasOwnProperty('Key') && polymorphicArgument.hasOwnProperty('Value')){ + var singleElement = polymorphicArgument; this.Add(singleElement.Key, singleElement.Value); } } diff --git a/engine/ListDefinition.js b/engine/ListDefinition.js index 5ca2c731..2b1d63fd 100644 --- a/engine/ListDefinition.js +++ b/engine/ListDefinition.js @@ -49,7 +49,7 @@ export class ListDefinition{ return this._itemNameToValues[itemName] !== undefined; } TryGetItemWithValue(val, item){//item was an out - //the original function returns a boolean and has a second parameter called item that is an `out`. Both are needed and we can't just return the item because it'll always be truthy. Instead, we return an object containing the bool an dthe item + //the original function returns a boolean and has a second parameter called item that is an `out`. Both are needed and we can't just return the item because it'll always be truthy. Instead, we return an object containing the bool and the item for (var key in this._itemNameToValues){ if (this._itemNameToValues[key] == val) { item = new InkListItem(this.name, key); diff --git a/engine/NativeFunctionCall.js b/engine/NativeFunctionCall.js index e88d2a60..b2339487 100644 --- a/engine/NativeFunctionCall.js +++ b/engine/NativeFunctionCall.js @@ -311,7 +311,7 @@ export class NativeFunctionCall extends InkObject{ this.AddListBinaryOp(this.NotEquals, (x, y) => {return !x.Equals(y) ? 1 : 0}); this.AddListBinaryOp (this.And, (x, y) => {return x.Count > 0 && y.Count > 0 ? 1 : 0}); - this.AddListBinaryOp (this.Or, (x, y) => {return x.Count > 0 || y.Count > 0 ? 1 : 0}); + this.AddListBinaryOp (this.Or, (x, y) => {return x.Count > 0 || y.Count > 0 ? 1 : 0}); this.AddListUnaryOp(this.Not, (x) => {return x.Count == 0 ? 1 : 0}); diff --git a/engine/Story.js b/engine/Story.js index 6dc9b00e..7db58cf8 100644 --- a/engine/Story.js +++ b/engine/Story.js @@ -1304,11 +1304,16 @@ export class Story extends InkObject{ return tags; } BuildStringOfHierarchy(){ - var sb = new StringBuilder; + var sb = new StringBuilder(); this.mainContentContainer.BuildStringOfHierarchy(sb, 0, this.state.currentContentObject); - return sb.toString(); + return sb.toString(); + } + BuildStringOfContainer(container){ + var sb = new StringBuilder(); + container.BuildStringOfHierarchy(sb, 0, this.state.currentContentObject); + return sb.toString(); } NextContent(){ // Setting previousContentObject is critical for VisitChangedContainersDueToDivert diff --git a/engine/StoryState.js b/engine/StoryState.js index 7cc282a5..db63d5c8 100644 --- a/engine/StoryState.js +++ b/engine/StoryState.js @@ -217,6 +217,9 @@ export class StoryState{ set previousContentObject(value){ this.callStack.currentThread.previousContentObject = value; } + get callstackDepth(){ + return this.callStack.depth; + } get jsonToken(){ var obj = {}; From f5375156974d974cb00f83288b86972e9734601f Mon Sep 17 00:00:00 2001 From: Jon Ingold Date: Wed, 28 Jun 2017 11:07:02 +0100 Subject: [PATCH 09/12] Better cloning of the original callstack To ensure it really is a deep copy --- engine/StoryState.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/StoryState.js b/engine/StoryState.js index 5d1f62cf..52654405 100644 --- a/engine/StoryState.js +++ b/engine/StoryState.js @@ -734,7 +734,7 @@ export class StoryState{ } copy.callStack = new CallStack(this.callStack); - copy._originalCallstack = this._originalCallstack; + if (this._originalCallstack) { copy._originalCallstack = new CallStack(this._originalCallstack); } copy._variablesState = new VariablesState(copy.callStack, this.story.listDefinitions); copy.variablesState.CopyFrom(this.variablesState); From c757ef62b47636aeb543bdc519f3950657cd86ae Mon Sep 17 00:00:00 2001 From: Yannick Lohse Date: Wed, 28 Jun 2017 20:12:49 +0200 Subject: [PATCH 10/12] Changes from ink 7.4 --- engine/Container.js | 12 ++++++------ engine/StoryState.js | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/engine/Container.js b/engine/Container.js index c6339711..a2ad380a 100644 --- a/engine/Container.js +++ b/engine/Container.js @@ -33,24 +33,24 @@ export class Container extends InkObject{//also implements INamedContent. Not su this.AddContent(value); } get namedOnlyContent(){ - var namedOnlyContent = {}; + var namedOnlyContentDict = {}; for (var key in this.namedContent){ - namedOnlyContent[key] = this.namedContent[key]; + namedOnlyContentDict[key] = this.namedContent[key]; } this.content.forEach(c => { // var named = c as INamedContent; var named = c; if (named.name && named.hasValidName) { - delete namedOnlyContent[named.name]; + delete namedOnlyContentDict[named.name]; } }); - if (namedOnlyContent.length == 0) - namedOnlyContent = null; + if (Object.keys(namedOnlyContentDict).length == 0) + namedOnlyContentDict = null; - return namedOnlyContent; + return namedOnlyContentDict; } set namedOnlyContent(value){ var existingNamedOnly = this.namedOnlyContent; diff --git a/engine/StoryState.js b/engine/StoryState.js index e8f0d976..5aa9e65f 100644 --- a/engine/StoryState.js +++ b/engine/StoryState.js @@ -737,13 +737,13 @@ export class StoryState{ } copy.callStack = new CallStack(this.callStack); - if (this._originalCallstack) { copy._originalCallstack = new CallStack(this._originalCallstack); } + if (this._originalCallstack) copy._originalCallstack = new CallStack(this._originalCallstack); copy._variablesState = new VariablesState(copy.callStack, this.story.listDefinitions); copy.variablesState.CopyFrom(this.variablesState); copy.evaluationStack.push.apply(copy.evaluationStack, this.evaluationStack); - copy._originalEvaluationStackHeight = this._originalEvaluationStackHeight; + copy._originalEvaluationStackHeight = this._originalEvaluationStackHeight; if (this.divertedTargetObject != null) copy.divertedTargetObject = this.divertedTargetObject; From f55fed9017b3eb9fd0fa28a254e055cbb0d9c0b8 Mon Sep 17 00:00:00 2001 From: Yannick Lohse Date: Wed, 28 Jun 2017 20:30:22 +0200 Subject: [PATCH 11/12] Fix #70 --- engine/JsonSerialisation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/JsonSerialisation.js b/engine/JsonSerialisation.js index 95a0f341..f3bb0a4a 100644 --- a/engine/JsonSerialisation.js +++ b/engine/JsonSerialisation.js @@ -228,10 +228,10 @@ export class JsonSerialisation{ varAss.isGlobal = isGlobalVar; return varAss; } - if (propValue = obj["#"]){ + if (obj["#"] !== undefined){ + propValue = obj["#"] return new Tag(propValue.toString()); } - //list value if (propValue = obj["list"]) { // var listContent = (Dictionary)propValue; From d25e7aa45258e608fd542331586c87c38925959b Mon Sep 17 00:00:00 2001 From: Yannick Lohse Date: Wed, 28 Jun 2017 20:33:45 +0200 Subject: [PATCH 12/12] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 33a762e6..dfff1ad8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "inkjs", - "version": "1.5.2", + "version": "1.6.0", "description": "A javascript port of inkle's ink scripting language (http://www.inklestudios.com/ink/)", "main": "dist/ink-es2015.js", "scripts": {