internal int?DebugLineNumberOfPath(Path path) { if (path == null) { return(null); } // Try to get a line number from debug metadata var root = this.rootContentContainer; if (root) { Runtime.Object targetContent = root.ContentAtPath(path).obj; if (targetContent) { var dm = targetContent.debugMetadata; if (dm != null) { return(dm.startLineNumber); } } } return(null); }
void PushToOutputStreamIndividual(Runtime.Object obj) { var glue = obj as Runtime.Glue; var text = obj as Runtime.StringValue; bool includeInOutput = true; if (glue) { // Found matching left-glue for right-glue? Close it. Glue matchingRightGlue = null; if (glue.isLeft) { matchingRightGlue = MatchRightGlueForLeftGlue(glue); } // Left/Right glue is auto-generated for inline expressions // where we want to absorb newlines but only in a certain direction. // "Bi" glue is written by the user in their ink with <> if (glue.isLeft || glue.isBi) { TrimNewlinesFromOutputStream(matchingRightGlue); } includeInOutput = glue.isBi || glue.isRight; } else if (text) { if (currentGlueIndex != -1) { // Absorb any new newlines if there's existing glue // in the output stream. // Also trim any extra whitespace (spaces/tabs) if so. if (text.isNewline) { TrimFromExistingGlue(); includeInOutput = false; } // Able to completely reset when else if (text.isNonWhitespace) { RemoveExistingGlue(); } } else if (text.isNewline) { if (outputStreamEndsInNewline || !outputStreamContainsContent) { includeInOutput = false; } } } if (includeInOutput) { _outputStream.Add(obj); } }
internal int?DebugLineNumberOfPath(Path path) { if (path == null) { return(null); } // Try to get a line number from debug metadata var root = this.rootContentContainer; if (root) { Runtime.Object targetContent = null; // Sometimes paths can be "invalid" if they're externally defined // in the game. TODO: Change ContentAtPath to return null, and // only throw an exception in places that actually care! try { targetContent = root.ContentAtPath(path); } catch { } if (targetContent) { var dm = targetContent.debugMetadata; if (dm != null) { return(dm.startLineNumber); } } } return(null); }
public bool RuntimeObjectsEqual(Runtime.Object obj1, Runtime.Object obj2) { if (obj1.GetType() != obj2.GetType()) { return(false); } // Perform equality on int/float manually to avoid boxing var intVal = obj1 as IntValue; if (intVal != null) { return(intVal.value == ((IntValue)obj2).value); } var floatVal = obj1 as FloatValue; if (floatVal != null) { return(floatVal.value == ((FloatValue)obj2).value); } // Other Value type (using proper Equals: list, string, divert path) var val1 = obj1 as Value; var val2 = obj2 as Value; if (val1 != null) { return(val1.valueObject.Equals(val2.valueObject)); } throw new System.Exception("FastRoughDefinitelyEquals: Unsupported runtime object type: " + obj1.GetType()); }
Runtime.Object GetRawVariableWithName(string name, int contextIndex) { Runtime.Object varValue = null; // 0 context = global if (contextIndex == 0 || contextIndex == -1) { if (_globalVariables.TryGetValue(name, out varValue)) { return(varValue); } // Getting variables can actually happen during globals set up since you can do // VAR x = A_LIST_ITEM // So _defaultGlobalVariables may be null. // We need to do this check though in case a new global is added, so we need to // revert to the default globals dictionary since an initial value hasn't yet been set. if (_defaultGlobalVariables != null && _defaultGlobalVariables.TryGetValue(name, out varValue)) { return(varValue); } var listItemValue = _listDefsOrigin.FindSingleItemListWithName(name); if (listItemValue) { return(listItemValue); } } // Temporary varValue = _callStack.GetTemporaryVariableWithName(name, contextIndex); return(varValue); }
Runtime.Object GetRawVariableWithName(string name, int contextIndex) { Runtime.Object varValue = null; // 0 context = global if (contextIndex == 0 || contextIndex == -1) { if (_globalVariables.TryGetValue(name, out varValue)) { return(varValue); } var listItemValue = _listDefsOrigin.FindSingleItemListWithName(name); if (listItemValue) { return(listItemValue); } } // Temporary varValue = _callStack.GetTemporaryVariableWithName(name, contextIndex); if (varValue == null) { throw new System.Exception("RUNTIME ERROR: Variable '" + name + "' could not be found in context '" + contextIndex + "'. This shouldn't be possible so is a bug in the ink engine. Please try to construct a minimal story that reproduces the problem and report to inkle, thank you!"); } return(varValue); }
internal void PushEvaluationStack(Runtime.Object obj) { // Include metadata about the origin List for list values when // they're used, so that lower level functions can make use // of the origin list to get related items, or make comparisons // with the integer values etc. var listValue = obj as ListValue; if (listValue) { // Update origin when list is has something to indicate the list origin var rawList = listValue.value; var names = rawList.originNames; if (names != null) { var origins = new List <ListDefinition> (); foreach (var n in names) { ListDefinition def = null; story.listDefinitions.TryGetDefinition(n, out def); if (!origins.Contains(def)) { origins.Add(def); } } rawList.origins = origins; } } evaluationStack.Add(obj); }
void AddDivertToResolve(Runtime.Divert divert, Runtime.Object targetContent) { _sequenceDivertsToResove.Add(new SequenceDivertToResolve() { divert = divert, targetContent = targetContent }); }
public void TryAddNamedContent(Runtime.Object contentObj) { var namedContentObj = contentObj as INamedContent; if (namedContentObj != null && namedContentObj.hasValidName) { AddToNamedContentOnly(namedContentObj); } }
internal void Assign(VariableAssignment varAss, Runtime.Object value) { var name = varAss.variableName; int contextIndex = -1; // Are we assigning to a global variable? bool setGlobal = false; if (varAss.isNewDeclaration) { setGlobal = varAss.isGlobal; } else { setGlobal = _globalVariables.ContainsKey(name); } // Constructing new variable pointer reference if (varAss.isNewDeclaration) { var varPointer = value as VariablePointerValue; if (varPointer) { var fullyResolvedVariablePointer = ResolveVariablePointer(varPointer); value = fullyResolvedVariablePointer; } } // Assign to existing variable pointer? // Then assign to the variable that the pointer is pointing to by name. else { // De-reference variable reference to point to VariablePointerValue existingPointer = null; do { existingPointer = GetRawVariableWithName(name, contextIndex) as VariablePointerValue; if (existingPointer) { name = existingPointer.variableName; contextIndex = existingPointer.contextIndex; setGlobal = (contextIndex == 0); } } while(existingPointer); } if (setGlobal) { SetGlobal(name, value); } else { _callStack.SetTemporaryVariable(name, value, varAss.isNewDeclaration, contextIndex); } }
void RetainListOriginsForAssignment(Runtime.Object oldValue, Runtime.Object newValue) { var oldList = oldValue as ListValue; var newList = newValue as ListValue; if (oldList && newList && newList.value.Count == 0) { newList.value.SetInitialOriginNames(oldList.value.originNames); } }
public static void RetainListOriginsForAssignment(Runtime.Object oldValue, Runtime.Object newValue) { var oldList = oldValue as ListValue; var newList = newValue as ListValue; // When assigning the emtpy list, try to retain any initial origin names if (oldList && newList && newList.value.Count == 0) { newList.value.SetInitialOriginNames(oldList.value.originNames); } }
public void AddContent(Runtime.Object contentObj) { content.Add(contentObj); if (contentObj.parent) { throw new System.Exception("content is already in " + contentObj.parent); } contentObj.parent = this; TryAddNamedContent(contentObj); }
public void InsertContent(Runtime.Object contentObj, int index) { content.Insert(index, contentObj); if (contentObj.parent) { throw new System.Exception("content is already in " + contentObj.parent); } contentObj.parent = this; TryAddNamedContent(contentObj); }
internal object CompleteFunctionEvaluationFromGame() { if (callStack.currentElement.type != PushPopType.FunctionEvaluationFromGame) { throw new StoryException("Expected external function evaluation to be complete. Stack trace: " + callStack.callStackTrace); } int originalEvaluationStackHeight = callStack.currentElement.evaluationStackHeightWhenPushed; // Do we have a returned value? // Potentially pop multiple values off the stack, in case we need // to clean up after ourselves (e.g. caller of EvaluateFunction may // have passed too many arguments, and we currently have no way to check for that) Runtime.Object returnedObj = null; while (evaluationStack.Count > originalEvaluationStackHeight) { var poppedObj = PopEvaluationStack(); if (returnedObj == null) { returnedObj = poppedObj; } } // Finally, pop the external function evaluation PopCallstack(PushPopType.FunctionEvaluationFromGame); // What did we get back? if (returnedObj) { if (returnedObj is Runtime.Void) { return(null); } // Some kind of value, if not void var returnVal = returnedObj as Runtime.Value; // DivertTargets get returned as the string of components // (rather than a Path, which isn't public) if (returnVal.valueType == ValueType.DivertTarget) { return(returnVal.valueObject.ToString()); } // Other types can just have their exact object type: // int, float, string. VariablePointers get returned as strings. return(returnVal.valueObject); } return(null); }
Runtime.Object GetVariableWithName(string name, int contextIndex) { Runtime.Object varValue = GetRawVariableWithName(name, contextIndex); // Get value from pointer? var varPointer = varValue as VariablePointerValue; if (varPointer) { varValue = ValueAtVariablePointer(varPointer); } return(varValue); }
internal object CompleteExternalFunctionEvaluation() { // Do we have a returned value? // Potentially pop multiple values off the stack, in case we need // to clean up after ourselves (e.g. caller of EvaluateFunction may // have passed too many arguments, and we currently have no way to check for that) Runtime.Object returnedObj = null; while (evaluationStack.Count > _originalEvaluationStackHeight) { var poppedObj = PopEvaluationStack(); if (returnedObj == null) { returnedObj = poppedObj; } } // Restore our own state callStack = _originalCallstack; _originalCallstack = null; _originalEvaluationStackHeight = 0; // Restore the callstack that the variablesState uses variablesState.callStack = callStack; // What did we get back? if (returnedObj) { if (returnedObj is Runtime.Void) { return(null); } // Some kind of value, if not void var returnVal = returnedObj as Runtime.Value; // DivertTargets get returned as the string of components // (rather than a Path, which isn't public) if (returnVal.valueType == ValueType.DivertTarget) { return(returnVal.valueObject.ToString()); } // Other types can just have their exact object type: // int, float, string. VariablePointers get returned as strings. return(returnVal.valueObject); } return(null); }
public void SetTemporaryVariable(string name, Runtime.Object value, bool declareNew, int contextIndex = -1) { if (contextIndex == -1) { contextIndex = currentElementIndex + 1; } var contextElement = callStack [contextIndex - 1]; if (!declareNew && !contextElement.temporaryVariables.ContainsKey(name)) { throw new System.Exception("Could not find temporary variable to set: " + name); } contextElement.temporaryVariables [name] = value; }
public void SetGlobal(string variableName, Runtime.Object value) { Runtime.Object oldValue = null; if (patch == null || !patch.TryGetGlobal(variableName, out oldValue)) { _globalVariables.TryGetValue(variableName, out oldValue); } if (patch != null) { patch.SetGlobal(variableName, value); } else { _globalVariables [variableName] = value; } }
// Normal content gets added into the latest Choice or Gather by default, // unless there hasn't been one yet. void AddGeneralRuntimeContent(Runtime.Object content) { // Content is allowed to evaluate runtimeObject to null // (e.g. AuthorWarning, which doesn't make it into the runtime) if (content == null) { return; } if (addContentToPreviousWeavePoint) { previousWeavePoint.runtimeContainer.AddContent(content); } else { currentContainer.AddContent(content); } }
public Thread(JToken jsonToken, Story storyContext) : this() { JObject jThreadObj = (JObject)jsonToken; threadIndex = jThreadObj ["threadIndex"].ToObject <int> (); JArray jThreadCallstack = (JArray)jThreadObj ["callstack"]; foreach (JToken jElTok in jThreadCallstack) { JObject jElementObj = (JObject)jElTok; PushPopType pushPopType = (PushPopType)jElementObj ["type"].ToObject <int>(); Container currentContainer = null; int contentIndex = 0; string currentContainerPathStr = null; JToken currentContainerPathStrToken; if (jElementObj.TryGetValue("cPath", out currentContainerPathStrToken)) { currentContainerPathStr = currentContainerPathStrToken.ToString(); currentContainer = storyContext.ContentAtPath(new Path(currentContainerPathStr)) as Container; contentIndex = jElementObj ["idx"].ToObject <int> (); } bool inExpressionEvaluation = jElementObj ["exp"].ToObject <bool> (); var el = new Element(pushPopType, currentContainer, contentIndex, inExpressionEvaluation); var jObjTemps = (JObject)jElementObj ["temp"]; el.temporaryVariables = Json.JObjectToDictionaryRuntimeObjs(jObjTemps); callstack.Add(el); } JToken prevContentObjPath = jThreadObj["previousContentObject"]; if (prevContentObjPath != null) { var prevPath = new Path(prevContentObjPath.ToString()); previousContentObject = storyContext.ContentAtPath(prevPath); } }
public SearchResult ContentAtPath(Path path, int partialPathStart = 0, int partialPathLength = -1) { if (partialPathLength == -1) { partialPathLength = path.length; } var result = new SearchResult(); result.approximate = false; Container currentContainer = this; Runtime.Object currentObj = this; for (int i = partialPathStart; i < partialPathLength; ++i) { var comp = path.GetComponent(i); // Path component was wrong type if (currentContainer == null) { result.approximate = true; break; } var foundObj = currentContainer.ContentWithPathComponent(comp); // Couldn't resolve entire path? if (foundObj == null) { result.approximate = true; break; } currentObj = foundObj; currentContainer = foundObj as Container; } result.obj = currentObj; return(result); }
void SetGlobal(string variableName, Runtime.Object value) { Runtime.Object oldValue = null; _globalVariables.TryGetValue(variableName, out oldValue); _globalVariables [variableName] = value; if (variableChangedEvent != null && !value.Equals(oldValue)) { if (batchObservingVariableChanges) { _changedVariables.Add(variableName); } else { variableChangedEvent(variableName, value); } } }
// Push to output stream, but split out newlines in text for consistency // in dealing with them later. internal 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); } return; } } PushToOutputStreamIndividual(obj); }
public Thread(Dictionary <string, object> jThreadObj, Story storyContext) : this() { threadIndex = (int)jThreadObj ["threadIndex"]; List <object> jThreadCallstack = (List <object>)jThreadObj ["callstack"]; foreach (object jElTok in jThreadCallstack) { var jElementObj = (Dictionary <string, object>)jElTok; PushPopType pushPopType = (PushPopType)(int)jElementObj ["type"]; Container currentContainer = null; int contentIndex = 0; string currentContainerPathStr = null; object currentContainerPathStrToken; if (jElementObj.TryGetValue("cPath", out currentContainerPathStrToken)) { currentContainerPathStr = currentContainerPathStrToken.ToString(); currentContainer = storyContext.ContentAtPath(new Path(currentContainerPathStr)) as Container; contentIndex = (int)jElementObj ["idx"]; } bool inExpressionEvaluation = (bool)jElementObj ["exp"]; var el = new Element(pushPopType, currentContainer, contentIndex, inExpressionEvaluation); var jObjTemps = (Dictionary <string, object>)jElementObj ["temp"]; el.temporaryVariables = Json.JObjectToDictionaryRuntimeObjs(jObjTemps); callstack.Add(el); } object prevContentObjPath; if (jThreadObj.TryGetValue("previousContentObject", out prevContentObjPath)) { var prevPath = new Path((string)prevContentObjPath); previousContentObject = storyContext.ContentAtPath(prevPath); } }
// Get variable value, dereferencing a variable pointer if necessary public Runtime.Object GetTemporaryVariableWithName(string name, int contextIndex = -1) { if (contextIndex == -1) { contextIndex = currentElementIndex + 1; } Runtime.Object varValue = null; var contextElement = callStack [contextIndex - 1]; if (contextElement.temporaryVariables.TryGetValue(name, out varValue)) { return(varValue); } else { return(null); } }
public void SetTemporaryVariable(string name, Runtime.Object value, bool declareNew, int contextIndex = -1) { if (contextIndex == -1) { contextIndex = currentElementIndex + 1; } var contextElement = callStack [contextIndex - 1]; if (!declareNew && !contextElement.temporaryVariables.ContainsKey(name)) { throw new StoryException("Could not find temporary variable to set: " + name); } Runtime.Object oldValue; if (contextElement.temporaryVariables.TryGetValue(name, out oldValue)) { ListValue.RetainListOriginsForAssignment(oldValue, value); } contextElement.temporaryVariables [name] = value; }
public void SetGlobal(string variableName, Runtime.Object value) { Runtime.Object oldValue = null; if (patch == null || !patch.TryGetGlobal(variableName, out oldValue)) { _globalVariables.TryGetValue(variableName, out oldValue); } ListValue.RetainListOriginsForAssignment(oldValue, value); if (patch != null) { patch.SetGlobal(variableName, value); } else { _globalVariables [variableName] = value; } if (variableChangedEvent != null && !value.Equals(oldValue)) { if (batchObservingVariableChanges) { if (patch != null) { patch.AddChangedVariable(variableName); } else if (_changedVariablesForBatchObs != null) { _changedVariablesForBatchObs.Add(variableName); } } else { variableChangedEvent(variableName, value); } } }
public Runtime.Object ContentAtPath(Path path, int partialPathLength = -1) { if (partialPathLength == -1) { partialPathLength = path.length; } Container currentContainer = this; Runtime.Object currentObj = this; for (int i = 0; i < partialPathLength; ++i) { var comp = path.GetComponent(i); if (currentContainer == null) { throw new System.Exception("Path continued, but previous object wasn't a container: " + currentObj); } currentObj = currentContainer.ContentWithPathComponent(comp); currentContainer = currentObj as Container; } return(currentObj); }
Runtime.Object GetRawVariableWithName(string name, int contextIndex) { Runtime.Object varValue = null; // 0 context = global if (contextIndex == 0 || contextIndex == -1) { if (_globalVariables.TryGetValue(name, out varValue)) { return(varValue); } var listItemValue = _listDefsOrigin.FindSingleItemListWithName(name); if (listItemValue) { return(listItemValue); } } // Temporary varValue = _callStack.GetTemporaryVariableWithName(name, contextIndex); return(varValue); }
public Thread(Dictionary<string, object> jThreadObj, Story storyContext) : this() { threadIndex = (int) jThreadObj ["threadIndex"]; List<object> jThreadCallstack = (List<object>) jThreadObj ["callstack"]; foreach (object jElTok in jThreadCallstack) { var jElementObj = (Dictionary<string, object>)jElTok; PushPopType pushPopType = (PushPopType)(int)jElementObj ["type"]; Container currentContainer = null; int contentIndex = 0; string currentContainerPathStr = null; object currentContainerPathStrToken; if (jElementObj.TryGetValue ("cPath", out currentContainerPathStrToken)) { currentContainerPathStr = currentContainerPathStrToken.ToString (); currentContainer = storyContext.ContentAtPath (new Path(currentContainerPathStr)) as Container; contentIndex = (int) jElementObj ["idx"]; } bool inExpressionEvaluation = (bool)jElementObj ["exp"]; var el = new Element (pushPopType, currentContainer, contentIndex, inExpressionEvaluation); var jObjTemps = (Dictionary<string, object>) jElementObj ["temp"]; el.temporaryVariables = Json.JObjectToDictionaryRuntimeObjs (jObjTemps); callstack.Add (el); } object prevContentObjPath; if( jThreadObj.TryGetValue("previousContentObject", out prevContentObjPath) ) { var prevPath = new Path((string)prevContentObjPath); previousContentObject = storyContext.ContentAtPath(prevPath); } }