- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.Diagnostics;
- using System.IO;
- namespace Ink.Runtime
- {
-
-
-
-
-
-
-
- public class StoryState
- {
-
-
-
-
-
-
-
- public const int kInkSaveStateVersion = 10;
- const int kMinCompatibleLoadVersion = 8;
-
-
-
- public event Action onDidLoadState;
-
-
-
-
- public string ToJson() {
- var writer = new SimpleJson.Writer();
- WriteJson(writer);
- return writer.ToString();
- }
-
-
-
-
- public void ToJson(Stream stream) {
- var writer = new SimpleJson.Writer(stream);
- WriteJson(writer);
- }
-
-
-
-
- public void LoadJson(string json)
- {
- var jObject = SimpleJson.TextToDictionary (json);
- LoadJsonObj(jObject);
- if(onDidLoadState != null) onDidLoadState();
- }
-
-
-
-
-
-
-
-
-
-
-
-
- public int VisitCountAtPathString(string pathString)
- {
- int visitCountOut;
- if ( _patch != null ) {
- var container = story.ContentAtPath(new Path(pathString)).container;
- if (container == null)
- throw new Exception("Content at path not found: " + pathString);
- if( _patch.TryGetVisitCount(container, out visitCountOut) )
- return visitCountOut;
- }
- if (_visitCounts.TryGetValue(pathString, out visitCountOut))
- return visitCountOut;
- return 0;
- }
- public int VisitCountForContainer(Container container)
- {
- if (!container.visitsShouldBeCounted)
- {
- story.Error("Read count for target (" + container.name + " - on " + container.debugMetadata + ") unknown.");
- return 0;
- }
- int count = 0;
- if (_patch != null && _patch.TryGetVisitCount(container, out count))
- return count;
-
- var containerPathStr = container.path.ToString();
- _visitCounts.TryGetValue(containerPathStr, out count);
- return count;
- }
- public void IncrementVisitCountForContainer(Container container)
- {
- if( _patch != null ) {
- var currCount = VisitCountForContainer(container);
- currCount++;
- _patch.SetVisitCount(container, currCount);
- return;
- }
- int count = 0;
- var containerPathStr = container.path.ToString();
- _visitCounts.TryGetValue(containerPathStr, out count);
- count++;
- _visitCounts[containerPathStr] = count;
- }
- public void RecordTurnIndexVisitToContainer(Container container)
- {
- if( _patch != null ) {
- _patch.SetTurnIndex(container, currentTurnIndex);
- return;
- }
- var containerPathStr = container.path.ToString();
- _turnIndices[containerPathStr] = currentTurnIndex;
- }
- public int TurnsSinceForContainer(Container container)
- {
- if (!container.turnIndexShouldBeCounted)
- {
- story.Error("TURNS_SINCE() for target (" + container.name + " - on " + container.debugMetadata + ") unknown.");
- }
- int index = 0;
- if ( _patch != null && _patch.TryGetTurnIndex(container, out index) ) {
- return currentTurnIndex - index;
- }
- var containerPathStr = container.path.ToString();
- if (_turnIndices.TryGetValue(containerPathStr, out index))
- {
- return currentTurnIndex - index;
- }
- else
- {
- return -1;
- }
- }
- public int callstackDepth {
- get {
- return callStack.depth;
- }
- }
-
-
-
- public List<Runtime.Object> outputStream {
- get {
- return _currentFlow.outputStream;
- }
- }
-
- public List<Choice> currentChoices {
- get {
-
-
-
- if( canContinue ) return new List<Choice>();
- return _currentFlow.currentChoices;
- }
- }
- public List<Choice> generatedChoices {
- get {
- return _currentFlow.currentChoices;
- }
- }
-
-
-
-
- public List<string> currentErrors { get; private set; }
- public List<string> currentWarnings { get; private set; }
- public VariablesState variablesState { get; private set; }
- public CallStack callStack {
- get {
- return _currentFlow.callStack;
- }
-
-
-
- }
- public List<Runtime.Object> evaluationStack { get; private set; }
- public Pointer divertedPointer { get; set; }
- public int currentTurnIndex { get; private set; }
- public int storySeed { get; set; }
- public int previousRandom { get; set; }
- public bool didSafeExit { get; set; }
- public Story story { get; set; }
-
-
-
- public string currentPathString {
- get {
- var pointer = currentPointer;
- if (pointer.isNull)
- return null;
- else
- return pointer.path.ToString();
- }
- }
- public Runtime.Pointer currentPointer {
- get {
- return callStack.currentElement.currentPointer;
- }
- set {
- callStack.currentElement.currentPointer = value;
- }
- }
- public Pointer previousPointer {
- get {
- return callStack.currentThread.previousPointer;
- }
- set {
- callStack.currentThread.previousPointer = value;
- }
- }
- public bool canContinue {
- get {
- return !currentPointer.isNull && !hasError;
- }
- }
-
- public bool hasError
- {
- get {
- return currentErrors != null && currentErrors.Count > 0;
- }
- }
- public bool hasWarning {
- get {
- return currentWarnings != null && currentWarnings.Count > 0;
- }
- }
- public string currentText
- {
- get
- {
- if( _outputStreamTextDirty ) {
- var sb = new StringBuilder ();
- bool inTag = false;
- foreach (var outputObj in outputStream) {
- var textContent = outputObj as StringValue;
- if (!inTag && textContent != null) {
- sb.Append(textContent.value);
- } else {
- var controlCommand = outputObj as ControlCommand;
- if( controlCommand != null ) {
- if( controlCommand.commandType == ControlCommand.CommandType.BeginTag ) {
- inTag = true;
- } else if( controlCommand.commandType == ControlCommand.CommandType.EndTag ) {
- inTag = false;
- }
- }
- }
- }
- _currentText = CleanOutputWhitespace (sb.ToString ());
- _outputStreamTextDirty = false;
- }
- return _currentText;
- }
- }
- string _currentText;
-
-
-
- public string CleanOutputWhitespace(string str)
- {
- var sb = new StringBuilder(str.Length);
- int currentWhitespaceStart = -1;
- int startOfLine = 0;
- for (int i = 0; i < str.Length; i++) {
- var c = str[i];
- bool isInlineWhitespace = c == ' ' || c == '\t';
- if (isInlineWhitespace && currentWhitespaceStart == -1)
- currentWhitespaceStart = i;
- if (!isInlineWhitespace) {
- if (c != '\n' && currentWhitespaceStart > 0 && currentWhitespaceStart != startOfLine) {
- sb.Append(' ');
- }
- currentWhitespaceStart = -1;
- }
- if (c == '\n')
- startOfLine = i + 1;
- if (!isInlineWhitespace)
- sb.Append(c);
- }
- return sb.ToString();
- }
- public List<string> currentTags
- {
- get
- {
- if( _outputStreamTagsDirty ) {
- _currentTags = new List<string>();
- bool inTag = false;
- var sb = new StringBuilder ();
- foreach (var outputObj in outputStream) {
- var controlCommand = outputObj as ControlCommand;
- if( controlCommand != null ) {
- if( controlCommand.commandType == ControlCommand.CommandType.BeginTag ) {
- if( inTag && sb.Length > 0 ) {
- var txt = CleanOutputWhitespace(sb.ToString());
- _currentTags.Add(txt);
- sb.Clear();
- }
- inTag = true;
- }
- else if( controlCommand.commandType == ControlCommand.CommandType.EndTag ) {
- if( sb.Length > 0 ) {
- var txt = CleanOutputWhitespace(sb.ToString());
- _currentTags.Add(txt);
- sb.Clear();
- }
- inTag = false;
- }
- }
- else if( inTag ) {
- var strVal = outputObj as StringValue;
- if( strVal != null ) {
- sb.Append(strVal.value);
- }
- }
- else {
- var tag = outputObj as Tag;
- if (tag != null && tag.text != null && tag.text.Length > 0) {
- _currentTags.Add (tag.text);
- }
- }
- }
- if( sb.Length > 0 ) {
- var txt = CleanOutputWhitespace(sb.ToString());
- _currentTags.Add(txt);
- sb.Clear();
- }
- _outputStreamTagsDirty = false;
- }
- return _currentTags;
- }
- }
- List<string> _currentTags;
- public string currentFlowName {
- get {
- return _currentFlow.name;
- }
- }
- public bool currentFlowIsDefaultFlow {
- get {
- return _currentFlow.name == kDefaultFlowName;
- }
- }
- public List<string> aliveFlowNames {
- get {
- if( _aliveFlowNamesDirty ) {
- _aliveFlowNames = new List<string>();
- if (_namedFlows != null)
- {
- foreach (string flowName in _namedFlows.Keys) {
- if (flowName != kDefaultFlowName) {
- _aliveFlowNames.Add(flowName);
- }
- }
- }
- _aliveFlowNamesDirty = false;
- }
- return _aliveFlowNames;
- }
- }
- List<string> _aliveFlowNames;
- public bool inExpressionEvaluation {
- get {
- return callStack.currentElement.inExpressionEvaluation;
- }
- set {
- callStack.currentElement.inExpressionEvaluation = value;
- }
- }
-
- public StoryState (Story story)
- {
- this.story = story;
- _currentFlow = new Flow(kDefaultFlowName, story);
-
- OutputStreamDirty();
- _aliveFlowNamesDirty = true;
- evaluationStack = new List<Runtime.Object> ();
- variablesState = new VariablesState (callStack, story.listDefinitions);
- _visitCounts = new Dictionary<string, int> ();
- _turnIndices = new Dictionary<string, int> ();
- currentTurnIndex = -1;
-
- int timeSeed = DateTime.Now.Millisecond;
- storySeed = (new Random (timeSeed)).Next () % 100;
- previousRandom = 0;
-
- GoToStart();
- }
- public void GoToStart()
- {
- callStack.currentElement.currentPointer = Pointer.StartOf (story.mainContentContainer);
- }
- internal void SwitchFlow_Internal(string flowName)
- {
- if(flowName == null) throw new System.Exception("Must pass a non-null string to Story.SwitchFlow");
-
- if( _namedFlows == null ) {
- _namedFlows = new Dictionary<string, Flow>();
- _namedFlows[kDefaultFlowName] = _currentFlow;
- }
- if( flowName == _currentFlow.name ) {
- return;
- }
- Flow flow;
- if( !_namedFlows.TryGetValue(flowName, out flow) ) {
- flow = new Flow(flowName, story);
- _namedFlows[flowName] = flow;
- _aliveFlowNamesDirty = true;
- }
- _currentFlow = flow;
- variablesState.callStack = _currentFlow.callStack;
-
- OutputStreamDirty();
- }
- internal void SwitchToDefaultFlow_Internal()
- {
- if( _namedFlows == null ) return;
- SwitchFlow_Internal(kDefaultFlowName);
- }
- internal void RemoveFlow_Internal(string flowName)
- {
- if(flowName == null) throw new System.Exception("Must pass a non-null string to Story.DestroyFlow");
- if(flowName == kDefaultFlowName) throw new System.Exception("Cannot destroy default flow");
-
- if( _currentFlow.name == flowName ) {
- SwitchToDefaultFlow_Internal();
- }
- _namedFlows.Remove(flowName);
- _aliveFlowNamesDirty = true;
- }
-
-
-
-
-
- public StoryState CopyAndStartPatching()
- {
- var copy = new StoryState(story);
- copy._patch = new StatePatch(_patch);
-
-
- copy._currentFlow.name = _currentFlow.name;
- copy._currentFlow.callStack = new CallStack (_currentFlow.callStack);
- copy._currentFlow.currentChoices.AddRange(_currentFlow.currentChoices);
- copy._currentFlow.outputStream.AddRange(_currentFlow.outputStream);
- copy.OutputStreamDirty();
-
-
-
-
- if( _namedFlows != null ) {
- copy._namedFlows = new Dictionary<string, Flow>();
- foreach(var namedFlow in _namedFlows)
- copy._namedFlows[namedFlow.Key] = namedFlow.Value;
- copy._namedFlows[_currentFlow.name] = copy._currentFlow;
- copy._aliveFlowNamesDirty = true;
- }
- if (hasError) {
- copy.currentErrors = new List<string> ();
- copy.currentErrors.AddRange (currentErrors);
- }
- if (hasWarning) {
- copy.currentWarnings = new List<string> ();
- copy.currentWarnings.AddRange (currentWarnings);
- }
-
-
-
-
- copy.variablesState = variablesState;
- copy.variablesState.callStack = copy.callStack;
- copy.variablesState.patch = copy._patch;
- copy.evaluationStack.AddRange (evaluationStack);
- if (!divertedPointer.isNull)
- copy.divertedPointer = divertedPointer;
- copy.previousPointer = previousPointer;
-
-
- copy._visitCounts = _visitCounts;
- copy._turnIndices = _turnIndices;
- copy.currentTurnIndex = currentTurnIndex;
- copy.storySeed = storySeed;
- copy.previousRandom = previousRandom;
- copy.didSafeExit = didSafeExit;
- return copy;
- }
- public void RestoreAfterPatch()
- {
-
-
-
-
- variablesState.callStack = callStack;
- variablesState.patch = _patch;
- }
- public void ApplyAnyPatch()
- {
- if (_patch == null) return;
- variablesState.ApplyPatch();
- foreach(var pathToCount in _patch.visitCounts)
- ApplyCountChanges(pathToCount.Key, pathToCount.Value, isVisit:true);
- foreach (var pathToIndex in _patch.turnIndices)
- ApplyCountChanges(pathToIndex.Key, pathToIndex.Value, isVisit:false);
- _patch = null;
- }
- void ApplyCountChanges(Container container, int newCount, bool isVisit)
- {
- var counts = isVisit ? _visitCounts : _turnIndices;
- counts[container.path.ToString()] = newCount;
- }
- void WriteJson(SimpleJson.Writer writer)
- {
- writer.WriteObjectStart();
-
- writer.WritePropertyStart("flows");
- writer.WriteObjectStart();
-
- if( _namedFlows != null ) {
- foreach(var namedFlow in _namedFlows) {
- writer.WriteProperty(namedFlow.Key, namedFlow.Value.WriteJson);
- }
- }
-
-
- else {
- writer.WriteProperty(_currentFlow.name, _currentFlow.WriteJson);
- }
- writer.WriteObjectEnd();
- writer.WritePropertyEnd();
- writer.WriteProperty("currentFlowName", _currentFlow.name);
- writer.WriteProperty("variablesState", variablesState.WriteJson);
- writer.WriteProperty("evalStack", w => Json.WriteListRuntimeObjs(w, evaluationStack));
- if (!divertedPointer.isNull)
- writer.WriteProperty("currentDivertTarget", divertedPointer.path.componentsString);
-
- writer.WriteProperty("visitCounts", w => Json.WriteIntDictionary(w, _visitCounts));
- writer.WriteProperty("turnIndices", w => Json.WriteIntDictionary(w, _turnIndices));
- writer.WriteProperty("turnIdx", currentTurnIndex);
- writer.WriteProperty("storySeed", storySeed);
- writer.WriteProperty("previousRandom", previousRandom);
- writer.WriteProperty("inkSaveVersion", kInkSaveStateVersion);
-
- writer.WriteProperty("inkFormatVersion", Story.inkVersionCurrent);
- writer.WriteObjectEnd();
- }
- void LoadJsonObj(Dictionary<string, object> jObject)
- {
- object jSaveVersion = null;
- if (!jObject.TryGetValue("inkSaveVersion", out jSaveVersion)) {
- throw new Exception ("ink save format incorrect, can't load.");
- }
- else if ((int)jSaveVersion < kMinCompatibleLoadVersion) {
- throw new Exception("Ink save format isn't compatible with the current version (saw '"+jSaveVersion+"', but minimum is "+kMinCompatibleLoadVersion+"), so can't load.");
- }
-
-
- object flowsObj = null;
- if (jObject.TryGetValue("flows", out flowsObj)) {
- var flowsObjDict = (Dictionary<string, object>)flowsObj;
-
-
- if( flowsObjDict.Count == 1 )
- _namedFlows = null;
-
- else if( _namedFlows == null )
- _namedFlows = new Dictionary<string, Flow>();
-
- else
- _namedFlows.Clear();
-
- foreach(var namedFlowObj in flowsObjDict) {
- var name = namedFlowObj.Key;
- var flowObj = (Dictionary<string, object>)namedFlowObj.Value;
-
- var flow = new Flow(name, story, flowObj);
- if( flowsObjDict.Count == 1 ) {
- _currentFlow = new Flow(name, story, flowObj);
- } else {
- _namedFlows[name] = flow;
- }
- }
- if( _namedFlows != null && _namedFlows.Count > 1 ) {
- var currFlowName = (string)jObject["currentFlowName"];
- _currentFlow = _namedFlows[currFlowName];
- }
- }
-
- else {
- _namedFlows = null;
- _currentFlow.name = kDefaultFlowName;
- _currentFlow.callStack.SetJsonToken ((Dictionary < string, object > )jObject ["callstackThreads"], story);
- _currentFlow.outputStream = Json.JArrayToRuntimeObjList ((List<object>)jObject ["outputStream"]);
- _currentFlow.currentChoices = Json.JArrayToRuntimeObjList<Choice>((List<object>)jObject ["currentChoices"]);
- object jChoiceThreadsObj = null;
- jObject.TryGetValue("choiceThreads", out jChoiceThreadsObj);
- _currentFlow.LoadFlowChoiceThreads((Dictionary<string, object>)jChoiceThreadsObj, story);
- }
- OutputStreamDirty();
- _aliveFlowNamesDirty = true;
- variablesState.SetJsonToken((Dictionary < string, object> )jObject["variablesState"]);
- variablesState.callStack = _currentFlow.callStack;
- evaluationStack = Json.JArrayToRuntimeObjList ((List<object>)jObject ["evalStack"]);
- object currentDivertTargetPath;
- if (jObject.TryGetValue("currentDivertTarget", out currentDivertTargetPath)) {
- var divertPath = new Path (currentDivertTargetPath.ToString ());
- divertedPointer = story.PointerAtPath (divertPath);
- }
-
- _visitCounts = Json.JObjectToIntDictionary((Dictionary<string, object>)jObject["visitCounts"]);
- _turnIndices = Json.JObjectToIntDictionary((Dictionary<string, object>)jObject["turnIndices"]);
- currentTurnIndex = (int)jObject ["turnIdx"];
- storySeed = (int)jObject ["storySeed"];
-
- object previousRandomObj = null;
- if( jObject.TryGetValue("previousRandom", out previousRandomObj) ) {
- previousRandom = (int)previousRandomObj;
- } else {
- previousRandom = 0;
- }
- }
-
- public void ResetErrors()
- {
- currentErrors = null;
- currentWarnings = null;
- }
-
- public void ResetOutput(List<Runtime.Object> objs = null)
- {
- outputStream.Clear ();
- if( objs != null ) outputStream.AddRange (objs);
- OutputStreamDirty();
- }
-
-
- public void PushToOutputStream(Runtime.Object obj)
- {
- var text = obj as StringValue;
- if (text) {
- var listText = TrySplittingHeadTailWhitespace (text);
- if (listText != null) {
- foreach (var textObj in listText) {
- PushToOutputStreamIndividual (textObj);
- }
- OutputStreamDirty();
- return;
- }
- }
- PushToOutputStreamIndividual (obj);
- OutputStreamDirty();
- }
- public void PopFromOutputStream (int count)
- {
- outputStream.RemoveRange (outputStream.Count - count, count);
- OutputStreamDirty ();
- }
-
-
-
-
-
-
-
-
-
-
-
- List<Runtime.StringValue> TrySplittingHeadTailWhitespace(Runtime.StringValue single)
- {
- string str = single.value;
- int headFirstNewlineIdx = -1;
- int headLastNewlineIdx = -1;
- for (int i = 0; i < str.Length; i++) {
- char c = str [i];
- if (c == '\n') {
- if (headFirstNewlineIdx == -1)
- headFirstNewlineIdx = i;
- headLastNewlineIdx = i;
- }
- else if (c == ' ' || c == '\t')
- continue;
- else
- break;
- }
- int tailLastNewlineIdx = -1;
- int tailFirstNewlineIdx = -1;
- for (int i = str.Length-1; i >= 0; i--) {
- char c = str [i];
- if (c == '\n') {
- if (tailLastNewlineIdx == -1)
- tailLastNewlineIdx = i;
- tailFirstNewlineIdx = i;
- }
- else if (c == ' ' || c == '\t')
- continue;
- else
- break;
- }
-
- if (headFirstNewlineIdx == -1 && tailLastNewlineIdx == -1)
- return null;
-
- var listTexts = new List<Runtime.StringValue> ();
- int innerStrStart = 0;
- int innerStrEnd = str.Length;
- if (headFirstNewlineIdx != -1) {
- if (headFirstNewlineIdx > 0) {
- var leadingSpaces = new StringValue (str.Substring (0, headFirstNewlineIdx));
- listTexts.Add(leadingSpaces);
- }
- listTexts.Add (new StringValue ("\n"));
- innerStrStart = headLastNewlineIdx + 1;
- }
- if (tailLastNewlineIdx != -1) {
- innerStrEnd = tailFirstNewlineIdx;
- }
- if (innerStrEnd > innerStrStart) {
- var innerStrText = str.Substring (innerStrStart, innerStrEnd - innerStrStart);
- listTexts.Add (new StringValue (innerStrText));
- }
- if (tailLastNewlineIdx != -1 && tailFirstNewlineIdx > headLastNewlineIdx) {
- listTexts.Add (new StringValue ("\n"));
- if (tailLastNewlineIdx < str.Length - 1) {
- int numSpaces = (str.Length - tailLastNewlineIdx) - 1;
- var trailingSpaces = new StringValue (str.Substring (tailLastNewlineIdx + 1, numSpaces));
- listTexts.Add(trailingSpaces);
- }
- }
- return listTexts;
- }
- void PushToOutputStreamIndividual(Runtime.Object obj)
- {
- var glue = obj as Runtime.Glue;
- var text = obj as Runtime.StringValue;
- bool includeInOutput = true;
-
- if (glue) {
- TrimNewlinesFromOutputStream();
- includeInOutput = true;
- }
-
-
-
-
-
- else if( text ) {
-
- var functionTrimIndex = -1;
- var currEl = callStack.currentElement;
- if (currEl.type == PushPopType.Function) {
- functionTrimIndex = currEl.functionStartInOuputStream;
- }
-
-
-
-
-
- int glueTrimIndex = -1;
- for (int i = outputStream.Count - 1; i >= 0; i--) {
- var o = outputStream [i];
- var c = o as ControlCommand;
- var g = o as Glue;
-
- if (g) {
- glueTrimIndex = i;
- break;
- }
-
- else if (c && c.commandType == ControlCommand.CommandType.BeginString) {
- if (i >= functionTrimIndex) {
- functionTrimIndex = -1;
- }
- break;
- }
- }
-
- var trimIndex = -1;
- if (glueTrimIndex != -1 && functionTrimIndex != -1)
- trimIndex = Math.Min (functionTrimIndex, glueTrimIndex);
- else if (glueTrimIndex != -1)
- trimIndex = glueTrimIndex;
- else
- trimIndex = functionTrimIndex;
-
- if (trimIndex != -1) {
-
-
- if (text.isNewline) {
- includeInOutput = false;
- }
-
- else if (text.isNonWhitespace) {
- if( glueTrimIndex > -1 )
- RemoveExistingGlue ();
-
-
- if (functionTrimIndex > -1) {
- var callstackElements = callStack.elements;
- for (int i = callstackElements.Count - 1; i >= 0; i--) {
- var el = callstackElements [i];
- if (el.type == PushPopType.Function) {
- el.functionStartInOuputStream = -1;
- } else {
- break;
- }
- }
- }
- }
- }
-
- else if (text.isNewline) {
- if (outputStreamEndsInNewline || !outputStreamContainsContent)
- includeInOutput = false;
- }
- }
- if (includeInOutput) {
- outputStream.Add (obj);
- OutputStreamDirty();
- }
- }
- void TrimNewlinesFromOutputStream()
- {
- int removeWhitespaceFrom = -1;
-
-
-
-
-
-
- int i = outputStream.Count-1;
- while (i >= 0) {
- var obj = outputStream [i];
- var cmd = obj as ControlCommand;
- var txt = obj as StringValue;
- if (cmd || (txt && txt.isNonWhitespace)) {
- break;
- }
- else if (txt && txt.isNewline) {
- removeWhitespaceFrom = i;
- }
- i--;
- }
-
- if (removeWhitespaceFrom >= 0) {
- i=removeWhitespaceFrom;
- while(i < outputStream.Count) {
- var text = outputStream [i] as StringValue;
- if (text) {
- outputStream.RemoveAt (i);
- } else {
- i++;
- }
- }
- }
- OutputStreamDirty();
- }
-
- void RemoveExistingGlue()
- {
- for (int i = outputStream.Count - 1; i >= 0; i--) {
- var c = outputStream [i];
- if (c is Glue) {
- outputStream.RemoveAt (i);
- } else if( c is ControlCommand ) {
- break;
- }
- }
- OutputStreamDirty();
- }
- public bool outputStreamEndsInNewline {
- get {
- if (outputStream.Count > 0) {
- for (int i = outputStream.Count - 1; i >= 0; i--) {
- var obj = outputStream [i];
- if (obj is ControlCommand)
- break;
- var text = outputStream [i] as StringValue;
- if (text) {
- if (text.isNewline)
- return true;
- else if (text.isNonWhitespace)
- break;
- }
- }
- }
- return false;
- }
- }
- public bool outputStreamContainsContent {
- get {
- foreach (var content in outputStream) {
- if (content is StringValue)
- return true;
- }
- return false;
- }
- }
- public bool inStringEvaluation {
- get {
- for (int i = outputStream.Count - 1; i >= 0; i--) {
- var cmd = outputStream [i] as ControlCommand;
- if (cmd && cmd.commandType == ControlCommand.CommandType.BeginString) {
- return true;
- }
- }
- return false;
- }
- }
- public void PushEvaluationStack(Runtime.Object obj)
- {
-
-
-
-
- var listValue = obj as ListValue;
- if (listValue) {
-
-
- var rawList = listValue.value;
- if (rawList.originNames != null) {
- if( rawList.origins == null ) rawList.origins = new List<ListDefinition>();
- rawList.origins.Clear();
- foreach (var n in rawList.originNames) {
- ListDefinition def = null;
- story.listDefinitions.TryListGetDefinition (n, out def);
- if( !rawList.origins.Contains(def) )
- rawList.origins.Add (def);
- }
- }
- }
- evaluationStack.Add(obj);
- }
- public Runtime.Object PopEvaluationStack()
- {
- var obj = evaluationStack [evaluationStack.Count - 1];
- evaluationStack.RemoveAt (evaluationStack.Count - 1);
- return obj;
- }
- public Runtime.Object PeekEvaluationStack()
- {
- return evaluationStack [evaluationStack.Count - 1];
- }
- public List<Runtime.Object> PopEvaluationStack(int numberOfObjects)
- {
- if(numberOfObjects > evaluationStack.Count) {
- throw new System.Exception ("trying to pop too many objects");
- }
- var popped = evaluationStack.GetRange (evaluationStack.Count - numberOfObjects, numberOfObjects);
- evaluationStack.RemoveRange (evaluationStack.Count - numberOfObjects, numberOfObjects);
- return popped;
- }
-
-
-
-
-
-
-
-
- public void ForceEnd()
- {
- callStack.Reset();
- _currentFlow.currentChoices.Clear();
- currentPointer = Pointer.Null;
- previousPointer = Pointer.Null;
- didSafeExit = true;
- }
-
-
-
-
- void TrimWhitespaceFromFunctionEnd ()
- {
- Debug.Assert (callStack.currentElement.type == PushPopType.Function);
- var functionStartPoint = callStack.currentElement.functionStartInOuputStream;
-
-
- if (functionStartPoint == -1) {
- functionStartPoint = 0;
- }
-
- for (int i = outputStream.Count - 1; i >= functionStartPoint; i--) {
- var obj = outputStream [i];
- var txt = obj as StringValue;
- var cmd = obj as ControlCommand;
- if (!txt) continue;
- if (cmd) break;
- if (txt.isNewline || txt.isInlineWhitespace) {
- outputStream.RemoveAt (i);
- OutputStreamDirty ();
- } else {
- break;
- }
- }
- }
- public void PopCallstack (PushPopType? popType = null)
- {
-
- if (callStack.currentElement.type == PushPopType.Function)
- TrimWhitespaceFromFunctionEnd ();
- callStack.Pop (popType);
- }
-
- public void SetChosenPath(Path path, bool incrementingTurnIndex)
- {
-
- _currentFlow.currentChoices.Clear ();
- var newPointer = story.PointerAtPath (path);
- if (!newPointer.isNull && newPointer.index == -1)
- newPointer.index = 0;
- currentPointer = newPointer;
- if( incrementingTurnIndex )
- currentTurnIndex++;
- }
- public void StartFunctionEvaluationFromGame (Container funcContainer, params object[] arguments)
- {
- callStack.Push (PushPopType.FunctionEvaluationFromGame, evaluationStack.Count);
- callStack.currentElement.currentPointer = Pointer.StartOf (funcContainer);
- PassArgumentsToEvaluationStack (arguments);
- }
- public void PassArgumentsToEvaluationStack (params object [] arguments)
- {
-
- if (arguments != null) {
- for (int i = 0; i < arguments.Length; i++) {
- if (!(arguments [i] is int || arguments [i] is float || arguments [i] is string || arguments [i] is bool || arguments [i] is InkList)) {
- throw new System.ArgumentException ("ink arguments when calling EvaluateFunction / ChoosePathStringWithParameters must be int, float, string, bool or InkList. Argument was "+(arguments [i] == null ? "null" : arguments [i].GetType().Name));
- }
- PushEvaluationStack (Runtime.Value.Create (arguments [i]));
- }
- }
- }
-
- public bool TryExitFunctionEvaluationFromGame ()
- {
- if( callStack.currentElement.type == PushPopType.FunctionEvaluationFromGame ) {
- currentPointer = Pointer.Null;
- didSafeExit = true;
- return true;
- }
- return false;
- }
- public object CompleteFunctionEvaluationFromGame ()
- {
- if (callStack.currentElement.type != PushPopType.FunctionEvaluationFromGame) {
- throw new Exception ("Expected external function evaluation to be complete. Stack trace: "+callStack.callStackTrace);
- }
- int originalEvaluationStackHeight = callStack.currentElement.evaluationStackHeightWhenPushed;
-
-
-
-
-
- Runtime.Object returnedObj = null;
- while (evaluationStack.Count > originalEvaluationStackHeight) {
- var poppedObj = PopEvaluationStack ();
- if (returnedObj == null)
- returnedObj = poppedObj;
- }
-
- PopCallstack (PushPopType.FunctionEvaluationFromGame);
-
- if (returnedObj) {
- if (returnedObj is Runtime.Void)
- return null;
-
- var returnVal = returnedObj as Runtime.Value;
-
-
- if (returnVal.valueType == ValueType.DivertTarget) {
- return returnVal.valueObject.ToString ();
- }
-
-
- return returnVal.valueObject;
- }
- return null;
- }
- public void AddError(string message, bool isWarning)
- {
- if (!isWarning) {
- if (currentErrors == null) currentErrors = new List<string> ();
- currentErrors.Add (message);
- } else {
- if (currentWarnings == null) currentWarnings = new List<string> ();
- currentWarnings.Add (message);
- }
- }
- void OutputStreamDirty()
- {
- _outputStreamTextDirty = true;
- _outputStreamTagsDirty = true;
- }
-
-
-
- Dictionary<string, int> _visitCounts;
- Dictionary<string, int> _turnIndices;
- bool _outputStreamTextDirty = true;
- bool _outputStreamTagsDirty = true;
- StatePatch _patch;
- Flow _currentFlow;
- Dictionary<string, Flow> _namedFlows;
- const string kDefaultFlowName = "DEFAULT_FLOW";
- bool _aliveFlowNamesDirty = true;
- }
- }