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/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/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/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/JsonSerialisation.js b/engine/JsonSerialisation.js index d36e40dc..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; @@ -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/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 b5079a87..b2339487 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 @@ -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}); @@ -406,4 +408,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; diff --git a/engine/Story.js b/engine/Story.js index d724d78a..7db58cf8 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 = this.TurnsSinceForContainer(container); + else + eitherCount = this.VisitCountForContainer(container); + + this.state.PushEvaluationStack(new IntValue(eitherCount)); break; case ControlCommand.CommandType.Random: @@ -1297,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 32cb4adb..5aa9e65f 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 = {}; @@ -734,11 +737,13 @@ export class StoryState{ } copy.callStack = new CallStack(this.callStack); + 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; if (this.divertedTargetObject != null) copy.divertedTargetObject = this.divertedTargetObject; @@ -773,5 +778,5 @@ export class StoryState{ } } -StoryState.kInkSaveStateVersion = 6; +StoryState.kInkSaveStateVersion = 7; StoryState.kMinCompatibleLoadVersion = 6; 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": {