From 798f51216fa06023082bf01ecac5d0e4c465e316 Mon Sep 17 00:00:00 2001 From: Joseph Humfrey Date: Tue, 6 Jun 2017 18:45:34 +0100 Subject: [PATCH] Add READ_COUNT for explicit read counts, and to be able to get the read count of a variable divert target. --- ink-engine-runtime/ControlCommand.cs | 6 +++ ink-engine-runtime/JsonSerialisation.cs | 1 + ink-engine-runtime/Story.cs | 13 ++++-- ink-engine-runtime/StoryState.cs | 2 +- inklecate/ParsedHierarchy/DivertTarget.cs | 2 +- inklecate/ParsedHierarchy/FunctionCall.cs | 56 +++++++++++++---------- tests/Tests.cs | 28 ++++++++++++ 7 files changed, 79 insertions(+), 29 deletions(-) diff --git a/ink-engine-runtime/ControlCommand.cs b/ink-engine-runtime/ControlCommand.cs index ecfef5dc..8c666315 100644 --- a/ink-engine-runtime/ControlCommand.cs +++ b/ink-engine-runtime/ControlCommand.cs @@ -19,6 +19,7 @@ public enum CommandType NoOp, ChoiceCount, TurnsSince, + ReadCount, Random, SeedRandom, VisitIndex, @@ -104,6 +105,11 @@ internal override Object Copy() return new ControlCommand(CommandType.TurnsSince); } + public static ControlCommand ReadCount () + { + return new ControlCommand (CommandType.ReadCount); + } + public static ControlCommand Random () { return new ControlCommand (CommandType.Random); diff --git a/ink-engine-runtime/JsonSerialisation.cs b/ink-engine-runtime/JsonSerialisation.cs index 93665bda..0ddc86bb 100644 --- a/ink-engine-runtime/JsonSerialisation.cs +++ b/ink-engine-runtime/JsonSerialisation.cs @@ -653,6 +653,7 @@ static Json() _controlCommandNames [(int)ControlCommand.CommandType.NoOp] = "nop"; _controlCommandNames [(int)ControlCommand.CommandType.ChoiceCount] = "choiceCnt"; _controlCommandNames [(int)ControlCommand.CommandType.TurnsSince] = "turns"; + _controlCommandNames [(int)ControlCommand.CommandType.ReadCount] = "readc"; _controlCommandNames [(int)ControlCommand.CommandType.Random] = "rnd"; _controlCommandNames [(int)ControlCommand.CommandType.SeedRandom] = "srnd"; _controlCommandNames [(int)ControlCommand.CommandType.VisitIndex] = "visit"; diff --git a/ink-engine-runtime/Story.cs b/ink-engine-runtime/Story.cs index 7e96eff0..c33de134 100644 --- a/ink-engine-runtime/Story.cs +++ b/ink-engine-runtime/Story.cs @@ -15,7 +15,7 @@ public class Story : Runtime.Object /// /// The current version of the ink story file format. /// - public const int inkVersionCurrent = 16; + public const int inkVersionCurrent = 17; // Version numbers are for engine itself and story file, rather // than the story state save format (which is um, currently nonexistant) @@ -853,6 +853,7 @@ bool PerformLogicAndFlowControl(Runtime.Object contentObj) break; case ControlCommand.CommandType.TurnsSince: + case ControlCommand.CommandType.ReadCount: var target = state.PopEvaluationStack(); if( !(target is DivertTargetValue) ) { string extraNote = ""; @@ -864,8 +865,14 @@ bool PerformLogicAndFlowControl(Runtime.Object contentObj) var divertTarget = target as DivertTargetValue; var container = ContentAtPath (divertTarget.targetPath) as Container; - int turnCount = TurnsSinceForContainer (container); - state.PushEvaluationStack (new IntValue (turnCount)); + + int eitherCount; + if (evalCommand.commandType == ControlCommand.CommandType.TurnsSince) + eitherCount = TurnsSinceForContainer (container); + else + eitherCount = VisitCountForContainer (container); + + state.PushEvaluationStack (new IntValue (eitherCount)); break; case ControlCommand.CommandType.Random: diff --git a/ink-engine-runtime/StoryState.cs b/ink-engine-runtime/StoryState.cs index 0a26c807..da0f9377 100755 --- a/ink-engine-runtime/StoryState.cs +++ b/ink-engine-runtime/StoryState.cs @@ -17,7 +17,7 @@ public class StoryState /// /// The current version of the state save file JSON-based format. /// - public const int kInkSaveStateVersion = 6; + public const int kInkSaveStateVersion = 7; const int kMinCompatibleLoadVersion = 6; /// diff --git a/inklecate/ParsedHierarchy/DivertTarget.cs b/inklecate/ParsedHierarchy/DivertTarget.cs index 54f6a932..ef0bcaf6 100644 --- a/inklecate/ParsedHierarchy/DivertTarget.cs +++ b/inklecate/ParsedHierarchy/DivertTarget.cs @@ -50,7 +50,7 @@ public override void ResolveReferences (Story context) } else if( usageParent is FunctionCall ) { var funcCall = usageParent as FunctionCall; - if( !funcCall.isTurnsSince ) { + if( !funcCall.isTurnsSince && !funcCall.isReadCount ) { badUsage = true; } foundUsage = true; diff --git a/inklecate/ParsedHierarchy/FunctionCall.cs b/inklecate/ParsedHierarchy/FunctionCall.cs index c8de341c..815f45d2 100644 --- a/inklecate/ParsedHierarchy/FunctionCall.cs +++ b/inklecate/ParsedHierarchy/FunctionCall.cs @@ -12,6 +12,7 @@ internal class FunctionCall : Expression public bool isRandom { get { return name == "RANDOM"; } } public bool isSeedRandom { get { return name == "SEED_RANDOM"; } } public bool isListRange { get { return name == "LIST_RANGE"; } } + public bool isReadCount { get { return name == "READ_COUNT"; } } public bool shouldPopReturnedValue; @@ -33,34 +34,37 @@ public override void GenerateIntoContainer (Runtime.Container container) container.AddContent (Runtime.ControlCommand.ChoiceCount ()); - } else if (isTurnsSince) { + } else if (isTurnsSince || isReadCount) { var divertTarget = arguments [0] as DivertTarget; var variableDivertTarget = arguments [0] as VariableReference; if (arguments.Count != 1 || (divertTarget == null && variableDivertTarget == null)) { - Error ("The TURNS_SINCE() function should take one argument: a divert target to the target knot, stitch, gather or choice you want to check. e.g. TURNS_SINCE(-> myKnot)"); + Error ("The "+name+"() function should take one argument: a divert target to the target knot, stitch, gather or choice you want to check. e.g. TURNS_SINCE(-> myKnot)"); return; } if (divertTarget) { - _turnCountDivertTarget = divertTarget; - AddContent (_turnCountDivertTarget); + _divertTargetToCount = divertTarget; + AddContent (_divertTargetToCount); - _turnCountDivertTarget.GenerateIntoContainer (container); + _divertTargetToCount.GenerateIntoContainer (container); } else { - _turnCountVariableReference = variableDivertTarget; - AddContent (_turnCountVariableReference); + _variableReferenceToCount = variableDivertTarget; + AddContent (_variableReferenceToCount); - _turnCountVariableReference.GenerateIntoContainer (container); + _variableReferenceToCount.GenerateIntoContainer (container); if (!story.countAllVisits) { Error ("Attempting to get TURNS_SINCE for a variable target without -c compiler option. You need the compiler switch turned on so that it can track turn counts for everything, not just those you directly reference."); } } - - container.AddContent (Runtime.ControlCommand.TurnsSince ()); + if (isTurnsSince) + container.AddContent (Runtime.ControlCommand.TurnsSince ()); + else + container.AddContent (Runtime.ControlCommand.ReadCount ()); + } else if (isRandom) { if (arguments.Count != 2) Error ("RANDOM should take 2 parameters: a minimum and a maximum integer"); @@ -103,7 +107,7 @@ public override void GenerateIntoContainer (Runtime.Container container) // Don't attempt to resolve as a divert content.Remove (_proxyDivert); - } else if (Runtime.NativeFunctionCall.CallExistsWithName(name)) { + } else if (Runtime.NativeFunctionCall.CallExistsWithName (name)) { var nativeCall = Runtime.NativeFunctionCall.CallWithName (name); @@ -121,8 +125,7 @@ public override void GenerateIntoContainer (Runtime.Container container) // Don't attempt to resolve as a divert content.Remove (_proxyDivert); - } - else if (foundList != null) { + } else if (foundList != null) { if (arguments.Count > 1) Error ("Can currently only construct a list from one integer (or an empty list from a given list definition)"); @@ -131,7 +134,7 @@ public override void GenerateIntoContainer (Runtime.Container container) container.AddContent (new Runtime.StringValue (name)); arguments [0].GenerateIntoContainer (container); container.AddContent (Runtime.ControlCommand.ListFromInt ()); - } + } // Empty list with given origin. else { @@ -144,8 +147,8 @@ public override void GenerateIntoContainer (Runtime.Container container) content.Remove (_proxyDivert); } - // Normal function call - else { + // Normal function call + else { container.AddContent (_proxyDivert.runtimeObject); } @@ -170,8 +173,8 @@ public override void ResolveReferences (Story context) arg.ResolveReferences (context); } - if( _turnCountDivertTarget ) { - var divert = _turnCountDivertTarget.divert; + if( _divertTargetToCount ) { + var divert = _divertTargetToCount.divert; var attemptingTurnCountOfVariableTarget = divert.runtimeDivert.variableDivertName != null; if( attemptingTurnCountOfVariableTarget ) { @@ -189,10 +192,10 @@ public override void ResolveReferences (Story context) } } - else if( _turnCountVariableReference ) { - var runtimeVarRef = _turnCountVariableReference.runtimeVarRef; + else if( _variableReferenceToCount ) { + var runtimeVarRef = _variableReferenceToCount.runtimeVarRef; if( runtimeVarRef.pathForCount != null ) { - Error("Should be TURNS_SINCE(-> "+_turnCountVariableReference.name+"). Without the '->' it expects a variable target"); + Error("Should be "+name+"(-> "+_variableReferenceToCount.name+"). Usage without the '->' only makes sense for variable targets."); } } } @@ -202,7 +205,12 @@ public static bool IsBuiltIn(string name) if (Runtime.NativeFunctionCall.CallExistsWithName (name)) return true; - return name == "CHOICE_COUNT" || name == "TURNS_SINCE" || name == "RANDOM" || name == "SEED_RANDOM" || name == "LIST_VALUE"; + return name == "CHOICE_COUNT" + || name == "TURNS_SINCE" + || name == "RANDOM" + || name == "SEED_RANDOM" + || name == "LIST_VALUE" + || name == "READ_COUNT"; } public override string ToString () @@ -212,8 +220,8 @@ public override string ToString () } Parsed.Divert _proxyDivert; - Parsed.DivertTarget _turnCountDivertTarget; - Parsed.VariableReference _turnCountVariableReference; + Parsed.DivertTarget _divertTargetToCount; + Parsed.VariableReference _variableReferenceToCount; } } diff --git a/tests/Tests.cs b/tests/Tests.cs index eb6c2ca7..4e089ac9 100644 --- a/tests/Tests.cs +++ b/tests/Tests.cs @@ -2889,6 +2889,34 @@ public void TestTunnelOnwardsWithParamDefaultChoice () Assert.AreEqual ("8\n", story.ContinueMaximally ()); } + + [Test ()] + public void TestReadCountVariableTarget () + { + var storyStr = +@" +VAR x = ->knot + +Count start: {READ_COUNT (x)} {READ_COUNT (-> knot)} {knot} + +-> x (1) -> +-> x (2) -> +-> x (3) -> + +Count end: {READ_COUNT (x)} {READ_COUNT (-> knot)} {knot} +-> END + + +== knot (a) == +{a} +->-> +"; + + var story = CompileString (storyStr, countAllVisits:true); + Assert.AreEqual ("Count start: 0 0 0\n1\n2\n3\nCount end: 3 3 3\n", story.ContinueMaximally ()); + } + + [Test ()] public void TestDivertTargetsWithParameters () {