From 1e32c99e46e3b70e018cda7195debe640fef8b0e Mon Sep 17 00:00:00 2001 From: Jon Ingold Date: Thu, 9 Mar 2017 14:09:55 +0000 Subject: [PATCH 1/6] Fix for copying empty lists Port of ink commit #a5ae973 --- engine/RawList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/RawList.js b/engine/RawList.js index 210869f5..4e667f41 100644 --- a/engine/RawList.js +++ b/engine/RawList.js @@ -126,7 +126,7 @@ export class RawList{ this._originNames = [initialOriginName]; } SetInitialOriginNames(initialOriginNames){ - this._originNames = initialOriginNames.slice();//store a copy + this._originNames = initialOriginNames ? initialOriginNames.slice() : null; //store a copy } get maxItem(){ var max = { @@ -282,4 +282,4 @@ export class RawList{ valueOf(){ return NaN; } -} \ No newline at end of file +} From 92f6109f919ef1365d460420dc984d245f616c68 Mon Sep 17 00:00:00 2001 From: Felix Laurie von Massenbach Date: Sat, 18 Mar 2017 11:58:13 +0000 Subject: [PATCH 2/6] Fix declaration inside nested statement This is an error in strict mode. --- engine/Story.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/Story.js b/engine/Story.js index 8adf2817..c17fd83b 100644 --- a/engine/Story.js +++ b/engine/Story.js @@ -878,7 +878,7 @@ export class Story extends InkObject{ // Allow either int or a particular list item to be passed for the bounds, // so wrap up a function to handle this casting for us. - function IntBound(obj){ + var IntBound = function IntBound(obj){ // var listValue = obj as ListValue; var listValue = obj; if (listValue instanceof ListValue) { From d6fbef2a0f2f6711671714a4233c1c300005a557 Mon Sep 17 00:00:00 2001 From: Jon Ingold Date: Tue, 21 Mar 2017 21:58:54 +0000 Subject: [PATCH 3/6] Bug fix: string didn't need ".value" Minor bug fix for ~ temp listItem = ListName(intValue) type ink-calls --- engine/Story.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/Story.js b/engine/Story.js index 8adf2817..6064ff66 100644 --- a/engine/Story.js +++ b/engine/Story.js @@ -851,7 +851,7 @@ export class Story extends InkObject{ var generatedListValue = null; var foundListDef; - if (foundListDef = this.listDefinitions.TryGetDefinition(listNameVal.value, foundListDef)) { + if (foundListDef = this.listDefinitions.TryGetDefinition(listNameVal, foundListDef)) { var foundItem = foundListDef.TryGetItemWithValue(intVal.value); if (foundItem.exists) { generatedListValue = new ListValue(foundItem.item, intVal.value); From 6189bedf1a857489ef4a7e50458aed8922e5763b Mon Sep 17 00:00:00 2001 From: Jon Ingold Date: Tue, 21 Mar 2017 23:25:35 +0000 Subject: [PATCH 4/6] Null value caused crash It should perhaps have not even been null here, but it's easy enough to guard against. --- engine/Story.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/Story.js b/engine/Story.js index 8adf2817..9693f947 100644 --- a/engine/Story.js +++ b/engine/Story.js @@ -629,7 +629,7 @@ export class Story extends InkObject{ var output = this.state.PopEvaluationStack(); // Functions may evaluate to Void, in which case we skip output - if (!(output instanceof Void)) { + if (output != null && !(output instanceof Void)) { // TODO: Should we really always blanket convert to string? // It would be okay to have numbers in the output stream the // only problem is when exporting text for viewing, it skips over numbers etc. From abbadfa44bb563f04f7e7f99206af1eacc324ccc Mon Sep 17 00:00:00 2001 From: Yannick Lohse Date: Sun, 2 Apr 2017 15:50:19 +0200 Subject: [PATCH 5/6] FIx #57 --- engine/CallStack.js | 2 +- engine/{RawList.js => InkList.js} | 107 +++++++++++++++++++++++------- engine/JsonSerialisation.js | 6 +- engine/ListDefinition.js | 19 ++++-- engine/ListDefinitionsOrigin.js | 8 +-- engine/NativeFunctionCall.js | 13 ++-- engine/Story.js | 8 ++- engine/StoryState.js | 7 +- engine/Value.js | 12 ++-- 9 files changed, 126 insertions(+), 56 deletions(-) rename engine/{RawList.js => InkList.js} (66%) diff --git a/engine/CallStack.js b/engine/CallStack.js index 4e2054b4..97cd6656 100644 --- a/engine/CallStack.js +++ b/engine/CallStack.js @@ -198,8 +198,8 @@ export class CallStack{ } PushThread(){ var newThread = this.currentThread.Copy(); - newThread.threadIndex = this._threadCounter; this._threadCounter++; + newThread.threadIndex = this._threadCounter; this._threads.push(newThread); } PopThread(){ diff --git a/engine/RawList.js b/engine/InkList.js similarity index 66% rename from engine/RawList.js rename to engine/InkList.js index 4e667f41..9c930bcd 100644 --- a/engine/RawList.js +++ b/engine/InkList.js @@ -1,6 +1,6 @@ import {StringBuilder} from './StringBuilder'; -export class RawListItem{ +export class InkListItem{ constructor(fullNameOrOriginName, itemName){ if (itemName !== undefined){ this.originName = fullNameOrOriginName; @@ -13,7 +13,7 @@ export class RawListItem{ } } static Null(){ - return new RawListItem(null, null); + return new InkListItem(null, null); } isNull(){ return this.originName == null && this.itemName == null; @@ -25,8 +25,8 @@ export class RawListItem{ return this.fullname; } Equals(obj){ - if (obj instanceof RawListItem) { -// var otherItem = (RawListItem)obj; + if (obj instanceof InkListItem) { +// var otherItem = (InkListItem)obj; var otherItem = obj; return otherItem.itemName == this.itemName && otherItem.originName == this.originName; @@ -36,7 +36,7 @@ export class RawListItem{ } //GetHashCode not implemented toString(){ - //WARNING: experimental. RawListItem are structs and are used as keys inside hashes. In js, we can't use an object as a key, as the key needs to be a string. C# gets around that with the internal GetHashCode, and the js equivalent to that is toString. So here, toString acts as C#'s GetHashCode + //WARNING: experimental. InkListItem are structs and are used as keys inside hashes. In js, we can't use an object as a key, as the key needs to be a string. C# gets around that with the internal GetHashCode, and the js equivalent to that is toString. So here, toString acts as C#'s GetHashCode var originCode = '0'; var itemCode = (this.itemName) ? this.itemName.toString() : 'null'; if (this.originName != null) @@ -46,9 +46,9 @@ export class RawListItem{ } } -//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 RawList class properties. -//instead RawList-js has a special _values property wich contains the actual "Dictionnary", and a few Dictionnary methods are re-implemented on RawList. This also means directly iterating over the RawList won't work as expected. Maybe we can return a proxy if that's required. -export class RawList{ +//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. +export class InkList { constructor(otherListOrSingleElement){ this._keys = {}; this._values = {}; @@ -57,7 +57,7 @@ export class RawList{ //polymorphioc constructor if (otherListOrSingleElement){ - if (otherListOrSingleElement instanceof RawList){ + if (otherListOrSingleElement instanceof InkList){ var otherList = otherListOrSingleElement; otherList.forEach((kv)=>{ this.Add(kv.Key, kv.Value); @@ -79,6 +79,60 @@ export class RawList{ }); } } + AddItem(itemOrItemName){ + if (itemOrItemName instanceof InkListItem){ + var item = itemOrItemName; + + if (item.originName == null) { + this.AddItem(item.itemName); + return; + } + + this.origins.forEach((origin)=>{ + if (origin.name == item.originName) { + var intVal; + intVal = origin.TryGetValueForItem(item, intVal); + if (intVal !== undefined) { + this.Add(item, intVal); + return; + } else { + throw "Could not add the item " + item + " to this list because it doesn't exist in the original list definition in ink."; + } + } + }); + + throw "Failed to add item to list because the item was from a new list definition that wasn't previously known to this list. Only items from previously known lists can be used, so that the int value can be found."; + } + else{ + var itemName = itemOrItemName; + + var foundListDef = null; + + this.origins.forEach((origin)=>{ + if (origin.ContainsItemWithName(itemName)) { + if (foundListDef != null) { + throw "Could not add the item " + itemName + " to this list because it could come from either " + origin.name + " or " + foundListDef.name; + } else { + foundListDef = origin; + } + } + }); + + if (foundListDef == null) + throw "Could not add the item " + itemName + " to this list because it isn't known to any list definitions previously associated with this list."; + + var item = new InkListItem(foundListDef.name, itemName); + var itemVal = foundListDef.ValueForItem(item); + this.Add(item, itemVal); + } + } + ContainsItemNamed(itemName){ + var contains = false; + this.forEach(itemWithValue => { + if (itemWithValue.Key.itemName == itemName) contains = true; + }); + return contains; + } ContainsKey(key){ return key in this._values; } @@ -126,7 +180,10 @@ export class RawList{ this._originNames = [initialOriginName]; } SetInitialOriginNames(initialOriginNames){ - this._originNames = initialOriginNames ? initialOriginNames.slice() : null; //store a copy + if (initialOriginNames == null) + this._originNames = null; + else + this._originNames = initialOriginNames.slice();//store a copy } get maxItem(){ var max = { @@ -153,7 +210,7 @@ export class RawList{ return min; } get inverse(){ - var list = new RawList(); + var list = new InkList(); if (this.origins != null) { this.origins.forEach((origin)=>{ origin.items.forEach((itemAndValue)=>{ @@ -165,7 +222,7 @@ export class RawList{ return list; } get all(){ - var list = new RawList(); + var list = new InkList(); if (this.origins != null) { this.origins.forEach(function(origin){ origin.items.forEach(function(itemAndValue){ @@ -176,14 +233,14 @@ export class RawList{ return list; } Union(otherList){ - var union = new RawList(this); + var union = new InkList(this); otherList.forEach(function(kv){ union.Add(kv.Key, kv.Value); }); return union; } Intersect(otherList){ - var intersection = new RawList(); + var intersection = new InkList(); this.forEach(function(kv){ if (otherList.ContainsKey(kv.Key)) intersection.Add(kv.Key, kv.Value); @@ -191,7 +248,7 @@ export class RawList{ return intersection; } Without(listToRemove){ - var result = new RawList(this); + var result = new InkList(this); listToRemove.forEach(function(kv){ result.Remove(kv.Key); }); @@ -233,25 +290,25 @@ export class RawList{ } MaxAsList(){ if (this.Count > 0) - return new RawList(this.maxItem); + return new InkList(this.maxItem); else - return new RawList(); + return new InkList(); } MinAsList(){ if (this.Count > 0) - return new RawList(this.minItem); + return new InkList(this.minItem); else - return new RawList(); + return new InkList(); } Equals(other){ -// var otherRawList = other as RawList; - var otherRawList = other; - if (otherRawList instanceof RawList === false) return false; - if (otherRawList.Count != this.Count) return false; +// var otherInkList = other as InkList; + var otherInkList = other; + if (otherInkList instanceof InkList === false) return false; + if (otherInkList.Count != this.Count) return false; var equals = true; this.forEach(function(kv){ - if (!otherRawList.ContainsKey(kv.Key)) + if (!otherInkList.ContainsKey(kv.Key)) equals = false; }); @@ -278,7 +335,7 @@ export class RawList{ return sb.toString(); } - //casting a RawList to a Number, for somereason, actually gives a number. This messes up the type detection when creating a Value from a RawList. Returning NaN here prevents that. + //casting a InkList to a Number, for somereason, actually gives a number. This messes up the type detection when creating a Value from a InkList. Returning NaN here prevents that. valueOf(){ return NaN; } diff --git a/engine/JsonSerialisation.js b/engine/JsonSerialisation.js index ce55779c..d36e40dc 100644 --- a/engine/JsonSerialisation.js +++ b/engine/JsonSerialisation.js @@ -14,7 +14,7 @@ import {Path} from './Path'; import {Choice} from './Choice'; import {ListDefinition} from './ListDefinition'; import {ListDefinitionsOrigin} from './ListDefinitionsOrigin'; -import {RawListItem, RawList} from './RawList'; +import {InkListItem, InkList} from './InkList'; import {Object as InkObject} from './Object'; export class JsonSerialisation{ @@ -236,7 +236,7 @@ export class JsonSerialisation{ if (propValue = obj["list"]) { // var listContent = (Dictionary)propValue; var listContent = propValue; - var rawList = new RawList(); + var rawList = new InkList(); if (propValue = obj["origins"]) { // var namesAsObjs = (List)propValue; var namesAsObjs = propValue; @@ -246,7 +246,7 @@ export class JsonSerialisation{ for (var key in listContent){ var nameToVal = listContent[key]; - var item = new RawListItem(key); + var item = new InkListItem(key); var val = parseInt(nameToVal); rawList.Add(item, val); } diff --git a/engine/ListDefinition.js b/engine/ListDefinition.js index 248e0690..5ca2c731 100644 --- a/engine/ListDefinition.js +++ b/engine/ListDefinition.js @@ -1,4 +1,4 @@ -import {RawList, RawListItem} from './RawList'; +import {InkList, InkListItem} from './InkList'; import {ListValue} from './Value'; export class ListDefinition{ @@ -16,7 +16,7 @@ export class ListDefinition{ this._items = {}; this._rawListItemsKeys = {}; for (var key in this._itemNameToValues){ - var item = new RawListItem(this.name, key); + var item = new InkListItem(this.name, key); this._rawListItemsKeys[item] = item; this._items[item] = this._itemNameToValues[key]; } @@ -45,11 +45,14 @@ export class ListDefinition{ return (item.itemName in this._itemNameToValues); } + ContainsItemWithName(itemName){ + 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 for (var key in this._itemNameToValues){ if (this._itemNameToValues[key] == val) { - item = new RawListItem(this.name, key); + item = new InkListItem(this.name, key); return { item :item, exists: true @@ -57,17 +60,21 @@ export class ListDefinition{ } } - item = RawListItem.Null; + item = InkListItem.Null; return { item :item, exists: false }; } + TryGetValueForItem(item, intval){//intval is an out + intVal = this._itemNameToValues[item.itemName]; + return intVal; + } ListRange(min, max){ - var rawList = new RawList(); + var rawList = new InkList(); for (var key in this._itemNameToValues){ if (this._itemNameToValues[key] >= min && this._itemNameToValues[key] <= max) { - var item = new RawListItem(this.name, key); + var item = new InkListItem(this.name, key); rawList.Add(item, this._itemNameToValues[key]); } } diff --git a/engine/ListDefinitionsOrigin.js b/engine/ListDefinitionsOrigin.js index 20c97d2d..ee20ab68 100644 --- a/engine/ListDefinitionsOrigin.js +++ b/engine/ListDefinitionsOrigin.js @@ -1,4 +1,4 @@ -import {RawListItem} from './RawList'; +import {InkListItem} from './InkList'; import {ListValue} from './Value'; export class ListDefinitionsOrigin{ @@ -22,17 +22,17 @@ export class ListDefinitionsOrigin{ return (name in this._lists) ? this._lists[name] : def; } FindSingleItemListWithName(name){ - var item = RawListItem.Null; + var item = InkListItem.Null; var list = null; var nameParts = name.split('.'); if (nameParts.length == 2) { - item = new RawListItem(nameParts[0], nameParts[1]); + item = new InkListItem(nameParts[0], nameParts[1]); list = this.TryGetDefinition(item.originName, list); } else { for (var key in this._lists){ var listWithItem = this._lists[key]; - item = new RawListItem(key, name); + item = new InkListItem(key, name); if (listWithItem.ContainsItem(item)) { list = listWithItem; break; diff --git a/engine/NativeFunctionCall.js b/engine/NativeFunctionCall.js index a8c8d6da..b5079a87 100644 --- a/engine/NativeFunctionCall.js +++ b/engine/NativeFunctionCall.js @@ -2,7 +2,7 @@ import {Value, ValueType, IntValue, ListValue} from './Value'; import {StoryException} from './StoryException'; import {Void} from './Void'; -import {RawList} from './RawList'; +import {InkList} from './InkList'; import {Object as InkObject} from './Object'; export class NativeFunctionCall extends InkObject{ @@ -162,7 +162,7 @@ export class NativeFunctionCall extends InkObject{ var intVal = listIntParams[1]; - var resultRawList = new RawList(); + var resultInkList = new InkList(); listVal.value.forEach(listItemWithValue => { var listItem = listItemWithValue.Key; @@ -185,11 +185,11 @@ export class NativeFunctionCall extends InkObject{ if (itemOrigin != null) { var incrementedItem = itemOrigin.TryGetItemWithValue(targetInt); if (incrementedItem.exists) - resultRawList.Add(incrementedItem.item, targetInt); + resultInkList.Add(incrementedItem.item, targetInt); } }); - return new ListValue(resultRawList); + return new ListValue(resultInkList); } CoerceValuesToSingleType(parametersIn){ var valType = ValueType.Int; @@ -293,8 +293,9 @@ export class NativeFunctionCall extends InkObject{ this.AddFloatBinaryOp(this.Min, (x, y) => {return Math.min(x, y)}); // String operations - this.AddStringBinaryOp(this.Add, (x, y) => {return x + y}); // concat - this.AddStringBinaryOp(this.Equal, (x, y) => {return x === y ? 1 : 0}); + this.AddStringBinaryOp(this.Add, (x, y) => {return x + y}); // concat + this.AddStringBinaryOp(this.Equal, (x, y) => {return x === y ? 1 : 0}); + 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)}); diff --git a/engine/Story.js b/engine/Story.js index c17fd83b..669eea93 100644 --- a/engine/Story.js +++ b/engine/Story.js @@ -982,11 +982,13 @@ export class Story extends InkObject{ // No control content, must be ordinary content return false; } - ChoosePathString(path){ + ChoosePathString(path, args){ + args = args || []; + this.state.PassArgumentsToEvaluationStack(args); this.ChoosePath(new Path(path)); } - ChoosePath(path){ - this.state.SetChosenPath(path); + ChoosePath(p){ + this.state.SetChosenPath(p); // Take a note of newly visited containers for read counts etc this.VisitChangedContainersDueToDivert(); diff --git a/engine/StoryState.js b/engine/StoryState.js index 327976df..32cb4adb 100644 --- a/engine/StoryState.js +++ b/engine/StoryState.js @@ -638,12 +638,15 @@ export class StoryState{ // we're saying it's okay to end the flow without a Done or End, // but with a ~ return instead. this._isExternalFunctionEvaluation = true; - + + this.PassArgumentsToEvaluationStack(args); + } + PassArgumentsToEvaluationStack(args){ // Pass arguments onto the evaluation stack if (args != null) { for (var i = 0; i < args.length; i++) { if (!(typeof args[i] === 'number' || typeof args[i] === 'string')) { - throw "ink arguments when calling EvaluateFunction must be int, float or string"; + throw "ink arguments when calling EvaluateFunction / ChoosePathStringWithParameters must be int, float or string"; } this.PushEvaluationStack(Value.Create(args[i])); diff --git a/engine/Value.js b/engine/Value.js index 3219f58c..5ec79b20 100644 --- a/engine/Value.js +++ b/engine/Value.js @@ -1,6 +1,6 @@ import {Object as InkObject} from './Object'; import {Path} from './Path'; -import {RawList} from './RawList'; +import {InkList} from './InkList'; export var ValueType = { // Used in coersion @@ -49,7 +49,7 @@ class AbstractValue extends InkObject{ return new StringValue(val); } else if (val instanceof Path) { return new DivertTargetValue(val); - } else if (val instanceof RawList) { + } else if (val instanceof InkList) { return new ListValue(val); } @@ -305,17 +305,17 @@ export class ListValue extends Value{ this._valueType = ValueType.List; - if (listOrSingleItem instanceof RawList){ - this.value = new RawList(listOrSingleItem); + if (listOrSingleItem instanceof InkList){ + this.value = new InkList(listOrSingleItem); } else if (listOrSingleItem !== undefined && singleValue !== undefined){ - this.value = new RawList({ + this.value = new InkList({ Key: listOrSingleItem, Value: singleValue }); } else{ - this.value = new RawList(); + this.value = new InkList(); } } static RetainListOriginsForAssignment(oldValue, newValue){ From 91958f915dc60609e028916099010adeaf3d85a5 Mon Sep 17 00:00:00 2001 From: Yannick Lohse Date: Sun, 2 Apr 2017 16:21:26 +0200 Subject: [PATCH 6/6] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 25a4e469..33a762e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "inkjs", - "version": "1.5.1", + "version": "1.5.2", "description": "A javascript port of inkle's ink scripting language (http://www.inklestudios.com/ink/)", "main": "dist/ink-es2015.js", "scripts": {