*click on page or press spacebar to fast forward text animation
-
-
-
-
-
-
-
-
What do you want to do next?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/prototype/ink-full.js b/prototype/ink-full.js
deleted file mode 100644
index 4ba6b45..0000000
--- a/prototype/ink-full.js
+++ /dev/null
@@ -1,2 +0,0 @@
-!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).inkjs={})}(this,(function(t){"use strict";class e{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;this.sourceFilename=t,this.pluginNames=e,this.countAllVisits=n,this.errorHandler=i,this.fileHandler=r}}class n{constructor(t,e,n){this.length=t,this.debugMetadata=e,this.text=n}}var i;!function(t){t[t.Author=0]="Author",t[t.Warning=1]="Warning",t[t.Error=2]="Error"}(i||(i={}));class r{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;this.identifier=t,this.isByReference=e,this.isDivertTarget=n}get typeName(){return"Argument"}}function s(t,e){return t instanceof e?u(t):null}function a(t,e){if(t instanceof e)return u(t);throw new Error(`${t} is not of type ${e}`)}function o(t){return t.hasValidName&&t.name?t:null}function l(t){return void 0===t?null:t}function h(t){return"object"==typeof t&&"function"==typeof t.Equals}function u(t,e){return t}function c(t){return null!=t}class d{constructor(){var t=this;this._alreadyHadError=!1,this._alreadyHadWarning=!1,this._debugMetadata=null,this._runtimeObject=null,this.content=[],this.parent=null,this.GetType=()=>this.typeName,this.AddContent=t=>{null===this.content&&(this.content=[]);const e=Array.isArray(t)?t:[t];for(const t of e)t.hasOwnProperty("parent")&&(t.parent=this),this.content.push(t);return Array.isArray(t)?void 0:t},this.InsertContent=(t,e)=>(null===this.content&&(this.content=[]),e.parent=this,this.content.splice(t,0,e),e),this.Find=e=>function(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,i=s(t,e);if(null!==i&&(null===n||!0===n(i)))return i;if(null===t.content)return null;for(const i of t.content){let t=i.Find&&i.Find(e)(n);if(t)return t}return null},this.FindAll=t=>(e,n)=>{const i=Array.isArray(n)?n:[],r=s(this,t);if(null===r||e&&!0!==e(r)||i.push(r),null===this.content)return[];for(const n of this.content)n.FindAll&&n.FindAll(t)(e,i);return i},this.Warning=function(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;t.Error(e,n,!0)}}get debugMetadata(){return null===this._debugMetadata&&this.parent?this.parent.debugMetadata:this._debugMetadata}set debugMetadata(t){this._debugMetadata=t}get hasOwnDebugMetadata(){return Boolean(this.debugMetadata)}get typeName(){return"ParsedObject"}get story(){let t=this;for(;t.parent;)t=t.parent;return t}get runtimeObject(){return this._runtimeObject||(this._runtimeObject=this.GenerateRuntimeObject(),this._runtimeObject&&(this._runtimeObject.debugMetadata=this.debugMetadata)),this._runtimeObject}set runtimeObject(t){this._runtimeObject=t}get runtimePath(){if(!this.runtimeObject.path)throw new Error;return this.runtimeObject.path}get containerForCounting(){return this.runtimeObject}get ancestry(){let t=[],e=this.parent;for(;e;)t.push(e),e=e.parent;return t=t.reverse(),t}ResolveReferences(t){if(null!==this.content)for(const e of this.content)e.ResolveReferences(t)}Error(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(null===e&&(e=this),!(e._alreadyHadError&&!n||e._alreadyHadWarning&&n)){if(!this.parent)throw new Error(`No parent object to send error to: ${t}`);this.parent.Error(t,e,n),n?e._alreadyHadWarning=!0:e._alreadyHadError=!0}}}class p extends d{constructor(t){super(),this.warningMessage=t,this.GenerateRuntimeObject=()=>(this.Warning(this.warningMessage),null)}get typeName(){return"AuthorWarning"}}let m=class t{constructor(){if(this._components=[],this._componentsString=null,this._isRelative=!1,"string"==typeof arguments[0]){let t=arguments[0];this.componentsString=t}else if(arguments[0]instanceof t.Component&&arguments[1]instanceof t){let t=arguments[0],e=arguments[1];this._components.push(t),this._components=this._components.concat(e._components)}else if(arguments[0]instanceof Array){let t=arguments[0],e=!!arguments[1];this._components=this._components.concat(t),this._isRelative=e}}get isRelative(){return this._isRelative}get componentCount(){return this._components.length}get head(){return this._components.length>0?this._components[0]:null}get tail(){if(this._components.length>=2){let e=this._components.slice(1,this._components.length);return new t(e)}return t.self}get length(){return this._components.length}get lastComponent(){let t=this._components.length-1;return t>=0?this._components[t]:null}get containsNamedComponent(){for(let t=0,e=this._components.length;t=0}get isParent(){return this.name==t.parentId}static ToParent(){return new e(t.parentId)}toString(){return this.isIndex?this.index.toString():this.name}Equals(t){return null!=t&&t.isIndex==this.isIndex&&(this.isIndex?this.index==t.index:this.name==t.name)}}t.Component=e}(m||(m={})),function(t){function e(t,e){if(!t)throw void 0!==e&&console.warn(e),console.trace&&console.trace(),new Error("")}t.AssertType=function(t,n,i){e(t instanceof n,i)},t.Assert=e}(f||(f={}));class C extends Error{}function S(t){throw new C(`${t} is null or undefined`)}class y{constructor(){this.parent=null,this._debugMetadata=null,this._path=null}get debugMetadata(){return null===this._debugMetadata&&this.parent?this.parent.debugMetadata:this._debugMetadata}set debugMetadata(t){this._debugMetadata=t}get ownDebugMetadata(){return this._debugMetadata}DebugLineNumberOfPath(t){if(null===t)return null;let e=this.rootContentContainer;if(e){let n=e.ContentAtPath(t).obj;if(n){let t=n.debugMetadata;if(null!==t)return t.startLineNumber}}return null}get path(){if(null==this._path)if(null==this.parent)this._path=new m;else{let t=[],e=this,n=s(e.parent,L);for(;null!==n;){let i=o(e);if(null!=i&&i.hasValidName){if(null===i.name)return S("namedChild.name");t.unshift(new m.Component(i.name))}else t.unshift(new m.Component(n.content.indexOf(e)));e=n,n=s(n.parent,L)}this._path=new m(t)}return this._path}ResolvePath(t){if(null===t)return S("path");if(t.isRelative){let e=s(this,L);return null===e&&(f.Assert(null!==this.parent,"Can't resolve relative path because we don't have a parent"),e=s(this.parent,L),f.Assert(null!==e,"Expected parent to be a container"),f.Assert(t.GetComponent(0).isParent),t=t.tail),null===e?S("nearestContainer"):e.ContentAtPath(t)}{let e=this.rootContentContainer;return null===e?S("contentContainer"):e.ContentAtPath(t)}}ConvertPathToRelative(t){let e=this.path,n=Math.min(t.length,e.length),i=-1;for(let r=0;r1?e-1:0),i=1;ivoid 0!==n[e]?n[e]:t))}toString(){return this.string}Clear(){this.string=""}}class w{constructor(){if(this.originName=null,this.itemName=null,void 0!==arguments[1]){let t=arguments[0],e=arguments[1];this.originName=t,this.itemName=e}else if(arguments[0]){let t=arguments[0].toString().split(".");this.originName=t[0],this.itemName=t[1]}}static get Null(){return new w(null,null)}get isNull(){return null==this.originName&&null==this.itemName}get fullName(){return(null!==this.originName?this.originName:"?")+"."+this.itemName}toString(){return this.fullName}Equals(t){if(t instanceof w){let e=t;return e.itemName==this.itemName&&e.originName==this.originName}return!1}copy(){return new w(this.originName,this.itemName)}serialized(){return JSON.stringify({originName:this.originName,itemName:this.itemName})}static fromSerializedKey(t){let e=JSON.parse(t);if(!w.isLikeInkListItem(e))return w.Null;let n=e;return new w(n.originName,n.itemName)}static isLikeInkListItem(t){return"object"==typeof t&&(!(!t.hasOwnProperty("originName")||!t.hasOwnProperty("itemName"))&&(("string"==typeof t.originName||null===typeof t.originName)&&("string"==typeof t.itemName||null===typeof t.itemName)))}}class E extends Map{constructor(){if(super(arguments[0]instanceof E?arguments[0]:[]),this.origins=null,this._originNames=[],arguments[0]instanceof E){let t=arguments[0],e=t.originNames;null!==e&&(this._originNames=e.slice()),null!==t.origins&&(this.origins=t.origins.slice())}else if("string"==typeof arguments[0]){let t=arguments[0],e=arguments[1];if(this.SetInitialOriginName(t),null===e.listDefinitions)return S("originStory.listDefinitions");let n=e.listDefinitions.TryListGetDefinition(t,null);if(!n.exists)throw new Error("InkList origin could not be found in story when constructing new list: "+t);if(null===n.result)return S("def.result");this.origins=[n.result]}else if("object"==typeof arguments[0]&&arguments[0].hasOwnProperty("Key")&&arguments[0].hasOwnProperty("Value")){let t=arguments[0];this.Add(t.Key,t.Value)}}static FromString(t,e){var n;let i=null===(n=e.listDefinitions)||void 0===n?void 0:n.FindSingleItemListWithName(t);if(i)return null===i.value?S("listValue.value"):new E(i.value);throw new Error("Could not find the InkListItem from the string '"+t+"' to create an InkList because it doesn't exist in the original list definition in ink.")}AddItem(t){if(t instanceof w){let e=t;if(null==e.originName)return void this.AddItem(e.itemName);if(null===this.origins)return S("this.origins");for(let t of this.origins)if(t.name==e.originName){let n=t.TryGetValueForItem(e,0);if(n.exists)return void this.Add(e,n.result);throw new Error("Could not add the item "+e+" to this list because it doesn't exist in the original list definition in ink.")}throw new Error("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.")}{let e=t,n=null;if(null===this.origins)return S("this.origins");for(let t of this.origins){if(null===e)return S("itemName");if(t.ContainsItemWithName(e)){if(null!=n)throw new Error("Could not add the item "+e+" to this list because it could come from either "+t.name+" or "+n.name);n=t}}if(null==n)throw new Error("Could not add the item "+e+" to this list because it isn't known to any list definitions previously associated with this list.");let i=new w(n.name,e),r=n.ValueForItem(i);this.Add(i,r)}}ContainsItemNamed(t){for(let[e]of this){if(w.fromSerializedKey(e).itemName==t)return!0}return!1}ContainsKey(t){return this.has(t.serialized())}Add(t,e){let n=t.serialized();if(this.has(n))throw new Error(`The Map already contains an entry for ${t}`);this.set(n,e)}Remove(t){return this.delete(t.serialized())}get Count(){return this.size}get originOfMaxItem(){if(null==this.origins)return null;let t=this.maxItem.Key.originName,e=null;return this.origins.every((n=>n.name!=t||(e=n,!1))),e}get originNames(){if(this.Count>0){null==this._originNames&&this.Count>0?this._originNames=[]:(this._originNames||(this._originNames=[]),this._originNames.length=0);for(let[t]of this){let e=w.fromSerializedKey(t);if(null===e.originName)return S("item.originName");this._originNames.push(e.originName)}}return this._originNames}SetInitialOriginName(t){this._originNames=[t]}SetInitialOriginNames(t){this._originNames=null==t?null:t.slice()}get maxItem(){let t={Key:w.Null,Value:0};for(let[e,n]of this){let i=w.fromSerializedKey(e);(t.Key.isNull||n>t.Value)&&(t={Key:i,Value:n})}return t}get minItem(){let t={Key:w.Null,Value:0};for(let[e,n]of this){let i=w.fromSerializedKey(e);(t.Key.isNull||nt.maxItem.Value)}GreaterThanOrEquals(t){return 0!=this.Count&&(0==t.Count||this.minItem.Value>=t.minItem.Value&&this.maxItem.Value>=t.maxItem.Value)}LessThan(t){return 0!=t.Count&&(0==this.Count||this.maxItem.Value0?new E(this.maxItem):new E}MinAsList(){return this.Count>0?new E(this.minItem):new E}ListWithSubRange(t,e){if(0==this.Count)return new E;let n=this.orderedItems,i=0,r=Number.MAX_SAFE_INTEGER;Number.isInteger(t)?i=t:t instanceof E&&t.Count>0&&(i=t.minItem.Value),Number.isInteger(e)?r=e:e instanceof E&&e.Count>0&&(r=e.maxItem.Value);let s=new E;s.SetInitialOriginNames(this.originNames);for(let t of n)t.Value>=i&&t.Value<=r&&s.Add(t.Key,t.Value);return s}Equals(t){if(t instanceof E==!1)return!1;if(t.Count!=this.Count)return!1;for(let[e]of this)if(!t.has(e))return!1;return!0}get orderedItems(){let t=new Array;for(let[e,n]of this){let i=w.fromSerializedKey(e);t.push({Key:i,Value:n})}return t.sort(((t,e)=>null===t.Key.originName?S("x.Key.originName"):null===e.Key.originName?S("y.Key.originName"):t.Value==e.Value?t.Key.originName.localeCompare(e.Key.originName):t.Valuee.Value?1:0)),t}toString(){let t=this.orderedItems,e=new b;for(let n=0;n0&&e.Append(", ");let i=t[n].Key;if(null===i.itemName)return S("item.itemName");e.Append(i.itemName)}return e.toString()}valueOf(){return NaN}}class _ extends Error{constructor(t){super(t),this.useEndLineNumber=!1,this.message=t,this.name="StoryException"}}function T(t,e,n){if(null===t)return{result:n,exists:!1};let i=t.get(e);return void 0===i?{result:n,exists:!1}:{result:i,exists:!0}}class x extends y{static Create(t,e){if(e){if(e===g.Int&&Number.isInteger(Number(t)))return new N(Number(t));if(e===g.Float&&!isNaN(t))return new O(Number(t))}return"boolean"==typeof t?new P(Boolean(t)):"string"==typeof t?new I(String(t)):Number.isInteger(Number(t))?new N(Number(t)):isNaN(t)?t instanceof m?new F(a(t,m)):t instanceof E?new R(a(t,E)):null:new O(Number(t))}Copy(){return a(x.Create(this.valueObject),y)}BadCastException(t){return new _("Can't cast "+this.valueObject+" from "+this.valueType+" to "+t)}}class A extends x{constructor(t){super(),this.value=t}get valueObject(){return this.value}toString(){return null===this.value?S("Value.value"):this.value.toString()}}class P extends A{constructor(t){super(t||!1)}get isTruthy(){return Boolean(this.value)}get valueType(){return g.Bool}Cast(t){if(null===this.value)return S("Value.value");if(t==this.valueType)return this;if(t==g.Int)return new N(this.value?1:0);if(t==g.Float)return new O(this.value?1:0);if(t==g.String)return new I(this.value?"true":"false");throw this.BadCastException(t)}toString(){return this.value?"true":"false"}}class N extends A{constructor(t){super(t||0)}get isTruthy(){return 0!=this.value}get valueType(){return g.Int}Cast(t){if(null===this.value)return S("Value.value");if(t==this.valueType)return this;if(t==g.Bool)return new P(0!==this.value);if(t==g.Float)return new O(this.value);if(t==g.String)return new I(""+this.value);throw this.BadCastException(t)}}class O extends A{constructor(t){super(t||0)}get isTruthy(){return 0!=this.value}get valueType(){return g.Float}Cast(t){if(null===this.value)return S("Value.value");if(t==this.valueType)return this;if(t==g.Bool)return new P(0!==this.value);if(t==g.Int)return new N(this.value);if(t==g.String)return new I(""+this.value);throw this.BadCastException(t)}}class I extends A{constructor(t){if(super(t||""),this._isNewline="\n"==this.value,this._isInlineWhitespace=!0,null===this.value)return S("Value.value");this.value.length>0&&this.value.split("").every((t=>" "==t||"\t"==t||(this._isInlineWhitespace=!1,!1)))}get valueType(){return g.String}get isTruthy(){return null===this.value?S("Value.value"):this.value.length>0}get isNewline(){return this._isNewline}get isInlineWhitespace(){return this._isInlineWhitespace}get isNonWhitespace(){return!this.isNewline&&!this.isInlineWhitespace}Cast(t){if(t==this.valueType)return this;if(t==g.Int){let e=function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=parseInt(t);return Number.isNaN(n)?{result:e,exists:!1}:{result:n,exists:!0}}(this.value);if(e.exists)return new N(e.result);throw this.BadCastException(t)}if(t==g.Float){let e=function(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=parseFloat(t);return Number.isNaN(n)?{result:e,exists:!1}:{result:n,exists:!0}}(this.value);if(e.exists)return new O(e.result);throw this.BadCastException(t)}throw this.BadCastException(t)}}class F extends A{constructor(){super(arguments.length>0&&void 0!==arguments[0]?arguments[0]:null)}get valueType(){return g.DivertTarget}get targetPath(){return null===this.value?S("Value.value"):this.value}set targetPath(t){this.value=t}get isTruthy(){throw new Error("Shouldn't be checking the truthiness of a divert target")}Cast(t){if(t==this.valueType)return this;throw this.BadCastException(t)}toString(){return"DivertTargetValue("+this.targetPath+")"}}class W extends A{constructor(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;super(t),this._contextIndex=e}get contextIndex(){return this._contextIndex}set contextIndex(t){this._contextIndex=t}get variableName(){return null===this.value?S("Value.value"):this.value}set variableName(t){this.value=t}get valueType(){return g.VariablePointer}get isTruthy(){throw new Error("Shouldn't be checking the truthiness of a variable pointer")}Cast(t){if(t==this.valueType)return this;throw this.BadCastException(t)}toString(){return"VariablePointerValue("+this.variableName+")"}Copy(){return new W(this.variableName,this.contextIndex)}}class R extends A{get isTruthy(){return null===this.value?S("this.value"):this.value.Count>0}get valueType(){return g.List}Cast(t){if(null===this.value)return S("Value.value");if(t==g.Int){let t=this.value.maxItem;return t.Key.isNull?new N(0):new N(t.Value)}if(t==g.Float){let t=this.value.maxItem;return t.Key.isNull?new O(0):new O(t.Value)}if(t==g.String){let t=this.value.maxItem;return t.Key.isNull?new I(""):new I(t.Key.toString())}if(t==this.valueType)return this;throw this.BadCastException(t)}constructor(t,e){super(null),t||e?t instanceof E?this.value=new E(t):t instanceof w&&"number"==typeof e&&(this.value=new E({Key:t,Value:e})):this.value=new E}static RetainListOriginsForAssignment(t,e){let n=s(t,R),i=s(e,R);return i&&null===i.value?S("newList.value"):n&&null===n.value?S("oldList.value"):void(n&&i&&0==i.value.Count&&i.value.SetInitialOriginNames(n.value.originNames))}}!function(t){t[t.Bool=-1]="Bool",t[t.Int=0]="Int",t[t.Float=1]="Float",t[t.List=2]="List",t[t.String=3]="String",t[t.DivertTarget=4]="DivertTarget",t[t.VariablePointer=5]="VariablePointer"}(g||(g={}));class k{constructor(){this.obj=null,this.approximate=!1}get correctObj(){return this.approximate?null:this.obj}get container(){return this.obj instanceof L?this.obj:null}copy(){let t=new k;return t.obj=this.obj,t.approximate=this.approximate,t}}class L extends y{constructor(){super(...arguments),this.name=null,this._content=[],this.namedContent=new Map,this.visitsShouldBeCounted=!1,this.turnIndexShouldBeCounted=!1,this.countingAtStartOnly=!1,this._pathToFirstLeafContent=null}get hasValidName(){return null!=this.name&&this.name.length>0}get content(){return this._content}set content(t){this.AddContent(t)}get namedOnlyContent(){let t=new Map;for(let[e,n]of this.namedContent){let i=a(n,y);t.set(e,i)}for(let e of this.content){let n=o(e);null!=n&&n.hasValidName&&t.delete(n.name)}return 0==t.size&&(t=null),t}set namedOnlyContent(t){let e=this.namedOnlyContent;if(null!=e)for(let[t]of e)this.namedContent.delete(t);if(null!=t)for(let[,e]of t){let t=o(e);null!=t&&this.AddToNamedContentOnly(t)}}get countFlags(){let t=0;return this.visitsShouldBeCounted&&(t|=L.CountFlags.Visits),this.turnIndexShouldBeCounted&&(t|=L.CountFlags.Turns),this.countingAtStartOnly&&(t|=L.CountFlags.CountStartOnly),t==L.CountFlags.CountStartOnly&&(t=0),t}set countFlags(t){let e=t;(e&L.CountFlags.Visits)>0&&(this.visitsShouldBeCounted=!0),(e&L.CountFlags.Turns)>0&&(this.turnIndexShouldBeCounted=!0),(e&L.CountFlags.CountStartOnly)>0&&(this.countingAtStartOnly=!0)}get pathToFirstLeafContent(){return null==this._pathToFirstLeafContent&&(this._pathToFirstLeafContent=this.path.PathByAppendingPath(this.internalPathToFirstLeafContent)),this._pathToFirstLeafContent}get internalPathToFirstLeafContent(){let t=[],e=this;for(;e instanceof L;)e.content.length>0&&(t.push(new m.Component(0)),e=e.content[0]);return new m(t)}AddContent(t){if(t instanceof Array){let e=t;for(let t of e)this.AddContent(t)}else{let e=t;if(this._content.push(e),e.parent)throw new Error("content is already in "+e.parent);e.parent=this,this.TryAddNamedContent(e)}}TryAddNamedContent(t){let e=o(t);null!=e&&e.hasValidName&&this.AddToNamedContentOnly(e)}AddToNamedContentOnly(t){if(f.AssertType(t,y,"Can only add Runtime.Objects to a Runtime.Container"),a(t,y).parent=this,null===t.name)return S("namedContentObj.name");this.namedContent.set(t.name,t)}ContentAtPath(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:-1;-1==n&&(n=t.length);let i=new k;i.approximate=!1;let r=this,a=this;for(let o=e;o=0&&t.index=0||r.set(t,e);if(r.size>0){i(),t.AppendLine("-- named: --");for(let[,i]of r){f.AssertType(i,L,"Can only print out named Containers"),i.BuildStringOfHierarchy(t,e,n),t.AppendLine()}}e--,i(),t.Append("]")}}!function(t){var e;(e=t.CountFlags||(t.CountFlags={}))[e.Start=0]="Start",e[e.Visits=1]="Visits",e[e.Turns=2]="Turns",e[e.CountStartOnly=4]="CountStartOnly"}(L||(L={}));class D extends y{get commandType(){return this._commandType}constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:D.CommandType.NotSet;super(),this._commandType=t}Copy(){return new D(this.commandType)}static EvalStart(){return new D(D.CommandType.EvalStart)}static EvalOutput(){return new D(D.CommandType.EvalOutput)}static EvalEnd(){return new D(D.CommandType.EvalEnd)}static Duplicate(){return new D(D.CommandType.Duplicate)}static PopEvaluatedValue(){return new D(D.CommandType.PopEvaluatedValue)}static PopFunction(){return new D(D.CommandType.PopFunction)}static PopTunnel(){return new D(D.CommandType.PopTunnel)}static BeginString(){return new D(D.CommandType.BeginString)}static EndString(){return new D(D.CommandType.EndString)}static NoOp(){return new D(D.CommandType.NoOp)}static ChoiceCount(){return new D(D.CommandType.ChoiceCount)}static Turns(){return new D(D.CommandType.Turns)}static TurnsSince(){return new D(D.CommandType.TurnsSince)}static ReadCount(){return new D(D.CommandType.ReadCount)}static Random(){return new D(D.CommandType.Random)}static SeedRandom(){return new D(D.CommandType.SeedRandom)}static VisitIndex(){return new D(D.CommandType.VisitIndex)}static SequenceShuffleIndex(){return new D(D.CommandType.SequenceShuffleIndex)}static StartThread(){return new D(D.CommandType.StartThread)}static Done(){return new D(D.CommandType.Done)}static End(){return new D(D.CommandType.End)}static ListFromInt(){return new D(D.CommandType.ListFromInt)}static ListRange(){return new D(D.CommandType.ListRange)}static ListRandom(){return new D(D.CommandType.ListRandom)}static BeginTag(){return new D(D.CommandType.BeginTag)}static EndTag(){return new D(D.CommandType.EndTag)}toString(){return"ControlCommand "+this.commandType.toString()}}!function(t){var e;(e=t.CommandType||(t.CommandType={}))[e.NotSet=-1]="NotSet",e[e.EvalStart=0]="EvalStart",e[e.EvalOutput=1]="EvalOutput",e[e.EvalEnd=2]="EvalEnd",e[e.Duplicate=3]="Duplicate",e[e.PopEvaluatedValue=4]="PopEvaluatedValue",e[e.PopFunction=5]="PopFunction",e[e.PopTunnel=6]="PopTunnel",e[e.BeginString=7]="BeginString",e[e.EndString=8]="EndString",e[e.NoOp=9]="NoOp",e[e.ChoiceCount=10]="ChoiceCount",e[e.Turns=11]="Turns",e[e.TurnsSince=12]="TurnsSince",e[e.ReadCount=13]="ReadCount",e[e.Random=14]="Random",e[e.SeedRandom=15]="SeedRandom",e[e.VisitIndex=16]="VisitIndex",e[e.SequenceShuffleIndex=17]="SequenceShuffleIndex",e[e.StartThread=18]="StartThread",e[e.Done=19]="Done",e[e.End=20]="End",e[e.ListFromInt=21]="ListFromInt",e[e.ListRange=22]="ListRange",e[e.ListRandom=23]="ListRandom",e[e.BeginTag=24]="BeginTag",e[e.EndTag=25]="EndTag",e[e.TOTAL_VALUES=26]="TOTAL_VALUES"}(D||(D={}));class V extends d{constructor(){super(...arguments),this._prototypeRuntimeConstantExpression=null,this.outputWhenComplete=!1,this.GenerateRuntimeObject=()=>{const t=new L;return t.AddContent(D.EvalStart()),this.GenerateIntoContainer(t),this.outputWhenComplete&&t.AddContent(D.EvalOutput()),t.AddContent(D.EvalEnd()),t},this.GenerateConstantIntoContainer=t=>{null===this._prototypeRuntimeConstantExpression&&(this._prototypeRuntimeConstantExpression=new L,this.GenerateIntoContainer(this._prototypeRuntimeConstantExpression));for(const e of this._prototypeRuntimeConstantExpression.content){const n=e.Copy();n&&t.AddContent(n)}},this.toString=()=>"No string value in JavaScript."}get typeName(){return"Expression"}Equals(t){return!1}}class j extends y{toString(){return"Void"}}class B extends y{static CallWithName(t){return new B(t)}static CallExistsWithName(t){return this.GenerateNativeFunctionsIfNecessary(),this._nativeFunctions.get(t)}get name(){return null===this._name?S("NativeFunctionCall._name"):this._name}set name(t){this._name=t,this._isPrototype||(null===B._nativeFunctions?S("NativeFunctionCall._nativeFunctions"):this._prototype=B._nativeFunctions.get(this._name)||null)}get numberOfParameters(){return this._prototype?this._prototype.numberOfParameters:this._numberOfParameters}set numberOfParameters(t){this._numberOfParameters=t}Call(t){if(this._prototype)return this._prototype.Call(t);if(this.numberOfParameters!=t.length)throw new Error("Unexpected number of parameters");let e=!1;for(let n of t){if(n instanceof j)throw new _('Attempting to perform operation on a void value. Did you forget to "return" a value from a function you called here?');n instanceof R&&(e=!0)}if(2==t.length&&e)return this.CallBinaryListOperation(t);let n=this.CoerceValuesToSingleType(t),i=n[0].valueType;return i==g.Int||i==g.Float||i==g.String||i==g.DivertTarget||i==g.List?this.CallType(n):null}CallType(t){let e=a(t[0],A),n=e.valueType,i=e,r=t.length;if(2==r||1==r){if(null===this._operationFuncs)return S("NativeFunctionCall._operationFuncs");let s=this._operationFuncs.get(n);if(!s){const t=g[n];throw new _("Cannot perform operation "+this.name+" on "+t)}if(2==r){let e=a(t[1],A),n=s;if(null===i.value||null===e.value)return S("NativeFunctionCall.Call BinaryOp values");let r=n(i.value,e.value);return A.Create(r)}{let t=s;if(null===i.value)return S("NativeFunctionCall.Call UnaryOp value");let n=t(i.value);return this.name===B.Int?A.Create(n,g.Int):this.name===B.Float?A.Create(n,g.Float):A.Create(n,e.valueType)}}throw new Error("Unexpected number of parameters to NativeFunctionCall: "+t.length)}CallBinaryListOperation(t){if(("+"==this.name||"-"==this.name)&&t[0]instanceof R&&t[1]instanceof N)return this.CallListIncrementOperation(t);let e=a(t[0],A),n=a(t[1],A);if(!("&&"!=this.name&&"||"!=this.name||e.valueType==g.List&&n.valueType==g.List)){if(null===this._operationFuncs)return S("NativeFunctionCall._operationFuncs");let t=this._operationFuncs.get(g.Int);if(null===t)return S("NativeFunctionCall.CallBinaryListOperation op");let i=function(t){if("boolean"==typeof t)return t;throw new Error(`${t} is not a boolean`)}(t(e.isTruthy?1:0,n.isTruthy?1:0));return new P(i)}if(e.valueType==g.List&&n.valueType==g.List)return this.CallType([e,n]);throw new _("Can not call use "+this.name+" operation on "+g[e.valueType]+" and "+g[n.valueType])}CallListIncrementOperation(t){let e=a(t[0],R),n=a(t[1],N),i=new E;if(null===e.value)return S("NativeFunctionCall.CallListIncrementOperation listVal.value");for(let[t,r]of e.value){let s=w.fromSerializedKey(t);if(null===this._operationFuncs)return S("NativeFunctionCall._operationFuncs");let a=this._operationFuncs.get(g.Int);if(null===n.value)return S("NativeFunctionCall.CallListIncrementOperation intVal.value");let o=a(r,n.value),l=null;if(null===e.value.origins)return S("NativeFunctionCall.CallListIncrementOperation listVal.value.origins");for(let t of e.value.origins)if(t.name==s.originName){l=t;break}if(null!=l){let t=l.TryGetItemWithValue(o,w.Null);t.exists&&i.Add(t.result,o)}}return new R(i)}CoerceValuesToSingleType(t){let e=g.Int,n=null;for(let i of t){let t=a(i,A);t.valueType>e&&(e=t.valueType),t.valueType==g.List&&(n=s(t,R))}let i=[];if(g[e]==g[g.List])for(let e of t){let t=a(e,A);if(t.valueType==g.List)i.push(t);else{if(t.valueType!=g.Int){const e=g[t.valueType];throw new _("Cannot mix Lists and "+e+" values in this operation")}{let e=parseInt(t.valueObject);if(n=a(n,R),null===n.value)return S("NativeFunctionCall.CoerceValuesToSingleType specialCaseList.value");let r=n.value.originOfMaxItem;if(null===r)return S("NativeFunctionCall.CoerceValuesToSingleType list");let s=r.TryGetItemWithValue(e,w.Null);if(!s.exists)throw new _("Could not find List item with the value "+e+" in "+r.name);{let t=new R(s.result,e);i.push(t)}}}}else for(let n of t){let t=a(n,A).Cast(e);i.push(t)}return i}constructor(){if(super(),this._name=null,this._numberOfParameters=0,this._prototype=null,this._isPrototype=!1,this._operationFuncs=null,0===arguments.length)B.GenerateNativeFunctionsIfNecessary();else if(1===arguments.length){let t=arguments[0];B.GenerateNativeFunctionsIfNecessary(),this.name=t}else if(2===arguments.length){let t=arguments[0],e=arguments[1];this._isPrototype=!0,this.name=t,this.numberOfParameters=e}}static Identity(t){return t}static GenerateNativeFunctionsIfNecessary(){if(null==this._nativeFunctions){this._nativeFunctions=new Map,this.AddIntBinaryOp(this.Add,((t,e)=>t+e)),this.AddIntBinaryOp(this.Subtract,((t,e)=>t-e)),this.AddIntBinaryOp(this.Multiply,((t,e)=>t*e)),this.AddIntBinaryOp(this.Divide,((t,e)=>Math.floor(t/e))),this.AddIntBinaryOp(this.Mod,((t,e)=>t%e)),this.AddIntUnaryOp(this.Negate,(t=>-t)),this.AddIntBinaryOp(this.Equal,((t,e)=>t==e)),this.AddIntBinaryOp(this.Greater,((t,e)=>t>e)),this.AddIntBinaryOp(this.Less,((t,e)=>tt>=e)),this.AddIntBinaryOp(this.LessThanOrEquals,((t,e)=>t<=e)),this.AddIntBinaryOp(this.NotEquals,((t,e)=>t!=e)),this.AddIntUnaryOp(this.Not,(t=>0==t)),this.AddIntBinaryOp(this.And,((t,e)=>0!=t&&0!=e)),this.AddIntBinaryOp(this.Or,((t,e)=>0!=t||0!=e)),this.AddIntBinaryOp(this.Max,((t,e)=>Math.max(t,e))),this.AddIntBinaryOp(this.Min,((t,e)=>Math.min(t,e))),this.AddIntBinaryOp(this.Pow,((t,e)=>Math.pow(t,e))),this.AddIntUnaryOp(this.Floor,B.Identity),this.AddIntUnaryOp(this.Ceiling,B.Identity),this.AddIntUnaryOp(this.Int,B.Identity),this.AddIntUnaryOp(this.Float,(t=>t)),this.AddFloatBinaryOp(this.Add,((t,e)=>t+e)),this.AddFloatBinaryOp(this.Subtract,((t,e)=>t-e)),this.AddFloatBinaryOp(this.Multiply,((t,e)=>t*e)),this.AddFloatBinaryOp(this.Divide,((t,e)=>t/e)),this.AddFloatBinaryOp(this.Mod,((t,e)=>t%e)),this.AddFloatUnaryOp(this.Negate,(t=>-t)),this.AddFloatBinaryOp(this.Equal,((t,e)=>t==e)),this.AddFloatBinaryOp(this.Greater,((t,e)=>t>e)),this.AddFloatBinaryOp(this.Less,((t,e)=>tt>=e)),this.AddFloatBinaryOp(this.LessThanOrEquals,((t,e)=>t<=e)),this.AddFloatBinaryOp(this.NotEquals,((t,e)=>t!=e)),this.AddFloatUnaryOp(this.Not,(t=>0==t)),this.AddFloatBinaryOp(this.And,((t,e)=>0!=t&&0!=e)),this.AddFloatBinaryOp(this.Or,((t,e)=>0!=t||0!=e)),this.AddFloatBinaryOp(this.Max,((t,e)=>Math.max(t,e))),this.AddFloatBinaryOp(this.Min,((t,e)=>Math.min(t,e))),this.AddFloatBinaryOp(this.Pow,((t,e)=>Math.pow(t,e))),this.AddFloatUnaryOp(this.Floor,(t=>Math.floor(t))),this.AddFloatUnaryOp(this.Ceiling,(t=>Math.ceil(t))),this.AddFloatUnaryOp(this.Int,(t=>Math.floor(t))),this.AddFloatUnaryOp(this.Float,B.Identity),this.AddStringBinaryOp(this.Add,((t,e)=>t+e)),this.AddStringBinaryOp(this.Equal,((t,e)=>t===e)),this.AddStringBinaryOp(this.NotEquals,((t,e)=>!(t===e))),this.AddStringBinaryOp(this.Has,((t,e)=>t.includes(e))),this.AddStringBinaryOp(this.Hasnt,((t,e)=>!t.includes(e))),this.AddListBinaryOp(this.Add,((t,e)=>t.Union(e))),this.AddListBinaryOp(this.Subtract,((t,e)=>t.Without(e))),this.AddListBinaryOp(this.Has,((t,e)=>t.Contains(e))),this.AddListBinaryOp(this.Hasnt,((t,e)=>!t.Contains(e))),this.AddListBinaryOp(this.Intersect,((t,e)=>t.Intersect(e))),this.AddListBinaryOp(this.Equal,((t,e)=>t.Equals(e))),this.AddListBinaryOp(this.Greater,((t,e)=>t.GreaterThan(e))),this.AddListBinaryOp(this.Less,((t,e)=>t.LessThan(e))),this.AddListBinaryOp(this.GreaterThanOrEquals,((t,e)=>t.GreaterThanOrEquals(e))),this.AddListBinaryOp(this.LessThanOrEquals,((t,e)=>t.LessThanOrEquals(e))),this.AddListBinaryOp(this.NotEquals,((t,e)=>!t.Equals(e))),this.AddListBinaryOp(this.And,((t,e)=>t.Count>0&&e.Count>0)),this.AddListBinaryOp(this.Or,((t,e)=>t.Count>0||e.Count>0)),this.AddListUnaryOp(this.Not,(t=>0==t.Count?1:0)),this.AddListUnaryOp(this.Invert,(t=>t.inverse)),this.AddListUnaryOp(this.All,(t=>t.all)),this.AddListUnaryOp(this.ListMin,(t=>t.MinAsList())),this.AddListUnaryOp(this.ListMax,(t=>t.MaxAsList())),this.AddListUnaryOp(this.Count,(t=>t.Count)),this.AddListUnaryOp(this.ValueOfList,(t=>t.maxItem.Value));let t=(t,e)=>t.Equals(e),e=(t,e)=>!t.Equals(e);this.AddOpToNativeFunc(this.Equal,2,g.DivertTarget,t),this.AddOpToNativeFunc(this.NotEquals,2,g.DivertTarget,e)}}AddOpFuncForType(t,e){null==this._operationFuncs&&(this._operationFuncs=new Map),this._operationFuncs.set(t,e)}static AddOpToNativeFunc(t,e,n,i){if(null===this._nativeFunctions)return S("NativeFunctionCall._nativeFunctions");let r=this._nativeFunctions.get(t);r||(r=new B(t,e),this._nativeFunctions.set(t,r)),r.AddOpFuncForType(n,i)}static AddIntBinaryOp(t,e){this.AddOpToNativeFunc(t,2,g.Int,e)}static AddIntUnaryOp(t,e){this.AddOpToNativeFunc(t,1,g.Int,e)}static AddFloatBinaryOp(t,e){this.AddOpToNativeFunc(t,2,g.Float,e)}static AddFloatUnaryOp(t,e){this.AddOpToNativeFunc(t,1,g.Float,e)}static AddStringBinaryOp(t,e){this.AddOpToNativeFunc(t,2,g.String,e)}static AddListBinaryOp(t,e){this.AddOpToNativeFunc(t,2,g.List,e)}static AddListUnaryOp(t,e){this.AddOpToNativeFunc(t,1,g.List,e)}toString(){return'Native "'+this.name+'"'}}B.Add="+",B.Subtract="-",B.Divide="/",B.Multiply="*",B.Mod="%",B.Negate="_",B.Equal="==",B.Greater=">",B.Less="<",B.GreaterThanOrEquals=">=",B.LessThanOrEquals="<=",B.NotEquals="!=",B.Not="!",B.And="&&",B.Or="||",B.Min="MIN",B.Max="MAX",B.Pow="POW",B.Floor="FLOOR",B.Ceiling="CEILING",B.Int="INT",B.Float="FLOAT",B.Has="?",B.Hasnt="!?",B.Intersect="^",B.ListMin="LIST_MIN",B.ListMax="LIST_MAX",B.All="LIST_ALL",B.Count="LIST_COUNT",B.ValueOfList="LIST_VALUE",B.Invert="LIST_INVERT",B._nativeFunctions=null;class M extends V{constructor(t,e){if(super(),this.isInt=()=>"int"==this.subtype,this.isFloat=()=>"float"==this.subtype,this.isBool=()=>"bool"==this.subtype,this.GenerateIntoContainer=t=>{this.isInt()?t.AddContent(new N(this.value)):this.isFloat()?t.AddContent(new O(this.value)):this.isBool()&&t.AddContent(new P(this.value))},this.toString=()=>String(this.value),("number"!=typeof t||Number.isNaN(t))&&"boolean"!=typeof t)throw new Error("Unexpected object type in NumberExpression.");this.value=t,this.subtype=e}get typeName(){return"Number"}Equals(t){const e=s(t,M);return!!e&&(e.subtype==this.subtype&&e.value==this.value)}}class G extends V{get nativeNameForOp(){return"-"===this.op?"_":"not"===this.op?"!":this.op}constructor(t,e){super(),this.op=e,this.GenerateIntoContainer=t=>{this.innerExpression.GenerateIntoContainer(t),t.AddContent(B.CallWithName(this.nativeNameForOp))},this.toString=()=>this.nativeNameForOp+this.innerExpression,this.innerExpression=this.AddContent(t)}get typeName(){return"UnaryExpression"}}G.WithInner=(t,e)=>{const n=s(t,M);if(n){if("-"===e){if(n.isInt())return new M(-n.value,"int");if(n.isFloat())return new M(-n.value,"float")}else if("!"==e||"not"==e){if(n.isInt())return new M(0==n.value,"bool");if(n.isFloat())return new M(0==n.value,"bool");if(n.isBool())return new M(!n.value,"bool")}throw new Error("Unexpected operation or number type")}return new G(t,e)};class $ extends V{constructor(t,e,n){super(),this.opName=n,this.GenerateIntoContainer=t=>{this.leftExpression.GenerateIntoContainer(t),this.rightExpression.GenerateIntoContainer(t),this.opName=this.NativeNameForOp(this.opName),t.AddContent(B.CallWithName(this.opName))},this.NativeNameForOp=t=>"and"===t?"&&":"or"===t?"||":"mod"===t?"%":"has"===t?"?":"hasnt"===t?"!?":t,this.toString=()=>`(${this.leftExpression} ${this.opName} ${this.rightExpression})`,this.leftExpression=this.AddContent(t),this.rightExpression=this.AddContent(e),this.opName=n}get typeName(){return"BinaryExpression"}ResolveReferences(t){if(super.ResolveReferences(t),"?"===this.NativeNameForOp(this.opName)){const t=s(this.leftExpression,G);null===t||"not"!==t.op&&"!"!==t.op||this.Error(`Using 'not' or '!' here negates '${t.innerExpression}' rather than the result of the '?' or 'has' operator. You need to add parentheses around the (A ? B) expression.`)}}}class q{constructor(t){this.set=new Set,this.Add=t=>this.set.add(t),this.AddRange=(t,e)=>{for(let n=t.charCodeAt(0);n<=e.charCodeAt(0);++n)this.Add(String.fromCharCode(n));return this},this.AddCharacters=t=>{if("string"==typeof t||Array.isArray(t))for(const e of t)this.Add(e);else for(const e of t.set)this.Add(e);return this},t&&this.AddCharacters(t)}}q.FromRange=(t,e)=>(new q).AddRange(t,e);class U{constructor(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];if(this._start=t,this._end=e,this._correspondingCharSet=new q,this._excludes=new Set,this.ToCharacterSet=()=>{if(0===this._correspondingCharSet.set.size)for(let t,e=this.start.charCodeAt(0);e<=this.end.charCodeAt(0);e+=1)t=String.fromCharCode(e),this._excludes.has(t)||this._correspondingCharSet.AddCharacters(t);return this._correspondingCharSet},n instanceof q)this._excludes=n.set;else for(const t of n)this._excludes.add(t)}get start(){return this._start}get end(){return this._end}}U.Define=function(t,e){return new U(t,e,arguments.length>2&&void 0!==arguments[2]?arguments[2]:[])};class K extends y{constructor(){let t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];super(),this._pathOnChoice=null,this.hasCondition=!1,this.hasStartContent=!1,this.hasChoiceOnlyContent=!1,this.isInvisibleDefault=!1,this.onceOnly=!0,this.onceOnly=t}get pathOnChoice(){if(null!=this._pathOnChoice&&this._pathOnChoice.isRelative){let t=this.choiceTarget;t&&(this._pathOnChoice=t.path)}return this._pathOnChoice}set pathOnChoice(t){this._pathOnChoice=t}get choiceTarget(){return null===this._pathOnChoice?S("ChoicePoint._pathOnChoice"):this.ResolvePath(this._pathOnChoice).container}get pathStringOnChoice(){return null===this.pathOnChoice?S("ChoicePoint.pathOnChoice"):this.CompactPathString(this.pathOnChoice)}set pathStringOnChoice(t){this.pathOnChoice=new m(t)}get flags(){let t=0;return this.hasCondition&&(t|=1),this.hasStartContent&&(t|=2),this.hasChoiceOnlyContent&&(t|=4),this.isInvisibleDefault&&(t|=8),this.onceOnly&&(t|=16),t}set flags(t){this.hasCondition=(1&t)>0,this.hasStartContent=(2&t)>0,this.hasChoiceOnlyContent=(4&t)>0,this.isInvisibleDefault=(8&t)>0,this.onceOnly=(16&t)>0}toString(){if(null===this.pathOnChoice)return S("ChoicePoint.pathOnChoice");return"Choice: -> "+this.pathOnChoice.toString()}}!function(t){t[t.Tunnel=0]="Tunnel",t[t.Function=1]="Function",t[t.FunctionEvaluationFromGame=2]="FunctionEvaluationFromGame"}(v||(v={}));class H{constructor(){this.container=null,this.index=-1,2===arguments.length&&(this.container=arguments[0],this.index=arguments[1])}Resolve(){return this.index<0?this.container:null==this.container?null:0==this.container.content.length?this.container:this.index>=this.container.content.length?null:this.container.content[this.index]}get isNull(){return null==this.container}get path(){return this.isNull?null:this.index>=0?this.container.path.PathByAppendingComponent(new m.Component(this.index)):this.container.path}toString(){return this.container?"Ink Pointer -> "+this.container.path.toString()+" -- index "+this.index:"Ink Pointer (null)"}copy(){return new H(this.container,this.index)}static StartOf(t){return new H(t,0)}static get Null(){return new H(null,-1)}}let J=class t extends y{get targetPath(){if(null!=this._targetPath&&this._targetPath.isRelative){let t=this.targetPointer.Resolve();t&&(this._targetPath=t.path)}return this._targetPath}set targetPath(t){this._targetPath=t,this._targetPointer=H.Null}get targetPointer(){if(this._targetPointer.isNull){let t=this.ResolvePath(this._targetPath).obj;if(null===this._targetPath)return S("this._targetPath");if(null===this._targetPath.lastComponent)return S("this._targetPath.lastComponent");if(this._targetPath.lastComponent.isIndex){if(null===t)return S("targetObj");this._targetPointer.container=t.parent instanceof L?t.parent:null,this._targetPointer.index=this._targetPath.lastComponent.index}else this._targetPointer=H.StartOf(t instanceof L?t:null)}return this._targetPointer.copy()}get targetPathString(){return null==this.targetPath?null:this.CompactPathString(this.targetPath)}set targetPathString(t){this.targetPath=null==t?null:new m(t)}get hasVariableTarget(){return null!=this.variableDivertName}constructor(t){super(),this._targetPath=null,this._targetPointer=H.Null,this.variableDivertName=null,this.pushesToStack=!1,this.stackPushType=0,this.isExternal=!1,this.externalArgs=0,this.isConditional=!1,this.pushesToStack=!1,void 0!==t&&(this.pushesToStack=!0,this.stackPushType=t)}Equals(e){let n=e;return n instanceof t&&this.hasVariableTarget==n.hasVariableTarget&&(this.hasVariableTarget?this.variableDivertName==n.variableDivertName:null===this.targetPath?S("this.targetPath"):this.targetPath.Equals(n.targetPath))}toString(){if(this.hasVariableTarget)return"Divert(variable: "+this.variableDivertName+")";if(null==this.targetPath)return"Divert(null)";{let t=new b,e=this.targetPath.toString();return t.Append("Divert"),this.isConditional&&t.Append("?"),this.pushesToStack&&(this.stackPushType==v.Function?t.Append(" function"):t.Append(" tunnel")),t.Append(" -> "),t.Append(this.targetPathString),t.Append(" ("),t.Append(e),t.Append(")"),t.toString()}}};var z;!function(t){t[t.Knot=0]="Knot",t[t.List=1]="List",t[t.ListItem=2]="ListItem",t[t.Var=3]="Var",t[t.SubFlowAndWeave=4]="SubFlowAndWeave",t[t.Arg=5]="Arg",t[t.Temp=6]="Temp"}(z||(z={}));let X=class extends y{constructor(t,e){super(),this.variableName=t||null,this.isNewDeclaration=!!e,this.isGlobal=!1}toString(){return"VarAssign to "+this.variableName}},Y=class extends d{get runtimeChoice(){if(!this._runtimeChoice)throw new Error;return this._runtimeChoice}get name(){var t;return(null===(t=this.identifier)||void 0===t?void 0:t.name)||null}get condition(){return this._condition}set condition(t){this._condition=t,t&&this.AddContent(t)}get runtimeContainer(){return this._innerContentContainer}get innerContentContainer(){return this._innerContentContainer}get containerForCounting(){return this._innerContentContainer}get runtimePath(){if(!this.innerContentContainer||!this.innerContentContainer.path)throw new Error;return this.innerContentContainer.path}constructor(t,e,n){super(),this._condition=null,this._innerContentContainer=null,this._outerContainer=null,this._runtimeChoice=null,this._returnToR1=null,this._returnToR2=null,this._r1Label=null,this._r2Label=null,this._divertToStartContentOuter=null,this._divertToStartContentInner=null,this._startContentRuntimeContainer=null,this.isInvisibleDefault=!1,this.hasWeaveStyleInlineBrackets=!1,this.GenerateRuntimeObject=()=>{if(this._outerContainer=new L,this._runtimeChoice=new K(this.onceOnly),this._runtimeChoice.isInvisibleDefault=this.isInvisibleDefault,(this.startContent||this.choiceOnlyContent||this.condition)&&this._outerContainer.AddContent(D.EvalStart()),this.startContent){this._returnToR1=new F,this._outerContainer.AddContent(this._returnToR1);const t=new X("$r",!0);this._outerContainer.AddContent(t),this._outerContainer.AddContent(D.BeginString()),this._divertToStartContentOuter=new J,this._outerContainer.AddContent(this._divertToStartContentOuter),this._startContentRuntimeContainer=this.startContent.GenerateRuntimeObject(),this._startContentRuntimeContainer.name="s";const e=new J;e.variableDivertName="$r",this._startContentRuntimeContainer.AddContent(e),this._outerContainer.AddToNamedContentOnly(this._startContentRuntimeContainer),this._r1Label=new L,this._r1Label.name="$r1",this._outerContainer.AddContent(this._r1Label),this._outerContainer.AddContent(D.EndString()),this._runtimeChoice.hasStartContent=!0}if(this.choiceOnlyContent){this._outerContainer.AddContent(D.BeginString());const t=this.choiceOnlyContent.GenerateRuntimeObject();this._outerContainer.AddContentsOfContainer(t),this._outerContainer.AddContent(D.EndString()),this._runtimeChoice.hasChoiceOnlyContent=!0}if(this.condition&&(this.condition.GenerateIntoContainer(this._outerContainer),this._runtimeChoice.hasCondition=!0),(this.startContent||this.choiceOnlyContent||this.condition)&&this._outerContainer.AddContent(D.EvalEnd()),this._outerContainer.AddContent(this._runtimeChoice),this._innerContentContainer=new L,this.startContent){this._returnToR2=new F,this._innerContentContainer.AddContent(D.EvalStart()),this._innerContentContainer.AddContent(this._returnToR2),this._innerContentContainer.AddContent(D.EvalEnd());const t=new X("$r",!0);this._innerContentContainer.AddContent(t),this._divertToStartContentInner=new J,this._innerContentContainer.AddContent(this._divertToStartContentInner),this._r2Label=new L,this._r2Label.name="$r2",this._innerContentContainer.AddContent(this._r2Label)}if(this.innerContent){const t=this.innerContent.GenerateRuntimeObject();this._innerContentContainer.AddContentsOfContainer(t)}return this.story.countAllVisits&&(this._innerContentContainer.visitsShouldBeCounted=!0),this._innerContentContainer.countingAtStartOnly=!0,this._outerContainer},this.toString=()=>null!==this.choiceOnlyContent?`* ${this.startContent}[${this.choiceOnlyContent}]...`:`* ${this.startContent}...`,this.startContent=t,this.choiceOnlyContent=e,this.innerContent=n,this.indentationDepth=1,t&&this.AddContent(this.startContent),e&&this.AddContent(this.choiceOnlyContent),n&&this.AddContent(this.innerContent),this.onceOnly=!0}get typeName(){return"Choice"}ResolveReferences(t){var e;if(this._innerContentContainer&&(this.runtimeChoice.pathOnChoice=this._innerContentContainer.path,this.onceOnly&&(this._innerContentContainer.visitsShouldBeCounted=!0)),this._returnToR1){if(!this._r1Label)throw new Error;this._returnToR1.targetPath=this._r1Label.path}if(this._returnToR2){if(!this._r2Label)throw new Error;this._returnToR2.targetPath=this._r2Label.path}if(this._divertToStartContentOuter){if(!this._startContentRuntimeContainer)throw new Error;this._divertToStartContentOuter.targetPath=this._startContentRuntimeContainer.path}if(this._divertToStartContentInner){if(!this._startContentRuntimeContainer)throw new Error;this._divertToStartContentInner.targetPath=this._startContentRuntimeContainer.path}super.ResolveReferences(t),this.identifier&&((null===(e=this.identifier)||void 0===e?void 0:e.name)||"").length>0&&t.CheckForNamingCollisions(this,this.identifier,z.SubFlowAndWeave)}};class Z{constructor(){this.characterIndex=0,this.characterInLineIndex=0,this.lineIndex=0,this.reportedErrorInScope=!1,this.uniqueId=0,this.customFlags=0,this.CopyFrom=t=>{Z._uniqueIdCounter++,this.uniqueId=Z._uniqueIdCounter,this.characterIndex=t.characterIndex,this.characterInLineIndex=t.characterInLineIndex,this.lineIndex=t.lineIndex,this.customFlags=t.customFlags,this.reportedErrorInScope=!1},this.SquashFrom=t=>{this.characterIndex=t.characterIndex,this.characterInLineIndex=t.characterInLineIndex,this.lineIndex=t.lineIndex,this.reportedErrorInScope=t.reportedErrorInScope,this.customFlags=t.customFlags}}}Z._uniqueIdCounter=1e3;class Q{get currentElement(){return this._stack[this._numElements-1]}get lineIndex(){return this.currentElement.lineIndex}set lineIndex(t){this.currentElement.lineIndex=t}get characterIndex(){return this.currentElement.characterIndex}set characterIndex(t){this.currentElement.characterIndex=t}get characterInLineIndex(){return this.currentElement.characterInLineIndex}set characterInLineIndex(t){this.currentElement.characterInLineIndex=t}get customFlags(){return this.currentElement.customFlags}set customFlags(t){this.currentElement.customFlags=t}get errorReportedAlreadyInScope(){return this.currentElement.reportedErrorInScope}get stackHeight(){return this._numElements}constructor(){this._stack=[],this._numElements=0,this.StringParserState=()=>{this._stack=new Array(200);for(let t=0;t<200;++t)this._stack[t]=new Z;this._numElements=1},this.Push=()=>{if(this._numElements>=this._stack.length&&this._numElements>0)throw new Error("Stack overflow in parser state.");const t=this._stack[this._numElements-1],e=this._stack[this._numElements];return this._numElements++,e.CopyFrom(t),e.uniqueId},this.Pop=t=>{if(1==this._numElements)throw new Error("Attempting to remove final stack element is illegal! Mismatched Begin/Succceed/Fail?");if(this.currentElement.uniqueId!=t)throw new Error("Mismatched rule IDs while Poping - do you have mismatched Begin/Succeed/Fail?");this._numElements-=1},this.Peek=t=>{if(this.currentElement.uniqueId!=t)throw new Error("Mismatched rule IDs while Peeking - do you have mismatched Begin/Succeed/Fail?");return this._stack[this._numElements-1]},this.PeekPenultimate=()=>this._numElements>=2?this._stack[this._numElements-2]:null,this.Squash=()=>{if(this._numElements<2)throw new Error("Attempting to remove final stack element is illegal! Mismatched Begin/Succceed/Fail?");const t=this._stack[this._numElements-2],e=this._stack[this._numElements-1];t.SquashFrom(e),this._numElements-=1},this.NoteErrorReported=()=>{for(const t of this._stack)t.reportedErrorInScope=!0};for(let t=0;t<200;t++)this._stack[t]=new Z;this._numElements=1}}const tt=Symbol("ParseSuccessStruct");class et{constructor(t){var e=this;this.ParseRule=null,this.errorHandler=null,this.hadError=!1,this.BeginRule=()=>this.state.Push(),this.FailRule=t=>(this.state.Pop(t),null),this.CancelRule=t=>{this.state.Pop(t)},this.SucceedRule=function(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const i=e.state.Peek(t),r=e.state.PeekPenultimate();e.RuleDidSucceed&&e.RuleDidSucceed(n,r,i),e.state.Squash();let s=n;return null===s&&(s=et.ParseSuccess),s},this.Expect=function(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=e.ParseObject(t);if(null===r){let s;null===n&&(n=t.name);const a=e.LineRemainder();s=null===a||0===a.length?"end of line":`'${a}'`,e.Error(`Expected ${n} but saw ${s}`),null!==i&&(r=i())}return r},this.Error=function(t){let n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];e.ErrorOnLine(t,e.lineIndex+1,n)},this.ErrorWithParsedObject=function(t,n){let i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];e.ErrorOnLine(t,n.debugMetadata?n.debugMetadata.startLineNumber:-1,i)},this.ErrorOnLine=(t,e,n)=>{if(!this.state.errorReportedAlreadyInScope){const i=n?"Warning":"Error";if(!this.errorHandler)throw new Error(`${i} on line ${e}: ${t}`);this.errorHandler(t,this.index,e-1,n),this.state.NoteErrorReported()}n||(this.hadError=!0)},this.Warning=t=>this.Error(t,!0),this.LineRemainder=()=>this.Peek((()=>this.ParseUntilCharactersFromString("\n\r"))),this.SetFlag=(t,e)=>{e?this.state.customFlags|=t:this.state.customFlags&=~t},this.GetFlag=t=>Boolean(this.state.customFlags&t),this.ParseObject=t=>{const e=this.BeginRule(),n=this.state.stackHeight,i=t();if(n!==this.state.stackHeight)throw new Error("Mismatched Begin/Fail/Succeed rules");return null===i?this.FailRule(e):(this.SucceedRule(e,i),i)},this.Parse=t=>{const e=this.BeginRule(),n=t();return null===n?(this.FailRule(e),null):(this.SucceedRule(e,n),n)},this.OneOf=t=>{for(const e of t){const t=this.ParseObject(e);if(null!==t)return t}return null},this.OneOrMore=t=>{const e=[];let n=null;do{n=this.ParseObject(t),null!==n&&e.push(n)}while(null!==n);return e.length>0?e:null},this.Optional=t=>()=>{const e=this.ParseObject(t);return null===e?et.ParseSuccess:e},this.Exclude=t=>()=>this.ParseObject(t)&&et.ParseSuccess,this.OptionalExclude=t=>()=>(this.ParseObject(t),et.ParseSuccess),this.String=t=>()=>this.ParseString(t),this.TryAddResultToList=function(t,e){let n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];if(t!==et.ParseSuccess){if(n&&Array.isArray(t)){const n=t;if(null!==n){for(const t of n)e.push(t);return}}e.push(t)}},this.Interleave=function(t,n){let i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=!(arguments.length>3&&void 0!==arguments[3])||arguments[3];const s=e.BeginRule(),a=[],o=e.ParseObject(t);if(null===o)return e.FailRule(s);e.TryAddResultToList(o,a,r);let l=null,h=null;do{if(null!==i&&null!==e.Peek(i))break;if(l=e.ParseObject(n),null===l)break;if(e.TryAddResultToList(l,a,r),h=null,null!==l){if(h=e.ParseObject(t),null===h)break;e.TryAddResultToList(h,a,r)}}while((null!==l||null!==h)&&(l!==et.ParseSuccess||h!=et.ParseSuccess)&&e.remainingLength>0);return 0===a.length?e.FailRule(s):e.SucceedRule(s,a)},this.ParseString=t=>{if(t.length>this.remainingLength)return null;const e=this.BeginRule();let n=this.index,i=this.characterInLineIndex,r=this.lineIndex,s=!0;for(let e=0;e{if(this.remainingLength>0){const t=this._chars[this.index];return"\n"===t&&(this.lineIndex+=1,this.characterInLineIndex=-1),this.index+=1,this.characterInLineIndex+=1,t}return"0"},this.ParseUntilCharactersFromString=function(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return e.ParseCharactersFromString(t,!1,n)},this.ParseUntilCharactersFromCharSet=function(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return e.ParseCharactersFromCharSet(t,!1,n)},this.ParseCharactersFromString=function(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:-1;const r=new q(t);return"number"==typeof n?e.ParseCharactersFromCharSet(r,!0,n):e.ParseCharactersFromCharSet(r,n,i)},this.ParseCharactersFromCharSet=function(t){let n=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:-1;-1===i&&(i=Number.MAX_SAFE_INTEGER);const r=e.index;let s=e.index,a=e.characterInLineIndex,o=e.lineIndex,l=0;for(;sr?e._chars.slice(r,e.index).join(""):null},this.Peek=t=>{const e=this.BeginRule(),n=t();return this.CancelRule(e),n},this.ParseInt=()=>{const t=this.index,e=this.characterInLineIndex,n=null!==this.ParseString("-");this.ParseCharactersFromString(" \t");const i=this.ParseCharactersFromCharSet(et.numbersCharacterSet);if(null===i)return this.index=t,this.characterInLineIndex=e,null;let r;return Number.isNaN(Number(i))?(this.Error("Failed to read integer value: "+i+". Perhaps it's out of the range of acceptable numbers ink supports? ("+Number.MIN_SAFE_INTEGER+" to "+Number.MAX_SAFE_INTEGER+")"),null):(r=Number(i),n?-r:r)},this.ParseFloat=()=>{const t=this.index,e=this.characterInLineIndex,n=this.ParseInt();if(null!==n&&null!==this.ParseString(".")){const t=this.ParseCharactersFromCharSet(et.numbersCharacterSet);return Number(`${n}.${t}`)}return this.index=t,this.characterInLineIndex=e,null},this.ParseNewline=()=>{const t=this.BeginRule();return this.ParseString("\r"),null===this.ParseString("\n")?this.FailRule(t):this.SucceedRule(t,"\n")};const n=this.PreProcessInputString(t);this.state=new Q,this._chars=t?n.split(""):[],this.inputString=n}get currentCharacter(){return this.index>=0&&this.remainingLength>0?this._chars[this.index]:"0"}PreProcessInputString(t){return t}get endOfInput(){return this.index>=this._chars.length}get remainingString(){return this._chars.slice(this.index,this.index+this.remainingLength).join("")}get remainingLength(){return this._chars.length-this.index}get lineIndex(){return this.state.lineIndex}set lineIndex(t){this.state.lineIndex=t}set characterInLineIndex(t){this.state.characterInLineIndex=t}get characterInLineIndex(){return this.state.characterInLineIndex}get index(){return this.state.characterIndex}set index(t){this.state.characterIndex=t}ParseUntil(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const i=this.BeginRule(),r=new q;null!==e&&(r.set=new Set([...r.set.values(),...e.set.values()])),null!==n&&(r.set=new Set([...r.set.values(),...n.set.values()]));let s="",a=null;for(;;){const n=this.ParseUntilCharactersFromCharSet(r);if(n&&(s+=n),a=this.Peek(t),null!==a)break;{if(this.endOfInput)break;const t=this.currentCharacter;if(null!==e&&e.set.has(t)){s+=t,"\n"===t&&(this.lineIndex+=1,this.characterInLineIndex=-1),this.index+=1,this.characterInLineIndex+=1;continue}break}}return s.length>0?this.SucceedRule(i,String(s)):this.FailRule(i)}}et.ParseSuccess=tt,et.numbersCharacterSet=new q("0123456789");class nt extends et{constructor(){super(...arguments),this._commentOrNewlineStartCharacter=new q("/\r\n"),this._commentBlockEndCharacter=new q("*"),this._newlineCharacters=new q("\n\r"),this.Process=()=>{const t=this.Interleave(this.Optional(this.CommentsAndNewlines),this.Optional(this.MainInk));return null!==t?t.join(""):""},this.MainInk=()=>this.ParseUntil(this.CommentsAndNewlines,this._commentOrNewlineStartCharacter,null),this.CommentsAndNewlines=()=>{let t=this.Interleave(this.Optional(this.ParseNewline),this.Optional(this.ParseSingleComment));return null!==t?t.join(""):null},this.ParseSingleComment=()=>this.OneOf([this.EndOfLineComment,this.BlockComment]),this.EndOfLineComment=()=>null===this.ParseString("//")?null:(this.ParseUntilCharactersFromCharSet(this._newlineCharacters),""),this.BlockComment=()=>{if(null===this.ParseString("/*"))return null;const t=this.lineIndex,e=this.ParseUntil(this.String("*/"),this._commentBlockEndCharacter,null);return this.endOfInput||this.ParseString("*/"),null!=e?"\n".repeat(this.lineIndex-t):null}}PreProcessInputString(t){return t}}class it extends d{constructor(t,e){super(),this.initialCondition=t,this.branches=e,this._reJoinTarget=null,this.GenerateRuntimeObject=()=>{const t=new L;this.initialCondition&&t.AddContent(this.initialCondition.runtimeObject);for(const e of this.branches){const n=e.runtimeObject;t.AddContent(n)}return null===this.initialCondition||null===this.branches[0].ownExpression||this.branches[this.branches.length-1].isElse||t.AddContent(D.PopEvaluatedValue()),this._reJoinTarget=D.NoOp(),t.AddContent(this._reJoinTarget),t},this.initialCondition&&this.AddContent(this.initialCondition),null!==this.branches&&this.AddContent(this.branches)}get typeName(){return"Conditional"}ResolveReferences(t){const e=this._reJoinTarget.path;for(const t of this.branches){if(!t.returnDivert)throw new Error;t.returnDivert.targetPath=e}super.ResolveReferences(t)}}class rt extends d{constructor(t){super(),this.text=t,this.GenerateRuntimeObject=()=>new I(this.text),this.toString=()=>this.text}get typeName(){return"Text"}}class st extends d{get constantName(){var t;return null===(t=this.constantIdentifier)||void 0===t?void 0:t.name}get expression(){if(!this._expression)throw new Error;return this._expression}constructor(t,e){super(),this._expression=null,this.GenerateRuntimeObject=()=>null,this.constantIdentifier=t,e&&(this._expression=this.AddContent(e))}get typeName(){return"CONST"}ResolveReferences(t){super.ResolveReferences(t),t.CheckForNamingCollisions(this,this.constantIdentifier,z.Var)}}var at;!function(t){t[t.Story=0]="Story",t[t.Knot=1]="Knot",t[t.Stitch=2]="Stitch",t[t.WeavePoint=3]="WeavePoint"}(at||(at={}));class ot extends d{get name(){var t;return(null===(t=this.identifier)||void 0===t?void 0:t.name)||null}get runtimeContainer(){return this.runtimeObject}constructor(t,e){super(),this.indentationDepth=e,this.GenerateRuntimeObject=()=>{const t=new L;if(t.name=this.name,this.story.countAllVisits&&(t.visitsShouldBeCounted=!0),t.countingAtStartOnly=!0,this.content)for(const e of this.content)t.AddContent(e.runtimeObject);return t},this.toString=()=>{var t,e;return"- "+((null===(t=this.identifier)||void 0===t?void 0:t.name)?"("+(null===(e=this.identifier)||void 0===e?void 0:e.name)+")":"gather")},t&&(this.identifier=t)}get typeName(){return"Gather"}ResolveReferences(t){super.ResolveReferences(t),this.identifier&&(this.identifier.name||"").length>0&&t.CheckForNamingCollisions(this,this.identifier,z.SubFlowAndWeave)}}class lt{get baseTargetLevel(){return this.baseLevelIsAmbiguous?at.Story:this._baseTargetLevel}get baseLevelIsAmbiguous(){return!this._baseTargetLevel}get firstComponent(){return null!=this.components&&this.components.length?this.components[0].name:null}get numberOfComponents(){return this.components?this.components.length:0}get dotSeparatedComponents(){return null==this._dotSeparatedComponents&&(this._dotSeparatedComponents=(this.components?this.components:[]).map((t=>t.name)).filter(c).join(".")),this._dotSeparatedComponents}constructor(t,e){this._dotSeparatedComponents=null,this.toString=()=>null===this.components||0===this.components.length?this.baseTargetLevel===at.WeavePoint?"-> ":"":`-> ${this.dotSeparatedComponents}`,this.ResolveFromContext=t=>{if(null==this.components||0==this.components.length)return null;let e=this.ResolveBaseTarget(t);return null===e?null:this.components.length>1?this.ResolveTailComponents(e):e},this.ResolveBaseTarget=t=>{const e=this.firstComponent;let n=t;for(;n;){const i=n===t,r=this.GetChildFromContext(n,e,null,i);if(r)return r;n=n.parent}return null},this.ResolveTailComponents=t=>{let e=t;if(!this.components)return null;for(let t=1;t3&&void 0!==arguments[3]&&arguments[3];const r=null===n,a=s(t,Pt);if(e&&null!==a&&(r||n===at.WeavePoint))return a.WeavePointNamed(e);let o=s(t,dt);if(e&&null!==o){const t=i||o.flowLevel===at.Knot;return o.ContentWithNameAtLevel(e,n,t)}return null},Object.values(at).includes(t)?(this._baseTargetLevel=t,this.components=e||[]):Array.isArray(t)?(this._baseTargetLevel=null,this.components=t||[]):(this._baseTargetLevel=null,this.components=[t])}get typeName(){return"Path"}}class ht extends d{constructor(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;super(),this.returnedExpression=null,this.GenerateRuntimeObject=()=>{const t=new L;return this.returnedExpression?t.AddContent(this.returnedExpression.runtimeObject):(t.AddContent(D.EvalStart()),t.AddContent(new j),t.AddContent(D.EvalEnd())),t.AddContent(D.PopFunction()),t},t&&(this.returnedExpression=this.AddContent(t))}get typeName(){return"ReturnType"}}function ut(t){let e=t.parent;for(;e;){if(e.hasOwnProperty("iamFlowbase")&&e.iamFlowbase())return e;e=e.parent}return null}class ct{constructor(t){this.debugMetadata=null,this.toString=()=>this.name||"undefined identifer",this.name=t}get typeName(){return"Identifier"}static Done(){return new ct("DONE")}}class dt extends d{get hasParameters(){return null!==this.args&&this.args.length>0}get subFlowsByName(){return this._subFlowsByName}get typeName(){return this.isFunction?"Function":String(this.flowLevel)}get name(){var t;return(null===(t=this.identifier)||void 0===t?void 0:t.name)||null}constructor(t){var e;let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=arguments.length>4&&void 0!==arguments[4]&&arguments[4];super(),e=this,this.isFunction=r,this._rootWeave=null,this._subFlowsByName=new Map,this._startingSubFlowDivert=null,this._startingSubFlowRuntime=null,this._firstChildFlow=null,this.variableDeclarations=new Map,this.identifier=null,this.args=null,this.iamFlowbase=()=>!0,this.SplitWeaveAndSubFlowContent=(t,e)=>{var n,i;const r=[],a=[];this._subFlowsByName=new Map;for(const e of t){const t=s(e,dt);t?(null===this._firstChildFlow&&(this._firstChildFlow=t),a.push(e),(null===(n=t.identifier)||void 0===n?void 0:n.name)&&this._subFlowsByName.set(null===(i=t.identifier)||void 0===i?void 0:i.name,t)):r.push(e)}e&&r.push(new ot(null,1),new St(new lt(ct.Done())));const o=[];return r.length>0&&(this._rootWeave=new Pt(r,0),o.push(this._rootWeave)),a.length>0&&o.push(...a),o},this.ResolveVariableWithName=(t,e)=>{var n;const i={},r=null===e?this:ut(e);if(r){if(null!==r.args)for(const e of r.args)if((null===(n=e.identifier)||void 0===n?void 0:n.name)===t)return i.found=!0,i.isArgument=!0,i.ownerFlow=r,i;if(r!==this.story&&r.variableDeclarations.has(t))return i.found=!0,i.ownerFlow=r,i.isTemporary=!0,i}return this.story.variableDeclarations.has(t)?(i.found=!0,i.ownerFlow=this.story,i.isGlobal=!0,i):(i.found=!1,i)},this.AddNewVariableDeclaration=t=>{const e=t.variableName;if(this.variableDeclarations.has(e)){const n=this.variableDeclarations.get(e);let i="";return n.debugMetadata&&(i=` (${n.debugMetadata})`),void this.Error(`found declaration variable '${e}' that was already declared${i}`,t,!1)}this.variableDeclarations.set(t.variableName,t)},this.ResolveWeavePointNaming=()=>{this._rootWeave&&this._rootWeave.ResolveWeavePointNaming();for(const[,t]of this._subFlowsByName)t.hasOwnProperty("ResolveWeavePointNaming")&&t.ResolveWeavePointNaming()},this.GenerateRuntimeObject=()=>{var t;let e=null;this.isFunction?this.CheckForDisallowedFunctionFlowControl():this.flowLevel!==at.Knot&&this.flowLevel!==at.Stitch||(e=this.Find(ht)(),null!==e&&this.Error(`Return statements can only be used in knots that are declared as functions: == function ${this.identifier} ==`,e));const n=new L;n.name=null===(t=this.identifier)||void 0===t?void 0:t.name,this.story.countAllVisits&&(n.visitsShouldBeCounted=!0),this.GenerateArgumentVariableAssignments(n);let i=0;for(;null!==this.content&&i{var e;if(null!==this.args&&0!==this.args.length)for(let n=this.args.length-1;n>=0;--n){const i=(null===(e=this.args[n].identifier)||void 0===e?void 0:e.name)||null,r=new X(i,!0);t.AddContent(r)}},this.ContentWithNameAtLevel=function(t){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];var r;if((n===e.flowLevel||null===n)&&t===(null===(r=e.identifier)||void 0===r?void 0:r.name))return e;if(n===at.WeavePoint||null===n){let r=null;if(e._rootWeave&&(r=e._rootWeave.WeavePointNamed(t),r))return r;if(n===at.WeavePoint)return i?e.DeepSearchForAnyLevelContent(t):null}if(null!==n&&n{const e=this.ContentWithNameAtLevel(t,at.WeavePoint,!1);if(e)return e;for(const[,e]of this._subFlowsByName){const n=e.ContentWithNameAtLevel(t,null,!0);if(n)return n}return null},this.CheckForDisallowedFunctionFlowControl=()=>{this.flowLevel!==at.Knot&&this.Error("Functions cannot be stitches - i.e. they should be defined as '== function myFunc ==' rather than internal to another knot.");for(const[t,e]of this._subFlowsByName)this.Error(`Functions may not contain stitches, but saw '${t}' within the function '${this.identifier}'`,e);if(!this._rootWeave)throw new Error;const t=this._rootWeave.FindAll(St)();for(const e of t)e.isFunctionCall||e.parent instanceof Ct||this.Error(`Functions may not contain diverts, but saw '${e}'`,e);const e=this._rootWeave.FindAll(Y)();for(const t of e)this.Error(`Functions may not contain choices, but saw '${t}'`,t)},this.WarningInTermination=t=>{let e="Apparent loose end exists where the flow runs out. Do you need a '-> DONE' statement, choice or divert?";t.parent===this._rootWeave&&this._firstChildFlow&&(e=`${e} Note that if you intend to enter '${this._firstChildFlow.identifier}' next, you need to divert to it explicitly.`);const n=s(t,St);n&&n.isTunnel&&(e+=` When final tunnel to '${n.target} ->' returns it won't have anywhere to go.`),this.Warning(e,t)},this.toString=()=>`${this.typeName} '${this.identifier}'`,this.identifier=t,this.args=i,null===n&&(n=[]),this.PreProcessTopLevelObjects(n),n=this.SplitWeaveAndSubFlowContent(n,"Story"==this.GetType()&&!a),this.AddContent(n)}PreProcessTopLevelObjects(t){}ResolveReferences(t){var e,n;if(this._startingSubFlowDivert){if(!this._startingSubFlowRuntime)throw new Error;this._startingSubFlowDivert.targetPath=this._startingSubFlowRuntime.path}if(super.ResolveReferences(t),null!==this.args){for(const e of this.args)t.CheckForNamingCollisions(this,e.identifier,z.Arg,"argument");for(let t=0;t{for(let t=this.content.length-1;t>=0;--t){const e=s(this.content[t],rt);if(null===e)break;if(e.text=e.text.replace(new RegExp(/[ \t]/g),""),0!==e.text.length)break;this.content.splice(t,1)}},this.GenerateRuntimeObject=()=>{const t=new L;if(null!==this.content)for(const e of this.content){const n=e.runtimeObject;n&&t.AddContent(n)}return this.dontFlatten&&this.story.DontFlattenContainer(t),t},this.toString=()=>`ContentList(${this.content.join(", ")})`,t&&this.AddContent(t);for(var e=arguments.length,n=new Array(e>1?e-1:0),i=1;i0&&void 0!==arguments[0]?arguments[0]:null;super(),this.pathForCount=null,this.name=t}toString(){if(null!=this.name)return"var("+this.name+")";return"read_count("+this.pathStringForCount+")"}};class ft extends V{get name(){return this.path.join(".")}get path(){return this.pathIdentifiers.map((t=>t.name)).filter(c)}get identifier(){if(!this.pathIdentifiers||0==this.pathIdentifiers.length)return null;const t=this.path.join(".");return new ct(t)}get runtimeVarRef(){return this._runtimeVarRef}constructor(t){super(),this.pathIdentifiers=t,this._runtimeVarRef=null,this.isConstantReference=!1,this.isListItemReference=!1,this.GenerateIntoContainer=t=>{let e=this.story.constants.get(this.name);if(e)return e.GenerateConstantIntoContainer(t),void(this.isConstantReference=!0);if(this._runtimeVarRef=new mt(this.name),1===this.path.length||2===this.path.length){let t="",e="";1===this.path.length?t=this.path[0]:(e=this.path[0],t=this.path[1]);this.story.ResolveListItem(e,t,this)&&(this.isListItemReference=!0)}t.AddContent(this._runtimeVarRef)},this.toString=()=>`{${this.path.join(".")}}`}get typeName(){return"ref"}ResolveReferences(t){if(super.ResolveReferences(t),this.isConstantReference||this.isListItemReference)return;const e=new lt(this.pathIdentifiers),n=e.ResolveFromContext(this);if(n){if(!n.containerForCounting)throw new Error;if(n.containerForCounting.visitsShouldBeCounted=!0,null===this._runtimeVarRef)return;this._runtimeVarRef.pathForCount=n.runtimePath,this._runtimeVarRef.name=null;let t=s(n,dt);t&&t.isFunction&&(this.parent instanceof Pt||this.parent instanceof pt||this.parent instanceof dt)&&this.Warning(`'${t.identifier}' being used as read count rather than being called as function. Perhaps you intended to write ${t.identifier}()`)}else{if(this.path.length>1){let t=`Could not find target for read count: ${e}`;return this.path.length<=2&&(t+=`, or couldn't find list item with the name ${this.path.join(",")}`),void this.Error(t)}t.ResolveVariableWithName(this.name,this).found||this.Error(`Unresolved variable: ${this.name}`,this)}}}class gt extends V{get proxyDivert(){return this._proxyDivert}get name(){return this._proxyDivert.target.firstComponent||""}get args(){return this._proxyDivert.args}get runtimeDivert(){return this._proxyDivert.runtimeDivert}get isChoiceCount(){return"CHOICE_COUNT"===this.name}get isTurns(){return"TURNS"===this.name}get isTurnsSince(){return"TURNS_SINCE"===this.name}get isRandom(){return"RANDOM"===this.name}get isSeedRandom(){return"SEED_RANDOM"===this.name}get isListRange(){return"LIST_RANGE"===this.name}get isListRandom(){return"LIST_RANDOM"===this.name}get isReadCount(){return"READ_COUNT"===this.name}constructor(t,e){super(),this._divertTargetToCount=null,this._variableReferenceToCount=null,this.shouldPopReturnedValue=!1,this.GenerateIntoContainer=t=>{const e=this.story.ResolveList(this.name);let n=!1;if(this.isChoiceCount)this.args.length>0&&this.Error("The CHOICE_COUNT() function shouldn't take any arguments"),t.AddContent(D.ChoiceCount());else if(this.isTurns)this.args.length>0&&this.Error("The TURNS() function shouldn't take any arguments"),t.AddContent(D.Turns());else if(this.isTurnsSince||this.isReadCount){const e=s(this.args[0],Ct),n=s(this.args[0],ft);if(1!==this.args.length||null===e&&null===n)return void this.Error(`The ${this.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)`);e?(this._divertTargetToCount=e,this.AddContent(this._divertTargetToCount),this._divertTargetToCount.GenerateIntoContainer(t)):n&&(this._variableReferenceToCount=n,this.AddContent(this._variableReferenceToCount),this._variableReferenceToCount.GenerateIntoContainer(t)),this.isTurnsSince?t.AddContent(D.TurnsSince()):t.AddContent(D.ReadCount())}else if(this.isRandom){2!==this.args.length&&this.Error("RANDOM should take 2 parameters: a minimum and a maximum integer");for(let e=0;e1&&(t+="s"),this.Error(t)}for(let e=0;e1&&this.Error("Can currently only construct a list from one integer (or an empty list from a given list definition)"),1===this.args.length)t.AddContent(new I(this.name)),this.args[0].GenerateIntoContainer(t),t.AddContent(D.ListFromInt());else{const e=new E;e.SetInitialOriginName(this.name),t.AddContent(new R(e))}else t.AddContent(this._proxyDivert.runtimeObject),n=!0;n||this.content.splice(this.content.indexOf(this._proxyDivert),1),this.shouldPopReturnedValue&&t.AddContent(D.PopEvaluatedValue())},this.toString=()=>{const t=this.args.join(", ");return`${this.name}(${t})`},this._proxyDivert=new St(new lt(t),e),this._proxyDivert.isFunctionCall=!0,this.AddContent(this._proxyDivert)}get typeName(){return"FunctionCall"}ResolveReferences(t){if(super.ResolveReferences(t),!this.content.includes(this._proxyDivert)&&null!==this.args)for(const e of this.args)e.ResolveReferences(t);if(this._divertTargetToCount){const t=this._divertTargetToCount.divert,e=null!=t.runtimeDivert.variableDivertName;if(e)return void this.Error(`When getting the TURNS_SINCE() of a variable target, remove the '->' - i.e. it should just be TURNS_SINCE(${t.runtimeDivert.variableDivertName})`);const n=t.targetContent;if(null===n)e||this.Error(`Failed to find target for TURNS_SINCE: '${t.target}'`);else{if(!n.containerForCounting)throw new Error;n.containerForCounting.turnIndexShouldBeCounted=!0}}else if(this._variableReferenceToCount){const t=this._variableReferenceToCount.runtimeVarRef;if(!t)throw new Error;null!==t.pathForCount&&this.Error(`Should be '${gt.name}'(-> '${this._variableReferenceToCount.name}). Usage without the '->' only makes sense for variable targets.`)}}}gt.IsBuiltIn=t=>!!B.CallExistsWithName(t)||("CHOICE_COUNT"===t||"TURNS_SINCE"===t||"TURNS"===t||"RANDOM"===t||"SEED_RANDOM"===t||"LIST_VALUE"===t||"LIST_RANDOM"===t||"READ_COUNT"===t);class vt extends V{get subExpressions(){return this.content}constructor(t){super(),this.GenerateIntoContainer=t=>{let e=!0;for(const n of this.subExpressions)n.GenerateIntoContainer(t),e||t.AddContent(B.CallWithName("&&")),e=!1},this.AddContent(t)}get typeName(){return"MultipleConditionExpression"}}class Ct extends V{get runtimeDivert(){if(!this._runtimeDivert)throw new Error;return this._runtimeDivert}get runtimeDivertTargetValue(){if(!this._runtimeDivertTargetValue)throw new Error;return this._runtimeDivertTargetValue}constructor(t){super(),this._runtimeDivert=null,this._runtimeDivertTargetValue=null,this.GenerateIntoContainer=t=>{this.divert.GenerateRuntimeObject(),this._runtimeDivert=this.divert.runtimeDivert,this._runtimeDivertTargetValue=new F,t.AddContent(this.runtimeDivertTargetValue)},this.Equals=t=>{const e=s(t,Ct);if(!e||!this.divert.target||!e.divert.target)return!1;return this.divert.target.dotSeparatedComponents===e.divert.target.dotSeparatedComponents},this.divert=this.AddContent(t)}get typeName(){return"DivertTarget"}ResolveReferences(t){if(super.ResolveReferences(t),this.divert.isDone||this.divert.isEnd)return void this.Error("Can't use -> DONE or -> END as variable divert targets",this);let e=this;for(;e&&e instanceof V;){let t=!1,n=!1;const i=e.parent;if(i instanceof $){const e=i;"=="!==e.opName&&"!="!==e.opName?t=!0:(e.leftExpression instanceof Ct||e.leftExpression instanceof ft)&&(e.rightExpression instanceof Ct||e.rightExpression instanceof ft)||(t=!0),n=!0}else if(i instanceof gt){const e=i;e.isTurnsSince||e.isReadCount||(t=!0),n=!0}else(i instanceof V||i instanceof vt||i instanceof Y&&i.condition===e||i instanceof it||i instanceof Nt)&&(t=!0,n=!0);if(t&&this.Error(`Can't use a divert target like that. Did you intend to call '${this.divert.target}' as a function: likeThis(), or check the read count: likeThis, with no arrows?`,this),n)break;e=i}if(this.runtimeDivert.hasVariableTarget){if(!this.divert.target)throw new Error;this.Error(`Since '${this.divert.target.dotSeparatedComponents}' is a variable, it shouldn't be preceded by '->' here.`)}this.runtimeDivert.targetPath&&(this.runtimeDivertTargetValue.targetPath=this.runtimeDivert.targetPath);let n=this.divert.targetContent;if(null!==n){let t=n.containerForCounting;if(null!==t){const e=s(this.parent,gt);e&&e.isTurnsSince||(t.visitsShouldBeCounted=!0),t.turnIndexShouldBeCounted=!0}let e=s(n,dt);if(null!=e&&null!==e.args)for(const t of e.args)t.isByReference&&this.Error(`Can't store a divert target to a knot or function that has by-reference arguments ('${e.identifier}' has 'ref ${t.identifier}').`)}}}class St extends d{get runtimeDivert(){if(!this._runtimeDivert)throw new Error;return this._runtimeDivert}set runtimeDivert(t){this._runtimeDivert=t}get isEnd(){return Boolean(this.target&&"END"===this.target.dotSeparatedComponents)}get isDone(){return Boolean(this.target&&"DONE"===this.target.dotSeparatedComponents)}constructor(t,e){super(),this.args=[],this.target=null,this.targetContent=null,this._runtimeDivert=null,this.isFunctionCall=!1,this.isEmpty=!1,this.isTunnel=!1,this.isThread=!1,this.GenerateRuntimeObject=()=>{if(this.isEnd)return D.End();if(this.isDone)return D.Done();this.runtimeDivert=new J,this.ResolveTargetContent(),this.CheckArgumentValidity();const t=null!==this.args&&this.args.length>0;if(t||this.isFunctionCall||this.isTunnel||this.isThread){const e=new L;if(t){this.isFunctionCall||e.AddContent(D.EvalStart());let t=null;this.targetContent&&(t=this.targetContent.args);for(let n=0;nthis.target?this.target.firstComponent:null,this.ResolveTargetContent=()=>{if(!this.isEmpty&&!this.isEnd&&null===this.targetContent){let t=this.PathAsVariableName();if(null!==t){const e=s(ut(this),dt);if(e){const n=e.ResolveVariableWithName(t,this);if(n.found){if(n.isArgument&&n.ownerFlow&&n.ownerFlow.args){let e=n.ownerFlow.args.find((e=>{var n;return(null===(n=e.identifier)||void 0===n?void 0:n.name)==t}));e&&!e.isDivertTarget&&this.Error(`Since '${e.identifier}' is used as a variable divert target (on ${this.debugMetadata}), it should be marked as: -> ${e.identifier}`,n.ownerFlow)}return void(this.runtimeDivert.variableDivertName=t)}}}if(!this.target)throw new Error;this.targetContent=this.target.ResolveFromContext(this)}},this.CheckArgumentValidity=()=>{if(this.isEmpty)return;let t=0;if(null!==this.args&&this.args.length>0&&(t=this.args.length),null===this.targetContent)return;const e=s(this.targetContent,dt);if(!(0!==t||null!==e&&e.hasParameters))return;if(null===e&&t>0)return void this.Error("target needs to be a knot or stitch in order to pass arguments");if(null!==e&&(null===e.args||!e.args&&t>0))return void this.Error(`target (${e.name}) doesn't take parameters`);if(this.parent instanceof Ct)return void(t>0&&this.Error("can't store arguments in a divert target variable"));const n=e.args.length;if(n!==t){let i;return i=0===t?"but there weren't any passed to it":t ${n.identifier} but saw ${i}`,i)}}null!==e||this.Error("Can't call as a function or with arguments unless it's a knot or stitch")},this.CheckExternalArgumentValidity=t=>{const e=this.target?this.target.firstComponent:null,n=t.externals.get(e);if(!n)throw new Error("external not found");const i=n.argumentNames.length;let r=0;this.args&&(r=this.args.length),r!==i&&this.Error(`incorrect number of arguments sent to external function '${e}'. Expected ${i} but got ${r}`)},this.toString=()=>{let t="";return null===this.target?"-> ":(t+=this.target.toString(),this.isTunnel&&(t+=" ->"),this.isFunctionCall&&(t+=" ()"),t)},t&&(this.target=t),e&&(this.args=e,this.AddContent(e))}get typeName(){return"Divert"}ResolveReferences(t){if(this.isEmpty||this.isEnd||this.isDone)return;if(!this.runtimeDivert)throw new Error;this.targetContent&&(this.runtimeDivert.targetPath=this.targetContent.runtimePath),super.ResolveReferences(t);let e=s(this.targetContent,dt);e&&(!e.isFunction&&this.isFunctionCall?super.Error(`${e.identifier} hasn't been marked as a function, but it's being called as one. Do you need to declare the knot as '== function ${e.identifier} =='?`):!e.isFunction||this.isFunctionCall||this.parent instanceof Ct||super.Error(e.identifier+" can't be diverted to. It can only be called as a function since it's been marked as such: '"+e.identifier+"(...)'"));const n=null!==this.targetContent;let i=!1,r=!1;if(!this.target)throw new Error;if(1===this.target.numberOfComponents){if(!this.target.firstComponent)throw new Error;if(i=gt.IsBuiltIn(this.target.firstComponent),r=t.IsExternal(this.target.firstComponent),i||r)return this.isFunctionCall||super.Error(`${this.target.firstComponent} must be called as a function: ~ ${this.target.firstComponent}()`),void(r&&(this.runtimeDivert.isExternal=!0,null!==this.args&&(this.runtimeDivert.externalArgs=this.args.length),this.runtimeDivert.pushesToStack=!1,this.runtimeDivert.targetPath=new m(this.target.firstComponent),this.CheckExternalArgumentValidity(t)))}null==this.runtimeDivert.variableDivertName&&(n||i||r||this.Error(`target not found: '${this.target}'`))}Error(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];e!==this&&e?super.Error(t,e):this.isFunctionCall?super.Error(`Function call ${t}`,e,n):super.Error(`Divert ${t}`,e,n)}}class yt{constructor(t,e){this.divert=t,this.targetRuntimeObj=e}}class bt{constructor(t,e){this.divert=t,this.targetContent=e}}var wt;!function(t){t[t.Stopping=1]="Stopping",t[t.Cycle=2]="Cycle",t[t.Shuffle=4]="Shuffle",t[t.Once=8]="Once"}(wt||(wt={}));class Et extends d{constructor(t,e){super(),this.sequenceType=e,this._sequenceDivertsToResolve=[],this.GenerateRuntimeObject=()=>{const t=new L;t.visitsShouldBeCounted=!0,t.countingAtStartOnly=!0,this._sequenceDivertsToResolve=[],t.AddContent(D.EvalStart()),t.AddContent(D.VisitIndex());const e=(this.sequenceType&wt.Once)>0,n=(this.sequenceType&wt.Cycle)>0,i=(this.sequenceType&wt.Stopping)>0,r=(this.sequenceType&wt.Shuffle)>0;let s=this.sequenceElements.length;if(e&&(s+=1),i||e?(t.AddContent(new N(s-1)),t.AddContent(B.CallWithName("MIN"))):n&&(t.AddContent(new N(this.sequenceElements.length)),t.AddContent(B.CallWithName("%"))),r){const n=D.NoOp();if(e||i){const e=i?this.sequenceElements.length-1:this.sequenceElements.length;t.AddContent(D.Duplicate()),t.AddContent(new N(e)),t.AddContent(B.CallWithName("=="));const r=new J;r.isConditional=!0,t.AddContent(r),this.AddDivertToResolve(r,n)}let r=this.sequenceElements.length;i&&(r-=1),t.AddContent(new N(r)),t.AddContent(D.SequenceShuffleIndex()),(e||i)&&t.AddContent(n)}t.AddContent(D.EvalEnd());const a=D.NoOp();for(let e=0;e{this._sequenceDivertsToResolve.push(new bt(t,e))},this.sequenceType=e,this.sequenceElements=[];for(const e of t){const t=e.content;let n=null;n=null===t||0===t.length?e:new Pt(t),this.sequenceElements.push(n),this.AddContent(n)}}get typeName(){return"Sequence"}ResolveReferences(t){super.ResolveReferences(t);for(const t of this._sequenceDivertsToResolve)t.divert.targetPath=t.targetContent.path}}class _t extends d{constructor(){super(...arguments),this._overrideDivertTarget=null,this._divertAfter=null,this.GenerateRuntimeObject=()=>{const t=new L;if(t.AddContent(D.EvalStart()),this.divertAfter){const e=this.divertAfter.GenerateRuntimeObject(),n=e;if(n){const e=this.divertAfter.args;if(null!==e&&e.length>0){let e=-1,i=-1;for(let t=0;t` -> ${this._divertAfter}`}get divertAfter(){return this._divertAfter}set divertAfter(t){this._divertAfter=t,this._divertAfter&&this.AddContent(this._divertAfter)}get typeName(){return"TunnelOnwards"}ResolveReferences(t){super.ResolveReferences(t),this.divertAfter&&this.divertAfter.targetContent&&(this._overrideDivertTarget.targetPath=this.divertAfter.targetContent.runtimePath)}}let Tt=class{constructor(t,e){this._name=t||"",this._items=null,this._itemNameToValues=e||new Map}get name(){return this._name}get items(){if(null==this._items){this._items=new Map;for(let[t,e]of this._itemNameToValues){let n=new w(this.name,t);this._items.set(n.serialized(),e)}}return this._items}ValueForItem(t){if(!t.itemName)return 0;let e=this._itemNameToValues.get(t.itemName);return void 0!==e?e:0}ContainsItem(t){return!!t.itemName&&(t.originName==this.name&&this._itemNameToValues.has(t.itemName))}ContainsItemWithName(t){return this._itemNameToValues.has(t)}TryGetItemWithValue(t,e){for(let[e,n]of this._itemNameToValues)if(n==t)return{result:new w(this.name,e),exists:!0};return{result:w.Null,exists:!1}}TryGetValueForItem(t,e){if(!t.itemName)return{result:0,exists:!1};let n=this._itemNameToValues.get(t.itemName);return n?{result:n,exists:!0}:{result:0,exists:!1}}};class xt extends d{get typeName(){return"ListDefinition"}get runtimeListDefinition(){var t;const e=new Map;for(const t of this.itemDefinitions)e.has(t.name)?this.Error(`List '${this.identifier}' contains duplicate items called '${t.name}'`):e.set(t.name,t.seriesValue);return new Tt((null===(t=this.identifier)||void 0===t?void 0:t.name)||"",e)}constructor(t){super(),this.itemDefinitions=t,this.identifier=null,this.variableAssignment=null,this._elementsByName=null,this.ItemNamed=t=>{if(null===this._elementsByName){this._elementsByName=new Map;for(const t of this.itemDefinitions)this._elementsByName.set(t.name,t)}return this._elementsByName.get(t)||null},this.GenerateRuntimeObject=()=>{var t,e;const n=new E;for(const e of this.itemDefinitions)if(e.inInitialList){const i=new w((null===(t=this.identifier)||void 0===t?void 0:t.name)||null,e.name||null);n.Add(i,e.seriesValue)}return n.SetInitialOriginName((null===(e=this.identifier)||void 0===e?void 0:e.name)||""),new R(n)};let e=1;for(const t of this.itemDefinitions)null!==t.explicitValue&&(e=t.explicitValue),t.seriesValue=e,e+=1;this.AddContent(t)}ResolveReferences(t){super.ResolveReferences(t),t.CheckForNamingCollisions(this,this.identifier,z.List)}}class At extends d{get variableName(){return this.variableIdentifier.name}get typeName(){return this.isNewTemporaryDeclaration?"temp":this.isGlobalDeclaration?null!==this.listDefinition?"LIST":"VAR":"variable assignment"}get isDeclaration(){return this.isGlobalDeclaration||this.isNewTemporaryDeclaration}constructor(t){let{assignedExpression:e,isGlobalDeclaration:n,isTemporaryNewDeclaration:i,listDef:r,variableIdentifier:s}=t;super(),this._runtimeAssignment=null,this.expression=null,this.listDefinition=null,this.GenerateRuntimeObject=()=>{let t=null;if(this.isGlobalDeclaration?t=this.story:this.isNewTemporaryDeclaration&&(t=ut(this)),t&&t.AddNewVariableDeclaration(this),this.isGlobalDeclaration)return null;const e=new L;return this.expression?e.AddContent(this.expression.runtimeObject):this.listDefinition&&e.AddContent(this.listDefinition.runtimeObject),this._runtimeAssignment=new X(this.variableName,this.isNewTemporaryDeclaration),e.AddContent(this._runtimeAssignment),e},this.toString=()=>`${this.isGlobalDeclaration?"VAR":this.isNewTemporaryDeclaration?"~ temp":""} ${this.variableName}`,this.variableIdentifier=s,this.isGlobalDeclaration=Boolean(n),this.isNewTemporaryDeclaration=Boolean(i),r instanceof xt?(this.listDefinition=this.AddContent(r),this.listDefinition.variableAssignment=this,this.isGlobalDeclaration=!0):e&&(this.expression=this.AddContent(e))}ResolveReferences(t){if(super.ResolveReferences(t),this.isDeclaration&&null===this.listDefinition&&t.CheckForNamingCollisions(this,this.variableIdentifier,this.isGlobalDeclaration?z.Var:z.Temp),this.isGlobalDeclaration){const t=s(this.expression,ft);!t||t.isConstantReference||t.isListItemReference||this.Error("global variable assignments cannot refer to other variables, only literal values, constants and list items")}if(!this.isNewTemporaryDeclaration){const e=t.ResolveVariableWithName(this.variableName,this);e.found||(this.variableName in this.story.constants?this.Error(`Can't re-assign to a constant (do you need to use VAR when declaring '${this.variableName}'?)`,this):this.Error(`Variable could not be found to assign to: '${this.variableName}'`,this)),this._runtimeAssignment&&(this._runtimeAssignment.isGlobal=e.isGlobal)}}}class Pt extends d{get rootContainer(){return this._rootContainer||(this._rootContainer=this.GenerateRuntimeObject()),this._rootContainer}get namedWeavePoints(){return this._namedWeavePoints}get lastParsedSignificantObject(){if(0===this.content.length)return null;let t=null;for(let e=this.content.length-1;e>=0;--e){t=this.content[e];let n=s(t,rt);if((!n||"\n"!==n.text)&&!this.IsGlobalDeclaration(t))break}const e=s(t,Pt);return e&&(t=e.lastParsedSignificantObject),t}constructor(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;super(),this.previousWeavePoint=null,this.addContentToPreviousWeavePoint=!1,this.hasSeenChoiceInSection=!1,this.currentContainer=null,this._unnamedGatherCount=0,this._choiceCount=0,this._rootContainer=null,this._namedWeavePoints=new Map,this.looseEnds=[],this.gatherPointsToResolve=[],this.ResolveWeavePointNaming=()=>{var t,e,n;const i=[...this.FindAll(ot)((t=>!(null===t.name||void 0===t.name))),...this.FindAll(Y)((t=>!(null===t.name||void 0===t.name)))];this._namedWeavePoints=new Map;for(const r of i){const i=this.namedWeavePoints.get((null===(t=r.identifier)||void 0===t?void 0:t.name)||"");if(i){const t=i instanceof ot?"gather":"choice",e=i;this.Error(`A ${t} with the same label name '${r.name}' already exists in this context on line ${e.debugMetadata?e.debugMetadata.startLineNumber:"NO DEBUG METADATA AVAILABLE"}`,r)}(null===(e=r.identifier)||void 0===e?void 0:e.name)&&this.namedWeavePoints.set(null===(n=r.identifier)||void 0===n?void 0:n.name,r)}},this.ConstructWeaveHierarchyFromIndentation=()=>{let t=0;for(;tthis.baseIndentIndex){let e=t;for(;t{for(const e of t)if(e instanceof Y||e instanceof ot)return e.indentationDepth-1;return 0},this.GenerateRuntimeObject=()=>{this._rootContainer=new L,this.currentContainer=this._rootContainer,this.looseEnds=[],this.gatherPointsToResolve=[];for(const t of this.content)if(t instanceof Y||t instanceof ot)this.AddRuntimeForWeavePoint(t);else if(t instanceof Pt){const e=t;this.AddRuntimeForNestedWeave(e),this.gatherPointsToResolve.splice(0,0,...e.gatherPointsToResolve)}else this.AddGeneralRuntimeContent(t.runtimeObject);return this.PassLooseEndsToAncestors(),this._rootContainer},this.AddRuntimeForGather=t=>{const e=!this.hasSeenChoiceInSection;this.hasSeenChoiceInSection=!1;const n=t.runtimeContainer;if(t.name||(n.name=`g-${this._unnamedGatherCount}`,this._unnamedGatherCount+=1),e){if(!this.currentContainer)throw new Error;this.currentContainer.AddContent(n)}else this.rootContainer.AddToNamedContentOnly(n);for(const e of this.looseEnds){const i=e;if(i instanceof ot){if(i.indentationDepth==t.indentationDepth)continue}let r=null;if(i instanceof St)r=i.runtimeObject;else{r=new J;const t=i;if(!t.runtimeContainer)throw new Error;t.runtimeContainer.AddContent(r)}this.gatherPointsToResolve.push(new yt(r,n))}this.looseEnds=[],this.currentContainer=n},this.AddRuntimeForWeavePoint=t=>{if(t instanceof ot)this.AddRuntimeForGather(t);else if(t instanceof Y){if(!this.currentContainer)throw new Error;this.previousWeavePoint instanceof ot&&this.looseEnds.splice(this.looseEnds.indexOf(this.previousWeavePoint),1);const e=t;if(this.currentContainer.AddContent(e.runtimeObject),!e.innerContentContainer)throw new Error;e.innerContentContainer.name=`c-${this._choiceCount}`,this.currentContainer.AddToNamedContentOnly(e.innerContentContainer),this._choiceCount+=1,this.hasSeenChoiceInSection=!0}if(this.addContentToPreviousWeavePoint=!1,this.WeavePointHasLooseEnd(t)){this.looseEnds.push(t);s(t,Y)&&(this.addContentToPreviousWeavePoint=!0)}this.previousWeavePoint=t},this.AddRuntimeForNestedWeave=t=>{this.AddGeneralRuntimeContent(t.rootContainer),null!==this.previousWeavePoint&&(this.looseEnds.splice(this.looseEnds.indexOf(this.previousWeavePoint),1),this.addContentToPreviousWeavePoint=!1)},this.AddGeneralRuntimeContent=t=>{if(null!==t)if(this.addContentToPreviousWeavePoint){if(!this.previousWeavePoint||!this.previousWeavePoint.runtimeContainer)throw new Error;this.previousWeavePoint.runtimeContainer.AddContent(t)}else{if(!this.currentContainer)throw new Error;this.currentContainer.AddContent(t)}},this.PassLooseEndsToAncestors=()=>{if(0===this.looseEnds.length)return;let t=null,e=null,n=!1;for(let i=this.parent;null!==i;i=i.parent){const r=s(i,Pt);r&&(n||null!==t||(t=r),n&&null===e&&(e=r)),(i instanceof Et||i instanceof it)&&(n=!0)}if(null!==t||null!==e)for(let i=this.looseEnds.length-1;i>=0;i-=1){const r=this.looseEnds[i];let s=!1;if(n){if(r instanceof Y&&null!==t)t.ReceiveLooseEnd(r),s=!0;else if(!(r instanceof Y)){const n=t||e;null!==n&&(n.ReceiveLooseEnd(r),s=!0)}}else(null==t?void 0:t.hasOwnProperty("ReceiveLooseEnd"))&&t.ReceiveLooseEnd(r),s=!0;s&&this.looseEnds.splice(i,1)}},this.ReceiveLooseEnd=t=>{this.looseEnds.push(t)},this.WeavePointNamed=t=>{if(!this.namedWeavePoints)return null;let e=this.namedWeavePoints.get(t);return e||null},this.IsGlobalDeclaration=t=>{const e=s(t,At);if(e&&e.isGlobalDeclaration&&e.isDeclaration)return!0;return!!s(t,st)},this.ContentThatFollowsWeavePoint=t=>{const e=[],n=t;if(null!==n.content)for(const t of n.content)this.IsGlobalDeclaration(t)||e.push(t);const i=s(n.parent,Pt);if(null===i)throw new Error("Expected weave point parent to be weave?");for(let t=i.content.indexOf(n)+1;t{if(this.lastParsedSignificantObject instanceof p)return;if(null!==this.looseEnds&&this.looseEnds.length>0)for(const e of this.looseEnds){const n=this.ContentThatFollowsWeavePoint(e);this.ValidateFlowOfObjectsTerminates(n,e,t)}else{for(const t of this.content)if(t instanceof Y||t instanceof St)return;this.ValidateFlowOfObjectsTerminates(this.content,this,t)}},this.BadNestedTerminationHandler=t=>{let e=null;for(let n=t.parent;null!==n;n=n.parent)if(n instanceof Et||n instanceof it){e=s(n,it);break}let n="Choices nested in conditionals or sequences need to explicitly divert afterwards.";if(null!==e){1===e.FindAll(Y)().length&&(n=`Choices with conditions should be written: '* {condition} choice'. Otherwise, ${n.toLowerCase()}`)}this.Error(n,t)},this.ValidateFlowOfObjectsTerminates=(t,e,n)=>{let i=!1,r=e;for(const e of t){if(null!==e.Find(St)((t=>!(t.isThread||t.isTunnel||t.isFunctionCall||t.parent instanceof Ct)))&&(i=!0),null!=e.Find(_t)()){i=!0;break}r=e}if(!i){if(r instanceof p)return;n(r)}},this.WeavePointHasLooseEnd=t=>{if(null===t.content)return!0;for(let e=t.content.length-1;e>=0;--e){let n=s(t.content[e],St);if(n){if(!(n.isThread||n.isTunnel||n.isFunctionCall))return!1}}return!0},this.CheckForWeavePointNamingCollisions=()=>{if(!this.namedWeavePoints)return;const t=[];for(const e of this.ancestry){const n=s(e,dt);if(!n)break;t.push(n)}for(const[e,n]of this.namedWeavePoints)for(const i of t){const t=i.ContentWithNameAtLevel(e);if(t&&t!==n){const i=`${n.GetType()} '${e}' has the same label name as a ${t.GetType()} (on ${t.debugMetadata})`;this.Error(i,n)}}},this.baseIndentIndex=-1==e?this.DetermineBaseIndentationFromContent(t):e,this.AddContent(t),this.ConstructWeaveHierarchyFromIndentation()}get typeName(){return"Weave"}ResolveReferences(t){if(super.ResolveReferences(t),null!==this.looseEnds&&this.looseEnds.length>0){let t=!1;for(let e=this.parent;null!==e;e=e.parent)if(e instanceof Et||e instanceof it){t=!0;break}t&&this.ValidateTermination(this.BadNestedTerminationHandler)}for(const t of this.gatherPointsToResolve)t.divert.targetPath=t.targetRuntimeObj.path;this.CheckForWeavePointNamingCollisions()}}class Nt extends d{get ownExpression(){return this._ownExpression}set ownExpression(t){this._ownExpression=t,this._ownExpression&&this.AddContent(this._ownExpression)}constructor(t){super(),this._contentContainer=null,this._conditionalDivert=null,this._ownExpression=null,this._innerWeave=null,this.isTrueBranch=!1,this.matchingEquality=!1,this.isElse=!1,this.isInline=!1,this.returnDivert=null,this.GenerateRuntimeObject=()=>{if(this._innerWeave)for(const t of this._innerWeave.content){const e=s(t,rt);e&&e.text.startsWith("else:")&&this.Warning("Saw the text 'else:' which is being treated as content. Did you mean '- else:'?",e)}const t=new L,e=this.matchingEquality&&!this.isElse;if(e&&t.AddContent(D.Duplicate()),this._conditionalDivert=new J,this._conditionalDivert.isConditional=!this.isElse,!this.isTrueBranch&&!this.isElse){const e=null!==this.ownExpression;e&&t.AddContent(D.EvalStart()),this.ownExpression&&this.ownExpression.GenerateIntoContainer(t),this.matchingEquality&&t.AddContent(B.CallWithName("==")),e&&t.AddContent(D.EvalEnd())}return t.AddContent(this._conditionalDivert),this._contentContainer=this.GenerateRuntimeForContent(),this._contentContainer.name="b",this.isInline||this._contentContainer.InsertContent(new I("\n"),0),(e||this.isElse&&this.matchingEquality)&&this._contentContainer.InsertContent(D.PopEvaluatedValue(),0),t.AddToNamedContentOnly(this._contentContainer),this.returnDivert=new J,this._contentContainer.AddContent(this.returnDivert),t},this.GenerateRuntimeForContent=()=>null===this._innerWeave?new L:this._innerWeave.rootContainer,t&&(this._innerWeave=new Pt(t),this.AddContent(this._innerWeave))}get typeName(){return"ConditionalSingleBranch"}ResolveReferences(t){if(!this._conditionalDivert||!this._contentContainer)throw new Error;this._conditionalDivert.targetPath=this._contentContainer.path,super.ResolveReferences(t)}}var Ot;!function(t){t[t.ParsingString=1]="ParsingString",t[t.TagActive=2]="TagActive"}(Ot||(Ot={}));class It{constructor(){this.startLineNumber=0,this.endLineNumber=0,this.startCharacterNumber=0,this.endCharacterNumber=0,this.fileName=null,this.sourceName=null}Merge(t){let e=new It;return e.fileName=this.fileName,e.sourceName=this.sourceName,this.startLineNumbert.startLineNumber?(e.startLineNumber=t.startLineNumber,e.startCharacterNumber=t.startCharacterNumber):(e.startLineNumber=this.startLineNumber,e.startCharacterNumber=Math.min(this.startCharacterNumber,t.startCharacterNumber)),this.endLineNumber>t.endLineNumber?(e.endLineNumber=this.endLineNumber,e.endCharacterNumber=this.endCharacterNumber):this.endLineNumber(this.story.AddExternal(this),null)}get typeName(){return"EXTERNAL"}toString(){var t;return`EXTERNAL ${null===(t=this.identifier)||void 0===t?void 0:t.name}`}}class Wt{constructor(t,e,n){this.name=t,this.args=e,this.isFunction=n}}class Rt extends d{constructor(t){super(),this._objToWrap=t,this.GenerateRuntimeObject=()=>this._objToWrap}}let kt=class extends Rt{constructor(t){super(t)}get typeName(){return"Glue"}};class Lt extends y{toString(){return"Glue"}}class Dt extends V{constructor(t,e,n){super(),this.varIdentifier=t,this._runtimeAssignment=null,this.expression=null,this.GenerateIntoContainer=t=>{var e,n;t.AddContent(new mt((null===(e=this.varIdentifier)||void 0===e?void 0:e.name)||null)),this.expression?this.expression.GenerateIntoContainer(t):t.AddContent(new N(1)),t.AddContent(B.CallWithName(this.isInc?"+":"-")),this._runtimeAssignment=new X((null===(n=this.varIdentifier)||void 0===n?void 0:n.name)||null,!1),t.AddContent(this._runtimeAssignment)},this.toString=()=>{var t,e;return this.expression?`${null===(t=this.varIdentifier)||void 0===t?void 0:t.name}${this.isInc?" += ":" -= "}${this.expression}`:`${null===(e=this.varIdentifier)||void 0===e?void 0:e.name}`+(this.isInc?"++":"--")},e instanceof V?(this.expression=e,this.AddContent(this.expression),this.isInc=Boolean(n)):this.isInc=e}get typeName(){return"IncDecExpression"}ResolveReferences(t){var e;super.ResolveReferences(t);const n=t.ResolveVariableWithName((null===(e=this.varIdentifier)||void 0===e?void 0:e.name)||"",this);if(n.found||this.Error(`variable for ${this.incrementDecrementWord} could not be found: '${this.varIdentifier}' after searching: {this.descriptionOfScope}`),!this._runtimeAssignment)throw new Error;this._runtimeAssignment.isGlobal=n.isGlobal,this.parent instanceof Pt||this.parent instanceof dt||this.parent instanceof pt||this.Error(`Can't use ${this.incrementDecrementWord} as sub-expression`)}get incrementDecrementWord(){return this.isInc?"increment":"decrement"}}class Vt extends d{constructor(t){super(),this.includedStory=t,this.GenerateRuntimeObject=()=>null}get typeName(){return"IncludedFile"}}class jt{constructor(t,e,n){this.type=t,this.precedence=e,this.requireWhitespace=n,this.toString=()=>this.type}}class Bt extends dt{get flowLevel(){return at.Knot}constructor(t,e,n,i){super(t,e,n,i)}get typeName(){return this.isFunction?"Function":"Knot"}ResolveReferences(t){super.ResolveReferences(t);let e=this.story;for(const t in this.subFlowsByName){const n=e.ContentWithNameAtLevel(t,at.Knot,!1);if(n){const e=this.subFlowsByName.get(t),i=`Stitch '${e?e.name:"NO STITCH FOUND"}' has the same name as a knot (on ${n.debugMetadata})`;this.Error(i,e)}}}}class Mt extends V{constructor(t){super(),this.itemIdentifierList=t,this.GenerateIntoContainer=t=>{var e,n;const i=new E;if(null!=this.itemIdentifierList)for(const t of this.itemIdentifierList){const r=(null===(e=null==t?void 0:t.name)||void 0===e?void 0:e.split("."))||[];let s=null,a="";r.length>1?(s=r[0],a=r[1]):a=r[0];const o=this.story.ResolveListItem(s,a,this);if(null===o)null===s?this.Error(`Could not find list definition that contains item '${t}'`):this.Error(`Could not find list item ${t}`);else{if(null==o.parent)return void this.Error(`Could not find list definition for item ${t}`);s||(s=(null===(n=o.parent.identifier)||void 0===n?void 0:n.name)||null);const e=new w(s,o.name||null);i.has(e.serialized())?this.Warning(`Duplicate of item '${t}' in list.`):i.Add(e,o.seriesValue)}}t.AddContent(new R(i))}}get typeName(){return"List"}}class Gt extends d{get fullName(){var t;const e=this.parent;if(null===e)throw new Error("Can't get full name without a parent list.");return`${null===(t=e.identifier)||void 0===t?void 0:t.name}.${this.name}`}get typeName(){return"ListElement"}get name(){var t;return(null===(t=this.indentifier)||void 0===t?void 0:t.name)||null}constructor(t,e){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;super(),this.indentifier=t,this.inInitialList=e,this.explicitValue=n,this.seriesValue=0,this.parent=null,this.GenerateRuntimeObject=()=>{throw new Error("Not implemented.")},this.toString=()=>this.fullName,this.parent=this.parent}ResolveReferences(t){super.ResolveReferences(t),t.CheckForNamingCollisions(this,this.indentifier,z.ListItem)}}var $t;t.StatementLevel=void 0,($t=t.StatementLevel||(t.StatementLevel={}))[$t.InnerBlock=0]="InnerBlock",$t[$t.Stitch=1]="Stitch",$t[$t.Knot=2]="Knot",$t[$t.Top=3]="Top";class qt extends dt{get flowLevel(){return at.Stitch}constructor(t,e,n,i){super(t,e,n,i),this.baseToString=this.toString,this.toString=()=>`${null!==this.parent?this.parent+" > ":""}${this.baseToString()}`}get typeName(){return"Stitch"}}let Ut=class extends y{constructor(t){super(),this.text=t.toString()||""}toString(){return"# "+this.text}};class Kt extends y{constructor(){super(...arguments),this.text="",this.index=0,this.threadAtGeneration=null,this.sourcePath="",this.targetPath=null,this.isInvisibleDefault=!1,this.tags=null,this.originalThreadIndex=0}get pathStringOnChoice(){return null===this.targetPath?S("Choice.targetPath"):this.targetPath.toString()}set pathStringOnChoice(t){this.targetPath=new m(t)}}class Ht{constructor(t){this._lists=new Map,this._allUnambiguousListValueCache=new Map;for(let e of t){this._lists.set(e.name,e);for(let[t,n]of e.items){let e=w.fromSerializedKey(t),i=new R(e,n);if(!e.itemName)throw new Error("item.itemName is null or undefined.");this._allUnambiguousListValueCache.set(e.itemName,i),this._allUnambiguousListValueCache.set(e.fullName,i)}}}get lists(){let t=[];for(let[,e]of this._lists)t.push(e);return t}TryListGetDefinition(t,e){if(null===t)return{result:e,exists:!1};let n=this._lists.get(t);return n?{result:n,exists:!0}:{result:e,exists:!1}}FindSingleItemListWithName(t){if(null===t)return S("name");let e=this._allUnambiguousListValueCache.get(t);return void 0!==e?e:null}}class Jt{static JArrayToRuntimeObjList(t){let e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=t.length;e&&n--;let i=[];for(let e=0;e";return i.isExternal?n="x()":i.pushesToStack&&(i.stackPushType==v.Function?n="f()":i.stackPushType==v.Tunnel&&(n="->t->")),e=i.hasVariableTarget?i.variableDivertName:i.targetPathString,t.WriteObjectStart(),t.WriteProperty(n,e),i.hasVariableTarget&&t.WriteProperty("var",!0),i.isConditional&&t.WriteProperty("c",!0),i.externalArgs>0&&t.WriteIntProperty("exArgs",i.externalArgs),void t.WriteObjectEnd()}let r=s(e,K);if(r)return t.WriteObjectStart(),t.WriteProperty("*",r.pathStringOnChoice),t.WriteIntProperty("flg",r.flags),void t.WriteObjectEnd();let a=s(e,P);if(a)return void t.WriteBool(a.value);let o=s(e,N);if(o)return void t.WriteInt(o.value);let l=s(e,O);if(l)return void t.WriteFloat(l.value);let h=s(e,I);if(h)return void(h.isNewline?t.Write("\n",!1):(t.WriteStringStart(),t.WriteStringInner("^"),t.WriteStringInner(h.value),t.WriteStringEnd()));let u=s(e,R);if(u)return void this.WriteInkList(t,u);let c=s(e,F);if(c)return t.WriteObjectStart(),null===c.value?S("divTargetVal.value"):(t.WriteProperty("^->",c.value.componentsString),void t.WriteObjectEnd());let d=s(e,W);if(d)return t.WriteObjectStart(),t.WriteProperty("^var",d.value),t.WriteIntProperty("ci",d.contextIndex),void t.WriteObjectEnd();if(s(e,Lt))return void t.Write("<>");let p=s(e,D);if(p)return void t.Write(Jt._controlCommandNames[p.commandType]);let m=s(e,B);if(m){let e=m.name;return"^"==e&&(e="L^"),void t.Write(e)}let f=s(e,mt);if(f){t.WriteObjectStart();let e=f.pathStringForCount;return null!=e?t.WriteProperty("CNT?",e):t.WriteProperty("VAR?",f.name),void t.WriteObjectEnd()}let g=s(e,X);if(g){t.WriteObjectStart();let e=g.isGlobal?"VAR=":"temp=";return t.WriteProperty(e,g.variableName),g.isNewDeclaration||t.WriteProperty("re",!0),void t.WriteObjectEnd()}if(s(e,j))return void t.Write("void");let C=s(e,Ut);if(C)return t.WriteObjectStart(),t.WriteProperty("#",C.text),void t.WriteObjectEnd();let y=s(e,Kt);if(!y)throw new Error("Failed to convert runtime object to Json token: "+e);this.WriteChoice(t,y)}static JObjectToDictionaryRuntimeObjs(t){let e=new Map;for(let n in t)if(t.hasOwnProperty(n)){let i=this.JTokenToRuntimeObject(t[n]);if(null===i)return S("inkObject");e.set(n,i)}return e}static JObjectToIntDictionary(t){let e=new Map;for(let n in t)t.hasOwnProperty(n)&&e.set(n,parseInt(t[n]));return e}static JTokenToRuntimeObject(t){if("number"==typeof t&&!isNaN(t)||"boolean"==typeof t)return A.Create(t);if("string"==typeof t){let e=t.toString(),n=e[0];if("^"==n)return new I(e.substring(1));if("\n"==n&&1==e.length)return new I("\n");if("<>"==e)return new Lt;for(let t=0;t->"==e)return D.PopTunnel();if("~ret"==e)return D.PopFunction();if("void"==e)return new j}if("object"==typeof t&&!Array.isArray(t)){let e,n=t;if(n["^->"])return e=n["^->"],new F(new m(e.toString()));if(n["^var"]){e=n["^var"];let t=new W(e.toString());return"ci"in n&&(e=n.ci,t.contextIndex=parseInt(e)),t}let i=!1,r=!1,s=v.Function,a=!1;if((e=n["->"])?i=!0:(e=n["f()"])?(i=!0,r=!0,s=v.Function):(e=n["->t->"])?(i=!0,r=!0,s=v.Tunnel):(e=n["x()"])&&(i=!0,a=!0,r=!1,s=v.Function),i){let t=new J;t.pushesToStack=r,t.stackPushType=s,t.isExternal=a;let i=e.toString();return(e=n.var)?t.variableDivertName=i:t.targetPathString=i,t.isConditional=!!n.c,a&&(e=n.exArgs)&&(t.externalArgs=parseInt(e)),t}if(e=n["*"]){let t=new K;return t.pathStringOnChoice=e.toString(),(e=n.flg)&&(t.flags=parseInt(e)),t}if(e=n["VAR?"])return new mt(e.toString());if(e=n["CNT?"]){let t=new mt;return t.pathStringForCount=e.toString(),t}let o=!1,l=!1;if((e=n["VAR="])?(o=!0,l=!0):(e=n["temp="])&&(o=!0,l=!1),o){let t=e.toString(),i=!n.re,r=new X(t,i);return r.isGlobal=l,r}if(void 0!==n["#"])return e=n["#"],new Ut(e.toString());if(e=n.list){let t=e,i=new E;if(e=n.origins){let t=e;i.SetInitialOriginNames(t)}for(let e in t)if(t.hasOwnProperty(e)){let n=t[e],r=new w(e),s=parseInt(n);i.Add(r,s)}return new R(i)}if(null!=n.originalChoicePath)return this.JObjectToChoice(n)}if(Array.isArray(t))return this.JArrayToContainer(t);if(null==t)return null;throw new Error("Failed to convert token to runtime object: "+this.toJson(t,["parent"]))}static toJson(t,e,n){return JSON.stringify(t,((t,n)=>(null==e?void 0:e.some((e=>e===t)))?void 0:n),n)}static WriteRuntimeContainer(t,e){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(t.WriteArrayStart(),null===e)return S("container");for(let n of e.content)this.WriteRuntimeObject(t,n);let i=e.namedOnlyContent,r=e.countFlags,a=null!=e.name&&!n,o=null!=i||r>0||a;if(o&&t.WriteObjectStart(),null!=i)for(let[e,n]of i){let i=e,r=s(n,L);t.WritePropertyStart(i),this.WriteRuntimeContainer(t,r,!0),t.WritePropertyEnd()}r>0&&t.WriteIntProperty("#f",r),a&&t.WriteProperty("#n",e.name),o?t.WriteObjectEnd():t.WriteNull(),t.WriteArrayEnd()}static JArrayToContainer(t){let e=new L;e.content=this.JArrayToRuntimeObjList(t,!0);let n=t[t.length-1];if(null!=n){let t=new Map;for(let i in n)if("#f"==i)e.countFlags=parseInt(n[i]);else if("#n"==i)e.name=n[i].toString();else{let e=this.JTokenToRuntimeObject(n[i]),r=s(e,L);r&&(r.name=i),t.set(i,e)}e.namedOnlyContent=t}return e}static JObjectToChoice(t){let e=new Kt;return e.text=t.text.toString(),e.index=parseInt(t.index),e.sourcePath=t.originalChoicePath.toString(),e.originalThreadIndex=parseInt(t.originalThreadIndex),e.pathStringOnChoice=t.targetPath.toString(),t.tags&&(e.tags=t.tags),e}static WriteChoice(t,e){t.WriteObjectStart(),t.WriteProperty("text",e.text),t.WriteIntProperty("index",e.index),t.WriteProperty("originalChoicePath",e.sourcePath),t.WriteIntProperty("originalThreadIndex",e.originalThreadIndex),t.WriteProperty("targetPath",e.pathStringOnChoice),e.tags&&t.WriteProperty("tags",(t=>{t.WriteArrayStart();for(const n of e.tags)t.WriteStringStart(),t.WriteStringInner(n),t.WriteStringEnd();t.WriteArrayEnd()})),t.WriteObjectEnd()}static WriteInkList(t,e){let n=e.value;if(null===n)return S("rawList");t.WriteObjectStart(),t.WritePropertyStart("list"),t.WriteObjectStart();for(let[e,i]of n){let n=w.fromSerializedKey(e),r=i;if(null===n.itemName)return S("item.itemName");t.WritePropertyNameStart(),t.WritePropertyNameInner(n.originName?n.originName:"?"),t.WritePropertyNameInner("."),t.WritePropertyNameInner(n.itemName),t.WritePropertyNameEnd(),t.Write(r),t.WritePropertyEnd()}if(t.WriteObjectEnd(),t.WritePropertyEnd(),0==n.Count&&null!=n.originNames&&n.originNames.length>0){t.WritePropertyStart("origins"),t.WriteArrayStart();for(let e of n.originNames)t.Write(e);t.WriteArrayEnd(),t.WritePropertyEnd()}t.WriteObjectEnd()}static ListDefinitionsToJToken(t){let e={};for(let n of t.lists){let t={};for(let[e,i]of n.items){let n=w.fromSerializedKey(e);if(null===n.itemName)return S("item.itemName");t[n.itemName]=i}e[n.name]=t}return e}static JTokenToListDefinitions(t){let e=t,n=[];for(let t in e)if(e.hasOwnProperty(t)){let i=t.toString(),r=e[t],s=new Map;for(let n in r)if(e.hasOwnProperty(t)){let t=r[n];s.set(n,parseInt(t))}let a=new Tt(i,s);n.push(a)}return new Ht(n)}}Jt._controlCommandNames=(()=>{let t=[];t[D.CommandType.EvalStart]="ev",t[D.CommandType.EvalOutput]="out",t[D.CommandType.EvalEnd]="/ev",t[D.CommandType.Duplicate]="du",t[D.CommandType.PopEvaluatedValue]="pop",t[D.CommandType.PopFunction]="~ret",t[D.CommandType.PopTunnel]="->->",t[D.CommandType.BeginString]="str",t[D.CommandType.EndString]="/str",t[D.CommandType.NoOp]="nop",t[D.CommandType.ChoiceCount]="choiceCnt",t[D.CommandType.Turns]="turn",t[D.CommandType.TurnsSince]="turns",t[D.CommandType.ReadCount]="readc",t[D.CommandType.Random]="rnd",t[D.CommandType.SeedRandom]="srnd",t[D.CommandType.VisitIndex]="visit",t[D.CommandType.SequenceShuffleIndex]="seq",t[D.CommandType.StartThread]="thread",t[D.CommandType.Done]="done",t[D.CommandType.End]="end",t[D.CommandType.ListFromInt]="listInt",t[D.CommandType.ListRange]="range",t[D.CommandType.ListRandom]="lrnd",t[D.CommandType.BeginTag]="#",t[D.CommandType.EndTag]="/#";for(let e=0;e1}constructor(){if(this._threadCounter=0,this._startOfRoot=H.Null,arguments[0]instanceof re){let t=arguments[0];this._startOfRoot=H.StartOf(t.rootContentContainer),this.Reset()}else{let t=arguments[0];this._threads=[];for(let e of t._threads)this._threads.push(e.Copy());this._threadCounter=t._threadCounter,this._startOfRoot=t._startOfRoot.copy()}}Reset(){this._threads=[],this._threads.push(new zt.Thread),this._threads[0].callstack.push(new zt.Element(v.Tunnel,this._startOfRoot))}SetJsonToken(t,e){this._threads.length=0;let n=t.threads;for(let t of n){let n=t,i=new zt.Thread(n,e);this._threads.push(i)}this._threadCounter=parseInt(t.threadCounter),this._startOfRoot=H.StartOf(e.rootContentContainer)}WriteJson(t){t.WriteObject((t=>{t.WritePropertyStart("threads"),t.WriteArrayStart();for(let e of this._threads)e.WriteJson(t);t.WriteArrayEnd(),t.WritePropertyEnd(),t.WritePropertyStart("threadCounter"),t.WriteInt(this._threadCounter),t.WritePropertyEnd()}))}PushThread(){let t=this.currentThread.Copy();this._threadCounter++,t.threadIndex=this._threadCounter,this._threads.push(t)}ForkThread(){let t=this.currentThread.Copy();return this._threadCounter++,t.threadIndex=this._threadCounter,t}PopThread(){if(!this.canPopThread)throw new Error("Can't pop thread");this._threads.splice(this._threads.indexOf(this.currentThread),1)}get canPopThread(){return this._threads.length>1&&!this.elementIsEvaluateFromGame}get elementIsEvaluateFromGame(){return this.currentElement.type==v.FunctionEvaluationFromGame}Push(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,i=new zt.Element(t,this.currentElement.currentPointer,!1);i.evaluationStackHeightWhenPushed=e,i.functionStartInOutputStream=n,this.callStack.push(i)}CanPop(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;return!!this.canPop&&(null==t||this.currentElement.type==t)}Pop(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;if(!this.CanPop(t))throw new Error("Mismatched push/pop in Callstack");this.callStack.pop()}GetTemporaryVariableWithName(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;-1==e&&(e=this.currentElementIndex+1);let n=T(this.callStack[e-1].temporaryVariables,t,null);return n.exists?n.result:null}SetTemporaryVariable(t,e,n){let i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:-1;-1==i&&(i=this.currentElementIndex+1);let r=this.callStack[i-1];if(!n&&!r.temporaryVariables.get(t))throw new Error("Could not find temporary variable to set: "+t);let s=T(r.temporaryVariables,t,null);s.exists&&R.RetainListOriginsForAssignment(s.result,e),r.temporaryVariables.set(t,e)}ContextForVariableNamed(t){return this.currentElement.temporaryVariables.get(t)?this.currentElementIndex+1:0}ThreadWithIndex(t){let e=this._threads.filter((e=>{if(e.threadIndex==t)return e}));return e.length>0?e[0]:null}get callStack(){return this.currentThread.callstack}get callStackTrace(){let t=new b;for(let e=0;e")}}}return t.toString()}}!function(t){class e{constructor(t,e){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];this.evaluationStackHeightWhenPushed=0,this.functionStartInOutputStream=0,this.currentPointer=e.copy(),this.inExpressionEvaluation=n,this.temporaryVariables=new Map,this.type=t}Copy(){let t=new e(this.type,this.currentPointer,this.inExpressionEvaluation);return t.temporaryVariables=new Map(this.temporaryVariables),t.evaluationStackHeightWhenPushed=this.evaluationStackHeightWhenPushed,t.functionStartInOutputStream=this.functionStartInOutputStream,t}}t.Element=e;class n{constructor(){if(this.threadIndex=0,this.previousPointer=H.Null,this.callstack=[],arguments[0]&&arguments[1]){let t=arguments[0],n=arguments[1];this.threadIndex=parseInt(t.threadIndex);let i=t.callstack;for(let t of i){let i,r=t,s=parseInt(r.type),a=H.Null,o=r.cPath;if(void 0!==o){i=o.toString();let t=n.ContentAtPath(new m(i));if(a.container=t.container,a.index=parseInt(r.idx),null==t.obj)throw new Error("When loading state, internal story location couldn't be found: "+i+". Has the story changed since this save data was created?");if(t.approximate){if(null===a.container)return S("pointer.container");n.Warning("When loading state, exact internal story location couldn't be found: '"+i+"', so it was approximated to '"+a.container.path.toString()+"' to recover. Has the story changed since this save data was created?")}}let l=!!r.exp,h=new e(s,a,l),u=r.temp;void 0!==u?h.temporaryVariables=Jt.JObjectToDictionaryRuntimeObjs(u):h.temporaryVariables.clear(),this.callstack.push(h)}let r=t.previousContentObject;if(void 0!==r){let t=new m(r.toString());this.previousPointer=n.PointerAtPath(t)}}}Copy(){let t=new n;t.threadIndex=this.threadIndex;for(let e of this.callstack)t.callstack.push(e.Copy());return t.previousPointer=this.previousPointer.copy(),t}WriteJson(t){t.WriteObjectStart(),t.WritePropertyStart("callstack"),t.WriteArrayStart();for(let e of this.callstack){if(t.WriteObjectStart(),!e.currentPointer.isNull){if(null===e.currentPointer.container)return S("el.currentPointer.container");t.WriteProperty("cPath",e.currentPointer.container.path.componentsString),t.WriteIntProperty("idx",e.currentPointer.index)}t.WriteProperty("exp",e.inExpressionEvaluation),t.WriteIntProperty("type",e.type),e.temporaryVariables.size>0&&(t.WritePropertyStart("temp"),Jt.WriteDictionaryRuntimeObjs(t,e.temporaryVariables),t.WritePropertyEnd()),t.WriteObjectEnd()}if(t.WriteArrayEnd(),t.WritePropertyEnd(),t.WriteIntProperty("threadIndex",this.threadIndex),!this.previousPointer.isNull){let e=this.previousPointer.Resolve();if(null===e)return S("this.previousPointer.Resolve()");t.WriteProperty("previousContentObject",e.path.toString())}t.WriteObjectEnd()}}t.Thread=n}(zt||(zt={}));class Xt extends class{}{variableChangedEvent(t,e){for(let n of this.variableChangedEventCallbacks)n(t,e)}get batchObservingVariableChanges(){return this._batchObservingVariableChanges}set batchObservingVariableChanges(t){if(this._batchObservingVariableChanges=t,t)this._changedVariablesForBatchObs=new Set;else if(null!=this._changedVariablesForBatchObs){for(let t of this._changedVariablesForBatchObs){let e=this._globalVariables.get(t);e?this.variableChangedEvent(t,e):S("currentValue")}this._changedVariablesForBatchObs=null}}get callStack(){return this._callStack}set callStack(t){this._callStack=t}$(t,e){if(void 0===e){let e=null;return null!==this.patch&&(e=this.patch.TryGetGlobal(t,null),e.exists)?e.result.valueObject:(e=this._globalVariables.get(t),void 0===e&&(e=this._defaultGlobalVariables.get(t)),void 0!==e?e.valueObject:null)}{if(void 0===this._defaultGlobalVariables.get(t))throw new _("Cannot assign to a variable ("+t+") that hasn't been declared in the story");let n=A.Create(e);if(null==n)throw null==e?new Error("Cannot pass null to VariableState"):new Error("Invalid value passed to VariableState: "+e.toString());this.SetGlobal(t,n)}}constructor(t,e){super(),this.variableChangedEventCallbacks=[],this.patch=null,this._batchObservingVariableChanges=!1,this._defaultGlobalVariables=new Map,this._changedVariablesForBatchObs=new Set,this._globalVariables=new Map,this._callStack=t,this._listDefsOrigin=e;try{return new Proxy(this,{get:(t,e)=>e in t?t[e]:t.$(e),set:(t,e,n)=>(e in t?t[e]=n:t.$(e,n),!0)})}catch(t){}}ApplyPatch(){if(null===this.patch)return S("this.patch");for(let[t,e]of this.patch.globals)this._globalVariables.set(t,e);if(null!==this._changedVariablesForBatchObs)for(let t of this.patch.changedVariables)this._changedVariablesForBatchObs.add(t);this.patch=null}SetJsonToken(t){this._globalVariables.clear();for(let[e,n]of this._defaultGlobalVariables){let i=t[e];if(void 0!==i){let t=Jt.JTokenToRuntimeObject(i);if(null===t)return S("tokenInkObject");this._globalVariables.set(e,t)}else this._globalVariables.set(e,n)}}WriteJson(t){t.WriteObjectStart();for(let[e,n]of this._globalVariables){let i=e,r=n;if(Xt.dontSaveDefaultValues&&this._defaultGlobalVariables.has(i)){let t=this._defaultGlobalVariables.get(i);if(this.RuntimeObjectsEqual(r,t))continue}t.WritePropertyStart(i),Jt.WriteRuntimeObject(t,r),t.WritePropertyEnd()}t.WriteObjectEnd()}RuntimeObjectsEqual(t,e){if(null===t)return S("obj1");if(null===e)return S("obj2");if(t.constructor!==e.constructor)return!1;let n=s(t,P);if(null!==n)return n.value===a(e,P).value;let i=s(t,N);if(null!==i)return i.value===a(e,N).value;let r=s(t,O);if(null!==r)return r.value===a(e,O).value;let o=s(t,A),l=s(e,A);if(null!==o&&null!==l)return h(o.valueObject)&&h(l.valueObject)?o.valueObject.Equals(l.valueObject):o.valueObject===l.valueObject;throw new Error("FastRoughDefinitelyEquals: Unsupported runtime object type: "+t.constructor.name)}GetVariableWithName(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1,n=this.GetRawVariableWithName(t,e),i=s(n,W);return null!==i&&(n=this.ValueAtVariablePointer(i)),n}TryGetDefaultVariableValue(t){let e=T(this._defaultGlobalVariables,t,null);return e.exists?e.result:null}GlobalVariableExistsWithName(t){return this._globalVariables.has(t)||null!==this._defaultGlobalVariables&&this._defaultGlobalVariables.has(t)}GetRawVariableWithName(t,e){let n=null;if(0==e||-1==e){let e=null;if(null!==this.patch&&(e=this.patch.TryGetGlobal(t,null),e.exists))return e.result;if(e=T(this._globalVariables,t,null),e.exists)return e.result;if(null!==this._defaultGlobalVariables&&(e=T(this._defaultGlobalVariables,t,null),e.exists))return e.result;if(null===this._listDefsOrigin)return S("VariablesState._listDefsOrigin");let n=this._listDefsOrigin.FindSingleItemListWithName(t);if(n)return n}return n=this._callStack.GetTemporaryVariableWithName(t,e),n}ValueAtVariablePointer(t){return this.GetVariableWithName(t.variableName,t.contextIndex)}Assign(t,e){let n=t.variableName;if(null===n)return S("name");let i=-1,r=!1;if(r=t.isNewDeclaration?t.isGlobal:this.GlobalVariableExistsWithName(n),t.isNewDeclaration){let t=s(e,W);if(null!==t){e=this.ResolveVariablePointer(t)}}else{let t=null;do{t=s(this.GetRawVariableWithName(n,i),W),null!=t&&(n=t.variableName,i=t.contextIndex,r=0==i)}while(null!=t)}r?this.SetGlobal(n,e):this._callStack.SetTemporaryVariable(n,e,t.isNewDeclaration,i)}SnapshotDefaultGlobals(){this._defaultGlobalVariables=new Map(this._globalVariables)}RetainListOriginsForAssignment(t,e){let n=a(t,R),i=a(e,R);n.value&&i.value&&0==i.value.Count&&i.value.SetInitialOriginNames(n.value.originNames)}SetGlobal(t,e){let n=null;if(null===this.patch&&(n=T(this._globalVariables,t,null)),null!==this.patch&&(n=this.patch.TryGetGlobal(t,null),n.exists||(n=T(this._globalVariables,t,null))),R.RetainListOriginsForAssignment(n.result,e),null===t)return S("variableName");if(null!==this.patch?this.patch.SetGlobal(t,e):this._globalVariables.set(t,e),null!==this.variableChangedEvent&&null!==n&&e!==n.result)if(this.batchObservingVariableChanges){if(null===this._changedVariablesForBatchObs)return S("this._changedVariablesForBatchObs");null!==this.patch?this.patch.AddChangedVariable(t):null!==this._changedVariablesForBatchObs&&this._changedVariablesForBatchObs.add(t)}else this.variableChangedEvent(t,e)}ResolveVariablePointer(t){let e=t.contextIndex;-1==e&&(e=this.GetContextIndexOfVariableNamed(t.variableName));let n=s(this.GetRawVariableWithName(t.variableName,e),W);return null!=n?n:new W(t.variableName,e)}GetContextIndexOfVariableNamed(t){return this.GlobalVariableExistsWithName(t)?0:this._callStack.currentElementIndex}ObserveVariableChange(t){this.variableChangedEventCallbacks.push(t)}}Xt.dontSaveDefaultValues=!0;class Yt{constructor(t){this.seed=t%2147483647,this.seed<=0&&(this.seed+=2147483646)}next(){return this.seed=48271*this.seed%2147483647}nextFloat(){return(this.next()-1)/2147483646}}class Zt{get globals(){return this._globals}get changedVariables(){return this._changedVariables}get visitCounts(){return this._visitCounts}get turnIndices(){return this._turnIndices}constructor(){if(this._changedVariables=new Set,this._visitCounts=new Map,this._turnIndices=new Map,1===arguments.length&&null!==arguments[0]){let t=arguments[0];this._globals=new Map(t._globals),this._changedVariables=new Set(t._changedVariables),this._visitCounts=new Map(t._visitCounts),this._turnIndices=new Map(t._turnIndices)}else this._globals=new Map,this._changedVariables=new Set,this._visitCounts=new Map,this._turnIndices=new Map}TryGetGlobal(t,e){return null!==t&&this._globals.has(t)?{result:this._globals.get(t),exists:!0}:{result:e,exists:!1}}SetGlobal(t,e){this._globals.set(t,e)}AddChangedVariable(t){return this._changedVariables.add(t)}TryGetVisitCount(t,e){return this._visitCounts.has(t)?{result:this._visitCounts.get(t),exists:!0}:{result:e,exists:!1}}SetVisitCount(t,e){this._visitCounts.set(t,e)}SetTurnIndex(t,e){this._turnIndices.set(t,e)}TryGetTurnIndex(t,e){return this._turnIndices.has(t)?{result:this._turnIndices.get(t),exists:!0}:{result:e,exists:!1}}}class Qt{static TextToDictionary(t){return new Qt.Reader(t).ToDictionary()}static TextToArray(t){return new Qt.Reader(t).ToArray()}}!function(t){t.Reader=class{constructor(t){this._rootObject=JSON.parse(t)}ToDictionary(){return this._rootObject}ToArray(){return this._rootObject}};class e{constructor(){this._currentPropertyName=null,this._currentString=null,this._stateStack=[],this._collectionStack=[],this._propertyNameStack=[],this._jsonObject=null}WriteObject(t){this.WriteObjectStart(),t(this),this.WriteObjectEnd()}WriteObjectStart(){this.StartNewObject(!0);let e={};if(this.state===t.Writer.State.Property){this.Assert(null!==this.currentCollection),this.Assert(null!==this.currentPropertyName);let t=this._propertyNameStack.pop();this.currentCollection[t]=e,this._collectionStack.push(e)}else this.state===t.Writer.State.Array?(this.Assert(null!==this.currentCollection),this.currentCollection.push(e),this._collectionStack.push(e)):(this.Assert(this.state===t.Writer.State.None),this._jsonObject=e,this._collectionStack.push(e));this._stateStack.push(new t.Writer.StateElement(t.Writer.State.Object))}WriteObjectEnd(){this.Assert(this.state===t.Writer.State.Object),this._collectionStack.pop(),this._stateStack.pop()}WriteProperty(t,e){if(this.WritePropertyStart(t),arguments[1]instanceof Function){(0,arguments[1])(this)}else{let t=arguments[1];this.Write(t)}this.WritePropertyEnd()}WriteIntProperty(t,e){this.WritePropertyStart(t),this.WriteInt(e),this.WritePropertyEnd()}WriteFloatProperty(t,e){this.WritePropertyStart(t),this.WriteFloat(e),this.WritePropertyEnd()}WritePropertyStart(e){this.Assert(this.state===t.Writer.State.Object),this._propertyNameStack.push(e),this.IncrementChildCount(),this._stateStack.push(new t.Writer.StateElement(t.Writer.State.Property))}WritePropertyEnd(){this.Assert(this.state===t.Writer.State.Property),this.Assert(1===this.childCount),this._stateStack.pop()}WritePropertyNameStart(){this.Assert(this.state===t.Writer.State.Object),this.IncrementChildCount(),this._currentPropertyName="",this._stateStack.push(new t.Writer.StateElement(t.Writer.State.Property)),this._stateStack.push(new t.Writer.StateElement(t.Writer.State.PropertyName))}WritePropertyNameEnd(){this.Assert(this.state===t.Writer.State.PropertyName),this.Assert(null!==this._currentPropertyName),this._propertyNameStack.push(this._currentPropertyName),this._currentPropertyName=null,this._stateStack.pop()}WritePropertyNameInner(e){this.Assert(this.state===t.Writer.State.PropertyName),this.Assert(null!==this._currentPropertyName),this._currentPropertyName+=e}WriteArrayStart(){this.StartNewObject(!0);let e=[];if(this.state===t.Writer.State.Property){this.Assert(null!==this.currentCollection),this.Assert(null!==this.currentPropertyName);let t=this._propertyNameStack.pop();this.currentCollection[t]=e,this._collectionStack.push(e)}else this.state===t.Writer.State.Array?(this.Assert(null!==this.currentCollection),this.currentCollection.push(e),this._collectionStack.push(e)):(this.Assert(this.state===t.Writer.State.None),this._jsonObject=e,this._collectionStack.push(e));this._stateStack.push(new t.Writer.StateElement(t.Writer.State.Array))}WriteArrayEnd(){this.Assert(this.state===t.Writer.State.Array),this._collectionStack.pop(),this._stateStack.pop()}Write(t){null!==t?(this.StartNewObject(!1),this._addToCurrentObject(t)):console.error("Warning: trying to write a null value")}WriteBool(t){null!==t&&(this.StartNewObject(!1),this._addToCurrentObject(t))}WriteInt(t){null!==t&&(this.StartNewObject(!1),this._addToCurrentObject(Math.floor(t)))}WriteFloat(t){null!==t&&(this.StartNewObject(!1),t==Number.POSITIVE_INFINITY?this._addToCurrentObject(34e37):t==Number.NEGATIVE_INFINITY?this._addToCurrentObject(-34e37):isNaN(t)?this._addToCurrentObject(0):this._addToCurrentObject(t))}WriteNull(){this.StartNewObject(!1),this._addToCurrentObject(null)}WriteStringStart(){this.StartNewObject(!1),this._currentString="",this._stateStack.push(new t.Writer.StateElement(t.Writer.State.String))}WriteStringEnd(){this.Assert(this.state==t.Writer.State.String),this._stateStack.pop(),this._addToCurrentObject(this._currentString),this._currentString=null}WriteStringInner(e){this.Assert(this.state===t.Writer.State.String),null!==e?this._currentString+=e:console.error("Warning: trying to write a null string")}toString(){return null===this._jsonObject?"":JSON.stringify(this._jsonObject)}StartNewObject(e){e?this.Assert(this.state===t.Writer.State.None||this.state===t.Writer.State.Property||this.state===t.Writer.State.Array):this.Assert(this.state===t.Writer.State.Property||this.state===t.Writer.State.Array),this.state===t.Writer.State.Property&&this.Assert(0===this.childCount),this.state!==t.Writer.State.Array&&this.state!==t.Writer.State.Property||this.IncrementChildCount()}get state(){return this._stateStack.length>0?this._stateStack[this._stateStack.length-1].type:t.Writer.State.None}get childCount(){return this._stateStack.length>0?this._stateStack[this._stateStack.length-1].childCount:0}get currentCollection(){return this._collectionStack.length>0?this._collectionStack[this._collectionStack.length-1]:null}get currentPropertyName(){return this._propertyNameStack.length>0?this._propertyNameStack[this._propertyNameStack.length-1]:null}IncrementChildCount(){this.Assert(this._stateStack.length>0);let t=this._stateStack.pop();t.childCount++,this._stateStack.push(t)}Assert(t){if(!t)throw Error("Assert failed while writing JSON")}_addToCurrentObject(e){this.Assert(null!==this.currentCollection),this.state===t.Writer.State.Array?(this.Assert(Array.isArray(this.currentCollection)),this.currentCollection.push(e)):this.state===t.Writer.State.Property&&(this.Assert(!Array.isArray(this.currentCollection)),this.Assert(null!==this.currentPropertyName),this.currentCollection[this.currentPropertyName]=e,this._propertyNameStack.pop())}}t.Writer=e,function(e){var n;(n=e.State||(e.State={}))[n.None=0]="None",n[n.Object=1]="Object",n[n.Array=2]="Array",n[n.Property=3]="Property",n[n.PropertyName=4]="PropertyName",n[n.String=5]="String";e.StateElement=class{constructor(e){this.type=t.Writer.State.None,this.childCount=0,this.type=e}}}(e=t.Writer||(t.Writer={}))}(Qt||(Qt={}));class te{constructor(){let t=arguments[0],e=arguments[1];if(this.name=t,this.callStack=new zt(e),arguments[2]){let t=arguments[2];this.callStack.SetJsonToken(t.callstack,e),this.outputStream=Jt.JArrayToRuntimeObjList(t.outputStream),this.currentChoices=Jt.JArrayToRuntimeObjList(t.currentChoices);let n=t.choiceThreads;void 0!==n&&this.LoadFlowChoiceThreads(n,e)}else this.outputStream=[],this.currentChoices=[]}WriteJson(t){t.WriteObjectStart(),t.WriteProperty("callstack",(t=>this.callStack.WriteJson(t))),t.WriteProperty("outputStream",(t=>Jt.WriteListRuntimeObjs(t,this.outputStream)));let e=!1;for(let n of this.currentChoices){if(null===n.threadAtGeneration)return S("c.threadAtGeneration");n.originalThreadIndex=n.threadAtGeneration.threadIndex,null===this.callStack.ThreadWithIndex(n.originalThreadIndex)&&(e||(e=!0,t.WritePropertyStart("choiceThreads"),t.WriteObjectStart()),t.WritePropertyStart(n.originalThreadIndex),n.threadAtGeneration.WriteJson(t),t.WritePropertyEnd())}e&&(t.WriteObjectEnd(),t.WritePropertyEnd()),t.WriteProperty("currentChoices",(t=>{t.WriteArrayStart();for(let e of this.currentChoices)Jt.WriteChoice(t,e);t.WriteArrayEnd()})),t.WriteObjectEnd()}LoadFlowChoiceThreads(t,e){for(let n of this.currentChoices){let i=this.callStack.ThreadWithIndex(n.originalThreadIndex);if(null!==i)n.threadAtGeneration=i.Copy();else{let i=t[`${n.originalThreadIndex}`];n.threadAtGeneration=new zt.Thread(i,e)}}}}class ee{ToJson(){let t=new Qt.Writer;return this.WriteJson(t),t.toString()}toJson(){let t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];return this.ToJson(t)}LoadJson(t){let e=Qt.TextToDictionary(t);this.LoadJsonObj(e),null!==this.onDidLoadState&&this.onDidLoadState()}VisitCountAtPathString(t){let e;if(null!==this._patch){let n=this.story.ContentAtPath(new m(t)).container;if(null===n)throw new Error("Content at path not found: "+t);if(e=this._patch.TryGetVisitCount(n,0),e.exists)return e.result}return e=T(this._visitCounts,t,null),e.exists?e.result:0}VisitCountForContainer(t){if(null===t)return S("container");if(!t.visitsShouldBeCounted)return this.story.Error("Read count for target ("+t.name+" - on "+t.debugMetadata+") unknown. The story may need to be compiled with countAllVisits flag (-c)."),0;if(null!==this._patch){let e=this._patch.TryGetVisitCount(t,0);if(e.exists)return e.result}let e=t.path.toString(),n=T(this._visitCounts,e,null);return n.exists?n.result:0}IncrementVisitCountForContainer(t){if(null!==this._patch){let e=this.VisitCountForContainer(t);return e++,void this._patch.SetVisitCount(t,e)}let e=t.path.toString(),n=T(this._visitCounts,e,null);n.exists?this._visitCounts.set(e,n.result+1):this._visitCounts.set(e,1)}RecordTurnIndexVisitToContainer(t){if(null!==this._patch)return void this._patch.SetTurnIndex(t,this.currentTurnIndex);let e=t.path.toString();this._turnIndices.set(e,this.currentTurnIndex)}TurnsSinceForContainer(t){if(t.turnIndexShouldBeCounted||this.story.Error("TURNS_SINCE() for target ("+t.name+" - on "+t.debugMetadata+") unknown. The story may need to be compiled with countAllVisits flag (-c)."),null!==this._patch){let e=this._patch.TryGetTurnIndex(t,0);if(e.exists)return this.currentTurnIndex-e.result}let e=t.path.toString(),n=T(this._turnIndices,e,0);return n.exists?this.currentTurnIndex-n.result:-1}get callstackDepth(){return this.callStack.depth}get outputStream(){return this._currentFlow.outputStream}get currentChoices(){return this.canContinue?[]:this._currentFlow.currentChoices}get generatedChoices(){return this._currentFlow.currentChoices}get currentErrors(){return this._currentErrors}get currentWarnings(){return this._currentWarnings}get variablesState(){return this._variablesState}set variablesState(t){this._variablesState=t}get callStack(){return this._currentFlow.callStack}get evaluationStack(){return this._evaluationStack}get currentTurnIndex(){return this._currentTurnIndex}set currentTurnIndex(t){this._currentTurnIndex=t}get currentPathString(){let t=this.currentPointer;return t.isNull?null:null===t.path?S("pointer.path"):t.path.toString()}get currentPointer(){return this.callStack.currentElement.currentPointer.copy()}set currentPointer(t){this.callStack.currentElement.currentPointer=t.copy()}get previousPointer(){return this.callStack.currentThread.previousPointer.copy()}set previousPointer(t){this.callStack.currentThread.previousPointer=t.copy()}get canContinue(){return!this.currentPointer.isNull&&!this.hasError}get hasError(){return null!=this.currentErrors&&this.currentErrors.length>0}get hasWarning(){return null!=this.currentWarnings&&this.currentWarnings.length>0}get currentText(){if(this._outputStreamTextDirty){let t=new b,e=!1;for(let n of this.outputStream){let i=s(n,I);if(e||null===i){let t=s(n,D);null!==t&&(t.commandType==D.CommandType.BeginTag?e=!0:t.commandType==D.CommandType.EndTag&&(e=!1))}else t.Append(i.value)}this._currentText=this.CleanOutputWhitespace(t.toString()),this._outputStreamTextDirty=!1}return this._currentText}CleanOutputWhitespace(t){let e=new b,n=-1,i=0;for(let r=0;r0&&n!=i&&e.Append(" "),n=-1),"\n"==s&&(i=r+1),a||e.Append(s)}return e.toString()}get currentTags(){if(this._outputStreamTagsDirty){this._currentTags=[];let t=!1,e=new b;for(let n of this.outputStream){let i=s(n,D);if(null!=i){if(i.commandType==D.CommandType.BeginTag){if(t&&e.Length>0){let t=this.CleanOutputWhitespace(e.toString());this._currentTags.push(t),e.Clear()}t=!0}else if(i.commandType==D.CommandType.EndTag){if(e.Length>0){let t=this.CleanOutputWhitespace(e.toString());this._currentTags.push(t),e.Clear()}t=!1}}else if(t){let t=s(n,I);null!==t&&e.Append(t.value)}else{let t=s(n,Ut);null!=t&&null!=t.text&&t.text.length>0&&this._currentTags.push(t.text)}}if(e.Length>0){let t=this.CleanOutputWhitespace(e.toString());this._currentTags.push(t),e.Clear()}this._outputStreamTagsDirty=!1}return this._currentTags}get currentFlowName(){return this._currentFlow.name}get currentFlowIsDefaultFlow(){return this._currentFlow.name==this.kDefaultFlowName}get aliveFlowNames(){if(this._aliveFlowNamesDirty){if(this._aliveFlowNames=[],null!=this._namedFlows)for(let t of this._namedFlows.keys())t!=this.kDefaultFlowName&&this._aliveFlowNames.push(t);this._aliveFlowNamesDirty=!1}return this._aliveFlowNames}get inExpressionEvaluation(){return this.callStack.currentElement.inExpressionEvaluation}set inExpressionEvaluation(t){this.callStack.currentElement.inExpressionEvaluation=t}constructor(t){this.kInkSaveStateVersion=10,this.kMinCompatibleLoadVersion=8,this.onDidLoadState=null,this._currentErrors=null,this._currentWarnings=null,this.divertedPointer=H.Null,this._currentTurnIndex=0,this.storySeed=0,this.previousRandom=0,this.didSafeExit=!1,this._currentText=null,this._currentTags=null,this._outputStreamTextDirty=!0,this._outputStreamTagsDirty=!0,this._patch=null,this._aliveFlowNames=null,this._namedFlows=null,this.kDefaultFlowName="DEFAULT_FLOW",this._aliveFlowNamesDirty=!0,this.story=t,this._currentFlow=new te(this.kDefaultFlowName,t),this.OutputStreamDirty(),this._aliveFlowNamesDirty=!0,this._evaluationStack=[],this._variablesState=new Xt(this.callStack,t.listDefinitions),this._visitCounts=new Map,this._turnIndices=new Map,this.currentTurnIndex=-1;let e=(new Date).getTime();this.storySeed=new Yt(e).next()%100,this.previousRandom=0,this.GoToStart()}GoToStart(){this.callStack.currentElement.currentPointer=H.StartOf(this.story.mainContentContainer)}SwitchFlow_Internal(t){if(null===t)throw new Error("Must pass a non-null string to Story.SwitchFlow");if(null===this._namedFlows&&(this._namedFlows=new Map,this._namedFlows.set(this.kDefaultFlowName,this._currentFlow)),t===this._currentFlow.name)return;let e,n=T(this._namedFlows,t,null);n.exists?e=n.result:(e=new te(t,this.story),this._namedFlows.set(t,e),this._aliveFlowNamesDirty=!0),this._currentFlow=e,this.variablesState.callStack=this._currentFlow.callStack,this.OutputStreamDirty()}SwitchToDefaultFlow_Internal(){null!==this._namedFlows&&this.SwitchFlow_Internal(this.kDefaultFlowName)}RemoveFlow_Internal(t){if(null===t)throw new Error("Must pass a non-null string to Story.DestroyFlow");if(t===this.kDefaultFlowName)throw new Error("Cannot destroy default flow");if(this._currentFlow.name===t&&this.SwitchToDefaultFlow_Internal(),null===this._namedFlows)return S("this._namedFlows");this._namedFlows.delete(t),this._aliveFlowNamesDirty=!0}CopyAndStartPatching(){let t=new ee(this.story);if(t._patch=new Zt(this._patch),t._currentFlow.name=this._currentFlow.name,t._currentFlow.callStack=new zt(this._currentFlow.callStack),t._currentFlow.currentChoices.push(...this._currentFlow.currentChoices),t._currentFlow.outputStream.push(...this._currentFlow.outputStream),t.OutputStreamDirty(),null!==this._namedFlows){t._namedFlows=new Map;for(let[e,n]of this._namedFlows)t._namedFlows.set(e,n),t._aliveFlowNamesDirty=!0;t._namedFlows.set(this._currentFlow.name,t._currentFlow)}return this.hasError&&(t._currentErrors=[],t._currentErrors.push(...this.currentErrors||[])),this.hasWarning&&(t._currentWarnings=[],t._currentWarnings.push(...this.currentWarnings||[])),t.variablesState=this.variablesState,t.variablesState.callStack=t.callStack,t.variablesState.patch=t._patch,t.evaluationStack.push(...this.evaluationStack),this.divertedPointer.isNull||(t.divertedPointer=this.divertedPointer.copy()),t.previousPointer=this.previousPointer.copy(),t._visitCounts=this._visitCounts,t._turnIndices=this._turnIndices,t.currentTurnIndex=this.currentTurnIndex,t.storySeed=this.storySeed,t.previousRandom=this.previousRandom,t.didSafeExit=this.didSafeExit,t}RestoreAfterPatch(){this.variablesState.callStack=this.callStack,this.variablesState.patch=this._patch}ApplyAnyPatch(){if(null!==this._patch){this.variablesState.ApplyPatch();for(let[t,e]of this._patch.visitCounts)this.ApplyCountChanges(t,e,!0);for(let[t,e]of this._patch.turnIndices)this.ApplyCountChanges(t,e,!1);this._patch=null}}ApplyCountChanges(t,e,n){(n?this._visitCounts:this._turnIndices).set(t.path.toString(),e)}WriteJson(t){if(t.WriteObjectStart(),t.WritePropertyStart("flows"),t.WriteObjectStart(),null!==this._namedFlows)for(let[e,n]of this._namedFlows)t.WriteProperty(e,(t=>n.WriteJson(t)));else t.WriteProperty(this._currentFlow.name,(t=>this._currentFlow.WriteJson(t)));if(t.WriteObjectEnd(),t.WritePropertyEnd(),t.WriteProperty("currentFlowName",this._currentFlow.name),t.WriteProperty("variablesState",(t=>this.variablesState.WriteJson(t))),t.WriteProperty("evalStack",(t=>Jt.WriteListRuntimeObjs(t,this.evaluationStack))),!this.divertedPointer.isNull){if(null===this.divertedPointer.path)return S("divertedPointer");t.WriteProperty("currentDivertTarget",this.divertedPointer.path.componentsString)}t.WriteProperty("visitCounts",(t=>Jt.WriteIntDictionary(t,this._visitCounts))),t.WriteProperty("turnIndices",(t=>Jt.WriteIntDictionary(t,this._turnIndices))),t.WriteIntProperty("turnIdx",this.currentTurnIndex),t.WriteIntProperty("storySeed",this.storySeed),t.WriteIntProperty("previousRandom",this.previousRandom),t.WriteIntProperty("inkSaveVersion",this.kInkSaveStateVersion),t.WriteIntProperty("inkFormatVersion",re.inkVersionCurrent),t.WriteObjectEnd()}LoadJsonObj(t){let e=t,n=e.inkSaveVersion;if(null==n)throw new Error("ink save format incorrect, can't load.");if(parseInt(n)1){let t=e.currentFlowName;this._currentFlow=this._namedFlows.get(t)}}else{this._namedFlows=null,this._currentFlow.name=this.kDefaultFlowName,this._currentFlow.callStack.SetJsonToken(e.callstackThreads,this.story),this._currentFlow.outputStream=Jt.JArrayToRuntimeObjList(e.outputStream),this._currentFlow.currentChoices=Jt.JArrayToRuntimeObjList(e.currentChoices);let t=e.choiceThreads;this._currentFlow.LoadFlowChoiceThreads(t,this.story)}this.OutputStreamDirty(),this._aliveFlowNamesDirty=!0,this.variablesState.SetJsonToken(e.variablesState),this.variablesState.callStack=this._currentFlow.callStack,this._evaluationStack=Jt.JArrayToRuntimeObjList(e.evalStack);let r=e.currentDivertTarget;if(null!=r){let t=new m(r.toString());this.divertedPointer=this.story.PointerAtPath(t)}this._visitCounts=Jt.JObjectToIntDictionary(e.visitCounts),this._turnIndices=Jt.JObjectToIntDictionary(e.turnIndices),this.currentTurnIndex=parseInt(e.turnIdx),this.storySeed=parseInt(e.storySeed),this.previousRandom=parseInt(e.previousRandom)}ResetErrors(){this._currentErrors=null,this._currentWarnings=null}ResetOutput(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;this.outputStream.length=0,null!==t&&this.outputStream.push(...t),this.OutputStreamDirty()}PushToOutputStream(t){let e=s(t,I);if(null!==e){let t=this.TrySplittingHeadTailWhitespace(e);if(null!==t){for(let e of t)this.PushToOutputStreamIndividual(e);return void this.OutputStreamDirty()}}this.PushToOutputStreamIndividual(t),this.OutputStreamDirty()}PopFromOutputStream(t){this.outputStream.splice(this.outputStream.length-t,t),this.OutputStreamDirty()}TrySplittingHeadTailWhitespace(t){let e=t.value;if(null===e)return S("single.value");let n=-1,i=-1;for(let t=0;t=0;t--){let n=e[t];if("\n"!=n){if(" "==n||"\t"==n)continue;break}-1==r&&(r=t),s=t}if(-1==n&&-1==r)return null;let a=[],o=0,l=e.length;if(-1!=n){if(n>0){let t=new I(e.substring(0,n));a.push(t)}a.push(new I("\n")),o=i+1}if(-1!=r&&(l=s),l>o){let t=e.substring(o,l);a.push(new I(t))}if(-1!=r&&s>i&&(a.push(new I("\n")),r=0;e--){let n=this.outputStream[e],i=n instanceof D?n:null;if(null!=(n instanceof Lt?n:null)){r=e;break}if(null!=i&&i.commandType==D.CommandType.BeginString){e>=t&&(t=-1);break}}let s=-1;if(s=-1!=r&&-1!=t?Math.min(t,r):-1!=r?r:t,-1!=s){if(n.isNewline)i=!1;else if(n.isNonWhitespace&&(r>-1&&this.RemoveExistingGlue(),t>-1)){let t=this.callStack.elements;for(let e=t.length-1;e>=0;e--){let n=t[e];if(n.type!=v.Function)break;n.functionStartInOutputStream=-1}}}else n.isNewline&&(!this.outputStreamEndsInNewline&&this.outputStreamContainsContent||(i=!1))}if(i){if(null===t)return S("obj");this.outputStream.push(t),this.OutputStreamDirty()}}TrimNewlinesFromOutputStream(){let t=-1,e=this.outputStream.length-1;for(;e>=0;){let n=this.outputStream[e],i=s(n,D),r=s(n,I);if(null!=i||null!=r&&r.isNonWhitespace)break;null!=r&&r.isNewline&&(t=e),e--}if(t>=0)for(e=t;e=0;t--){let e=this.outputStream[t];if(e instanceof Lt)this.outputStream.splice(t,1);else if(e instanceof D)break}this.OutputStreamDirty()}get outputStreamEndsInNewline(){if(this.outputStream.length>0)for(let t=this.outputStream.length-1;t>=0;t--){if(this.outputStream[t]instanceof D)break;let e=this.outputStream[t];if(e instanceof I){if(e.isNewline)return!0;if(e.isNonWhitespace)break}}return!1}get outputStreamContainsContent(){for(let t of this.outputStream)if(t instanceof I)return!0;return!1}get inStringEvaluation(){for(let t=this.outputStream.length-1;t>=0;t--){let e=s(this.outputStream[t],D);if(e instanceof D&&e.commandType==D.CommandType.BeginString)return!0}return!1}PushEvaluationStack(t){let e=s(t,R);if(e){let t=e.value;if(null===t)return S("rawList");if(null!=t.originNames){t.origins||(t.origins=[]),t.origins.length=0;for(let e of t.originNames){if(null===this.story.listDefinitions)return S("StoryState.story.listDefinitions");let n=this.story.listDefinitions.TryListGetDefinition(e,null);if(null===n.result)return S("StoryState def.result");t.origins.indexOf(n.result)<0&&t.origins.push(n.result)}}}if(null===t)return S("obj");this.evaluationStack.push(t)}PopEvaluationStack(t){if(void 0===t){return l(this.evaluationStack.pop())}if(t>this.evaluationStack.length)throw new Error("trying to pop too many objects");return l(this.evaluationStack.splice(this.evaluationStack.length-t,t))}PeekEvaluationStack(){return this.evaluationStack[this.evaluationStack.length-1]}ForceEnd(){this.callStack.Reset(),this._currentFlow.currentChoices.length=0,this.currentPointer=H.Null,this.previousPointer=H.Null,this.didSafeExit=!0}TrimWhitespaceFromFunctionEnd(){f.Assert(this.callStack.currentElement.type==v.Function);let t=this.callStack.currentElement.functionStartInOutputStream;-1==t&&(t=0);for(let e=this.outputStream.length-1;e>=t;e--){let t=this.outputStream[e],n=s(t,I),i=s(t,D);if(null!=n){if(i)break;if(!n.isNewline&&!n.isInlineWhitespace)break;this.outputStream.splice(e,1),this.OutputStreamDirty()}}}PopCallStack(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;this.callStack.currentElement.type==v.Function&&this.TrimWhitespaceFromFunctionEnd(),this.callStack.Pop(t)}SetChosenPath(t,e){this._currentFlow.currentChoices.length=0;let n=this.story.PointerAtPath(t);n.isNull||-1!=n.index||(n.index=0),this.currentPointer=n,e&&this.currentTurnIndex++}StartFunctionEvaluationFromGame(t,e){this.callStack.Push(v.FunctionEvaluationFromGame,this.evaluationStack.length),this.callStack.currentElement.currentPointer=H.StartOf(t),this.PassArgumentsToEvaluationStack(e)}PassArgumentsToEvaluationStack(t){if(null!==t)for(let e=0;et;){let t=this.PopEvaluationStack();null===e&&(e=t)}if(this.PopCallStack(v.FunctionEvaluationFromGame),e){if(e instanceof j)return null;let t=a(e,A);return t.valueType==g.DivertTarget?t.valueObject.toString():t.valueObject}return null}AddError(t,e){e?(null==this._currentWarnings&&(this._currentWarnings=[]),this._currentWarnings.push(t)):(null==this._currentErrors&&(this._currentErrors=[]),this._currentErrors.push(t))}OutputStreamDirty(){this._outputStreamTextDirty=!0,this._outputStreamTagsDirty=!0}}class ne{constructor(){this.startTime=void 0}get ElapsedMilliseconds(){return void 0===this.startTime?0:(new Date).getTime()-this.startTime}Start(){this.startTime=(new Date).getTime()}Stop(){this.startTime=void 0}}var ie;!function(t){t[t.Author=0]="Author",t[t.Warning=1]="Warning",t[t.Error=2]="Error"}(ie||(ie={})),Number.isInteger||(Number.isInteger=function(t){return"number"==typeof t&&isFinite(t)&&t>-9007199254740992&&t<9007199254740992&&Math.floor(t)===t});let re=class t extends y{get currentChoices(){let t=[];if(null===this._state)return S("this._state");for(let e of this._state.currentChoices)e.isInvisibleDefault||(e.index=t.length,t.push(e));return t}get currentText(){return this.IfAsyncWeCant("call currentText since it's a work in progress"),this.state.currentText}get currentTags(){return this.IfAsyncWeCant("call currentTags since it's a work in progress"),this.state.currentTags}get currentErrors(){return this.state.currentErrors}get currentWarnings(){return this.state.currentWarnings}get currentFlowName(){return this.state.currentFlowName}get currentFlowIsDefaultFlow(){return this.state.currentFlowIsDefaultFlow}get aliveFlowNames(){return this.state.aliveFlowNames}get hasError(){return this.state.hasError}get hasWarning(){return this.state.hasWarning}get variablesState(){return this.state.variablesState}get listDefinitions(){return this._listDefinitions}get state(){return this._state}StartProfiling(){}EndProfiling(){}constructor(){let e;super(),this.inkVersionMinimumCompatible=18,this.onError=null,this.onDidContinue=null,this.onMakeChoice=null,this.onEvaluateFunction=null,this.onCompleteEvaluateFunction=null,this.onChoosePathString=null,this._prevContainers=[],this.allowExternalFunctionFallbacks=!1,this._listDefinitions=null,this._variableObservers=null,this._hasValidatedExternals=!1,this._temporaryEvaluationContainer=null,this._asyncContinueActive=!1,this._stateSnapshotAtLastNewline=null,this._sawLookaheadUnsafeFunctionAfterNewline=!1,this._recursiveContinueCount=0,this._asyncSaving=!1,this._profiler=null;let n=null,i=null;if(arguments[0]instanceof L)e=arguments[0],void 0!==arguments[1]&&(n=arguments[1]),this._mainContentContainer=e;else if("string"==typeof arguments[0]){let t=arguments[0];i=Qt.TextToDictionary(t)}else i=arguments[0];if(null!=n&&(this._listDefinitions=new Ht(n)),this._externals=new Map,null!==i){let e=i,n=e.inkVersion;if(null==n)throw new Error("ink version number not found. Are you sure it's a valid .ink.json file?");let r=parseInt(n);if(r>t.inkVersionCurrent)throw new Error("Version of ink used to build story was newer than the current version of the engine");if(rJt.WriteRuntimeContainer(t,this._mainContentContainer))),null!=this._listDefinitions){e.WritePropertyStart("listDefs"),e.WriteObjectStart();for(let t of this._listDefinitions.lists){e.WritePropertyStart(t.name),e.WriteObjectStart();for(let[n,i]of t.items){let t=w.fromSerializedKey(n),r=i;e.WriteIntProperty(t.itemName,r)}e.WriteObjectEnd(),e.WritePropertyEnd()}e.WriteObjectEnd(),e.WritePropertyEnd()}if(e.WriteObjectEnd(),n)return e.toString()}ResetState(){this.IfAsyncWeCant("ResetState"),this._state=new ee(this),this._state.variablesState.ObserveVariableChange(this.VariableStateDidChangeEvent.bind(this)),this.ResetGlobals()}ResetErrors(){if(null===this._state)return S("this._state");this._state.ResetErrors()}ResetCallstack(){if(this.IfAsyncWeCant("ResetCallstack"),null===this._state)return S("this._state");this._state.ForceEnd()}ResetGlobals(){if(this._mainContentContainer.namedContent.get("global decl")){let t=this.state.currentPointer.copy();this.ChoosePath(new m("global decl"),!1),this.ContinueInternal(),this.state.currentPointer=t}this.state.variablesState.SnapshotDefaultGlobals()}SwitchFlow(t){if(this.IfAsyncWeCant("switch flow"),this._asyncSaving)throw new Error("Story is already in background saving mode, can't switch flow to "+t);this.state.SwitchFlow_Internal(t)}RemoveFlow(t){this.state.RemoveFlow_Internal(t)}SwitchToDefaultFlow(){this.state.SwitchToDefaultFlow_Internal()}Continue(){return this.ContinueAsync(0),this.currentText}get canContinue(){return this.state.canContinue}get asyncContinueComplete(){return!this._asyncContinueActive}ContinueAsync(t){this._hasValidatedExternals||this.ValidateExternalBindings(),this.ContinueInternal(t)}ContinueInternal(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;null!=this._profiler&&this._profiler.PreContinue();let e=t>0;if(this._recursiveContinueCount++,!this._asyncContinueActive){if(this._asyncContinueActive=e,!this.canContinue)throw new Error("Can't continue - should check canContinue before calling Continue");this._state.didSafeExit=!1,this._state.ResetOutput(),1==this._recursiveContinueCount&&(this._state.variablesState.batchObservingVariableChanges=!0)}let n=new ne;n.Start();let i=!1;this._sawLookaheadUnsafeFunctionAfterNewline=!1;do{try{i=this.ContinueSingleStep()}catch(t){if(!(t instanceof _))throw t;this.AddError(t.message,void 0,t.useEndLineNumber);break}if(i)break;if(this._asyncContinueActive&&n.ElapsedMilliseconds>t)break}while(this.canContinue);if(n.Stop(),!i&&this.canContinue||(null!==this._stateSnapshotAtLastNewline&&this.RestoreStateSnapshot(),this.canContinue||(this.state.callStack.canPopThread&&this.AddError("Thread available to pop, threads should always be flat by the end of evaluation?"),0!=this.state.generatedChoices.length||this.state.didSafeExit||null!=this._temporaryEvaluationContainer||(this.state.callStack.CanPop(v.Tunnel)?this.AddError("unexpectedly reached end of content. Do you need a '->->' to return from a tunnel?"):this.state.callStack.CanPop(v.Function)?this.AddError("unexpectedly reached end of content. Do you need a '~ return'?"):this.state.callStack.canPop?this.AddError("unexpectedly reached end of content for unknown reason. Please debug compiler!"):this.AddError("ran out of content. Do you need a '-> DONE' or '-> END'?"))),this.state.didSafeExit=!1,this._sawLookaheadUnsafeFunctionAfterNewline=!1,1==this._recursiveContinueCount&&(this._state.variablesState.batchObservingVariableChanges=!1),this._asyncContinueActive=!1,null!==this.onDidContinue&&this.onDidContinue()),this._recursiveContinueCount--,null!=this._profiler&&this._profiler.PostContinue(),this.state.hasError||this.state.hasWarning){if(null===this.onError){let t=new b;throw t.Append("Ink had "),this.state.hasError&&(t.Append(`${this.state.currentErrors.length}`),t.Append(1==this.state.currentErrors.length?" error":"errors"),this.state.hasWarning&&t.Append(" and ")),this.state.hasWarning&&(t.Append(`${this.state.currentWarnings.length}`),t.Append(1==this.state.currentWarnings.length?" warning":"warnings"),this.state.hasWarning&&t.Append(" and ")),t.Append(". It is strongly suggested that you assign an error handler to story.onError. The first issue was: "),t.Append(this.state.hasError?this.state.currentErrors[0]:this.state.currentWarnings[0]),new _(t.toString())}if(this.state.hasError)for(let t of this.state.currentErrors)this.onError(t,ie.Error);if(this.state.hasWarning)for(let t of this.state.currentWarnings)this.onError(t,ie.Warning);this.ResetErrors()}}ContinueSingleStep(){if(null!=this._profiler&&this._profiler.PreStep(),this.Step(),null!=this._profiler&&this._profiler.PostStep(),this.canContinue||this.state.callStack.elementIsEvaluateFromGame||this.TryFollowDefaultInvisibleChoice(),null!=this._profiler&&this._profiler.PreSnapshot(),!this.state.inStringEvaluation){if(null!==this._stateSnapshotAtLastNewline){if(null===this._stateSnapshotAtLastNewline.currentTags)return S("this._stateAtLastNewline.currentTags");if(null===this.state.currentTags)return S("this.state.currentTags");let e=this.CalculateNewlineOutputStateChange(this._stateSnapshotAtLastNewline.currentText,this.state.currentText,this._stateSnapshotAtLastNewline.currentTags.length,this.state.currentTags.length);if(e==t.OutputStateChange.ExtendedBeyondNewline||this._sawLookaheadUnsafeFunctionAfterNewline)return this.RestoreStateSnapshot(),!0;e==t.OutputStateChange.NewlineRemoved&&this.DiscardSnapshot()}this.state.outputStreamEndsInNewline&&(this.canContinue?null==this._stateSnapshotAtLastNewline&&this.StateSnapshot():this.DiscardSnapshot())}return null!=this._profiler&&this._profiler.PostSnapshot(),!1}CalculateNewlineOutputStateChange(e,n,i,r){if(null===e)return S("prevText");if(null===n)return S("currText");let s=n.length>=e.length&&e.length>0&&"\n"==n.charAt(e.length-1);if(i==r&&e.length==n.length&&s)return t.OutputStateChange.NoChange;if(!s)return t.OutputStateChange.NewlineRemoved;if(r>i)return t.OutputStateChange.ExtendedBeyondNewline;for(let i=e.length;i0?this.Error("Failed to find content at path '"+t+"', and no approximation of it was possible."):i.approximate&&this.Warning("Failed to find content at path '"+t+"', so it was approximated to: '"+i.obj.path+"'."),e)}StateSnapshot(){this._stateSnapshotAtLastNewline=this._state,this._state=this._state.CopyAndStartPatching()}RestoreStateSnapshot(){null===this._stateSnapshotAtLastNewline&&S("_stateSnapshotAtLastNewline"),this._stateSnapshotAtLastNewline.RestoreAfterPatch(),this._state=this._stateSnapshotAtLastNewline,this._stateSnapshotAtLastNewline=null,this._asyncSaving||this._state.ApplyAnyPatch()}DiscardSnapshot(){this._asyncSaving||this._state.ApplyAnyPatch(),this._stateSnapshotAtLastNewline=null}CopyStateForBackgroundThreadSave(){if(this.IfAsyncWeCant("start saving on a background thread"),this._asyncSaving)throw new Error("Story is already in background saving mode, can't call CopyStateForBackgroundThreadSave again!");let t=this._state;return this._state=this._state.CopyAndStartPatching(),this._asyncSaving=!0,t}BackgroundSaveComplete(){null===this._stateSnapshotAtLastNewline&&this._state.ApplyAnyPatch(),this._asyncSaving=!1}Step(){let t=!0,e=this.state.currentPointer.copy();if(e.isNull)return;let n=s(e.Resolve(),L);for(;n&&(this.VisitContainer(n,!0),0!=n.content.length);)e=H.StartOf(n),n=s(e.Resolve(),L);this.state.currentPointer=e.copy(),null!=this._profiler&&this._profiler.Step(this.state.callStack);let i=e.Resolve(),r=this.PerformLogicAndFlowControl(i);if(this.state.currentPointer.isNull)return;r&&(t=!1);let a=s(i,K);if(a){let e=this.ProcessChoice(a);e&&this.state.generatedChoices.push(e),i=null,t=!1}if(i instanceof L&&(t=!1),t){let t=s(i,W);if(t&&-1==t.contextIndex){let e=this.state.callStack.ContextForVariableNamed(t.variableName);i=new W(t.variableName,e)}this.state.inExpressionEvaluation?this.state.PushEvaluationStack(i):this.state.PushToOutputStream(i)}this.NextContent();let o=s(i,D);o&&o.commandType==D.CommandType.StartThread&&this.state.callStack.PushThread()}VisitContainer(t,e){t.countingAtStartOnly&&!e||(t.visitsShouldBeCounted&&this.state.IncrementVisitCountForContainer(t),t.turnIndexShouldBeCounted&&this.state.RecordTurnIndexVisitToContainer(t))}VisitChangedContainersDueToDivert(){let t=this.state.previousPointer.copy(),e=this.state.currentPointer.copy();if(e.isNull||-1==e.index)return;if(this._prevContainers.length=0,!t.isNull){let e=s(t.Resolve(),L)||s(t.container,L);for(;e;)this._prevContainers.push(e),e=s(e.parent,L)}let n=e.Resolve();if(null==n)return;let i=s(n.parent,L),r=!0;for(;i&&(this._prevContainers.indexOf(i)<0||i.countingAtStartOnly);){let t=i.content.length>0&&n==i.content[0]&&r;t||(r=!1),this.VisitContainer(i,t),n=i,i=s(i.parent,L)}}PopChoiceStringAndTags(t){let e=a(this.state.PopEvaluationStack(),I);for(;this.state.evaluationStack.length>0&&null!=s(this.state.PeekEvaluationStack(),Ut);){let e=s(this.state.PopEvaluationStack(),Ut);e&&t.push(e.text)}return e.value}ProcessChoice(t){let e=!0;if(t.hasCondition){let t=this.state.PopEvaluationStack();this.IsTruthy(t)||(e=!1)}let n="",i="",r=[];if(t.hasChoiceOnlyContent&&(i=this.PopChoiceStringAndTags(r)||""),t.hasStartContent&&(n=this.PopChoiceStringAndTags(r)||""),t.onceOnly){this.state.VisitCountForContainer(t.choiceTarget)>0&&(e=!1)}if(!e)return null;let s=new Kt;return s.targetPath=t.pathOnChoice,s.sourcePath=t.path.toString(),s.isInvisibleDefault=t.isInvisibleDefault,s.threadAtGeneration=this.state.callStack.ForkThread(),s.tags=r.reverse(),s.text=(n+i).replace(/^[ \t]+|[ \t]+$/g,""),s}IsTruthy(t){if(t instanceof A){let e=t;if(e instanceof F){let t=e;return this.Error("Shouldn't use a divert target (to "+t.targetPath+") as a conditional value. Did you intend a function call 'likeThis()' or a read count check 'likeThis'? (no arrows)"),!1}return e.isTruthy}return!1}PerformLogicAndFlowControl(t){if(null==t)return!1;if(t instanceof J){let e=t;if(e.isConditional){let t=this.state.PopEvaluationStack();if(!this.IsTruthy(t))return!0}if(e.hasVariableTarget){let t=e.variableDivertName,n=this.state.variablesState.GetVariableWithName(t);if(null==n)this.Error("Tried to divert using a target from a variable that could not be found ("+t+")");else if(!(n instanceof F)){let e=s(n,N),i="Tried to divert to a target from a variable, but the variable ("+t+") didn't contain a divert target, it ";e instanceof N&&0==e.value?i+="was empty/null (the value 0).":i+="contained '"+n+"'.",this.Error(i)}let i=a(n,F);this.state.divertedPointer=this.PointerAtPath(i.targetPath)}else{if(e.isExternal)return this.CallExternalFunction(e.targetPathString,e.externalArgs),!0;this.state.divertedPointer=e.targetPointer.copy()}return e.pushesToStack&&this.state.callStack.Push(e.stackPushType,void 0,this.state.outputStream.length),this.state.divertedPointer.isNull&&!e.isExternal&&(e&&e.debugMetadata&&null!=e.debugMetadata.sourceName?this.Error("Divert target doesn't exist: "+e.debugMetadata.sourceName):this.Error("Divert resolution failed: "+e)),!0}if(t instanceof D){let e=t;switch(e.commandType){case D.CommandType.EvalStart:this.Assert(!1===this.state.inExpressionEvaluation,"Already in expression evaluation?"),this.state.inExpressionEvaluation=!0;break;case D.CommandType.EvalEnd:this.Assert(!0===this.state.inExpressionEvaluation,"Not in expression evaluation mode"),this.state.inExpressionEvaluation=!1;break;case D.CommandType.EvalOutput:if(this.state.evaluationStack.length>0){let t=this.state.PopEvaluationStack();if(!(t instanceof j)){let e=new I(t.toString());this.state.PushToOutputStream(e)}}break;case D.CommandType.NoOp:break;case D.CommandType.Duplicate:this.state.PushEvaluationStack(this.state.PeekEvaluationStack());break;case D.CommandType.PopEvaluatedValue:this.state.PopEvaluationStack();break;case D.CommandType.PopFunction:case D.CommandType.PopTunnel:let t=e.commandType==D.CommandType.PopFunction?v.Function:v.Tunnel,n=null;if(t==v.Tunnel){let t=this.state.PopEvaluationStack();n=s(t,F),null===n&&this.Assert(t instanceof j,"Expected void if ->-> doesn't override target")}if(this.state.TryExitFunctionEvaluationFromGame())break;if(this.state.callStack.currentElement.type==t&&this.state.callStack.canPop)this.state.PopCallStack(),n&&(this.state.divertedPointer=this.PointerAtPath(n.targetPath));else{let e=new Map;e.set(v.Function,"function return statement (~ return)"),e.set(v.Tunnel,"tunnel onwards statement (->->)");let n=e.get(this.state.callStack.currentElement.type);this.state.callStack.canPop||(n="end of flow (-> END or choice)");let i="Found "+e.get(t)+", when expected "+n;this.Error(i)}break;case D.CommandType.BeginString:this.state.PushToOutputStream(e),this.Assert(!0===this.state.inExpressionEvaluation,"Expected to be in an expression when evaluating a string"),this.state.inExpressionEvaluation=!1;break;case D.CommandType.BeginTag:this.state.PushToOutputStream(e);break;case D.CommandType.EndTag:if(this.state.inStringEvaluation){let t=[],e=0;for(let n=this.state.outputStream.length-1;n>=0;--n){let i=this.state.outputStream[n];e++;let r=s(i,D);if(null!=r){if(r.commandType==D.CommandType.BeginTag)break;this.Error("Unexpected ControlCommand while extracting tag from choice");break}i instanceof I&&t.push(i)}this.state.PopFromOutputStream(e);let n=new b;for(let e of t.reverse())n.Append(e.toString());let i=new Ut(this.state.CleanOutputWhitespace(n.toString()));this.state.PushEvaluationStack(i)}else this.state.PushToOutputStream(e);break;case D.CommandType.EndString:{let t=[],e=[],n=0;for(let i=this.state.outputStream.length-1;i>=0;--i){let r=this.state.outputStream[i];n++;let a=s(r,D);if(a&&a.commandType==D.CommandType.BeginString)break;r instanceof Ut&&e.push(r),r instanceof I&&t.push(r)}this.state.PopFromOutputStream(n);for(let t of e)this.state.PushToOutputStream(t);t=t.reverse();let i=new b;for(let e of t)i.Append(e.toString());this.state.inExpressionEvaluation=!0,this.state.PushEvaluationStack(new I(i.toString()));break}case D.CommandType.ChoiceCount:let i=this.state.generatedChoices.length;this.state.PushEvaluationStack(new N(i));break;case D.CommandType.Turns:this.state.PushEvaluationStack(new N(this.state.currentTurnIndex+1));break;case D.CommandType.TurnsSince:case D.CommandType.ReadCount:let r=this.state.PopEvaluationStack();if(!(r instanceof F)){let t="";r instanceof N&&(t=". Did you accidentally pass a read count ('knot_name') instead of a target ('-> knot_name')?"),this.Error("TURNS_SINCE / READ_COUNT expected a divert target (knot, stitch, label name), but saw "+r+t);break}let o,l=a(r,F),h=s(this.ContentAtPath(l.targetPath).correctObj,L);null!=h?o=e.commandType==D.CommandType.TurnsSince?this.state.TurnsSinceForContainer(h):this.state.VisitCountForContainer(h):(o=e.commandType==D.CommandType.TurnsSince?-1:0,this.Warning("Failed to find container for "+e.toString()+" lookup at "+l.targetPath.toString())),this.state.PushEvaluationStack(new N(o));break;case D.CommandType.Random:{let t=s(this.state.PopEvaluationStack(),N),e=s(this.state.PopEvaluationStack(),N);if(null==e||e instanceof N==!1)return this.Error("Invalid value for minimum parameter of RANDOM(min, max)");if(null==t||t instanceof N==!1)return this.Error("Invalid value for maximum parameter of RANDOM(min, max)");if(null===t.value)return S("maxInt.value");if(null===e.value)return S("minInt.value");let n=t.value-e.value+1;(!isFinite(n)||n>Number.MAX_SAFE_INTEGER)&&(n=Number.MAX_SAFE_INTEGER,this.Error("RANDOM was called with a range that exceeds the size that ink numbers can use.")),n<=0&&this.Error("RANDOM was called with minimum as "+e.value+" and maximum as "+t.value+". The maximum must be larger");let i=this.state.storySeed+this.state.previousRandom,r=new Yt(i).next(),a=r%n+e.value;this.state.PushEvaluationStack(new N(a)),this.state.previousRandom=r;break}case D.CommandType.SeedRandom:let u=s(this.state.PopEvaluationStack(),N);if(null==u||u instanceof N==!1)return this.Error("Invalid value passed to SEED_RANDOM");if(null===u.value)return S("minInt.value");this.state.storySeed=u.value,this.state.previousRandom=0,this.state.PushEvaluationStack(new j);break;case D.CommandType.VisitIndex:let c=this.state.VisitCountForContainer(this.state.currentPointer.container)-1;this.state.PushEvaluationStack(new N(c));break;case D.CommandType.SequenceShuffleIndex:let d=this.NextSequenceShuffleIndex();this.state.PushEvaluationStack(new N(d));break;case D.CommandType.StartThread:break;case D.CommandType.Done:this.state.callStack.canPopThread?this.state.callStack.PopThread():(this.state.didSafeExit=!0,this.state.currentPointer=H.Null);break;case D.CommandType.End:this.state.ForceEnd();break;case D.CommandType.ListFromInt:let p=s(this.state.PopEvaluationStack(),N),m=a(this.state.PopEvaluationStack(),I);if(null===p)throw new _("Passed non-integer when creating a list element from a numerical value.");let f=null;if(null===this.listDefinitions)return S("this.listDefinitions");let g=this.listDefinitions.TryListGetDefinition(m.value,null);if(!g.exists)throw new _("Failed to find LIST called "+m.value);{if(null===p.value)return S("minInt.value");let t=g.result.TryGetItemWithValue(p.value,w.Null);t.exists&&(f=new R(t.result,p.value))}null==f&&(f=new R),this.state.PushEvaluationStack(f);break;case D.CommandType.ListRange:let C=s(this.state.PopEvaluationStack(),A),y=s(this.state.PopEvaluationStack(),A),T=s(this.state.PopEvaluationStack(),R);if(null===T||null===y||null===C)throw new _("Expected list, minimum and maximum for LIST_RANGE");if(null===T.value)return S("targetList.value");let x=T.value.ListWithSubRange(y.valueObject,C.valueObject);this.state.PushEvaluationStack(new R(x));break;case D.CommandType.ListRandom:{let t=this.state.PopEvaluationStack();if(null===t)throw new _("Expected list for LIST_RANDOM");let e=t.value,n=null;if(null===e)throw S("list");if(0==e.Count)n=new E;else{let t=this.state.storySeed+this.state.previousRandom,i=new Yt(t).next(),r=i%e.Count,s=e.entries();for(let t=0;t<=r-1;t++)s.next();let a=s.next().value,o={Key:w.fromSerializedKey(a[0]),Value:a[1]};if(null===o.Key.originName)return S("randomItem.Key.originName");n=new E(o.Key.originName,this),n.Add(o.Key,o.Value),this.state.previousRandom=i}this.state.PushEvaluationStack(new R(n));break}default:this.Error("unhandled ControlCommand: "+e)}return!0}if(t instanceof X){let e=t,n=this.state.PopEvaluationStack();return this.state.variablesState.Assign(e,n),!0}if(t instanceof mt){let e=t,n=null;if(null!=e.pathForCount){let t=e.containerForCount,i=this.state.VisitCountForContainer(t);n=new N(i)}else n=this.state.variablesState.GetVariableWithName(e.name),null==n&&(this.Warning("Variable not found: '"+e.name+"'. Using default value of 0 (false). This can happen with temporary variables if the declaration hasn't yet been hit. Globals are always given a default value on load if a value doesn't exist in the save state."),n=new N(0));return this.state.PushEvaluationStack(n),!0}if(t instanceof B){let e=t,n=this.state.PopEvaluationStack(e.numberOfParameters),i=e.Call(n);return this.state.PushEvaluationStack(i),!0}return!1}ChoosePathString(t){let e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[];if(this.IfAsyncWeCant("call ChoosePathString right now"),null!==this.onChoosePathString&&this.onChoosePathString(t,n),e)this.ResetCallstack();else if(this.state.callStack.currentElement.type==v.Function){let e="",n=this.state.callStack.currentElement.currentPointer.container;throw null!=n&&(e="("+n.path.toString()+") "),new Error("Story was running a function "+e+"when you called ChoosePathString("+t+") - this is almost certainly not not what you want! Full stack trace: \n"+this.state.callStack.callStackTrace)}this.state.PassArgumentsToEvaluationStack(n),this.ChoosePath(new m(t))}IfAsyncWeCant(t){if(this._asyncContinueActive)throw new Error("Can't "+t+". Story is in the middle of a ContinueAsync(). Make more ContinueAsync() calls or a single Continue() call beforehand.")}ChoosePath(t){let e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];this.state.SetChosenPath(t,e),this.VisitChangedContainersDueToDivert()}ChooseChoiceIndex(t){let e=this.currentChoices;this.Assert(t>=0&&t1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(null!==this.onEvaluateFunction&&this.onEvaluateFunction(t,e),this.IfAsyncWeCant("evaluate a function"),null==t)throw new Error("Function is null");if(""==t||""==t.trim())throw new Error("Function is empty or white space.");let i=this.KnotContainerWithName(t);if(null==i)throw new Error("Function doesn't exist: '"+t+"'");let r=[];r.push(...this.state.outputStream),this._state.ResetOutput(),this.state.StartFunctionEvaluationFromGame(i,e);let s=new b;for(;this.canContinue;)s.Append(this.Continue());let a=s.toString();this._state.ResetOutput(r);let o=this.state.CompleteFunctionEvaluationFromGame();return null!=this.onCompleteEvaluateFunction&&this.onCompleteEvaluateFunction(t,e,a,o),n?{returned:o,output:a}:o}EvaluateExpression(t){let e=this.state.callStack.elements.length;this.state.callStack.Push(v.Tunnel),this._temporaryEvaluationContainer=t,this.state.GoToStart();let n=this.state.evaluationStack.length;return this.Continue(),this._temporaryEvaluationContainer=null,this.state.callStack.elements.length>e&&this.state.PopCallStack(),this.state.evaluationStack.length>n?this.state.PopEvaluationStack():null}CallExternalFunction(t,e){if(null===t)return S("funcName");let n=this._externals.get(t),i=null,r=void 0!==n;if(r&&!n.lookAheadSafe&&null!==this._stateSnapshotAtLastNewline)return void(this._sawLookaheadUnsafeFunctionAfterNewline=!0);if(!r){if(this.allowExternalFunctionFallbacks)return i=this.KnotContainerWithName(t),this.Assert(null!==i,"Trying to call EXTERNAL function '"+t+"' which has not been bound, and fallback ink function could not be found."),this.state.callStack.Push(v.Function,void 0,this.state.outputStream.length),void(this.state.divertedPointer=H.StartOf(i));this.Assert(!1,"Trying to call EXTERNAL function '"+t+"' which has not been bound (and ink fallbacks disabled).")}let s=[];for(let t=0;t2&&void 0!==arguments[2])||arguments[2];this.IfAsyncWeCant("bind an external function"),this.Assert(!this._externals.has(t),"Function '"+t+"' has already been bound."),this._externals.set(t,{function:e,lookAheadSafe:n})}TryCoerce(t){return t}BindExternalFunction(t,e){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];this.Assert(null!=e,"Can't bind a null function"),this.BindExternalFunctionGeneral(t,(t=>{this.Assert(t.length>=e.length,"External function expected "+e.length+" arguments");let n=[];for(let e=0,i=t.length;e1?"s":"",t+=": '",t+=Array.from(n).join("', '"),t+="' ",t+=this.allowExternalFunctionFallbacks?", and no fallback ink function found.":" (ink fallbacks disabled)",this.Error(t)}else if(null!=t){for(let e of t.content){null!=e&&e.hasValidName||this.ValidateExternalBindings(e,n)}for(let[,e]of t.namedContent)this.ValidateExternalBindings(s(e,y),n)}else if(null!=e){let t=s(e,J);if(t&&t.isExternal){let e=t.targetPathString;if(null===e)return S("name");if(!this._externals.has(e))if(this.allowExternalFunctionFallbacks){this.mainContentContainer.namedContent.has(e)||n.add(e)}else n.add(e)}}}ObserveVariable(t,e){if(this.IfAsyncWeCant("observe a new variable"),null===this._variableObservers&&(this._variableObservers=new Map),!this.state.variablesState.GlobalVariableExistsWithName(t))throw new Error("Cannot observe variable '"+t+"' because it wasn't declared in the ink story.");this._variableObservers.has(t)?this._variableObservers.get(t).push(e):this._variableObservers.set(t,[e])}ObserveVariables(t,e){for(let n=0,i=t.length;n=e.container.content.length;){t=!1;let n=s(e.container.parent,L);if(n instanceof L==!1)break;let i=n.content.indexOf(e.container);if(-1==i)break;if(e=new H(n,i),e.index++,t=!0,null===e.container)return S("pointer.container")}return t||(e=H.Null),this.state.callStack.currentElement.currentPointer=e.copy(),t}TryFollowDefaultInvisibleChoice(){let t=this._state.currentChoices,e=t.filter((t=>t.isInvisibleDefault));if(0==e.length||t.length>e.length)return!1;let n=e[0];return null===n.targetPath?S("choice.targetPath"):null===n.threadAtGeneration?S("choice.threadAtGeneration"):(this.state.callStack.currentThread=n.threadAtGeneration,null!==this._stateSnapshotAtLastNewline&&(this.state.callStack.currentThread=this.state.callStack.ForkThread()),this.ChoosePath(n.targetPath,!1),!0)}NextSequenceShuffleIndex(){let t=s(this.state.PopEvaluationStack(),N);if(!(t instanceof N))return this.Error("expected number of elements in sequence for shuffle index"),0;let e=this.state.currentPointer.container;if(null===e)return S("seqContainer");if(null===t.value)return S("numElementsIntVal.value");let n=t.value,i=a(this.state.PopEvaluationStack(),N).value;if(null===i)return S("seqCount");let r=i/n,o=i%n,l=e.path.toString(),h=0;for(let t=0,e=l.length;t1&&void 0!==arguments[1]&&arguments[1],n=new _(t);throw n.useEndLineNumber=e,n}Warning(t){this.AddError(t,!0)}AddError(t){let e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=this.currentDebugMetadata,r=e?"WARNING":"ERROR";if(null!=i){let e=n?i.endLineNumber:i.startLineNumber;t="RUNTIME "+r+": '"+i.fileName+"' line "+e+": "+t}else t=this.state.currentPointer.isNull?"RUNTIME "+r+": "+t:"RUNTIME "+r+": ("+this.state.currentPointer+"): "+t;this.state.AddError(t,e),e||this.state.ForceEnd()}Assert(t){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;if(0==t)throw null==e&&(e="Story assert"),new Error(e+" "+this.currentDebugMetadata)}get currentDebugMetadata(){let t,e=this.state.currentPointer;if(!e.isNull&&null!==e.Resolve()&&(t=e.Resolve().debugMetadata,null!==t))return t;for(let n=this.state.callStack.elements.length-1;n>=0;--n)if(e=this.state.callStack.elements[n].currentPointer,!e.isNull&&null!==e.Resolve()&&(t=e.Resolve().debugMetadata,null!==t))return t;for(let e=this.state.outputStream.length-1;e>=0;--e){if(t=this.state.outputStream[e].debugMetadata,null!==t)return t}return null}get mainContentContainer(){return this._temporaryEvaluationContainer?this._temporaryEvaluationContainer:this._mainContentContainer}};re.inkVersionCurrent=21,function(t){var e;(e=t.OutputStateChange||(t.OutputStateChange={}))[e.NoChange=0]="NoChange",e[e.ExtendedBeyondNewline=1]="ExtendedBeyondNewline",e[e.NewlineRemoved=2]="NewlineRemoved"}(re||(re={}));class se extends dt{get flowLevel(){return at.Story}get hadError(){return this._hadError}get hadWarning(){return this._hadWarning}constructor(t){var e;super(null,t,null,!1,arguments.length>1&&void 0!==arguments[1]&&arguments[1]),e=this,this._errorHandler=null,this._hadError=!1,this._hadWarning=!1,this._dontFlattenContainers=new Set,this._listDefs=new Map,this.constants=new Map,this.externals=new Map,this.countAllVisits=!1,this.ExportRuntime=function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null;var n,i;e._errorHandler=t,e.constants=new Map;for(const t of e.FindAll(st)()){const n=e.constants.get(t.constantName);if(n&&!n.Equals(t.expression)){const i=`CONST '${t.constantName}' has been redefined with a different value. Multiple definitions of the same CONST are valid so long as they contain the same value. Initial definition was on ${n.debugMetadata}.`;e.Error(i,t,!1)}e.constants.set(t.constantName,t.expression)}e._listDefs=new Map;for(const t of e.FindAll(xt)())(null===(n=t.identifier)||void 0===n?void 0:n.name)&&e._listDefs.set(null===(i=t.identifier)||void 0===i?void 0:i.name,t);e.externals=new Map,e.ResolveWeavePointNaming();const r=e.runtimeObject,s=new L;s.AddContent(D.EvalStart());const a=[];for(const[t,n]of e.variableDeclarations)if(n.isGlobalDeclaration){if(n.listDefinition)e._listDefs.set(t,n.listDefinition),s.AddContent(n.listDefinition.runtimeObject),a.push(n.listDefinition.runtimeListDefinition);else{if(!n.expression)throw new Error;n.expression.GenerateIntoContainer(s)}const i=new X(t,!0);i.isGlobal=!0,s.AddContent(i)}s.AddContent(D.EvalEnd()),s.AddContent(D.End()),e.variableDeclarations.size>0&&(s.name="global decl",r.AddToNamedContentOnly(s)),r.AddContent(D.Done());const o=new re(r,a);return e.runtimeObject=o,e.hadError?null:(e.FlattenContainersIn(r),e.ResolveReferences(e),e.hadError?null:(o.ResetState(),o))},this.ResolveList=t=>{let e=this._listDefs.get(t);return e||null},this.ResolveListItem=function(t,n){let i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=null;if(t)return(r=e._listDefs.get(t))?r.ItemNamed(n):null;{let t=null,r=null;for(const[,s]of e._listDefs.entries()){const a=s.ItemNamed(n);a&&(t?e.Error(`Ambiguous item name '${n}' found in multiple sets, including ${r.identifier} and ${s.identifier}`,i,!1):(t=a,r=s))}return t}},this.FlattenContainersIn=t=>{const e=new Set;if(t.content)for(const n of t.content){const t=s(n,L);t&&e.add(t)}if(t.namedContent)for(const[,n]of t.namedContent){const t=s(n,L);t&&e.add(t)}for(const t of e)this.TryFlattenContainer(t),this.FlattenContainersIn(t)},this.TryFlattenContainer=t=>{if(t.namedContent&&t.namedContent.size>0||t.hasValidName||this._dontFlattenContainers.has(t))return;const e=s(t.parent,L);if(e){let n=e.content.indexOf(t);e.content.splice(n,1);const i=t.ownDebugMetadata;if(t.content)for(const r of t.content)r.parent=null,null!==i&&null===r.ownDebugMetadata&&(r.debugMetadata=i),e.InsertContent(r,n),n+=1}},this.Error=(t,e,n)=>{let r=n?i.Warning:i.Error,s="";if(e instanceof p?(s+="TODO: ",r=i.Author):s+=n?"WARNING: ":"ERROR: ",e&&null!==e.debugMetadata&&e.debugMetadata.startLineNumber>=1&&(null!=e.debugMetadata.fileName&&(s+=`'${e.debugMetadata.fileName}' `),s+=`line ${e.debugMetadata.startLineNumber}: `),s+=t,t=s,null===this._errorHandler)throw new Error(t);this._errorHandler(t,r),this._hadError=r===i.Error,this._hadWarning=r===i.Warning},this.ResetError=()=>{this._hadError=!1,this._hadWarning=!1},this.IsExternal=t=>this.externals.has(t),this.AddExternal=t=>{this.externals.has(t.name)?this.Error(`Duplicate EXTERNAL definition of '${t.name}'`,t,!1):t.name&&this.externals.set(t.name,t)},this.DontFlattenContainer=t=>{this._dontFlattenContainers.add(t)},this.NameConflictError=(t,e,n,i)=>{t.Error(`${i} '${e}': name has already been used for a ${n.typeName.toLowerCase()} on ${n.debugMetadata}`)},this.CheckForNamingCollisions=function(t,n,i){var r;const a=(arguments.length>3&&void 0!==arguments[3]?arguments[3]:"")||t.typeName;if(se.IsReservedKeyword(null==n?void 0:n.name))return void t.Error(`'${n}' cannot be used for the name of a ${a.toLowerCase()} because it's a reserved keyword`);if(gt.IsBuiltIn((null==n?void 0:n.name)||""))return void t.Error(`'${n}' cannot be used for the name of a ${a.toLowerCase()} because it's a built in function`);const o=s(e.ContentWithNameAtLevel((null==n?void 0:n.name)||"",at.Knot),dt);if(o&&(o!==t||i===z.Arg))return void e.NameConflictError(t,(null==n?void 0:n.name)||"",o,a);if(i{switch(t){case"true":case"false":case"not":case"return":case"else":case"VAR":case"CONST":case"temp":case"LIST":case"function":return!0}return!1};class ae extends V{get isSingleString(){if(1!==this.content.length)return!1;return this.content[0]instanceof rt}constructor(t){super(),this.GenerateIntoContainer=t=>{t.AddContent(D.BeginString());for(const e of this.content)t.AddContent(e.runtimeObject);t.AddContent(D.EndString())},this.toString=()=>{let t="";for(const e of this.content)t+=e;return t},this.AddContent(t)}get typeName(){return"String"}Equals(t){const e=s(t,ae);if(null===e)return!1;if(!this.isSingleString||!e.isSingleString)return!1;return this.toString()===e.toString()}}class oe extends d{constructor(t){let e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];super(),this.GenerateRuntimeObject=()=>this.isStart?D.BeginTag():D.EndTag(),this.toString=()=>this.isStart?"#StartTag":"#EndTag",this.isStart=t,this.inChoice=e}get typeName(){return"Tag"}}class le{constructor(t){this.rootPath=t,this.ResolveInkFilename=()=>{throw Error("Can't resolve filename because no FileHandler was provided when instantiating the parser / compiler.")},this.LoadInkFileContents=()=>{throw Error("Can't load ink content because no FileHandler was provided when instantiating the parser / compiler.")}}}class he extends et{get fileHandler(){if(!this._fileHandler)throw new Error("No FileHandler defined");return this._fileHandler}set fileHandler(t){this._fileHandler=t}constructor(e){var n;let a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,l=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,h=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;if(super(e),n=this,this.ParseStory=()=>{const e=this.StatementsAtLevel(t.StatementLevel.Top);return new se(e,this._rootParser!==this)},this.SeparatedList=(t,e)=>{const n=this.Parse(t);if(null===n)return null;const i=[];for(i.push(n);;){const n=this.BeginRule();if(null===e()){this.FailRule(n);break}const r=this.Parse(t);if(null===r){this.FailRule(n);break}this.SucceedRule(n),i.push(r)}return i},this.CreateDebugMetadata=(t,e)=>{const n=new It;return n.startLineNumber=((null==t?void 0:t.lineIndex)||0)+1,n.endLineNumber=e.lineIndex+1,n.startCharacterNumber=((null==t?void 0:t.characterInLineIndex)||0)+1,n.endCharacterNumber=e.characterInLineIndex+1,n.fileName=this._filename,n},this.RuleDidSucceed=(t,e,n)=>{const i=s(t,d);i&&(i.debugMetadata=this.CreateDebugMetadata(e,n));const r=Array.isArray(t)?t:null;if(null!==r)for(const t of r){s(t,d)&&(t.hasOwnDebugMetadata||(t.debugMetadata=this.CreateDebugMetadata(e,n)))}const a=s(t,ct);null!=a&&(a.debugMetadata=this.CreateDebugMetadata(e,n))},this.OnStringParserError=function(t,e){let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:0,s=arguments.length>3&&void 0!==arguments[3]&&arguments[3];let a=s?"WARNING:":"ERROR:";if(null!==n._filename&&(a+=` '${n._filename}'`),a+=` line ${r+1}: ${t}`,null===n._externalErrorHandler)throw new Error(a);n._externalErrorHandler(a,s?i.Warning:i.Error)},this.AuthorWarning=()=>{this.Whitespace();const t=this.Parse(this.IdentifierWithMetadata);if(null===t||"TODO"!==t.name)return null;this.Whitespace(),this.ParseString(":"),this.Whitespace();const e=this.ParseUntilCharactersFromString("\n\r");return e?new p(e):null},this.ExtendIdentifierCharacterRanges=t=>{const e=he.ListAllCharacterRanges();for(const n of e)t.AddCharacters(n.ToCharacterSet())},this._parsingChoice=!1,this.Choice=()=>{let t=!0,e=this.Interleave(this.OptionalExclude(this.Whitespace),this.String("*"));if(!e){if(e=this.Interleave(this.OptionalExclude(this.Whitespace),this.String("+")),null===e)return null;t=!1}const n=this.Parse(this.BracketedName);this.Whitespace();const i=this.Parse(this.ChoiceCondition);if(this.Whitespace(),this._parsingChoice)throw new Error("Already parsing a choice - shouldn't have nested choices");this._parsingChoice=!0;let r=null;const a=this.Parse(this.MixedTextAndLogic);a&&(r=new pt(a));let o=null,l=null;const h=null!==this.ParseString("[");if(h){this.EndTagIfNecessary(r);const t=this.Parse(this.MixedTextAndLogic);null!==t&&(o=new pt(t)),this.Expect(this.String("]"),"closing ']' for weave-style option"),this.EndTagIfNecessary(o);let e=this.Parse(this.MixedTextAndLogic);null!==e&&(l=new pt(e))}this.Whitespace(),this.EndTagIfNecessary(null!=l?l:r);const u=this.Parse(this.MultiDivert);this._parsingChoice=!1,this.Whitespace();const c=!r&&!l&&!o;if(c&&null===u&&this.Warning("Choice is completely empty. Interpretting as a default fallback choice. Add a divert arrow to remove this warning: * ->"),r||!h||o||this.Warning("Blank choice - if you intended a default fallback choice, use the `* ->` syntax"),l||(l=new pt),this.EndTagIfNecessary(l),null!==u)for(const t of u){const e=s(t,St);e&&e.isEmpty||l.AddContent(t)}l.AddContent(new rt("\n"));const d=new Y(r,o,l);return n&&(d.identifier=n),d.indentationDepth=e.length,d.hasWeaveStyleInlineBrackets=h,d.condition=i,d.onceOnly=t,d.isInvisibleDefault=c,d},this.ChoiceCondition=()=>{const t=this.Interleave(this.ChoiceSingleCondition,this.ChoiceConditionsSpace);return null===t?null:1===t.length?t[0]:new vt(t)},this.ChoiceConditionsSpace=()=>(this.Newline(),this.Whitespace(),tt),this.ChoiceSingleCondition=()=>{if(null===this.ParseString("{"))return null;const t=this.Expect(this.Expression,"choice condition inside { }");return this.DisallowIncrement(t),this.Expect(this.String("}"),"closing '}' for choice condition"),t},this.Gather=()=>{const t=this.Parse(this.GatherDashes);if(null===t)return null;const e=Number(t),n=this.Parse(this.BracketedName),i=new ot(n,e);return this.Newline(),i},this.GatherDashes=()=>{this.Whitespace();let t=0;for(;null!==this.ParseDashNotArrow();)t+=1,this.Whitespace();return 0===t?null:t},this.ParseDashNotArrow=()=>{const t=this.BeginRule();return null===this.ParseString("->")&&"-"===this.ParseSingleCharacter()?this.SucceedRule(t):this.FailRule(t)},this.BracketedName=()=>{if(null===this.ParseString("("))return null;this.Whitespace();const t=this.Parse(this.IdentifierWithMetadata);return null===t?null:(this.Whitespace(),this.Expect(this.String(")"),"closing ')' for bracketed name"),t)},this.InnerConditionalContent=e=>{if(void 0===e){const t=this.Parse(this.ConditionExpression),e=this.Parse((()=>this.InnerConditionalContent(t)));return null===e?null:e}let n;const i=null!==e,r=null===this.Parse(this.Newline);if(r&&!i)return null;if(r)n=this.InlineConditionalBranches();else{if(n=this.MultilineConditionalBranches(),null===n){if(e){let e=this.StatementsAtLevel(t.StatementLevel.InnerBlock);if(null!==e){n=[new Nt(e)];const t=this.Parse(this.SingleMultilineCondition);t&&(t.isElse||(this.ErrorWithParsedObject("Expected an '- else:' clause here rather than an extra condition",t),t.isElse=!0),n.push(t))}}if(null===n)return null}else if(1===n.length&&n[0].isElse&&e){const t=new Nt(null);t.isTrueBranch=!0,n.unshift(t)}if(e){let t=!1;for(let e=0;e2?this.ErrorWithParsedObject("Only final branch can be an 'else'. Did you miss a ':'?",i):0===e?i.isTrueBranch=!0:i.isElse=!0}}else{for(let t=0;t{const t=this.Interleave(this.MixedTextAndLogic,this.Exclude(this.String("|")),null,!1);if(null===t||0===t.length)return null;const e=[];if(t.length>2)this.Error("Expected one or two alternatives separated by '|' in inline conditional");else{const n=new Nt(t[0]);if(n.isTrueBranch=!0,e.push(n),t.length>1){const n=new Nt(t[1]);n.isElse=!0,e.push(n)}}return e},this.MultilineConditionalBranches=()=>{this.MultilineWhitespace();const t=this.OneOrMore(this.SingleMultilineCondition);return null===t?null:(this.MultilineWhitespace(),t)},this.SingleMultilineCondition=()=>{if(this.Whitespace(),null!==this.ParseString("->")||null===this.ParseString("-"))return null;this.Whitespace();let e=null;const n=null!==this.Parse(this.ElseExpression);n||(e=this.Parse(this.ConditionExpression));let i=this.StatementsAtLevel(t.StatementLevel.InnerBlock);null===e&&null===i&&(this.Error("expected content for the conditional branch following '-'"),i=[new rt("")]),this.MultilineWhitespace();const r=new Nt(i);return r.ownExpression=e,r.isElse=n,r},this.ConditionExpression=()=>{const t=this.Parse(this.Expression);return null===t?null:(this.DisallowIncrement(t),this.Whitespace(),null===this.ParseString(":")?null:t)},this.ElseExpression=()=>null===this.ParseString("else")?null:(this.Whitespace(),null===this.ParseString(":")?null:tt),this._nonTextPauseCharacters=null,this._nonTextEndCharacters=null,this._notTextEndCharactersChoice=null,this._notTextEndCharactersString=null,this.TrimEndWhitespace=(t,e)=>{if(t.length>0){const n=t.length-1,i=t[n];if(i instanceof rt){const r=i;r.text=r.text.replace(new RegExp(/[ \t]+$/g),""),e?r.text+=" ":0===r.text.length&&(t.splice(n,1),this.TrimEndWhitespace(t,!1))}}},this.LineOfMixedTextAndLogic=()=>{this.Parse(this.Whitespace);let t=this.Parse(this.MixedTextAndLogic);if(!t||!t.length)return null;const e=t[0];if(e&&e.text&&e.text.startsWith("return")&&this.Warning("Do you need a '~' before 'return'? If not, perhaps use a glue: <> (since it's lowercase) or rewrite somehow?"),0===t.length)return null;return t[t.length-1]instanceof St||this.TrimEndWhitespace(t,!1),this.EndTagIfNecessary(t),t.length>0&&t[0]instanceof oe&&t[0].isStart||t.push(new rt("\n")),this.Expect(this.EndOfLine,"end of line",this.SkipToNextLine),t},this.MixedTextAndLogic=()=>{null!==this.ParseObject(this.Spaced(this.String("~")))&&this.Error("You shouldn't use a '~' here - tildas are for logic that's on its own line. To do inline logic, use { curly braces } instead");let t=this.Interleave(this.Optional(this.ContentText),this.Optional(this.InlineLogicOrGlueOrStartTag));if(!this._parsingChoice){const e=this.Parse(this.MultiDivert);null!==e&&(null===t&&(t=[]),this.EndTagIfNecessary(t),this.TrimEndWhitespace(t,!0),t.push(...e))}return t||null},this.ContentText=()=>this.ContentTextAllowingEscapeChar(),this.ContentTextAllowingEscapeChar=()=>{let t=null;for(;;){let e=this.Parse(this.ContentTextNoEscape);const n=null!==this.ParseString("\\");if(!n&&null===e)break;if(null===t&&(t=""),null!==e&&(t+=String(e)),n){t+=this.ParseSingleCharacter()}}return null!==t?new rt(t):null},this.ContentTextNoEscape=()=>{null===this._nonTextPauseCharacters&&(this._nonTextPauseCharacters=new q("-<")),null===this._nonTextEndCharacters&&(this._nonTextEndCharacters=new q("{}|\n\r\\#"),this._notTextEndCharactersChoice=new q(this._nonTextEndCharacters),this._notTextEndCharactersChoice.AddCharacters("[]"),this._notTextEndCharactersString=new q(this._nonTextEndCharacters),this._notTextEndCharactersString.AddCharacters('"'));let t=null;t=this.parsingStringExpression?this._notTextEndCharactersString:this._parsingChoice?this._notTextEndCharactersChoice:this._nonTextEndCharacters;const e=this.ParseUntil((()=>this.OneOf([this.ParseDivertArrow,this.ParseThreadArrow,this.EndOfLine,this.Glue])),this._nonTextPauseCharacters,t);return null!==e?e:null},this.MultiDivert=()=>{this.Whitespace();let t=[];const e=this.Parse(this.StartThread);if(e)return t=[e],t;const n=this.Interleave(this.ParseDivertArrowOrTunnelOnwards,this.DivertIdentifierWithArguments);if(!n)return null;t=[],this.EndTagIfNecessary(t);for(let e=0;e->"===n[e]){0===e||e===n.length-1||e===n.length-2||this.Error("Tunnel onwards '->->' must only come at the begining or the start of a divert");const i=new _t;if(e) are only valid on choices")}return t},this.StartThread=()=>{if(this.Whitespace(),null===this.ParseThreadArrow())return null;this.Whitespace();const t=this.Expect(this.DivertIdentifierWithArguments,"target for new thread",(()=>new St(null)));return t.isThread=!0,t},this.DivertIdentifierWithArguments=()=>{this.Whitespace();const t=this.Parse(this.DotSeparatedDivertPathComponents);if(!t)return null;this.Whitespace();const e=this.Parse(this.ExpressionFunctionCallArguments);this.Whitespace();const n=new lt(t);return new St(n,e)},this.SingleDivert=()=>{const t=this.Parse(this.MultiDivert);if(!t)return null;if(1!==t.length)return null;if(t[0]instanceof _t)return null;const e=t[0];return e.isTunnel?null:e},this.DotSeparatedDivertPathComponents=()=>this.Interleave(this.Spaced(this.IdentifierWithMetadata),this.Exclude(this.String("."))),this.ParseDivertArrowOrTunnelOnwards=()=>{let t=0;for(;null!==this.ParseString("->");)t+=1;return 0===t?null:1===t?"->":(2===t||this.Error("Unexpected number of arrows in divert. Should only have '->' or '->->'"),"->->")},this.ParseDivertArrow=()=>this.ParseString("->"),this.ParseThreadArrow=()=>this.ParseString("<-"),this._binaryOperators=[],this._maxBinaryOpLength=0,this.TempDeclarationOrAssignment=()=>{this.Whitespace();const t=this.ParseTempKeyword();this.Whitespace();let e=null;if(e=t?this.Expect(this.IdentifierWithMetadata,"variable name"):this.Parse(this.IdentifierWithMetadata),null===e)return null;this.Whitespace();const n=null!==this.ParseString("+"),i=null!==this.ParseString("-");if(n&&i&&this.Error("Unexpected sequence '+-'"),null===this.ParseString("="))return t&&this.Error("Expected '='"),null;const r=this.Expect(this.Expression,"value expression to be assigned");if(n||i){return new Dt(e,r,n)}return new At({variableIdentifier:e,assignedExpression:r,isTemporaryNewDeclaration:t})},this.DisallowIncrement=t=>{t instanceof Dt&&this.Error("Can't use increment/decrement here. It can only be used on a ~ line")},this.ParseTempKeyword=()=>{const t=this.BeginRule();return"temp"===this.Parse(this.Identifier)?(this.SucceedRule(t),!0):(this.FailRule(t),!1)},this.ReturnStatement=()=>{this.Whitespace();if("return"!==this.Parse(this.Identifier))return null;this.Whitespace();const t=this.Parse(this.Expression);return new ht(t)},this.Expression=function(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;n.Whitespace();let e=n.ExpressionUnary();if(null===e)return null;for(n.Whitespace();;){const i=n.BeginRule(),r=n.ParseInfixOperator();if(!(null!==r&&r.precedence>t)){n.FailRule(i);break}{const t=`right side of '${r.type}' expression`,s=n.Expect((()=>n.ExpressionInfixRight(e,r)),t);if(null===s)return n.FailRule(i),null;e=n.SucceedRule(i,s)}}return n.Whitespace(),e},this.ExpressionUnary=()=>{const t=this.Parse(this.ExpressionDivertTarget);if(null!==t)return t;let e=this.OneOf([this.String("-"),this.String("!")]);null===e&&(e=this.Parse(this.ExpressionNot)),this.Whitespace();let n=this.OneOf([this.ExpressionList,this.ExpressionParen,this.ExpressionFunctionCall,this.ExpressionVariableName,this.ExpressionLiteral]);if(null===n&&null!==e&&(n=this.ExpressionUnary()),null===n)return null;null!==e&&(n=G.WithInner(n,e)),this.Whitespace();const i=this.OneOf([this.String("++"),this.String("--")]);if(null!==i){const t="++"===i;if(n instanceof ft){n=new Dt(n.identifier,t)}else this.Error(`can only increment and decrement variables, but saw '${n}'.`)}return n},this.ExpressionNot=()=>{const t=this.Identifier();return"not"===t?t:null},this.ExpressionLiteral=()=>this.OneOf([this.ExpressionFloat,this.ExpressionInt,this.ExpressionBool,this.ExpressionString]),this.ExpressionDivertTarget=()=>{this.Whitespace();const t=this.Parse(this.SingleDivert);return!t||t&&t.isThread?null:(this.Whitespace(),new Ct(t))},this.ExpressionInt=()=>{const t=this.ParseInt();return null===t?null:new M(t,"int")},this.ExpressionFloat=()=>{const t=this.ParseFloat();return null===t?null:new M(t,"float")},this.ExpressionString=()=>{if(null===this.ParseString('"'))return null;this.parsingStringExpression=!0;let t=this.Parse(this.MixedTextAndLogic);return this.Expect(this.String('"'),"close quote for string expression"),this.parsingStringExpression=!1,null===t?t=[new rt("")]:t.find((t=>t instanceof St))&&this.Error("String expressions cannot contain diverts (->)"),new ae(t)},this.ExpressionBool=()=>{const t=this.Parse(this.Identifier);return"true"===t?new M(!0,"bool"):"false"===t?new M(!1,"bool"):null},this.ExpressionFunctionCall=()=>{const t=this.Parse(this.IdentifierWithMetadata);if(null===t)return null;this.Whitespace();const e=this.Parse(this.ExpressionFunctionCallArguments);return null===e?null:new gt(t,e)},this.ExpressionFunctionCallArguments=()=>{if(null===this.ParseString("("))return null;const t=this.Exclude(this.String(","));let e=this.Interleave(this.Expression,t);return null===e&&(e=[]),this.Whitespace(),this.Expect(this.String(")"),"closing ')' for function call"),e},this.ExpressionVariableName=()=>{const t=this.Interleave(this.IdentifierWithMetadata,this.Exclude(this.Spaced(this.String("."))));return null===t||se.IsReservedKeyword(t[0].name)?null:new ft(t)},this.ExpressionParen=()=>{if(null===this.ParseString("("))return null;const t=this.Parse(this.Expression);return null===t?null:(this.Whitespace(),this.Expect(this.String(")"),"closing parenthesis ')' for expression"),t)},this.ExpressionInfixRight=(t,e)=>{if(!t)return null;this.Whitespace();const n=this.Parse((()=>this.Expression(e.precedence)));if(n){return new $(t,n,e.type)}return null},this.ParseInfixOperator=()=>{for(const t of this._binaryOperators){const e=this.BeginRule();if(null!==this.ParseString(t.type)){if(t.requireWhitespace&&null===this.Whitespace()){this.FailRule(e);continue}return this.SucceedRule(e,t)}this.FailRule(e)}return null},this.ExpressionList=()=>{if(this.Whitespace(),null===this.ParseString("("))return null;this.Whitespace();const t=this.SeparatedList(this.ListMember,this.Spaced(this.String(",")));return this.Whitespace(),null===this.ParseString(")")?null:new Mt(t)},this.ListMember=()=>{this.Whitespace();let t=this.Parse(this.IdentifierWithMetadata);if(null===t)return null;if(null!==this.ParseString(".")){const e=this.Expect(this.IdentifierWithMetadata,`element name within the set ${t}`);t.name+=`.${null==e?void 0:e.name}`}return this.Whitespace(),t},this.RegisterExpressionOperators=()=>{this.RegisterBinaryOperator("&&",1),this.RegisterBinaryOperator("||",1),this.RegisterBinaryOperator("and",1,!0),this.RegisterBinaryOperator("or",1,!0),this.RegisterBinaryOperator("==",2),this.RegisterBinaryOperator(">=",2),this.RegisterBinaryOperator("<=",2),this.RegisterBinaryOperator("<",2),this.RegisterBinaryOperator(">",2),this.RegisterBinaryOperator("!=",2),this.RegisterBinaryOperator("?",3),this.RegisterBinaryOperator("has",3,!0),this.RegisterBinaryOperator("!?",3),this.RegisterBinaryOperator("hasnt",3,!0),this.RegisterBinaryOperator("^",3),this.RegisterBinaryOperator("+",4),this.RegisterBinaryOperator("-",5),this.RegisterBinaryOperator("*",6),this.RegisterBinaryOperator("/",7),this.RegisterBinaryOperator("%",8),this.RegisterBinaryOperator("mod",8,!0)},this.RegisterBinaryOperator=function(t,e){const i=new jt(t,e,arguments.length>2&&void 0!==arguments[2]&&arguments[2]);n._binaryOperators.push(i),n._maxBinaryOpLength=Math.max(n._maxBinaryOpLength,t.length)},this._openFilenames=[],this.IncludeStatement=()=>{if(this.Whitespace(),null===this.ParseString("INCLUDE"))return null;this.Whitespace();let t=this.Expect((()=>this.ParseUntilCharactersFromString("\n\r")),"filename for include statement");t=t.replace(new RegExp(/[ \t]+$/g),"");const e=this.fileHandler.ResolveInkFilename(t);if(this.FilenameIsAlreadyOpen(e))return this.Error(`Recursive INCLUDE detected: '${e}' is already open.`),this.ParseUntilCharactersFromString("\r\n"),new Vt(null);this.AddOpenFilename(e);let n=null,i="";try{i=this._rootParser.fileHandler.LoadInkFileContents(e)}catch(e){this.Error(`Failed to load: '${t}'.\nError:${e}`)}if(null!=i){n=new he(i,t,this._externalErrorHandler,this._rootParser,this.fileHandler).ParseStory()}return this.RemoveOpenFilename(e),new Vt(n)},this.FilenameIsAlreadyOpen=t=>this._rootParser._openFilenames.includes(t),this.AddOpenFilename=t=>{this._rootParser._openFilenames.push(t)},this.RemoveOpenFilename=t=>{this._rootParser._openFilenames.splice(this._rootParser._openFilenames.indexOf(t),1)},this.KnotDefinition=()=>{const e=this.Parse(this.KnotDeclaration);if(null===e)return null;this.Expect(this.EndOfLine,"end of line after knot name definition",this.SkipToNextLine);const n=this.Expect((()=>this.StatementsAtLevel(t.StatementLevel.Knot)),"at least one line within the knot",this.KnotStitchNoContentRecoveryRule);return new Bt(e.name,n,e.args,e.isFunction)},this.KnotDeclaration=()=>{if(this.Whitespace(),null===this.KnotTitleEquals())return null;this.Whitespace();const t=this.Parse(this.IdentifierWithMetadata);let e;const n="function"===(null==t?void 0:t.name);n?(this.Expect(this.Whitespace,"whitespace after the 'function' keyword"),e=this.Parse(this.IdentifierWithMetadata)):e=t,null===e&&(this.Error("Expected the name of the "+(n?"function":"knot")),e=new ct("")),this.Whitespace();const i=this.Parse(this.BracketedKnotDeclArguments);return this.Whitespace(),this.Parse(this.KnotTitleEquals),new Wt(e,i,n)},this.KnotTitleEquals=()=>{const t=this.ParseCharactersFromString("=");return null===t||t.length<=1?null:t},this.StitchDefinition=()=>{const e=this.Parse(this.StitchDeclaration);if(null===e)return null;this.Expect(this.EndOfLine,"end of line after stitch name",this.SkipToNextLine);const n=this.Expect((()=>this.StatementsAtLevel(t.StatementLevel.Stitch)),"at least one line within the stitch",this.KnotStitchNoContentRecoveryRule);return new qt(e.name,n,e.args,e.isFunction)},this.StitchDeclaration=()=>{if(this.Whitespace(),null===this.ParseString("="))return null;if(null!==this.ParseString("="))return null;this.Whitespace();const t=null!==this.ParseString("function");t&&this.Whitespace();const e=this.Parse(this.IdentifierWithMetadata);if(null===e)return null;this.Whitespace();const n=this.Parse(this.BracketedKnotDeclArguments);return this.Whitespace(),new Wt(e,n,t)},this.KnotStitchNoContentRecoveryRule=()=>{this.ParseUntil(this.KnotDeclaration,new q("="),null);return[new rt("")]},this.BracketedKnotDeclArguments=()=>{if(null===this.ParseString("("))return null;let t=this.Interleave(this.Spaced(this.FlowDeclArgument),this.Exclude(this.String(",")));return this.Expect(this.String(")"),"closing ')' for parameter list"),null===t&&(t=[]),t},this.FlowDeclArgument=()=>{const t=this.Parse(this.IdentifierWithMetadata);this.Whitespace();const e=this.ParseDivertArrow();this.Whitespace();const n=this.Parse(this.IdentifierWithMetadata);if(null==t&&null===n)return null;const i=new r;return null!==e&&(i.isDivertTarget=!0),null!==t&&"ref"===t.name?(null===n&&this.Error("Expected an parameter name after 'ref'"),i.identifier=n,i.isByReference=!0):(i.isDivertTarget?i.identifier=n:i.identifier=t,null===i.identifier&&this.Error("Expected an parameter name"),i.isByReference=!1),i},this.ExternalDeclaration=()=>{this.Whitespace();const t=this.Parse(this.IdentifierWithMetadata);if(null===t||"EXTERNAL"!=t.name)return null;this.Whitespace();const e=this.Expect(this.IdentifierWithMetadata,"name of external function")||new ct("");this.Whitespace();let n=this.Expect(this.BracketedKnotDeclArguments,`declaration of arguments for EXTERNAL, even if empty, i.e. 'EXTERNAL ${e}()'`);null===n&&(n=[]);const i=n.map((t=>{var e;return null===(e=t.identifier)||void 0===e?void 0:e.name})).filter(c);return new Ft(e,i)},this._identifierCharSet=null,this.LogicLine=()=>{if(this.Whitespace(),null===this.ParseString("~"))return null;this.Whitespace();let t=this.Expect((()=>this.OneOf([this.ReturnStatement,this.TempDeclarationOrAssignment,this.Expression])),"expression after '~'",this.SkipToNextLine);if(null===t)return new pt;t instanceof V&&!(t instanceof gt||t instanceof Dt)&&this.Error("Logic following a '~' can't be that type of expression. It can only be something like:\n\t~ return\n\t~ var x = blah\n\t~ x++\n\t~ myFunction()");const e=s(t,gt);return e&&(e.shouldPopReturnedValue=!0),null!==t.Find(gt)()&&(t=new pt(t,new rt("\n"))),this.Expect(this.EndOfLine,"end of line",this.SkipToNextLine),t},this.VariableDeclaration=()=>{this.Whitespace();if("VAR"!==this.Parse(this.Identifier))return null;this.Whitespace();const t=this.Expect(this.IdentifierWithMetadata,"variable name");this.Whitespace(),this.Expect(this.String("="),"the '=' for an assignment of a value, e.g. '= 5' (initial values are mandatory)"),this.Whitespace();const e=this.Expect(this.Expression,"initial value for ");if(e){if(e instanceof M||e instanceof ae||e instanceof Ct||e instanceof ft||e instanceof Mt||this.Error("initial value for a variable must be a number, constant, list or divert target"),null!==this.Parse(this.ListElementDefinitionSeparator))this.Error("Unexpected ','. If you're trying to declare a new list, use the LIST keyword, not VAR");else if(e instanceof ae){e.isSingleString||this.Error("Constant strings cannot contain any logic.")}return new At({assignedExpression:e,isGlobalDeclaration:!0,variableIdentifier:t})}return null},this.ListDeclaration=()=>{this.Whitespace();if("LIST"!=this.Parse(this.Identifier))return null;this.Whitespace();const t=this.Expect(this.IdentifierWithMetadata,"list name");this.Whitespace(),this.Expect(this.String("="),"the '=' for an assignment of the list definition"),this.Whitespace();const e=this.Expect(this.ListDefinition,"list item names");return e?(e.identifier=new ct(t.name),new At({variableIdentifier:t,listDef:e})):null},this.ListDefinition=()=>{this.AnyWhitespace();const t=this.SeparatedList(this.ListElementDefinition,this.ListElementDefinitionSeparator);return null===t?null:new xt(t)},this.ListElementDefinitionSeparator=()=>(this.AnyWhitespace(),null===this.ParseString(",")?null:(this.AnyWhitespace(),",")),this.ListElementDefinition=()=>{const t=null!==this.ParseString("(");let e=t;this.Whitespace();const n=this.Parse(this.IdentifierWithMetadata);if(null===n)return null;this.Whitespace(),t&&null!=this.ParseString(")")&&(e=!1,this.Whitespace());let i=null;if(null!==this.ParseString("=")){this.Whitespace();const t=this.Expect(this.ExpressionInt,"value to be assigned to list item");null!==t&&(i=t.value),e&&(this.Whitespace(),null!==this.ParseString(")")&&(e=!1))}return e&&this.Error("Expected closing ')'"),new Gt(n,t,i)},this.ConstDeclaration=()=>{this.Whitespace();if("CONST"!==this.Parse(this.Identifier))return null;this.Whitespace();const t=this.Expect(this.IdentifierWithMetadata,"constant name");this.Whitespace(),this.Expect(this.String("="),"the '=' for an assignment of a value, e.g. '= 5' (initial values are mandatory)"),this.Whitespace();const e=this.Expect(this.Expression,"initial value for ");if(e instanceof M||e instanceof Ct||e instanceof ae){if(e instanceof ae){e.isSingleString||this.Error("Constant strings cannot contain any logic.")}}else this.Error("initial value for a constant must be a number or divert target");return new st(t,e)},this.InlineLogicOrGlueOrStartTag=()=>this.OneOf([this.InlineLogic,this.Glue,this.StartTag]),this.Glue=()=>null!==this.ParseString("<>")?new kt(new Lt):null,this.InlineLogic=()=>{if(null===this.ParseString("{"))return null;let t=this.parsingStringExpression,e=this.tagActive;this.Whitespace();const n=this.Expect(this.InnerLogic,"some kind of logic, conditional or sequence within braces: { ... }");if(null===n)return this.parsingStringExpression=t,null;this.DisallowIncrement(n);let i=s(n,pt);return i||(i=new pt(n)),this.Whitespace(),this.Expect(this.String("}"),"closing brace '}' for inline logic"),this.parsingStringExpression=t,e||this.EndTagIfNecessary(i),i},this.InnerLogic=()=>{this.Whitespace();const t=this.ParseObject(this.SequenceTypeAnnotation);if(null!==t){const e=this.Expect(this.InnerSequenceObjects,"sequence elements (for cycle/stoping etc)");return null===e?null:new Et(e,t)}const e=this.Parse(this.ConditionExpression);if(e){return this.Expect((()=>this.InnerConditionalContent(e)),"conditional content following query")}const n=[this.InnerConditionalContent,this.InnerSequence,this.InnerExpression];for(const t of n){const e=this.BeginRule(),n=this.ParseObject(t);if(n){if(null!==this.Peek(this.Spaced(this.String("}"))))return this.SucceedRule(e,n);this.FailRule(e)}else this.FailRule(e)}return null},this.InnerExpression=()=>{const t=this.Parse(this.Expression);return t&&(t.outputWhenComplete=!0),t},this.IdentifierWithMetadata=()=>{const t=this.Identifier();return null===t?null:new ct(t)},this.Identifier=()=>{const t=this.ParseCharactersFromCharSet(this.identifierCharSet);if(null===t)return null;let e=!0;for(let n of t)if(!(n>="0"&&n<="9")){e=!1;break}return e?null:t},this._sequenceTypeSymbols=new q("!&~$"),this.InnerSequence=()=>{this.Whitespace();let t=wt.Stopping;const e=this.Parse(this.SequenceTypeAnnotation);null!==e&&(t=e);const n=this.Parse(this.InnerSequenceObjects);return null===n||n.length<=1?null:new Et(n,t)},this.SequenceTypeAnnotation=()=>{let t=this.Parse(this.SequenceTypeSymbolAnnotation);if(null===t&&(t=this.Parse(this.SequenceTypeWordAnnotation)),null===t)return null;switch(t){case wt.Once:case wt.Cycle:case wt.Stopping:case wt.Shuffle:case wt.Shuffle|wt.Stopping:case wt.Shuffle|wt.Once:break;default:return this.Error(`Sequence type combination not supported: ${t}`),wt.Stopping}return t},this.SequenceTypeSymbolAnnotation=()=>{null===this._sequenceTypeSymbols&&(this._sequenceTypeSymbols=new q("!&~$ "));let t=0;const e=this.ParseCharactersFromCharSet(this._sequenceTypeSymbols);if(null===e)return null;for(const n of e)switch(n){case"!":t|=wt.Once;break;case"&":t|=wt.Cycle;break;case"~":t|=wt.Shuffle;break;case"$":t|=wt.Stopping}return 0===t?null:t},this.SequenceTypeWordAnnotation=()=>{const t=this.Interleave(this.SequenceTypeSingleWord,this.Exclude(this.Whitespace));if(null===t||0===t.length)return null;if(null===this.ParseString(":"))return null;let e=0;for(const n of t)e|=n;return e},this.SequenceTypeSingleWord=()=>{let t=null;const e=this.Parse(this.IdentifierWithMetadata);if(null!==e)switch(e.name){case"once":t=wt.Once;break;case"cycle":t=wt.Cycle;break;case"shuffle":t=wt.Shuffle;break;case"stopping":t=wt.Stopping}return null===t?null:t},this.InnerSequenceObjects=()=>{let t=null;return t=null!==this.Parse(this.Newline)?this.Parse(this.InnerMultilineSequenceObjects):this.Parse(this.InnerInlineSequenceObjects),t},this.InnerInlineSequenceObjects=()=>{const t=this.Interleave(this.Optional(this.MixedTextAndLogic),this.String("|"),null,!1);if(null===t)return null;const e=[];let n=!1;for(const i of t)if("|"===i)n||e.push(new pt),n=!1;else{const t=i;null===t?this.Error(`Expected content, but got ${i} (this is an ink compiler bug!)`):e.push(new pt(t)),n=!0}return n||e.push(new pt),e},this.InnerMultilineSequenceObjects=()=>{this.MultilineWhitespace();const t=this.OneOrMore(this.SingleMultilineSequenceElement);return null===t?null:t},this.SingleMultilineSequenceElement=()=>{if(this.Whitespace(),null!==this.ParseString("->"))return null;if(null===this.ParseString("-"))return null;this.Whitespace();const e=this.StatementsAtLevel(t.StatementLevel.InnerBlock);return null===e?this.MultilineWhitespace():e.unshift(new rt("\n")),new pt(e)},this._statementRulesAtLevel=[],this._statementBreakRulesAtLevel=[],this.StatementsAtLevel=e=>{if(e===t.StatementLevel.InnerBlock){null!==this.Parse(this.GatherDashes)&&this.Error("You can't use a gather (the dashes) within the { curly braces } context. For multi-line sequences and conditions, you should only use one dash.")}return this.Interleave(this.Optional(this.MultilineWhitespace),(()=>this.StatementAtLevel(e)),(()=>this.StatementsBreakForLevel(e)))},this.StatementAtLevel=e=>{const n=this._statementRulesAtLevel[e],i=this.OneOf(n);return e===t.StatementLevel.Top&&i instanceof ht&&this.Error("should not have return statement outside of a knot"),i},this.StatementsBreakForLevel=t=>{this.Whitespace();const e=this._statementBreakRulesAtLevel[t],n=this.OneOf(e);return null===n?null:n},this.GenerateStatementLevelRules=()=>{const e=Object.values(t.StatementLevel);this._statementRulesAtLevel="f".repeat(e.length).split("f").map((()=>[])),this._statementBreakRulesAtLevel="f".repeat(e.length).split("f").map((()=>[]));for(const n of e){const e=[],i=[];e.push(this.Line(this.MultiDivert)),n>=t.StatementLevel.Top&&e.push(this.KnotDefinition),e.push(this.Line(this.Choice)),e.push(this.Line(this.AuthorWarning)),n>t.StatementLevel.InnerBlock&&e.push(this.Gather),n>=t.StatementLevel.Knot&&e.push(this.StitchDefinition),e.push(this.Line(this.ListDeclaration)),e.push(this.Line(this.VariableDeclaration)),e.push(this.Line(this.ConstDeclaration)),e.push(this.Line(this.ExternalDeclaration)),e.push(this.Line(this.IncludeStatement)),e.push(this.LogicLine),e.push(this.LineOfMixedTextAndLogic),n<=t.StatementLevel.Knot&&i.push(this.KnotDeclaration),n<=t.StatementLevel.Stitch&&i.push(this.StitchDeclaration),n<=t.StatementLevel.InnerBlock&&(i.push(this.ParseDashNotArrow),i.push(this.String("}"))),this._statementRulesAtLevel[n]=e,this._statementBreakRulesAtLevel[n]=i}},this.SkipToNextLine=()=>(this.ParseUntilCharactersFromString("\n\r"),this.ParseNewline(),tt),this.Line=t=>()=>{const e=this.ParseObject(t);return null===e?null:(this.Expect(this.EndOfLine,"end of line",this.SkipToNextLine),e)},this.StartTag=()=>{if(this.Whitespace(),null===this.ParseString("#"))return null;this.parsingStringExpression&&this.Error("Tags aren't allowed inside of strings. Please use \\# if you want a hash symbol.");let t=null;if(this.tagActive){let e=new pt;e.AddContent(new oe(!1)),e.AddContent(new oe(!0)),t=e}else t=new oe(!0);return this.tagActive=!0,this.Whitespace(),t},this._inlineWhitespaceChars=new q(" \t"),this.EndOfLine=()=>this.OneOf([this.Newline,this.EndOfFile]),this.Newline=()=>{this.Whitespace();return null!==this.ParseNewline()?tt:null},this.EndOfFile=()=>(this.Whitespace(),this.endOfInput?tt:null),this.MultilineWhitespace=()=>{let t=this.OneOrMore(this.Newline);if(null===t)return null;return t.length>=1?tt:null},this.Whitespace=()=>null!==this.ParseCharactersFromCharSet(this._inlineWhitespaceChars)?tt:null,this.Spaced=t=>()=>{this.Whitespace();const e=this.ParseObject(t);return null===e?null:(this.Whitespace(),e)},this.AnyWhitespace=()=>{let t=!1;for(;null!==this.OneOf([this.Whitespace,this.MultilineWhitespace]);)t=!0;return t?tt:null},this.MultiSpaced=t=>()=>{this.AnyWhitespace();const e=this.ParseObject(t);return null===e?null:(this.AnyWhitespace(),e)},this._filename=null,this._externalErrorHandler=null,this._fileHandler=null,this._filename=a,this.RegisterExpressionOperators(),this.GenerateStatementLevelRules(),this.errorHandler=this.OnStringParserError,this._externalErrorHandler=o,this._fileHandler=null===h?new le:h,null===l){if(this._rootParser=this,this._openFilenames=[],null!==this._filename){const t=this.fileHandler.ResolveInkFilename(this._filename);this._openFilenames.push(t)}}else this._rootParser=l}PreProcessInputString(t){return new nt(t).Process()}get parsingStringExpression(){return this.GetFlag(Number(Ot.ParsingString))}set parsingStringExpression(t){this.SetFlag(Number(Ot.ParsingString),t)}get tagActive(){return this.GetFlag(Number(Ot.TagActive))}set tagActive(t){this.SetFlag(Number(Ot.TagActive),t)}get identifierCharSet(){return null===this._identifierCharSet&&((this._identifierCharSet=new q).AddRange("A","Z").AddRange("a","z").AddRange("0","9").Add("_"),this.ExtendIdentifierCharacterRanges(this._identifierCharSet)),this._identifierCharSet}EndTagIfNecessary(t){this.tagActive&&(null!=t&&(t instanceof pt?t.AddContent(new oe(!1)):t.push(new oe(!1))),this.tagActive=!1)}}he.LatinBasic=U.Define("A","z",(new q).AddRange("[","`")),he.LatinExtendedA=U.Define("Ā","ſ"),he.LatinExtendedB=U.Define("ƀ","ɏ"),he.Greek=U.Define("Ͱ","Ͽ",(new q).AddRange("","΅").AddCharacters("ʹ͵·")),he.Cyrillic=U.Define("Ѐ","ӿ",(new q).AddRange("҂","҉")),he.Armenian=U.Define("","֏",(new q).AddCharacters("").AddRange("","ՠ").AddRange("ֈ","֎")),he.Hebrew=U.Define("","",new q),he.Arabic=U.Define("","ۿ",new q),he.Korean=U.Define("가","",new q),he.ListAllCharacterRanges=()=>[he.LatinBasic,he.LatinExtendedA,he.LatinExtendedB,he.Arabic,he.Armenian,he.Cyrillic,he.Greek,he.Hebrew,he.Korean];t.Compiler=class{get errors(){return this._errors}get warnings(){return this._warnings}get authorMessages(){return this._authorMessages}get inputString(){return this._inputString}get options(){return this._options}get parsedStory(){if(!this._parsedStory)throw new Error;return this._parsedStory}get runtimeStory(){if(!this._runtimeStory)throw new Error("Compilation failed.");return this._runtimeStory}get parser(){if(!this._parser)throw new Error;return this._parser}get debugSourceRanges(){return this._debugSourceRanges}constructor(t){let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;this._errors=[],this._warnings=[],this._authorMessages=[],this._parsedStory=null,this._runtimeStory=null,this._parser=null,this._debugSourceRanges=[],this.Compile=()=>(this._parser=new he(this.inputString,this.options.sourceFilename||null,this.OnError,null,this.options.fileHandler),this._parsedStory=this.parser.ParseStory(),0===this.errors.length?(this.parsedStory.countAllVisits=this.options.countAllVisits,this._runtimeStory=this.parsedStory.ExportRuntime(this.OnError)):this._runtimeStory=null,this.runtimeStory),this.RetrieveDebugSourceForLatestContent=()=>{var t;for(const e of this.runtimeStory.state.outputStream){const i=s(e,I);if(null!==i){const e=new n((null===(t=i.value)||void 0===t?void 0:t.length)||0,i.debugMetadata,i.value||"unknown");this.debugSourceRanges.push(e)}}},this.DebugMetadataForContentAtOffset=t=>{let e=0,n=null;for(const i of this.debugSourceRanges){if(null!==i.debugMetadata&&(n=i.debugMetadata),t>=e&&t{switch(e){case i.Author:this._authorMessages.push(t);break;case i.Warning:this._warnings.push(t);break;case i.Error:this._errors.push(t)}null!==this.options.errorHandler&&this.options.errorHandler(t,e)},this._inputString=t,this._options=r||new e}},t.CompilerOptions=e,t.InkList=E,t.InkParser=he,t.JsonFileHandler=class{constructor(t){this.fileHierarchy=t,this.ResolveInkFilename=t=>{if(Object.keys(this.fileHierarchy).includes(t))return t;throw new Error(`Cannot locate ${t}. Are you trying a relative import ? This is not yet implemented.`)},this.LoadInkFileContents=t=>{if(Object.keys(this.fileHierarchy).includes(t))return this.fileHierarchy[t];throw new Error(`Cannot open ${t}.`)}}},t.Story=re}));
-//# sourceMappingURL=ink-full.js.map
diff --git a/prototype/ink-full.js.map b/prototype/ink-full.js.map
deleted file mode 100644
index 3599f3a..0000000
--- a/prototype/ink-full.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"file":"ink-full.js","sources":["../src/compiler/CompilerOptions.ts","../src/compiler/DebugSourceRange.ts","../src/compiler/Parser/ErrorType.ts","../src/compiler/Parser/ParsedHierarchy/Argument.ts","../src/engine/TypeAssertion.ts","../src/compiler/Parser/ParsedHierarchy/Object.ts","../src/compiler/Parser/ParsedHierarchy/AuthorWarning.ts","../src/engine/Path.ts","../src/engine/Debug.ts","../src/engine/Value.ts","../src/engine/PushPop.ts","../src/engine/NullException.ts","../src/engine/Object.ts","../src/engine/StringBuilder.ts","../src/engine/InkList.ts","../src/engine/StoryException.ts","../src/engine/TryGetResult.ts","../src/engine/SearchResult.ts","../src/engine/Container.ts","../src/engine/ControlCommand.ts","../src/compiler/Parser/ParsedHierarchy/Expression/Expression.ts","../src/engine/Void.ts","../src/engine/NativeFunctionCall.ts","../src/compiler/Parser/ParsedHierarchy/Expression/NumberExpression.ts","../src/compiler/Parser/ParsedHierarchy/Expression/UnaryExpression.ts","../src/compiler/Parser/ParsedHierarchy/Expression/BinaryExpression.ts","../src/compiler/Parser/CharacterSet.ts","../src/compiler/Parser/CharacterRange.ts","../src/engine/ChoicePoint.ts","../src/engine/Pointer.ts","../src/engine/Divert.ts","../src/compiler/Parser/ParsedHierarchy/SymbolType.ts","../src/engine/VariableAssignment.ts","../src/compiler/Parser/ParsedHierarchy/Choice.ts","../src/compiler/Parser/StringParser/StringParserElement.ts","../src/compiler/Parser/StringParser/StringParserState.ts","../src/compiler/Parser/StringParser/StringParser.ts","../src/compiler/Parser/CommentEliminator.ts","../src/compiler/Parser/ParsedHierarchy/Conditional/Conditional.ts","../src/compiler/Parser/ParsedHierarchy/Text.ts","../src/compiler/Parser/ParsedHierarchy/Declaration/ConstantDeclaration.ts","../src/compiler/Parser/ParsedHierarchy/Flow/FlowLevel.ts","../src/compiler/Parser/ParsedHierarchy/Gather/Gather.ts","../src/compiler/Parser/ParsedHierarchy/Path.ts","../src/compiler/Parser/ParsedHierarchy/ReturnType.ts","../src/compiler/Parser/ParsedHierarchy/Flow/ClosestFlowBase.ts","../src/compiler/Parser/ParsedHierarchy/Identifier.ts","../src/compiler/Parser/ParsedHierarchy/Flow/FlowBase.ts","../src/compiler/Parser/ParsedHierarchy/ContentList.ts","../src/engine/VariableReference.ts","../src/compiler/Parser/ParsedHierarchy/Variable/VariableReference.ts","../src/compiler/Parser/ParsedHierarchy/FunctionCall.ts","../src/compiler/Parser/ParsedHierarchy/Expression/MultipleConditionExpression.ts","../src/compiler/Parser/ParsedHierarchy/Divert/DivertTarget.ts","../src/compiler/Parser/ParsedHierarchy/Divert/Divert.ts","../src/compiler/Parser/ParsedHierarchy/Gather/GatherPointToResolve.ts","../src/compiler/Parser/ParsedHierarchy/Sequence/SequenceDivertToResolve.ts","../src/compiler/Parser/ParsedHierarchy/Sequence/SequenceType.ts","../src/compiler/Parser/ParsedHierarchy/Sequence/Sequence.ts","../src/compiler/Parser/ParsedHierarchy/TunnelOnwards.ts","../src/engine/ListDefinition.ts","../src/compiler/Parser/ParsedHierarchy/List/ListDefinition.ts","../src/compiler/Parser/ParsedHierarchy/Variable/VariableAssignment.ts","../src/compiler/Parser/ParsedHierarchy/Weave.ts","../src/compiler/Parser/ParsedHierarchy/Conditional/ConditionalSingleBranch.ts","../src/compiler/Parser/CustomFlags.ts","../src/engine/DebugMetadata.ts","../src/compiler/Parser/ParsedHierarchy/Declaration/ExternalDeclaration.ts","../src/compiler/Parser/FlowDecl.ts","../src/compiler/Parser/ParsedHierarchy/Wrap.ts","../src/compiler/Parser/ParsedHierarchy/Glue.ts","../src/engine/Glue.ts","../src/compiler/Parser/ParsedHierarchy/Expression/IncDecExpression.ts","../src/compiler/Parser/ParsedHierarchy/IncludedFile.ts","../src/compiler/Parser/InfixOperator.ts","../src/compiler/Parser/ParsedHierarchy/Knot.ts","../src/compiler/Parser/ParsedHierarchy/List/List.ts","../src/compiler/Parser/ParsedHierarchy/List/ListElementDefinition.ts","../src/compiler/Parser/StatementLevel.ts","../src/compiler/Parser/ParsedHierarchy/Stitch.ts","../src/engine/Tag.ts","../src/engine/Choice.ts","../src/engine/ListDefinitionsOrigin.ts","../src/engine/JsonSerialisation.ts","../src/engine/CallStack.ts","../src/engine/VariablesState.ts","../src/engine/PRNG.ts","../src/engine/StatePatch.ts","../src/engine/SimpleJson.ts","../src/engine/Flow.ts","../src/engine/StoryState.ts","../src/engine/StopWatch.ts","../src/engine/Error.ts","../src/engine/Story.ts","../src/compiler/Parser/ParsedHierarchy/Story.ts","../src/compiler/Parser/ParsedHierarchy/Expression/StringExpression.ts","../src/compiler/Parser/ParsedHierarchy/Tag.ts","../src/compiler/FileHandler/DefaultFileHandler.ts","../src/compiler/Parser/InkParser.ts","../src/compiler/Compiler.ts","../src/compiler/FileHandler/JsonFileHandler.ts"],"sourcesContent":["import { ErrorHandler } from \"../engine/Error\";\nimport { IFileHandler } from \"./IFileHandler\";\n\nexport class CompilerOptions {\n constructor(\n public readonly sourceFilename: string | null = null,\n public readonly pluginNames: string[] = [],\n public readonly countAllVisits: boolean = false,\n public readonly errorHandler: ErrorHandler | null = null,\n public readonly fileHandler: IFileHandler | null = null\n ) {}\n}\n","import { DebugMetadata } from \"../engine/DebugMetadata\";\n\nexport class DebugSourceRange {\n constructor(\n public readonly length: number,\n public readonly debugMetadata: DebugMetadata | null,\n public text: string\n ) {}\n}\n","// TODO: Unifify with Engine.\n\nexport enum ErrorType {\n Author,\n Warning,\n Error,\n}\n","import { Identifier } from \"./Identifier\";\n\nexport class Argument {\n constructor(\n public identifier: Identifier | null = null,\n public isByReference: boolean | null = null,\n public isDivertTarget: boolean | null = null\n ) {}\n\n get typeName(): string {\n return \"Argument\";\n }\n}\n","import { INamedContent } from \"./INamedContent\";\n\nexport function asOrNull(\n obj: any,\n type: (new (...arg: any[]) => T) | (Function & { prototype: T })\n): T | null {\n if (obj instanceof type) {\n return unsafeTypeAssertion(obj, type);\n } else {\n return null;\n }\n}\n\nexport function asOrThrows(\n obj: any,\n type: (new (...arg: any[]) => T) | (Function & { prototype: T })\n): T | never {\n if (obj instanceof type) {\n return unsafeTypeAssertion(obj, type);\n } else {\n throw new Error(`${obj} is not of type ${type}`);\n }\n}\n\nexport function asNumberOrThrows(obj: any) {\n if (typeof obj === \"number\") {\n return obj as number;\n } else {\n throw new Error(`${obj} is not a number`);\n }\n}\n\nexport function asBooleanOrThrows(obj: any) {\n if (typeof obj === \"boolean\") {\n return obj as boolean;\n } else {\n throw new Error(`${obj} is not a boolean`);\n }\n}\n\n// So here, in the reference implementation, contentObj is casted to an INamedContent\n// but here we use js-style duck typing: if it implements the same props as the interface,\n// we treat it as valid.\nexport function asINamedContentOrNull(obj: any): INamedContent | null {\n if (obj.hasValidName && obj.name) {\n return obj as INamedContent;\n }\n\n return null;\n}\n\nexport function nullIfUndefined(obj: T | undefined): T | null {\n if (typeof obj === \"undefined\") {\n return null;\n }\n\n return obj;\n}\n\nexport function isEquatable(type: any) {\n return typeof type === \"object\" && typeof type.Equals === \"function\";\n}\n\nfunction unsafeTypeAssertion(\n obj: any,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n type: (new () => T) | (Function & { prototype: T })\n) {\n return obj as T;\n}\n\nexport function filterUndef(element: T | undefined): element is T {\n return element != undefined;\n}\n","import { Container as RuntimeContainer } from \"../../../engine/Container\";\nimport { DebugMetadata } from \"../../../engine/DebugMetadata\";\nimport { FindQueryFunc } from \"./FindQueryFunc\";\nimport { InkObject as RuntimeObject } from \"../../../engine/Object\";\nimport { Path as RuntimePath } from \"../../../engine/Path\";\nimport { Story } from \"./Story\";\nimport { asOrNull } from \"../../../engine/TypeAssertion\";\n\nexport abstract class ParsedObject {\n public abstract readonly GenerateRuntimeObject: () => RuntimeObject | null;\n\n private _alreadyHadError: boolean = false;\n private _alreadyHadWarning: boolean = false;\n private _debugMetadata: DebugMetadata | null = null;\n private _runtimeObject: RuntimeObject | null = null;\n\n public content: ParsedObject[] = [];\n public parent: ParsedObject | null = null;\n\n get debugMetadata() {\n if (this._debugMetadata === null && this.parent) {\n return this.parent.debugMetadata;\n }\n\n return this._debugMetadata;\n }\n\n set debugMetadata(value: DebugMetadata | null) {\n this._debugMetadata = value;\n }\n\n get hasOwnDebugMetadata(): boolean {\n return Boolean(this.debugMetadata);\n }\n\n get typeName(): string {\n return \"ParsedObject\";\n }\n\n public readonly GetType = (): string => this.typeName;\n\n get story(): Story {\n let ancestor: ParsedObject = this;\n while (ancestor.parent) {\n ancestor = ancestor.parent;\n }\n\n return ancestor as Story;\n }\n\n get runtimeObject(): RuntimeObject {\n if (!this._runtimeObject) {\n this._runtimeObject = this.GenerateRuntimeObject();\n if (this._runtimeObject) {\n this._runtimeObject.debugMetadata = this.debugMetadata;\n }\n }\n\n return this._runtimeObject as RuntimeObject;\n }\n\n set runtimeObject(value: RuntimeObject) {\n this._runtimeObject = value;\n }\n\n get runtimePath(): RuntimePath {\n if (!this.runtimeObject.path) {\n throw new Error();\n }\n\n return this.runtimeObject.path;\n }\n\n // When counting visits and turns since, different object\n // types may have different containers that needs to be counted.\n // For most it'll just be the object's main runtime object,\n // but for e.g. choices, it'll be the target container.\n get containerForCounting(): RuntimeContainer | null {\n return this.runtimeObject as RuntimeContainer;\n }\n\n get ancestry(): ParsedObject[] {\n let result = [];\n\n let ancestor = this.parent;\n while (ancestor) {\n result.push(ancestor);\n ancestor = ancestor.parent;\n }\n\n result = result.reverse();\n\n return result;\n }\n\n /*\n get descriptionOfScope(): string {\n const locationNames: string[] = [];\n\n let ancestor: ParsedObject | null = this;\n while (ancestor) {\n var ancestorFlow = ancestor as FlowBase;\n if (ancestorFlow && ancestorFlow.name != null) {\n locationNames.push(`'${ancestorFlow.name}'`);\n }\n ancestor = ancestor.parent;\n }\n\n let scopeSB = '';\n if (locationNames.length > 0) {\n const locationsListStr = locationNames.join(', ');\n scopeSB += `${locationsListStr} and`;\n }\n\n scopeSB += 'at top scope';\n\n return scopeSB;\n }\n*/\n\n // Return the object so that method can be chained easily\n public readonly AddContent = (\n subContent: V\n ) => {\n if (this.content === null) {\n this.content = [];\n }\n\n const sub = Array.isArray(subContent) ? subContent : [subContent];\n\n // Make resilient to content not existing, which can happen\n // in the case of parse errors where we've already reported\n // an error but still want a valid structure so we can\n // carry on parsing.\n for (const ss of sub) {\n if (ss.hasOwnProperty(\"parent\")) {\n ss.parent = this;\n }\n this.content.push(ss);\n }\n\n if (Array.isArray(subContent)) {\n return;\n } else {\n return subContent;\n }\n };\n\n public readonly InsertContent = (\n index: number,\n subContent: T\n ): T => {\n if (this.content === null) {\n this.content = [];\n }\n\n subContent.parent = this;\n this.content.splice(index, 0, subContent);\n\n return subContent;\n };\n\n public readonly Find =\n (\n type: (new (...arg: any[]) => T) | (Function & { prototype: T })\n ) =>\n (queryFunc: FindQueryFunc | null = null): T | null => {\n let tObj = asOrNull(this, type) as any as T;\n if (tObj !== null && (queryFunc === null || queryFunc(tObj) === true)) {\n return tObj;\n }\n\n if (this.content === null) {\n return null;\n }\n\n for (const obj of this.content) {\n let nestedResult = obj.Find && obj.Find(type)(queryFunc);\n if (nestedResult) {\n return nestedResult as T;\n }\n }\n\n return null;\n };\n\n public readonly FindAll =\n (\n type: (new (...arg: any[]) => T) | (Function & { prototype: T })\n ) =>\n (queryFunc?: FindQueryFunc, foundSoFar?: T[]): T[] => {\n const found = Array.isArray(foundSoFar) ? foundSoFar : [];\n\n const tObj = asOrNull(this, type);\n if (tObj !== null && (!queryFunc || queryFunc(tObj) === true)) {\n found.push(tObj);\n }\n\n if (this.content === null) {\n return [];\n }\n\n for (const obj of this.content) {\n obj.FindAll && obj.FindAll(type)(queryFunc, found);\n }\n\n return found;\n };\n\n public ResolveReferences(context: Story) {\n if (this.content !== null) {\n for (const obj of this.content) {\n obj.ResolveReferences(context);\n }\n }\n }\n\n public Error(\n message: string,\n source: ParsedObject | null = null,\n isWarning: boolean = false\n ): void {\n if (source === null) {\n source = this;\n }\n\n // Only allow a single parsed object to have a single error *directly* associated with it\n if (\n (source._alreadyHadError && !isWarning) ||\n (source._alreadyHadWarning && isWarning)\n ) {\n return;\n }\n\n if (this.parent) {\n this.parent.Error(message, source, isWarning);\n } else {\n throw new Error(`No parent object to send error to: ${message}`);\n }\n\n if (isWarning) {\n source._alreadyHadWarning = true;\n } else {\n source._alreadyHadError = true;\n }\n }\n\n public readonly Warning = (\n message: string,\n source: ParsedObject | null = null\n ): void => {\n this.Error(message, source, true);\n };\n}\n","import { ParsedObject } from \"./Object\";\n\nexport class AuthorWarning extends ParsedObject {\n constructor(public readonly warningMessage: string) {\n super();\n }\n\n get typeName(): string {\n return \"AuthorWarning\";\n }\n\n public readonly GenerateRuntimeObject = (): null => {\n this.Warning(this.warningMessage);\n return null;\n };\n}\n","export class Path {\n public static parentId = \"^\";\n\n public _isRelative: boolean;\n public _components: Path.Component[];\n public _componentsString: string | null;\n\n constructor();\n constructor(componentsString: string);\n constructor(head: Path.Component, tail: Path);\n constructor(head: Path.Component[], relative?: boolean);\n constructor() {\n this._components = [];\n this._componentsString = null;\n this._isRelative = false;\n\n if (typeof arguments[0] == \"string\") {\n let componentsString = arguments[0] as string;\n this.componentsString = componentsString;\n } else if (\n arguments[0] instanceof Path.Component &&\n arguments[1] instanceof Path\n ) {\n let head = arguments[0] as Path.Component;\n let tail = arguments[1] as Path;\n this._components.push(head);\n this._components = this._components.concat(tail._components);\n } else if (arguments[0] instanceof Array) {\n let head = arguments[0] as Path.Component[];\n let relative = !!arguments[1] as boolean;\n this._components = this._components.concat(head);\n this._isRelative = relative;\n }\n }\n get isRelative() {\n return this._isRelative;\n }\n get componentCount(): number {\n return this._components.length;\n }\n get head(): Path.Component | null {\n if (this._components.length > 0) {\n return this._components[0];\n } else {\n return null;\n }\n }\n get tail(): Path {\n if (this._components.length >= 2) {\n // careful, the original code uses length-1 here. This is because the second argument of\n // List.GetRange is a number of elements to extract, wherease Array.slice uses an index\n let tailComps = this._components.slice(1, this._components.length);\n return new Path(tailComps);\n } else {\n return Path.self;\n }\n }\n get length(): number {\n return this._components.length;\n }\n get lastComponent(): Path.Component | null {\n let lastComponentIdx = this._components.length - 1;\n if (lastComponentIdx >= 0) {\n return this._components[lastComponentIdx];\n } else {\n return null;\n }\n }\n get containsNamedComponent(): boolean {\n for (let i = 0, l = this._components.length; i < l; i++) {\n if (!this._components[i].isIndex) {\n return true;\n }\n }\n return false;\n }\n static get self(): Path {\n let path = new Path();\n path._isRelative = true;\n return path;\n }\n\n public GetComponent(index: number): Path.Component {\n return this._components[index];\n }\n public PathByAppendingPath(pathToAppend: Path): Path {\n let p = new Path();\n\n let upwardMoves = 0;\n for (let i = 0; i < pathToAppend._components.length; ++i) {\n if (pathToAppend._components[i].isParent) {\n upwardMoves++;\n } else {\n break;\n }\n }\n\n for (let i = 0; i < this._components.length - upwardMoves; ++i) {\n p._components.push(this._components[i]);\n }\n\n for (let i = upwardMoves; i < pathToAppend._components.length; ++i) {\n p._components.push(pathToAppend._components[i]);\n }\n\n return p;\n }\n get componentsString(): string {\n if (this._componentsString == null) {\n this._componentsString = this._components.join(\".\");\n if (this.isRelative)\n this._componentsString = \".\" + this._componentsString;\n }\n\n return this._componentsString;\n }\n set componentsString(value: string) {\n this._components.length = 0;\n\n this._componentsString = value;\n\n if (this._componentsString == null || this._componentsString == \"\") return;\n\n if (this._componentsString[0] == \".\") {\n this._isRelative = true;\n this._componentsString = this._componentsString.substring(1);\n }\n\n let componentStrings = this._componentsString.split(\".\");\n for (let str of componentStrings) {\n // we need to distinguish between named components that start with a number, eg \"42somewhere\", and indexed components\n // the normal parseInt won't do for the detection because it's too relaxed.\n // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt\n if (/^(\\-|\\+)?([0-9]+|Infinity)$/.test(str)) {\n this._components.push(new Path.Component(parseInt(str)));\n } else {\n this._components.push(new Path.Component(str));\n }\n }\n }\n public toString(): string {\n return this.componentsString;\n }\n public Equals(otherPath: Path | null): boolean {\n if (otherPath == null) return false;\n\n if (otherPath._components.length != this._components.length) return false;\n\n if (otherPath.isRelative != this.isRelative) return false;\n\n // the original code uses SequenceEqual here, so we need to iterate over the components manually.\n for (let i = 0, l = otherPath._components.length; i < l; i++) {\n // it's not quite clear whether this test should use Equals or a simple == operator,\n // see https://github.com/y-lohse/inkjs/issues/22\n if (!otherPath._components[i].Equals(this._components[i])) return false;\n }\n\n return true;\n }\n public PathByAppendingComponent(c: Path.Component): Path {\n let p = new Path();\n p._components.push(...this._components);\n p._components.push(c);\n return p;\n }\n}\n\nexport namespace Path {\n export class Component {\n public readonly index: number;\n public readonly name: string | null;\n\n constructor(indexOrName: string | number) {\n this.index = -1;\n this.name = null;\n if (typeof indexOrName == \"string\") {\n this.name = indexOrName;\n } else {\n this.index = indexOrName;\n }\n }\n get isIndex(): boolean {\n return this.index >= 0;\n }\n get isParent(): boolean {\n return this.name == Path.parentId;\n }\n\n public static ToParent(): Component {\n return new Component(Path.parentId);\n }\n public toString(): string | null {\n if (this.isIndex) {\n return this.index.toString();\n } else {\n return this.name;\n }\n }\n public Equals(otherComp: Component): boolean {\n if (otherComp != null && otherComp.isIndex == this.isIndex) {\n if (this.isIndex) {\n return this.index == otherComp.index;\n } else {\n return this.name == otherComp.name;\n }\n }\n\n return false;\n }\n }\n}\n","export namespace Debug {\n export function AssertType(\n variable: any,\n type: new () => T,\n message: string\n ): void | never {\n Assert(variable instanceof type, message);\n }\n\n export function Assert(condition: boolean, message?: string): void | never {\n if (!condition) {\n if (typeof message !== \"undefined\") {\n console.warn(message);\n }\n\n if (console.trace) {\n console.trace();\n }\n\n throw new Error(\"\");\n }\n }\n}\n","import { InkObject } from \"./Object\";\nimport { Path } from \"./Path\";\nimport { InkList, InkListItem } from \"./InkList\";\nimport { StoryException } from \"./StoryException\";\nimport { asOrNull, asOrThrows } from \"./TypeAssertion\";\nimport { tryParseInt, tryParseFloat } from \"./TryGetResult\";\nimport { throwNullException } from \"./NullException\";\n\nexport abstract class AbstractValue extends InkObject {\n public abstract get valueType(): ValueType;\n public abstract get isTruthy(): boolean;\n public abstract get valueObject(): any;\n\n public abstract Cast(newType: ValueType): Value;\n\n public static Create(\n val: any,\n preferredNumberType?: ValueType\n ): Value | null {\n // This code doesn't exist in upstream and is simply here to enforce\n // the creation of the proper number value.\n // If `preferredNumberType` is not provided or if value doesn't match\n // `preferredNumberType`, this conditional does nothing.\n if (preferredNumberType) {\n if (\n preferredNumberType === (ValueType.Int as ValueType) &&\n Number.isInteger(Number(val))\n ) {\n return new IntValue(Number(val));\n } else if (\n preferredNumberType === (ValueType.Float as ValueType) &&\n !isNaN(val)\n ) {\n return new FloatValue(Number(val));\n }\n }\n\n if (typeof val === \"boolean\") {\n return new BoolValue(Boolean(val));\n }\n\n // https://github.com/y-lohse/inkjs/issues/425\n // Changed condition sequence, because Number('') is\n // parsed to 0, which made setting string to empty\n // impossible\n if (typeof val === \"string\") {\n return new StringValue(String(val));\n } else if (Number.isInteger(Number(val))) {\n return new IntValue(Number(val));\n } else if (!isNaN(val)) {\n return new FloatValue(Number(val));\n } else if (val instanceof Path) {\n return new DivertTargetValue(asOrThrows(val, Path));\n } else if (val instanceof InkList) {\n return new ListValue(asOrThrows(val, InkList));\n }\n\n return null;\n }\n public Copy() {\n return asOrThrows(AbstractValue.Create(this.valueObject), InkObject);\n }\n public BadCastException(targetType: ValueType) {\n return new StoryException(\n \"Can't cast \" +\n this.valueObject +\n \" from \" +\n this.valueType +\n \" to \" +\n targetType\n );\n }\n}\n\nexport abstract class Value<\n T extends { toString: () => string },\n> extends AbstractValue {\n public value: T | null;\n\n constructor(val: T | null) {\n super();\n this.value = val;\n }\n public get valueObject() {\n return this.value;\n }\n public toString() {\n if (this.value === null) return throwNullException(\"Value.value\");\n return this.value.toString();\n }\n}\n\nexport class BoolValue extends Value {\n constructor(val: boolean) {\n super(val || false);\n }\n public get isTruthy() {\n return Boolean(this.value);\n }\n public get valueType() {\n return ValueType.Bool;\n }\n\n public Cast(newType: ValueType): Value {\n if (this.value === null) return throwNullException(\"Value.value\");\n\n if (newType == this.valueType) {\n return this;\n }\n\n if (newType == ValueType.Int) {\n return new IntValue(this.value ? 1 : 0);\n }\n\n if (newType == ValueType.Float) {\n return new FloatValue(this.value ? 1.0 : 0.0);\n }\n\n if (newType == ValueType.String) {\n return new StringValue(this.value ? \"true\" : \"false\");\n }\n\n throw this.BadCastException(newType);\n }\n\n public toString() {\n return this.value ? \"true\" : \"false\";\n }\n}\n\nexport class IntValue extends Value {\n constructor(val: number) {\n super(val || 0);\n }\n public get isTruthy() {\n return this.value != 0;\n }\n public get valueType() {\n return ValueType.Int;\n }\n\n public Cast(newType: ValueType): Value {\n if (this.value === null) return throwNullException(\"Value.value\");\n\n if (newType == this.valueType) {\n return this;\n }\n\n if (newType == ValueType.Bool) {\n return new BoolValue(this.value === 0 ? false : true);\n }\n\n if (newType == ValueType.Float) {\n return new FloatValue(this.value);\n }\n\n if (newType == ValueType.String) {\n return new StringValue(\"\" + this.value);\n }\n\n throw this.BadCastException(newType);\n }\n}\n\nexport class FloatValue extends Value {\n constructor(val: number) {\n super(val || 0.0);\n }\n public get isTruthy() {\n return this.value != 0.0;\n }\n public get valueType() {\n return ValueType.Float;\n }\n\n public Cast(newType: ValueType): Value {\n if (this.value === null) return throwNullException(\"Value.value\");\n\n if (newType == this.valueType) {\n return this;\n }\n\n if (newType == ValueType.Bool) {\n return new BoolValue(this.value === 0.0 ? false : true);\n }\n\n if (newType == ValueType.Int) {\n return new IntValue(this.value);\n }\n\n if (newType == ValueType.String) {\n return new StringValue(\"\" + this.value);\n }\n\n throw this.BadCastException(newType);\n }\n}\n\nexport class StringValue extends Value {\n public _isNewline: boolean;\n public _isInlineWhitespace: boolean;\n\n constructor(val: string) {\n super(val || \"\");\n\n this._isNewline = this.value == \"\\n\";\n this._isInlineWhitespace = true;\n\n if (this.value === null) return throwNullException(\"Value.value\");\n\n if (this.value.length > 0) {\n this.value.split(\"\").every((c) => {\n if (c != \" \" && c != \"\\t\") {\n this._isInlineWhitespace = false;\n return false;\n }\n\n return true;\n });\n }\n }\n public get valueType() {\n return ValueType.String;\n }\n public get isTruthy() {\n if (this.value === null) return throwNullException(\"Value.value\");\n return this.value.length > 0;\n }\n public get isNewline() {\n return this._isNewline;\n }\n public get isInlineWhitespace() {\n return this._isInlineWhitespace;\n }\n public get isNonWhitespace() {\n return !this.isNewline && !this.isInlineWhitespace;\n }\n\n public Cast(newType: ValueType): Value {\n if (newType == this.valueType) {\n return this;\n }\n\n if (newType == ValueType.Int) {\n let parsedInt = tryParseInt(this.value);\n if (parsedInt.exists) {\n return new IntValue(parsedInt.result);\n } else {\n throw this.BadCastException(newType);\n }\n }\n\n if (newType == ValueType.Float) {\n let parsedFloat = tryParseFloat(this.value);\n if (parsedFloat.exists) {\n return new FloatValue(parsedFloat.result);\n } else {\n throw this.BadCastException(newType);\n }\n }\n\n throw this.BadCastException(newType);\n }\n}\n\nexport class DivertTargetValue extends Value {\n constructor(targetPath: Path | null = null) {\n super(targetPath);\n }\n public get valueType() {\n return ValueType.DivertTarget;\n }\n public get targetPath() {\n if (this.value === null) return throwNullException(\"Value.value\");\n return this.value;\n }\n public set targetPath(value: Path) {\n this.value = value;\n }\n public get isTruthy(): never {\n throw new Error(\"Shouldn't be checking the truthiness of a divert target\");\n }\n\n public Cast(newType: ValueType): Value {\n if (newType == this.valueType) return this;\n\n throw this.BadCastException(newType);\n }\n public toString() {\n return \"DivertTargetValue(\" + this.targetPath + \")\";\n }\n}\n\nexport class VariablePointerValue extends Value {\n public _contextIndex: number;\n\n constructor(variableName: string, contextIndex: number = -1) {\n super(variableName);\n\n this._contextIndex = contextIndex;\n }\n\n public get contextIndex() {\n return this._contextIndex;\n }\n public set contextIndex(value: number) {\n this._contextIndex = value;\n }\n public get variableName() {\n if (this.value === null) return throwNullException(\"Value.value\");\n return this.value;\n }\n public set variableName(value: string) {\n this.value = value;\n }\n public get valueType() {\n return ValueType.VariablePointer;\n }\n\n public get isTruthy(): never {\n throw new Error(\n \"Shouldn't be checking the truthiness of a variable pointer\"\n );\n }\n\n public Cast(newType: ValueType): Value {\n if (newType == this.valueType) return this;\n\n throw this.BadCastException(newType);\n }\n public toString() {\n return \"VariablePointerValue(\" + this.variableName + \")\";\n }\n public Copy() {\n return new VariablePointerValue(this.variableName, this.contextIndex);\n }\n}\n\nexport class ListValue extends Value {\n public get isTruthy() {\n if (this.value === null) {\n return throwNullException(\"this.value\");\n }\n return this.value.Count > 0;\n }\n public get valueType() {\n return ValueType.List;\n }\n public Cast(newType: ValueType): Value {\n if (this.value === null) return throwNullException(\"Value.value\");\n\n if (newType == ValueType.Int) {\n let max = this.value.maxItem;\n if (max.Key.isNull) return new IntValue(0);\n else return new IntValue(max.Value);\n } else if (newType == ValueType.Float) {\n let max = this.value.maxItem;\n if (max.Key.isNull) return new FloatValue(0.0);\n else return new FloatValue(max.Value);\n } else if (newType == ValueType.String) {\n let max = this.value.maxItem;\n if (max.Key.isNull) return new StringValue(\"\");\n else {\n return new StringValue(max.Key.toString());\n }\n }\n\n if (newType == this.valueType) return this;\n\n throw this.BadCastException(newType);\n }\n constructor();\n constructor(list: InkList);\n constructor(listOrSingleItem: InkListItem, singleValue: number);\n constructor(listOrSingleItem?: InkListItem | InkList, singleValue?: number) {\n super(null);\n\n if (!listOrSingleItem && !singleValue) {\n this.value = new InkList();\n } else if (listOrSingleItem instanceof InkList) {\n this.value = new InkList(listOrSingleItem);\n } else if (\n listOrSingleItem instanceof InkListItem &&\n typeof singleValue === \"number\"\n ) {\n this.value = new InkList({\n Key: listOrSingleItem,\n Value: singleValue,\n });\n }\n }\n public static RetainListOriginsForAssignment(\n oldValue: InkObject | null,\n newValue: InkObject\n ) {\n let oldList = asOrNull(oldValue, ListValue);\n let newList = asOrNull(newValue, ListValue);\n\n if (newList && newList.value === null)\n return throwNullException(\"newList.value\");\n if (oldList && oldList.value === null)\n return throwNullException(\"oldList.value\");\n\n // When assigning the empty list, try to retain any initial origin names\n if (oldList && newList && newList.value!.Count == 0)\n newList.value!.SetInitialOriginNames(oldList.value!.originNames);\n }\n}\n\nexport enum ValueType {\n Bool = -1,\n Int = 0,\n Float = 1,\n List = 2,\n String = 3,\n DivertTarget = 4,\n VariablePointer = 5,\n}\n","export enum PushPopType {\n Tunnel = 0,\n Function = 1,\n FunctionEvaluationFromGame = 2,\n}\n","/**\n * In the original C# code, a SystemException would be thrown when passing\n * null to methods expected a valid instance. Javascript has no such\n * concept, but TypeScript will not allow `null` to be passed to methods\n * explicitely requiring a valid type.\n *\n * Whenever TypeScript complain about the possibility of a `null` value,\n * check the offending value and it it's null, throw this exception using\n * `throwNullException(name: string)`.\n */\nexport class NullException extends Error {}\n\n/**\n * Throw a NullException.\n *\n * @param name a short description of the offending value (often its name within the code).\n */\nexport function throwNullException(name: string): never {\n throw new NullException(`${name} is null or undefined`);\n}\n","import { Path } from \"./Path\";\nimport { Container } from \"./Container\";\nimport { Debug } from \"./Debug\";\nimport { asOrNull, asINamedContentOrNull } from \"./TypeAssertion\";\nimport { throwNullException } from \"./NullException\";\nimport { SearchResult } from \"./SearchResult\";\nimport { DebugMetadata } from \"./DebugMetadata\";\n\nexport class InkObject {\n public parent: InkObject | null = null;\n\n get debugMetadata(): DebugMetadata | null {\n if (this._debugMetadata === null) {\n if (this.parent) {\n return this.parent.debugMetadata;\n }\n }\n\n return this._debugMetadata;\n }\n\n set debugMetadata(value) {\n this._debugMetadata = value;\n }\n\n get ownDebugMetadata() {\n return this._debugMetadata;\n }\n\n private _debugMetadata: DebugMetadata | null = null;\n\n public DebugLineNumberOfPath(path: Path) {\n if (path === null) return null;\n\n // Try to get a line number from debug metadata\n let root = this.rootContentContainer;\n if (root) {\n let targetContent = root.ContentAtPath(path).obj;\n if (targetContent) {\n let dm = targetContent.debugMetadata;\n if (dm !== null) {\n return dm.startLineNumber;\n }\n }\n }\n\n return null;\n }\n\n get path() {\n if (this._path == null) {\n if (this.parent == null) {\n this._path = new Path();\n } else {\n let comps: Path.Component[] = [];\n\n let child: InkObject = this;\n let container = asOrNull(child.parent, Container);\n\n while (container !== null) {\n let namedChild = asINamedContentOrNull(child);\n if (namedChild != null && namedChild.hasValidName) {\n if (namedChild.name === null)\n return throwNullException(\"namedChild.name\");\n comps.unshift(new Path.Component(namedChild.name!));\n } else {\n comps.unshift(new Path.Component(container.content.indexOf(child)));\n }\n\n child = container;\n container = asOrNull(container.parent, Container);\n }\n\n this._path = new Path(comps);\n }\n }\n\n return this._path;\n }\n private _path: Path | null = null;\n\n public ResolvePath(path: Path | null): SearchResult {\n if (path === null) return throwNullException(\"path\");\n if (path.isRelative) {\n let nearestContainer = asOrNull(this, Container);\n\n if (nearestContainer === null) {\n Debug.Assert(\n this.parent !== null,\n \"Can't resolve relative path because we don't have a parent\"\n );\n nearestContainer = asOrNull(this.parent, Container);\n Debug.Assert(\n nearestContainer !== null,\n \"Expected parent to be a container\"\n );\n Debug.Assert(path.GetComponent(0).isParent);\n path = path.tail;\n }\n\n if (nearestContainer === null) {\n return throwNullException(\"nearestContainer\");\n }\n return nearestContainer.ContentAtPath(path);\n } else {\n let contentContainer = this.rootContentContainer;\n if (contentContainer === null) {\n return throwNullException(\"contentContainer\");\n }\n return contentContainer.ContentAtPath(path);\n }\n }\n\n public ConvertPathToRelative(globalPath: Path) {\n let ownPath = this.path;\n\n let minPathLength = Math.min(globalPath.length, ownPath.length);\n let lastSharedPathCompIndex = -1;\n\n for (let i = 0; i < minPathLength; ++i) {\n let ownComp = ownPath.GetComponent(i);\n let otherComp = globalPath.GetComponent(i);\n\n if (ownComp.Equals(otherComp)) {\n lastSharedPathCompIndex = i;\n } else {\n break;\n }\n }\n\n // No shared path components, so just use global path\n if (lastSharedPathCompIndex == -1) return globalPath;\n\n let numUpwardsMoves = ownPath.componentCount - 1 - lastSharedPathCompIndex;\n\n let newPathComps: Path.Component[] = [];\n\n for (let up = 0; up < numUpwardsMoves; ++up)\n newPathComps.push(Path.Component.ToParent());\n\n for (\n let down = lastSharedPathCompIndex + 1;\n down < globalPath.componentCount;\n ++down\n )\n newPathComps.push(globalPath.GetComponent(down));\n\n let relativePath = new Path(newPathComps, true);\n return relativePath;\n }\n\n public CompactPathString(otherPath: Path) {\n let globalPathStr = null;\n let relativePathStr = null;\n\n if (otherPath.isRelative) {\n relativePathStr = otherPath.componentsString;\n globalPathStr = this.path.PathByAppendingPath(otherPath).componentsString;\n } else {\n let relativePath = this.ConvertPathToRelative(otherPath);\n relativePathStr = relativePath.componentsString;\n globalPathStr = otherPath.componentsString;\n }\n\n if (relativePathStr.length < globalPathStr.length) return relativePathStr;\n else return globalPathStr;\n }\n\n get rootContentContainer() {\n let ancestor: InkObject = this;\n while (ancestor.parent) {\n ancestor = ancestor.parent;\n }\n return asOrNull(ancestor, Container);\n }\n\n public Copy(): InkObject {\n throw Error(\"Not Implemented: Doesn't support copying\");\n }\n // SetChild works slightly diferently in the js implementation.\n // Since we can't pass an objets property by reference, we instead pass\n // the object and the property string.\n // TODO: This method can probably be rewritten with type-safety in mind.\n public SetChild(obj: any, prop: any, value: any) {\n if (obj[prop]) obj[prop] = null;\n\n obj[prop] = value;\n\n if (obj[prop]) obj[prop].parent = this;\n }\n\n public Equals(obj: any) {\n return obj === this;\n }\n}\n","export class StringBuilder {\n private string: string;\n\n constructor(str?: string) {\n str = typeof str !== \"undefined\" ? str.toString() : \"\";\n this.string = str;\n }\n get Length(): number {\n return this.string.length;\n }\n public Append(str: string | null) {\n if (str !== null) {\n this.string += str;\n }\n }\n public AppendLine(str?: string) {\n if (typeof str !== \"undefined\") this.Append(str);\n this.string += \"\\n\";\n }\n public AppendFormat(format: string, ...args: any[]) {\n // taken from http://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format\n this.string += format.replace(/{(\\d+)}/g, (match: string, num: number) =>\n typeof args[num] != \"undefined\" ? args[num] : match\n );\n }\n public toString(): string {\n return this.string;\n }\n\n public Clear() {\n this.string = \"\";\n }\n}\n","import { throwNullException } from \"./NullException\";\nimport { StringBuilder } from \"./StringBuilder\";\nimport { ListDefinition } from \"./ListDefinition\";\nimport { Story } from \"./Story\";\n\nexport class InkListItem implements IInkListItem {\n // InkListItem is a struct\n\n public readonly originName: string | null = null;\n public readonly itemName: string | null = null;\n\n constructor(originName: string | null, itemName: string | null);\n constructor(fullName: string | null);\n constructor() {\n if (typeof arguments[1] !== \"undefined\") {\n let originName = arguments[0] as string | null;\n let itemName = arguments[1] as string | null;\n\n this.originName = originName;\n this.itemName = itemName;\n } else if (arguments[0]) {\n let fullName = arguments[0] as string;\n\n let nameParts = fullName.toString().split(\".\");\n this.originName = nameParts[0];\n this.itemName = nameParts[1];\n }\n }\n public static get Null() {\n return new InkListItem(null, null);\n }\n public get isNull() {\n return this.originName == null && this.itemName == null;\n }\n get fullName() {\n return (\n (this.originName !== null ? this.originName : \"?\") + \".\" + this.itemName\n );\n }\n public toString(): string {\n return this.fullName;\n }\n public Equals(obj: InkListItem) {\n if (obj instanceof InkListItem) {\n let otherItem = obj;\n return (\n otherItem.itemName == this.itemName &&\n otherItem.originName == this.originName\n );\n }\n\n return false;\n }\n\n // These methods did not exist in the original C# code. Their purpose is to\n // make `InkListItem` mimics the value-type semantics of the original\n // struct. Please refer to the end of this file, for a more in-depth\n // explanation.\n\n /**\n * Returns a shallow clone of the current instance.\n */\n public copy() {\n return new InkListItem(this.originName, this.itemName);\n }\n /**\n * Returns a `SerializedInkListItem` representing the current\n * instance. The result is intended to be used as a key inside a Map.\n */\n public serialized(): SerializedInkListItem {\n // We are simply using a JSON representation as a value-typed key.\n return JSON.stringify({\n originName: this.originName,\n itemName: this.itemName,\n });\n }\n\n /**\n * Reconstructs a `InkListItem` from the given SerializedInkListItem.\n */\n public static fromSerializedKey(key: SerializedInkListItem): InkListItem {\n let obj = JSON.parse(key);\n if (!InkListItem.isLikeInkListItem(obj)) return InkListItem.Null;\n\n let inkListItem = obj as IInkListItem;\n\n return new InkListItem(inkListItem.originName, inkListItem.itemName);\n }\n\n /**\n * Determines whether the given item is sufficiently `InkListItem`-like\n * to be used as a template when reconstructing the InkListItem.\n */\n private static isLikeInkListItem(item: any) {\n if (typeof item !== \"object\") return false;\n if (!item.hasOwnProperty(\"originName\") || !item.hasOwnProperty(\"itemName\"))\n return false;\n if (typeof item.originName !== \"string\" && typeof item.originName !== null)\n return false;\n if (typeof item.itemName !== \"string\" && typeof item.itemName !== null)\n return false;\n\n return true;\n }\n}\n\nexport class InkList extends Map {\n public origins: ListDefinition[] | null = null;\n public _originNames: string[] | null = [];\n\n constructor();\n constructor(otherList: InkList);\n constructor(singleOriginListName: string, originStory: Story);\n constructor(singleElement: KeyValuePair);\n constructor() {\n // Trying to be smart here, this emulates the constructor inheritance found\n // in the original code, but only if otherList is an InkList. IIFE FTW.\n super(\n (() => {\n if (arguments[0] instanceof InkList) {\n return arguments[0];\n } else {\n return [];\n }\n })()\n );\n\n if (arguments[0] instanceof InkList) {\n let otherList = arguments[0] as InkList;\n\n let otherOriginNames = otherList.originNames as string[];\n if (otherOriginNames !== null)\n this._originNames = otherOriginNames.slice();\n if (otherList.origins !== null) {\n this.origins = otherList.origins.slice();\n }\n } else if (typeof arguments[0] === \"string\") {\n let singleOriginListName = arguments[0] as string;\n let originStory = arguments[1] as Story;\n this.SetInitialOriginName(singleOriginListName);\n\n if (originStory.listDefinitions === null) {\n return throwNullException(\"originStory.listDefinitions\");\n }\n let def = originStory.listDefinitions.TryListGetDefinition(\n singleOriginListName,\n null\n );\n if (def.exists) {\n // Throwing now, because if the value is `null` it will\n // eventually throw down the line.\n if (def.result === null) {\n return throwNullException(\"def.result\");\n }\n this.origins = [def.result];\n } else {\n throw new Error(\n \"InkList origin could not be found in story when constructing new list: \" +\n singleOriginListName\n );\n }\n } else if (\n typeof arguments[0] === \"object\" &&\n arguments[0].hasOwnProperty(\"Key\") &&\n arguments[0].hasOwnProperty(\"Value\")\n ) {\n let singleElement = arguments[0] as KeyValuePair;\n this.Add(singleElement.Key, singleElement.Value);\n }\n }\n\n public static FromString(myListItem: string, originStory: Story) {\n let listValue =\n originStory.listDefinitions?.FindSingleItemListWithName(myListItem);\n if (listValue) {\n if (listValue.value === null) {\n return throwNullException(\"listValue.value\");\n }\n return new InkList(listValue.value);\n } else {\n throw new Error(\n \"Could not find the InkListItem from the string '\" +\n myListItem +\n \"' to create an InkList because it doesn't exist in the original list definition in ink.\"\n );\n }\n }\n\n public AddItem(itemOrItemName: InkListItem | string | null) {\n if (itemOrItemName instanceof InkListItem) {\n let item = itemOrItemName;\n\n if (item.originName == null) {\n this.AddItem(item.itemName);\n return;\n }\n\n if (this.origins === null) return throwNullException(\"this.origins\");\n\n for (let origin of this.origins) {\n if (origin.name == item.originName) {\n let intVal = origin.TryGetValueForItem(item, 0);\n if (intVal.exists) {\n this.Add(item, intVal.result);\n return;\n } else {\n throw new Error(\n \"Could not add the item \" +\n item +\n \" to this list because it doesn't exist in the original list definition in ink.\"\n );\n }\n }\n }\n\n throw new Error(\n \"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.\"\n );\n } else {\n let itemName = itemOrItemName as string | null;\n\n let foundListDef: ListDefinition | null = null;\n\n if (this.origins === null) return throwNullException(\"this.origins\");\n\n for (let origin of this.origins) {\n if (itemName === null) return throwNullException(\"itemName\");\n\n if (origin.ContainsItemWithName(itemName)) {\n if (foundListDef != null) {\n throw new Error(\n \"Could not add the item \" +\n itemName +\n \" to this list because it could come from either \" +\n origin.name +\n \" or \" +\n foundListDef.name\n );\n } else {\n foundListDef = origin;\n }\n }\n }\n\n if (foundListDef == null)\n throw new Error(\n \"Could not add the item \" +\n itemName +\n \" to this list because it isn't known to any list definitions previously associated with this list.\"\n );\n\n let item = new InkListItem(foundListDef.name, itemName);\n let itemVal = foundListDef.ValueForItem(item);\n this.Add(item, itemVal);\n }\n }\n public ContainsItemNamed(itemName: string | null) {\n for (let [key] of this) {\n let item = InkListItem.fromSerializedKey(key);\n if (item.itemName == itemName) return true;\n }\n\n return false;\n }\n public ContainsKey(key: InkListItem) {\n return this.has(key.serialized());\n }\n public Add(key: InkListItem, value: number) {\n let serializedKey = key.serialized();\n if (this.has(serializedKey)) {\n // Throw an exception to match the C# behavior.\n throw new Error(`The Map already contains an entry for ${key}`);\n }\n this.set(serializedKey, value);\n }\n public Remove(key: InkListItem) {\n return this.delete(key.serialized());\n }\n get Count() {\n return this.size;\n }\n get originOfMaxItem(): ListDefinition | null {\n if (this.origins == null) return null;\n\n let maxOriginName = this.maxItem.Key.originName;\n let result = null;\n this.origins.every((origin) => {\n if (origin.name == maxOriginName) {\n result = origin;\n return false;\n } else return true;\n });\n\n return result;\n }\n get originNames(): string[] {\n if (this.Count > 0) {\n if (this._originNames == null && this.Count > 0) this._originNames = [];\n else {\n if (!this._originNames) this._originNames = [];\n this._originNames.length = 0;\n }\n\n for (let [key] of this) {\n let item = InkListItem.fromSerializedKey(key);\n if (item.originName === null)\n return throwNullException(\"item.originName\");\n this._originNames.push(item.originName);\n }\n }\n\n return this._originNames as string[];\n }\n public SetInitialOriginName(initialOriginName: string) {\n this._originNames = [initialOriginName];\n }\n public SetInitialOriginNames(initialOriginNames: string[]) {\n if (initialOriginNames == null) this._originNames = null;\n else this._originNames = initialOriginNames.slice(); // store a copy\n }\n get maxItem() {\n let max: KeyValuePair = {\n Key: InkListItem.Null,\n Value: 0,\n };\n for (let [key, value] of this) {\n let item = InkListItem.fromSerializedKey(key);\n if (max.Key.isNull || value > max.Value)\n max = { Key: item, Value: value };\n }\n\n return max;\n }\n get minItem() {\n let min: KeyValuePair = {\n Key: InkListItem.Null,\n Value: 0,\n };\n for (let [key, value] of this) {\n let item = InkListItem.fromSerializedKey(key);\n if (min.Key.isNull || value < min.Value) {\n min = { Key: item, Value: value };\n }\n }\n return min;\n }\n get inverse() {\n let list = new InkList();\n if (this.origins != null) {\n for (let origin of this.origins) {\n for (let [key, value] of origin.items) {\n let item = InkListItem.fromSerializedKey(key);\n if (!this.ContainsKey(item)) list.Add(item, value);\n }\n }\n }\n return list;\n }\n get all() {\n let list = new InkList();\n if (this.origins != null) {\n for (let origin of this.origins) {\n for (let [key, value] of origin.items) {\n let item = InkListItem.fromSerializedKey(key);\n list.set(item.serialized(), value);\n }\n }\n }\n return list;\n }\n public Union(otherList: InkList) {\n let union = new InkList(this);\n for (let [key, value] of otherList) {\n union.set(key, value);\n }\n return union;\n }\n public Intersect(otherList: InkList) {\n let intersection = new InkList();\n for (let [key, value] of this) {\n if (otherList.has(key)) intersection.set(key, value);\n }\n\n return intersection;\n }\n public HasIntersection(otherList: InkList): boolean {\n for (let [key] of this) {\n if (otherList.has(key)) return true;\n }\n return false;\n }\n public Without(listToRemove: InkList) {\n let result = new InkList(this);\n for (let [key] of listToRemove) {\n result.delete(key);\n }\n\n return result;\n }\n\n public Contains(key: string): boolean;\n public Contains(otherList: InkList): boolean;\n public Contains(what: string | InkList): boolean {\n if (typeof what == \"string\") return this.ContainsItemNamed(what);\n const otherList = what;\n if (otherList.size == 0 || this.size == 0) return false;\n for (let [key] of otherList) {\n if (!this.has(key)) return false;\n }\n\n return true;\n }\n public GreaterThan(otherList: InkList) {\n if (this.Count == 0) return false;\n if (otherList.Count == 0) return true;\n\n return this.minItem.Value > otherList.maxItem.Value;\n }\n public GreaterThanOrEquals(otherList: InkList) {\n if (this.Count == 0) return false;\n if (otherList.Count == 0) return true;\n\n return (\n this.minItem.Value >= otherList.minItem.Value &&\n this.maxItem.Value >= otherList.maxItem.Value\n );\n }\n public LessThan(otherList: InkList) {\n if (otherList.Count == 0) return false;\n if (this.Count == 0) return true;\n\n return this.maxItem.Value < otherList.minItem.Value;\n }\n public LessThanOrEquals(otherList: InkList) {\n if (otherList.Count == 0) return false;\n if (this.Count == 0) return true;\n\n return (\n this.maxItem.Value <= otherList.maxItem.Value &&\n this.minItem.Value <= otherList.minItem.Value\n );\n }\n public MaxAsList() {\n if (this.Count > 0) return new InkList(this.maxItem);\n else return new InkList();\n }\n public MinAsList() {\n if (this.Count > 0) return new InkList(this.minItem);\n else return new InkList();\n }\n public ListWithSubRange(minBound: any, maxBound: any) {\n if (this.Count == 0) return new InkList();\n\n let ordered = this.orderedItems;\n\n let minValue = 0;\n let maxValue = Number.MAX_SAFE_INTEGER;\n\n if (Number.isInteger(minBound)) {\n minValue = minBound;\n } else {\n if (minBound instanceof InkList && minBound.Count > 0)\n minValue = minBound.minItem.Value;\n }\n\n if (Number.isInteger(maxBound)) {\n maxValue = maxBound;\n } else {\n if (maxBound instanceof InkList && maxBound.Count > 0)\n maxValue = maxBound.maxItem.Value;\n }\n\n let subList = new InkList();\n subList.SetInitialOriginNames(this.originNames);\n for (let item of ordered) {\n if (item.Value >= minValue && item.Value <= maxValue) {\n subList.Add(item.Key, item.Value);\n }\n }\n\n return subList;\n }\n public Equals(otherInkList: InkList) {\n if (otherInkList instanceof InkList === false) return false;\n if (otherInkList.Count != this.Count) return false;\n\n for (let [key] of this) {\n if (!otherInkList.has(key)) return false;\n }\n\n return true;\n }\n // GetHashCode not implemented\n get orderedItems() {\n // List>\n let ordered = new Array>();\n\n for (let [key, value] of this) {\n let item = InkListItem.fromSerializedKey(key);\n ordered.push({ Key: item, Value: value });\n }\n\n ordered.sort((x, y) => {\n if (x.Key.originName === null) {\n return throwNullException(\"x.Key.originName\");\n }\n if (y.Key.originName === null) {\n return throwNullException(\"y.Key.originName\");\n }\n\n if (x.Value == y.Value) {\n return x.Key.originName.localeCompare(y.Key.originName);\n } else {\n // TODO: refactor this bit into a numberCompareTo method?\n if (x.Value < y.Value) return -1;\n return x.Value > y.Value ? 1 : 0;\n }\n });\n\n return ordered;\n }\n public toString() {\n let ordered = this.orderedItems;\n\n let sb = new StringBuilder();\n for (let i = 0; i < ordered.length; i++) {\n if (i > 0) sb.Append(\", \");\n\n let item = ordered[i].Key;\n if (item.itemName === null) return throwNullException(\"item.itemName\");\n sb.Append(item.itemName);\n }\n\n return sb.toString();\n }\n // casting a InkList to a Number, for somereason, actually gives a number.\n // This messes up the type detection when creating a Value from a InkList.\n // Returning NaN here prevents that.\n public valueOf() {\n return NaN;\n }\n}\n\n/**\n * In the original C# code, `InkListItem` was defined as value type, meaning\n * that two `InkListItem` would be considered equal as long as they held the\n * same values. This doesn't hold true in Javascript, as `InkListItem` is a\n * reference type (Javascript doesn't allow the creation of custom value types).\n *\n * The key equality of Map objects is based on the \"SameValueZero\" algorithm;\n * since `InkListItem` is a value type, two keys will only be considered\n * equal if they are, in fact, the same object. As we are trying to emulate\n * the original behavior as close as possible, this will lead to unforeseen\n * side effects.\n *\n * In order to have a key equality based on value semantics, we'll convert\n * `InkListItem` to a valid string representation and use this representation\n * as a key (strings are value types in Javascript). Rather than using the\n * type `string` directly, we'll alias it to `SerializedInkListItem` and use\n * this type as the key for our Map-based `InkList`.\n *\n * Reducing `InkListItem` to a JSON representation would not be bulletproof\n * in the general case, but for our needs it works well. The major downside of\n * this method is that we will have to to reconstruct the original `InkListItem`\n * every time we'll need to access its properties.\n */\nexport type SerializedInkListItem = string;\n\n/**\n * An interface inherited by `InkListItem`, defining exposed\n * properties. It's mainly used when deserializing a `InkListItem` from its\n * key (`SerializedInkListItem`)\n */\ninterface IInkListItem {\n readonly originName: string | null;\n readonly itemName: string | null;\n}\nexport interface KeyValuePair {\n Key: K;\n Value: V;\n}\n","export class StoryException extends Error {\n public useEndLineNumber: boolean;\n public message: string;\n public name: string;\n\n constructor(message: string) {\n super(message);\n this.useEndLineNumber = false;\n this.message = message;\n this.name = \"StoryException\";\n }\n}\n","/**\n * This interface normalize the `TryGet` behavior found in the original\n * C# project. Any `TryGet` method will return a object conforming to this\n * interface.\n *\n * The original function returns a boolean and has a second parameter called\n * item that is an `out`. Both are needed and we can't just return the item\n * because it'll always be truthy. Instead, we return an object containing\n * whether the result exists (`exists`) and the result itself (`result`).\n *\n * For instance a `TryGet` prototype would look like this:\n```\nTryGetItemWithValue(val: number, item: InkListItem): TryGetResult{\n```\n *\n * On the other hand, dealing with the result can be done in the following way:\n```\nvar item = item.TryGetItemWithValue(intVal, InkListItem.Null);\nif (item.exists) {\n\tconsole.log(item.result)\n}\n```\n *\n */\nexport interface TryGetResult {\n result: T;\n exists: boolean;\n}\n\nexport function tryGetValueFromMap(\n map: Map | null,\n key: K,\n /* out */ value: V\n): TryGetResult {\n if (map === null) {\n return { result: value, exists: false };\n }\n\n let val = map.get(key);\n\n if (typeof val === \"undefined\") {\n return { result: value, exists: false };\n } else {\n return { result: val, exists: true };\n }\n}\n\nexport function tryParseInt(\n value: any,\n /* out */ defaultValue: number = 0\n): TryGetResult {\n let val = parseInt(value);\n\n if (!Number.isNaN(val)) {\n return { result: val, exists: true };\n } else {\n return { result: defaultValue, exists: false };\n }\n}\n\nexport function tryParseFloat(\n value: any,\n /* out */ defaultValue: number = 0\n): TryGetResult {\n let val = parseFloat(value);\n\n if (!Number.isNaN(val)) {\n return { result: val, exists: true };\n } else {\n return { result: defaultValue, exists: false };\n }\n}\n","import { InkObject } from \"./Object\";\nimport { Container } from \"./Container\";\n\nexport class SearchResult {\n public obj: InkObject | null = null;\n public approximate: boolean = false;\n\n get correctObj() {\n return this.approximate ? null : this.obj;\n }\n\n get container() {\n return this.obj instanceof Container ? this.obj : null;\n }\n\n public copy() {\n let searchResult = new SearchResult();\n searchResult.obj = this.obj;\n searchResult.approximate = this.approximate;\n\n return searchResult;\n }\n}\n","import { StringValue } from \"./Value\";\nimport { throwNullException } from \"./NullException\";\nimport { StringBuilder } from \"./StringBuilder\";\nimport { INamedContent } from \"./INamedContent\";\nimport { InkObject } from \"./Object\";\nimport { SearchResult } from \"./SearchResult\";\nimport { Path } from \"./Path\";\nimport { Debug } from \"./Debug\";\nimport { tryGetValueFromMap } from \"./TryGetResult\";\nimport { asINamedContentOrNull, asOrNull, asOrThrows } from \"./TypeAssertion\";\n\nexport class Container extends InkObject implements INamedContent {\n public name: string | null = null;\n\n public _content: InkObject[] = [];\n public namedContent: Map = new Map();\n\n public visitsShouldBeCounted: boolean = false;\n public turnIndexShouldBeCounted: boolean = false;\n public countingAtStartOnly: boolean = false;\n\n public _pathToFirstLeafContent: Path | null = null;\n\n get hasValidName() {\n return this.name != null && this.name.length > 0;\n }\n get content() {\n return this._content;\n }\n set content(value: InkObject[]) {\n this.AddContent(value);\n }\n get namedOnlyContent() {\n let namedOnlyContentDict: Map | null = new Map();\n\n for (let [key, value] of this.namedContent) {\n let inkObject = asOrThrows(value, InkObject);\n namedOnlyContentDict.set(key, inkObject);\n }\n\n for (let c of this.content) {\n let named = asINamedContentOrNull(c);\n if (named != null && named.hasValidName) {\n namedOnlyContentDict.delete(named.name!);\n }\n }\n\n if (namedOnlyContentDict.size == 0) namedOnlyContentDict = null;\n\n return namedOnlyContentDict;\n }\n set namedOnlyContent(value: Map | null) {\n let existingNamedOnly = this.namedOnlyContent;\n if (existingNamedOnly != null) {\n for (let [key] of existingNamedOnly) {\n this.namedContent.delete(key);\n }\n }\n\n if (value == null) return;\n\n for (let [, val] of value) {\n let named = asINamedContentOrNull(val);\n if (named != null) this.AddToNamedContentOnly(named);\n }\n }\n get countFlags(): number {\n let flags: Container.CountFlags = 0;\n if (this.visitsShouldBeCounted) flags |= Container.CountFlags.Visits;\n if (this.turnIndexShouldBeCounted) flags |= Container.CountFlags.Turns;\n if (this.countingAtStartOnly) flags |= Container.CountFlags.CountStartOnly;\n\n if (flags == Container.CountFlags.CountStartOnly) {\n flags = 0;\n }\n\n return flags;\n }\n set countFlags(value: number) {\n let flag: Container.CountFlags = value;\n if ((flag & Container.CountFlags.Visits) > 0)\n this.visitsShouldBeCounted = true;\n if ((flag & Container.CountFlags.Turns) > 0)\n this.turnIndexShouldBeCounted = true;\n if ((flag & Container.CountFlags.CountStartOnly) > 0)\n this.countingAtStartOnly = true;\n }\n get pathToFirstLeafContent() {\n if (this._pathToFirstLeafContent == null)\n this._pathToFirstLeafContent = this.path.PathByAppendingPath(\n this.internalPathToFirstLeafContent\n );\n\n return this._pathToFirstLeafContent;\n }\n get internalPathToFirstLeafContent() {\n let components: Path.Component[] = [];\n let container: Container = this;\n while (container instanceof Container) {\n if (container.content.length > 0) {\n components.push(new Path.Component(0));\n container = container.content[0] as Container;\n }\n }\n return new Path(components);\n }\n\n public AddContent(contentObjOrList: InkObject | InkObject[]) {\n if (contentObjOrList instanceof Array) {\n let contentList = contentObjOrList as InkObject[];\n\n for (let c of contentList) {\n this.AddContent(c);\n }\n } else {\n let contentObj = contentObjOrList as InkObject;\n\n this._content.push(contentObj);\n\n if (contentObj.parent) {\n throw new Error(\"content is already in \" + contentObj.parent);\n }\n\n contentObj.parent = this;\n\n this.TryAddNamedContent(contentObj);\n }\n }\n public TryAddNamedContent(contentObj: InkObject) {\n let namedContentObj = asINamedContentOrNull(contentObj);\n if (namedContentObj != null && namedContentObj.hasValidName) {\n this.AddToNamedContentOnly(namedContentObj);\n }\n }\n public AddToNamedContentOnly(namedContentObj: INamedContent) {\n Debug.AssertType(\n namedContentObj,\n InkObject,\n \"Can only add Runtime.Objects to a Runtime.Container\"\n );\n let runtimeObj = asOrThrows(namedContentObj, InkObject);\n runtimeObj.parent = this;\n\n if (namedContentObj.name === null)\n return throwNullException(\"namedContentObj.name\");\n this.namedContent.set(namedContentObj.name!, namedContentObj);\n }\n public ContentAtPath(\n path: Path,\n partialPathStart: number = 0,\n partialPathLength: number = -1\n ) {\n if (partialPathLength == -1) partialPathLength = path.length;\n\n let result = new SearchResult();\n result.approximate = false;\n\n let currentContainer: Container | null = this;\n let currentObj: InkObject = this;\n\n for (let i = partialPathStart; i < partialPathLength; ++i) {\n let comp = path.GetComponent(i);\n if (currentContainer == null) {\n result.approximate = true;\n break;\n }\n\n let foundObj: InkObject | null =\n currentContainer.ContentWithPathComponent(comp);\n\n if (foundObj == null) {\n result.approximate = true;\n break;\n }\n\n currentObj = foundObj;\n currentContainer = asOrNull(foundObj, Container);\n }\n\n result.obj = currentObj;\n\n return result;\n }\n public InsertContent(contentObj: InkObject, index: number) {\n this.content.splice(index, 0, contentObj);\n\n if (contentObj.parent) {\n throw new Error(\"content is already in \" + contentObj.parent);\n }\n\n contentObj.parent = this;\n\n this.TryAddNamedContent(contentObj);\n }\n public AddContentsOfContainer(otherContainer: Container) {\n this.content.push(...otherContainer.content);\n\n for (let obj of otherContainer.content) {\n obj.parent = this;\n this.TryAddNamedContent(obj);\n }\n }\n public ContentWithPathComponent(component: Path.Component): InkObject | null {\n if (component.isIndex) {\n if (component.index >= 0 && component.index < this.content.length) {\n return this.content[component.index];\n } else {\n return null;\n }\n } else if (component.isParent) {\n return this.parent;\n } else {\n if (component.name === null) {\n return throwNullException(\"component.name\");\n }\n let foundContent = tryGetValueFromMap(\n this.namedContent,\n component.name,\n null\n );\n if (foundContent.exists) {\n return asOrThrows(foundContent.result, InkObject);\n } else {\n return null;\n }\n }\n }\n public BuildStringOfHierarchy(): string;\n public BuildStringOfHierarchy(\n sb: StringBuilder,\n indentation: number,\n pointedObj: InkObject | null\n ): string;\n public BuildStringOfHierarchy() {\n let sb: StringBuilder;\n if (arguments.length == 0) {\n sb = new StringBuilder();\n this.BuildStringOfHierarchy(sb, 0, null);\n return sb.toString();\n }\n\n sb = arguments[0] as StringBuilder;\n let indentation = arguments[1] as number;\n let pointedObj = arguments[2] as InkObject | null;\n\n function appendIndentation() {\n const spacesPerIndent = 4; // Truly const in the original code\n for (let i = 0; i < spacesPerIndent * indentation; ++i) {\n sb.Append(\" \");\n }\n }\n\n appendIndentation();\n sb.Append(\"[\");\n\n if (this.hasValidName) {\n sb.AppendFormat(\" ({0})\", this.name);\n }\n\n if (this == pointedObj) {\n sb.Append(\" <---\");\n }\n\n sb.AppendLine();\n\n indentation++;\n\n for (let i = 0; i < this.content.length; ++i) {\n let obj = this.content[i];\n\n if (obj instanceof Container) {\n let container = obj as Container;\n\n container.BuildStringOfHierarchy(sb, indentation, pointedObj);\n } else {\n appendIndentation();\n if (obj instanceof StringValue) {\n sb.Append('\"');\n sb.Append(obj.toString().replace(\"\\n\", \"\\\\n\"));\n sb.Append('\"');\n } else {\n sb.Append(obj.toString());\n }\n }\n\n if (i != this.content.length - 1) {\n sb.Append(\",\");\n }\n\n if (!(obj instanceof Container) && obj == pointedObj) {\n sb.Append(\" <---\");\n }\n\n sb.AppendLine();\n }\n\n let onlyNamed: Map = new Map();\n\n for (let [key, value] of this.namedContent) {\n if (this.content.indexOf(asOrThrows(value, InkObject)) >= 0) {\n continue;\n } else {\n onlyNamed.set(key, value);\n }\n }\n\n if (onlyNamed.size > 0) {\n appendIndentation();\n sb.AppendLine(\"-- named: --\");\n\n for (let [, value] of onlyNamed) {\n Debug.AssertType(\n value,\n Container,\n \"Can only print out named Containers\"\n );\n let container = value as Container;\n container.BuildStringOfHierarchy(sb, indentation, pointedObj);\n sb.AppendLine();\n }\n }\n\n indentation--;\n\n appendIndentation();\n sb.Append(\"]\");\n }\n}\n\nexport namespace Container {\n export enum CountFlags {\n Start = 0,\n Visits = 1,\n Turns = 2,\n CountStartOnly = 4,\n }\n}\n","import { InkObject } from \"./Object\";\n\nexport class ControlCommand extends InkObject {\n private _commandType: ControlCommand.CommandType;\n\n get commandType(): ControlCommand.CommandType {\n return this._commandType;\n }\n\n constructor(\n commandType: ControlCommand.CommandType = ControlCommand.CommandType.NotSet\n ) {\n super();\n this._commandType = commandType;\n }\n\n public Copy() {\n return new ControlCommand(this.commandType);\n }\n public static EvalStart() {\n return new ControlCommand(ControlCommand.CommandType.EvalStart);\n }\n public static EvalOutput() {\n return new ControlCommand(ControlCommand.CommandType.EvalOutput);\n }\n public static EvalEnd() {\n return new ControlCommand(ControlCommand.CommandType.EvalEnd);\n }\n public static Duplicate() {\n return new ControlCommand(ControlCommand.CommandType.Duplicate);\n }\n public static PopEvaluatedValue() {\n return new ControlCommand(ControlCommand.CommandType.PopEvaluatedValue);\n }\n public static PopFunction() {\n return new ControlCommand(ControlCommand.CommandType.PopFunction);\n }\n public static PopTunnel() {\n return new ControlCommand(ControlCommand.CommandType.PopTunnel);\n }\n public static BeginString() {\n return new ControlCommand(ControlCommand.CommandType.BeginString);\n }\n public static EndString() {\n return new ControlCommand(ControlCommand.CommandType.EndString);\n }\n public static NoOp() {\n return new ControlCommand(ControlCommand.CommandType.NoOp);\n }\n public static ChoiceCount() {\n return new ControlCommand(ControlCommand.CommandType.ChoiceCount);\n }\n public static Turns() {\n return new ControlCommand(ControlCommand.CommandType.Turns);\n }\n public static TurnsSince() {\n return new ControlCommand(ControlCommand.CommandType.TurnsSince);\n }\n public static ReadCount() {\n return new ControlCommand(ControlCommand.CommandType.ReadCount);\n }\n public static Random() {\n return new ControlCommand(ControlCommand.CommandType.Random);\n }\n public static SeedRandom() {\n return new ControlCommand(ControlCommand.CommandType.SeedRandom);\n }\n public static VisitIndex() {\n return new ControlCommand(ControlCommand.CommandType.VisitIndex);\n }\n public static SequenceShuffleIndex() {\n return new ControlCommand(ControlCommand.CommandType.SequenceShuffleIndex);\n }\n public static StartThread() {\n return new ControlCommand(ControlCommand.CommandType.StartThread);\n }\n public static Done() {\n return new ControlCommand(ControlCommand.CommandType.Done);\n }\n public static End() {\n return new ControlCommand(ControlCommand.CommandType.End);\n }\n public static ListFromInt() {\n return new ControlCommand(ControlCommand.CommandType.ListFromInt);\n }\n public static ListRange() {\n return new ControlCommand(ControlCommand.CommandType.ListRange);\n }\n public static ListRandom() {\n return new ControlCommand(ControlCommand.CommandType.ListRandom);\n }\n public static BeginTag() {\n return new ControlCommand(ControlCommand.CommandType.BeginTag);\n }\n public static EndTag() {\n return new ControlCommand(ControlCommand.CommandType.EndTag);\n }\n public toString() {\n return \"ControlCommand \" + this.commandType.toString();\n }\n}\n\nexport namespace ControlCommand {\n export enum CommandType {\n NotSet = -1,\n EvalStart, // 0\n EvalOutput, // 1\n EvalEnd, // 2\n Duplicate, // 3\n PopEvaluatedValue, // 4\n PopFunction, // 5\n PopTunnel, // 6\n BeginString, // 7\n EndString, // 8\n NoOp, // 9\n ChoiceCount, // 10\n Turns, // 11\n TurnsSince, // 12\n ReadCount, // 13\n Random, // 14\n SeedRandom, // 15\n VisitIndex, // 16\n SequenceShuffleIndex, // 17\n StartThread, // 18\n Done, // 19\n End, // 20\n ListFromInt, // 21\n ListRange, // 22\n ListRandom, // 23\n BeginTag, // 24\n EndTag, // 25\n\n TOTAL_VALUES,\n }\n}\n","import { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { ControlCommand as RuntimeControlCommand } from \"../../../../engine/ControlCommand\";\nimport { ParsedObject } from \"../Object\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\n\nexport abstract class Expression extends ParsedObject {\n public abstract GenerateIntoContainer: (container: RuntimeContainer) => void;\n\n private _prototypeRuntimeConstantExpression: RuntimeContainer | null = null;\n public outputWhenComplete: boolean = false;\n\n public readonly GenerateRuntimeObject = (): RuntimeObject => {\n const container = new RuntimeContainer();\n\n // Tell Runtime to start evaluating the following content as an expression\n container.AddContent(RuntimeControlCommand.EvalStart());\n\n this.GenerateIntoContainer(container);\n\n // Tell Runtime to output the result of the expression evaluation to the output stream\n if (this.outputWhenComplete) {\n container.AddContent(RuntimeControlCommand.EvalOutput());\n }\n\n // Tell Runtime to stop evaluating the content as an expression\n container.AddContent(RuntimeControlCommand.EvalEnd());\n\n return container;\n };\n\n // When generating the value of a constant expression,\n // we can't just keep generating the same constant expression into\n // different places where the constant value is referenced, since then\n // the same runtime objects would be used in multiple places, which\n // is impossible since each runtime object should have one parent.\n // Instead, we generate a prototype of the runtime object(s), then\n // copy them each time they're used.\n public readonly GenerateConstantIntoContainer = (\n container: RuntimeContainer\n ): void => {\n if (this._prototypeRuntimeConstantExpression === null) {\n this._prototypeRuntimeConstantExpression = new RuntimeContainer();\n this.GenerateIntoContainer(this._prototypeRuntimeConstantExpression);\n }\n\n for (const runtimeObj of this._prototypeRuntimeConstantExpression.content) {\n const copy = runtimeObj.Copy();\n if (copy) {\n container.AddContent(copy);\n }\n }\n };\n\n get typeName(): string {\n return \"Expression\";\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n public Equals(obj: ParsedObject): boolean {\n return false;\n }\n\n public readonly toString = () => \"No string value in JavaScript.\";\n}\n","import { InkObject } from \"./Object\";\n\nexport class Void extends InkObject {\n public toString() {\n return \"Void\";\n }\n}\n","import { Value, ValueType, IntValue, ListValue, BoolValue } from \"./Value\";\nimport { StoryException } from \"./StoryException\";\nimport { Void } from \"./Void\";\nimport { Path } from \"./Path\";\nimport { InkList, InkListItem } from \"./InkList\";\nimport { InkObject } from \"./Object\";\nimport { asOrNull, asOrThrows, asBooleanOrThrows } from \"./TypeAssertion\";\nimport { throwNullException } from \"./NullException\";\n\ntype BinaryOp = (left: T, right: T) => any;\ntype UnaryOp = (val: T) => any;\n\nexport class NativeFunctionCall extends InkObject {\n public static readonly Add: string = \"+\";\n public static readonly Subtract: string = \"-\";\n public static readonly Divide: string = \"/\";\n public static readonly Multiply: string = \"*\";\n public static readonly Mod: string = \"%\";\n public static readonly Negate: string = \"_\";\n public static readonly Equal: string = \"==\";\n public static readonly Greater: string = \">\";\n public static readonly Less: string = \"<\";\n public static readonly GreaterThanOrEquals: string = \">=\";\n public static readonly LessThanOrEquals: string = \"<=\";\n public static readonly NotEquals: string = \"!=\";\n public static readonly Not: string = \"!\";\n public static readonly And: string = \"&&\";\n public static readonly Or: string = \"||\";\n public static readonly Min: string = \"MIN\";\n public static readonly Max: string = \"MAX\";\n public static readonly Pow: string = \"POW\";\n public static readonly Floor: string = \"FLOOR\";\n public static readonly Ceiling: string = \"CEILING\";\n public static readonly Int: string = \"INT\";\n public static readonly Float: string = \"FLOAT\";\n public static readonly Has: string = \"?\";\n public static readonly Hasnt: string = \"!?\";\n public static readonly Intersect: string = \"^\";\n public static readonly ListMin: string = \"LIST_MIN\";\n public static readonly ListMax: string = \"LIST_MAX\";\n public static readonly All: string = \"LIST_ALL\";\n public static readonly Count: string = \"LIST_COUNT\";\n public static readonly ValueOfList: string = \"LIST_VALUE\";\n public static readonly Invert: string = \"LIST_INVERT\";\n\n public static CallWithName(functionName: string) {\n return new NativeFunctionCall(functionName);\n }\n\n public static CallExistsWithName(functionName: string) {\n this.GenerateNativeFunctionsIfNecessary();\n return this._nativeFunctions!.get(functionName);\n }\n\n get name() {\n if (this._name === null)\n return throwNullException(\"NativeFunctionCall._name\");\n return this._name;\n }\n set name(value: string) {\n this._name = value;\n if (!this._isPrototype) {\n if (NativeFunctionCall._nativeFunctions === null)\n throwNullException(\"NativeFunctionCall._nativeFunctions\");\n else\n this._prototype =\n NativeFunctionCall._nativeFunctions.get(this._name) || null;\n }\n }\n public _name: string | null = null;\n\n get numberOfParameters() {\n if (this._prototype) {\n return this._prototype.numberOfParameters;\n } else {\n return this._numberOfParameters;\n }\n }\n set numberOfParameters(value: number) {\n this._numberOfParameters = value;\n }\n public _numberOfParameters: number = 0;\n\n public Call(parameters: InkObject[]): InkObject | null {\n if (this._prototype) {\n return this._prototype.Call(parameters);\n }\n\n if (this.numberOfParameters != parameters.length) {\n throw new Error(\"Unexpected number of parameters\");\n }\n\n let hasList = false;\n for (let p of parameters) {\n if (p instanceof Void)\n throw new StoryException(\n 'Attempting to perform operation on a void value. Did you forget to \"return\" a value from a function you called here?'\n );\n if (p instanceof ListValue) hasList = true;\n }\n\n if (parameters.length == 2 && hasList) {\n return this.CallBinaryListOperation(parameters);\n }\n\n let coercedParams = this.CoerceValuesToSingleType(parameters);\n let coercedType = coercedParams[0].valueType;\n\n if (coercedType == ValueType.Int) {\n return this.CallType(coercedParams);\n } else if (coercedType == ValueType.Float) {\n return this.CallType(coercedParams);\n } else if (coercedType == ValueType.String) {\n return this.CallType(coercedParams);\n } else if (coercedType == ValueType.DivertTarget) {\n return this.CallType(coercedParams);\n } else if (coercedType == ValueType.List) {\n return this.CallType(coercedParams);\n }\n\n return null;\n }\n\n public CallType string }>(\n parametersOfSingleType: Array>\n ) {\n let param1 = asOrThrows(parametersOfSingleType[0], Value);\n let valType = param1.valueType;\n\n let val1 = param1 as Value;\n\n let paramCount = parametersOfSingleType.length;\n\n if (paramCount == 2 || paramCount == 1) {\n if (this._operationFuncs === null)\n return throwNullException(\"NativeFunctionCall._operationFuncs\");\n let opForTypeObj = this._operationFuncs.get(valType);\n if (!opForTypeObj) {\n const key = ValueType[valType];\n throw new StoryException(\n \"Cannot perform operation \" + this.name + \" on \" + key\n );\n }\n\n if (paramCount == 2) {\n let param2 = asOrThrows(parametersOfSingleType[1], Value);\n\n let val2 = param2 as Value;\n\n let opForType = opForTypeObj as BinaryOp;\n\n if (val1.value === null || val2.value === null)\n return throwNullException(\"NativeFunctionCall.Call BinaryOp values\");\n let resultVal = opForType(val1.value, val2.value);\n\n return Value.Create(resultVal);\n } else {\n let opForType = opForTypeObj as UnaryOp;\n\n if (val1.value === null)\n return throwNullException(\"NativeFunctionCall.Call UnaryOp value\");\n let resultVal = opForType(val1.value);\n\n // This code is different from upstream. Since JavaScript treats\n // integers and floats as the same numbers, it's impossible\n // to force an number to be either an integer or a float.\n //\n // It can be useful to force a specific number type\n // (especially for divisions), so the result of INT() & FLOAT()\n // is coerced to the the proper value type.\n //\n // Note that we also force all other unary operation to\n // return the same value type, although this is only\n // meaningful for numbers. See `Value.Create`.\n if (this.name === NativeFunctionCall.Int) {\n return Value.Create(resultVal, ValueType.Int);\n } else if (this.name === NativeFunctionCall.Float) {\n return Value.Create(resultVal, ValueType.Float);\n } else {\n return Value.Create(resultVal, param1.valueType);\n }\n }\n } else {\n throw new Error(\n \"Unexpected number of parameters to NativeFunctionCall: \" +\n parametersOfSingleType.length\n );\n }\n }\n\n public CallBinaryListOperation(parameters: InkObject[]) {\n if (\n (this.name == \"+\" || this.name == \"-\") &&\n parameters[0] instanceof ListValue &&\n parameters[1] instanceof IntValue\n )\n return this.CallListIncrementOperation(parameters);\n\n let v1 = asOrThrows(parameters[0], Value);\n let v2 = asOrThrows(parameters[1], Value);\n\n if (\n (this.name == \"&&\" || this.name == \"||\") &&\n (v1.valueType != ValueType.List || v2.valueType != ValueType.List)\n ) {\n if (this._operationFuncs === null)\n return throwNullException(\"NativeFunctionCall._operationFuncs\");\n let op = this._operationFuncs.get(ValueType.Int) as BinaryOp;\n if (op === null)\n return throwNullException(\n \"NativeFunctionCall.CallBinaryListOperation op\"\n );\n let result = asBooleanOrThrows(\n op(v1.isTruthy ? 1 : 0, v2.isTruthy ? 1 : 0)\n );\n return new BoolValue(result);\n }\n\n if (v1.valueType == ValueType.List && v2.valueType == ValueType.List)\n return this.CallType([v1, v2]);\n\n throw new StoryException(\n \"Can not call use \" +\n this.name +\n \" operation on \" +\n ValueType[v1.valueType] +\n \" and \" +\n ValueType[v2.valueType]\n );\n }\n\n public CallListIncrementOperation(listIntParams: InkObject[]) {\n let listVal = asOrThrows(listIntParams[0], ListValue);\n let intVal = asOrThrows(listIntParams[1], IntValue);\n\n let resultInkList = new InkList();\n\n if (listVal.value === null)\n return throwNullException(\n \"NativeFunctionCall.CallListIncrementOperation listVal.value\"\n );\n for (let [listItemKey, listItemValue] of listVal.value) {\n let listItem = InkListItem.fromSerializedKey(listItemKey);\n\n if (this._operationFuncs === null)\n return throwNullException(\"NativeFunctionCall._operationFuncs\");\n let intOp = this._operationFuncs.get(ValueType.Int) as BinaryOp;\n\n if (intVal.value === null)\n return throwNullException(\n \"NativeFunctionCall.CallListIncrementOperation intVal.value\"\n );\n let targetInt = intOp(listItemValue, intVal.value);\n\n let itemOrigin = null;\n if (listVal.value.origins === null)\n return throwNullException(\n \"NativeFunctionCall.CallListIncrementOperation listVal.value.origins\"\n );\n for (let origin of listVal.value.origins) {\n if (origin.name == listItem.originName) {\n itemOrigin = origin;\n break;\n }\n }\n if (itemOrigin != null) {\n let incrementedItem = itemOrigin.TryGetItemWithValue(\n targetInt,\n InkListItem.Null\n );\n if (incrementedItem.exists)\n resultInkList.Add(incrementedItem.result, targetInt);\n }\n }\n\n return new ListValue(resultInkList);\n }\n\n public CoerceValuesToSingleType(parametersIn: InkObject[]) {\n let valType = ValueType.Int;\n\n let specialCaseList: null | ListValue = null;\n\n for (let obj of parametersIn) {\n let val = asOrThrows(obj, Value);\n if (val.valueType > valType) {\n valType = val.valueType;\n }\n\n if (val.valueType == ValueType.List) {\n specialCaseList = asOrNull(val, ListValue);\n }\n }\n\n let parametersOut = [];\n\n if (ValueType[valType] == ValueType[ValueType.List]) {\n for (let inkObjectVal of parametersIn) {\n let val = asOrThrows(inkObjectVal, Value);\n if (val.valueType == ValueType.List) {\n parametersOut.push(val);\n } else if (val.valueType == ValueType.Int) {\n let intVal = parseInt(val.valueObject);\n\n specialCaseList = asOrThrows(specialCaseList, ListValue);\n if (specialCaseList.value === null)\n return throwNullException(\n \"NativeFunctionCall.CoerceValuesToSingleType specialCaseList.value\"\n );\n let list = specialCaseList.value.originOfMaxItem;\n\n if (list === null)\n return throwNullException(\n \"NativeFunctionCall.CoerceValuesToSingleType list\"\n );\n let item = list.TryGetItemWithValue(intVal, InkListItem.Null);\n if (item.exists) {\n let castedValue = new ListValue(item.result, intVal);\n parametersOut.push(castedValue);\n } else\n throw new StoryException(\n \"Could not find List item with the value \" +\n intVal +\n \" in \" +\n list.name\n );\n } else {\n const key = ValueType[val.valueType];\n throw new StoryException(\n \"Cannot mix Lists and \" + key + \" values in this operation\"\n );\n }\n }\n } else {\n for (let inkObjectVal of parametersIn) {\n let val = asOrThrows(inkObjectVal, Value);\n let castedValue = val.Cast(valType);\n parametersOut.push(castedValue);\n }\n }\n\n return parametersOut;\n }\n\n constructor(name: string);\n constructor(name: string, numberOfParameters: number);\n constructor();\n constructor() {\n super();\n\n if (arguments.length === 0) {\n NativeFunctionCall.GenerateNativeFunctionsIfNecessary();\n } else if (arguments.length === 1) {\n let name = arguments[0];\n NativeFunctionCall.GenerateNativeFunctionsIfNecessary();\n this.name = name;\n } else if (arguments.length === 2) {\n let name = arguments[0];\n let numberOfParameters = arguments[1];\n\n this._isPrototype = true;\n this.name = name;\n this.numberOfParameters = numberOfParameters;\n }\n }\n\n public static Identity(t: T): any {\n return t;\n }\n\n public static GenerateNativeFunctionsIfNecessary() {\n if (this._nativeFunctions == null) {\n this._nativeFunctions = new Map();\n\n // Int operations\n this.AddIntBinaryOp(this.Add, (x, y) => x + y);\n this.AddIntBinaryOp(this.Subtract, (x, y) => x - y);\n this.AddIntBinaryOp(this.Multiply, (x, y) => x * y);\n this.AddIntBinaryOp(this.Divide, (x, y) => Math.floor(x / y));\n this.AddIntBinaryOp(this.Mod, (x, y) => x % y);\n this.AddIntUnaryOp(this.Negate, (x) => -x);\n\n this.AddIntBinaryOp(this.Equal, (x, y) => x == y);\n this.AddIntBinaryOp(this.Greater, (x, y) => x > y);\n this.AddIntBinaryOp(this.Less, (x, y) => x < y);\n this.AddIntBinaryOp(this.GreaterThanOrEquals, (x, y) => x >= y);\n this.AddIntBinaryOp(this.LessThanOrEquals, (x, y) => x <= y);\n this.AddIntBinaryOp(this.NotEquals, (x, y) => x != y);\n this.AddIntUnaryOp(this.Not, (x) => x == 0);\n\n this.AddIntBinaryOp(this.And, (x, y) => x != 0 && y != 0);\n this.AddIntBinaryOp(this.Or, (x, y) => x != 0 || y != 0);\n\n this.AddIntBinaryOp(this.Max, (x, y) => Math.max(x, y));\n this.AddIntBinaryOp(this.Min, (x, y) => Math.min(x, y));\n\n this.AddIntBinaryOp(this.Pow, (x, y) => Math.pow(x, y));\n this.AddIntUnaryOp(this.Floor, NativeFunctionCall.Identity);\n this.AddIntUnaryOp(this.Ceiling, NativeFunctionCall.Identity);\n this.AddIntUnaryOp(this.Int, NativeFunctionCall.Identity);\n this.AddIntUnaryOp(this.Float, (x) => x);\n\n // Float operations\n this.AddFloatBinaryOp(this.Add, (x, y) => x + y);\n this.AddFloatBinaryOp(this.Subtract, (x, y) => x - y);\n this.AddFloatBinaryOp(this.Multiply, (x, y) => x * y);\n this.AddFloatBinaryOp(this.Divide, (x, y) => x / y);\n this.AddFloatBinaryOp(this.Mod, (x, y) => x % y);\n this.AddFloatUnaryOp(this.Negate, (x) => -x);\n\n this.AddFloatBinaryOp(this.Equal, (x, y) => x == y);\n this.AddFloatBinaryOp(this.Greater, (x, y) => x > y);\n this.AddFloatBinaryOp(this.Less, (x, y) => x < y);\n this.AddFloatBinaryOp(this.GreaterThanOrEquals, (x, y) => x >= y);\n this.AddFloatBinaryOp(this.LessThanOrEquals, (x, y) => x <= y);\n this.AddFloatBinaryOp(this.NotEquals, (x, y) => x != y);\n this.AddFloatUnaryOp(this.Not, (x) => x == 0.0);\n\n this.AddFloatBinaryOp(this.And, (x, y) => x != 0.0 && y != 0.0);\n this.AddFloatBinaryOp(this.Or, (x, y) => x != 0.0 || y != 0.0);\n\n this.AddFloatBinaryOp(this.Max, (x, y) => Math.max(x, y));\n this.AddFloatBinaryOp(this.Min, (x, y) => Math.min(x, y));\n\n this.AddFloatBinaryOp(this.Pow, (x, y) => Math.pow(x, y));\n this.AddFloatUnaryOp(this.Floor, (x) => Math.floor(x));\n this.AddFloatUnaryOp(this.Ceiling, (x) => Math.ceil(x));\n this.AddFloatUnaryOp(this.Int, (x) => Math.floor(x));\n this.AddFloatUnaryOp(this.Float, NativeFunctionCall.Identity);\n\n // String operations\n this.AddStringBinaryOp(this.Add, (x, y) => x + y); // concat\n this.AddStringBinaryOp(this.Equal, (x, y) => x === y);\n this.AddStringBinaryOp(this.NotEquals, (x, y) => !(x === y));\n this.AddStringBinaryOp(this.Has, (x, y) => x.includes(y));\n this.AddStringBinaryOp(this.Hasnt, (x, y) => !x.includes(y));\n\n this.AddListBinaryOp(this.Add, (x, y) => x.Union(y));\n this.AddListBinaryOp(this.Subtract, (x, y) => x.Without(y));\n this.AddListBinaryOp(this.Has, (x, y) => x.Contains(y));\n this.AddListBinaryOp(this.Hasnt, (x, y) => !x.Contains(y));\n this.AddListBinaryOp(this.Intersect, (x, y) => x.Intersect(y));\n\n this.AddListBinaryOp(this.Equal, (x, y) => x.Equals(y));\n this.AddListBinaryOp(this.Greater, (x, y) => x.GreaterThan(y));\n this.AddListBinaryOp(this.Less, (x, y) => x.LessThan(y));\n this.AddListBinaryOp(this.GreaterThanOrEquals, (x, y) =>\n x.GreaterThanOrEquals(y)\n );\n this.AddListBinaryOp(this.LessThanOrEquals, (x, y) =>\n x.LessThanOrEquals(y)\n );\n this.AddListBinaryOp(this.NotEquals, (x, y) => !x.Equals(y));\n\n this.AddListBinaryOp(this.And, (x, y) => x.Count > 0 && y.Count > 0);\n this.AddListBinaryOp(this.Or, (x, y) => x.Count > 0 || y.Count > 0);\n\n this.AddListUnaryOp(this.Not, (x) => (x.Count == 0 ? 1 : 0));\n\n this.AddListUnaryOp(this.Invert, (x) => x.inverse);\n this.AddListUnaryOp(this.All, (x) => x.all);\n this.AddListUnaryOp(this.ListMin, (x) => x.MinAsList());\n this.AddListUnaryOp(this.ListMax, (x) => x.MaxAsList());\n this.AddListUnaryOp(this.Count, (x) => x.Count);\n this.AddListUnaryOp(this.ValueOfList, (x) => x.maxItem.Value);\n\n let divertTargetsEqual = (d1: Path, d2: Path) => d1.Equals(d2);\n let divertTargetsNotEqual = (d1: Path, d2: Path) => !d1.Equals(d2);\n this.AddOpToNativeFunc(\n this.Equal,\n 2,\n ValueType.DivertTarget,\n divertTargetsEqual\n );\n this.AddOpToNativeFunc(\n this.NotEquals,\n 2,\n ValueType.DivertTarget,\n divertTargetsNotEqual\n );\n }\n }\n\n public AddOpFuncForType(\n valType: ValueType,\n op: UnaryOp | BinaryOp\n ): void {\n if (this._operationFuncs == null) {\n this._operationFuncs = new Map();\n }\n\n this._operationFuncs.set(valType, op);\n }\n\n public static AddOpToNativeFunc(\n name: string,\n args: number,\n valType: ValueType,\n op: UnaryOp | BinaryOp\n ): void {\n if (this._nativeFunctions === null)\n return throwNullException(\"NativeFunctionCall._nativeFunctions\");\n let nativeFunc = this._nativeFunctions.get(name);\n if (!nativeFunc) {\n nativeFunc = new NativeFunctionCall(name, args);\n this._nativeFunctions.set(name, nativeFunc);\n }\n\n nativeFunc.AddOpFuncForType(valType, op);\n }\n\n public static AddIntBinaryOp(name: string, op: BinaryOp) {\n this.AddOpToNativeFunc(name, 2, ValueType.Int, op);\n }\n public static AddIntUnaryOp(name: string, op: UnaryOp) {\n this.AddOpToNativeFunc(name, 1, ValueType.Int, op);\n }\n\n public static AddFloatBinaryOp(name: string, op: BinaryOp) {\n this.AddOpToNativeFunc(name, 2, ValueType.Float, op);\n }\n public static AddFloatUnaryOp(name: string, op: UnaryOp) {\n this.AddOpToNativeFunc(name, 1, ValueType.Float, op);\n }\n\n public static AddStringBinaryOp(name: string, op: BinaryOp) {\n this.AddOpToNativeFunc(name, 2, ValueType.String, op);\n }\n\n public static AddListBinaryOp(name: string, op: BinaryOp) {\n this.AddOpToNativeFunc(name, 2, ValueType.List, op);\n }\n public static AddListUnaryOp(name: string, op: UnaryOp) {\n this.AddOpToNativeFunc(name, 1, ValueType.List, op);\n }\n\n public toString() {\n return 'Native \"' + this.name + '\"';\n }\n\n public _prototype: NativeFunctionCall | null = null;\n public _isPrototype: boolean = false;\n public _operationFuncs: Map | UnaryOp> | null =\n null;\n public static _nativeFunctions: Map | null = null;\n}\n","import { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { Expression } from \"./Expression\";\nimport { BoolValue, FloatValue, IntValue } from \"../../../../engine/Value\";\nimport { asOrNull } from \"../../../../engine/TypeAssertion\";\nimport { ParsedObject } from \"../Object\";\n\n// This class is named Number in the C# codebase\n// but this conflict with the built-in Number class\nexport class NumberExpression extends Expression {\n public value: number | boolean;\n public subtype: \"int\" | \"float\" | \"bool\";\n\n constructor(value: number | boolean, subtype: \"int\" | \"float\" | \"bool\") {\n super();\n\n if (\n (typeof value === \"number\" && !Number.isNaN(value)) ||\n typeof value == \"boolean\"\n ) {\n this.value = value;\n this.subtype = subtype;\n } else {\n throw new Error(\"Unexpected object type in NumberExpression.\");\n }\n }\n\n get typeName(): string {\n return \"Number\";\n }\n\n public isInt = (): boolean => this.subtype == \"int\";\n\n public isFloat = (): boolean => this.subtype == \"float\";\n\n public isBool = (): boolean => this.subtype == \"bool\";\n\n public readonly GenerateIntoContainer = (\n container: RuntimeContainer\n ): void => {\n if (this.isInt()) {\n container.AddContent(new IntValue(this.value as number));\n } else if (this.isFloat()) {\n container.AddContent(new FloatValue(this.value as number));\n } else if (this.isBool()) {\n container.AddContent(new BoolValue(this.value as boolean));\n }\n };\n\n public readonly toString = (): string => String(this.value);\n\n public Equals(obj: ParsedObject): boolean {\n const numberExpression = asOrNull(obj, NumberExpression);\n if (!numberExpression) return false;\n\n return (\n numberExpression.subtype == this.subtype &&\n numberExpression.value == this.value\n );\n }\n}\n","import { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { Expression } from \"./Expression\";\nimport { NativeFunctionCall } from \"../../../../engine/NativeFunctionCall\";\nimport { NumberExpression } from \"./NumberExpression\";\nimport { asOrNull } from \"../../../../engine/TypeAssertion\";\n\nexport class UnaryExpression extends Expression {\n get nativeNameForOp(): string {\n // Replace \"-\" with \"_\" to make it unique (compared to subtraction)\n if (this.op === \"-\") {\n return \"_\";\n } else if (this.op === \"not\") {\n return \"!\";\n }\n\n return this.op;\n }\n\n public innerExpression: Expression;\n\n // Attempt to flatten inner expression immediately\n // e.g. convert (-(5)) into (-5)\n public static readonly WithInner = (\n inner: Expression,\n op: string\n ): Expression => {\n const innerNumber = asOrNull(inner, NumberExpression);\n\n if (innerNumber) {\n if (op === \"-\") {\n if (innerNumber.isInt()) {\n return new NumberExpression(-innerNumber.value, \"int\");\n } else if (innerNumber.isFloat()) {\n return new NumberExpression(-innerNumber.value, \"float\");\n }\n } else if (op == \"!\" || op == \"not\") {\n if (innerNumber.isInt()) {\n return new NumberExpression(innerNumber.value == 0, \"bool\");\n } else if (innerNumber.isFloat()) {\n return new NumberExpression(innerNumber.value == 0.0, \"bool\");\n } else if (innerNumber.isBool()) {\n return new NumberExpression(!innerNumber.value, \"bool\");\n }\n }\n\n throw new Error(\"Unexpected operation or number type\");\n }\n\n // Normal fallback\n const unary = new UnaryExpression(inner, op);\n\n return unary;\n };\n\n constructor(\n inner: Expression,\n public readonly op: string\n ) {\n super();\n\n this.innerExpression = this.AddContent(inner) as Expression;\n }\n\n get typeName(): string {\n return \"UnaryExpression\";\n }\n\n public readonly GenerateIntoContainer = (container: RuntimeContainer) => {\n this.innerExpression.GenerateIntoContainer(container);\n container.AddContent(NativeFunctionCall.CallWithName(this.nativeNameForOp));\n };\n\n public readonly toString = (): string =>\n this.nativeNameForOp + this.innerExpression;\n}\n","import { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { Expression } from \"./Expression\";\nimport { NativeFunctionCall } from \"../../../../engine/NativeFunctionCall\";\nimport { Story } from \"../Story\";\nimport { UnaryExpression } from \"./UnaryExpression\";\nimport { asOrNull } from \"../../../../engine/TypeAssertion\";\n\nexport class BinaryExpression extends Expression {\n public readonly leftExpression: Expression;\n public readonly rightExpression: Expression;\n\n constructor(\n left: Expression,\n right: Expression,\n public opName: string\n ) {\n super();\n\n this.leftExpression = this.AddContent(left) as Expression;\n this.rightExpression = this.AddContent(right) as Expression;\n\n this.opName = opName;\n }\n\n get typeName(): string {\n return \"BinaryExpression\";\n }\n\n public readonly GenerateIntoContainer = (container: RuntimeContainer) => {\n this.leftExpression.GenerateIntoContainer(container);\n this.rightExpression.GenerateIntoContainer(container);\n this.opName = this.NativeNameForOp(this.opName);\n container.AddContent(NativeFunctionCall.CallWithName(this.opName));\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n\n // Check for the following case:\n //\n // (not A) ? B\n //\n // Since this easy to accidentally do:\n //\n // not A ? B\n //\n // when you intend:\n //\n // not (A ? B)\n if (this.NativeNameForOp(this.opName) === \"?\") {\n const leftUnary = asOrNull(this.leftExpression, UnaryExpression);\n if (\n leftUnary !== null &&\n (leftUnary.op === \"not\" || leftUnary.op === \"!\")\n ) {\n this.Error(\n `Using 'not' or '!' here negates '${leftUnary.innerExpression}' rather than the result of the '?' or 'has' operator. You need to add parentheses around the (A ? B) expression.`\n );\n }\n }\n }\n\n public readonly NativeNameForOp = (opName: string): string => {\n if (opName === \"and\") {\n return \"&&\";\n } else if (opName === \"or\") {\n return \"||\";\n } else if (opName === \"mod\") {\n return \"%\";\n } else if (opName === \"has\") {\n return \"?\";\n } else if (opName === \"hasnt\") {\n return \"!?\";\n }\n\n return opName;\n };\n\n public readonly toString = (): string =>\n `(${this.leftExpression} ${this.opName} ${this.rightExpression})`;\n}\n","export class CharacterSet {\n public static readonly FromRange = (\n start: string,\n end: string\n ): CharacterSet => new CharacterSet().AddRange(start, end);\n\n public set: Set = new Set();\n\n constructor(arg?: string | string[] | CharacterSet) {\n if (arg) {\n this.AddCharacters(arg);\n }\n }\n\n public readonly Add = (arg: string) => this.set.add(arg);\n\n public readonly AddRange = (start: string, end: string): CharacterSet => {\n for (let c = start.charCodeAt(0); c <= end.charCodeAt(0); ++c) {\n this.Add(String.fromCharCode(c));\n }\n\n return this;\n };\n\n public readonly AddCharacters = (\n chars: string | string[] | CharacterSet\n ): CharacterSet => {\n if (typeof chars === \"string\" || Array.isArray(chars)) {\n for (const c of chars) {\n this.Add(c);\n }\n } else {\n for (const c of chars.set) {\n this.Add(c);\n }\n }\n\n return this;\n };\n}\n","import { CharacterSet } from \"./CharacterSet\";\n\n/// \n/// A class representing a character range. Allows for lazy-loading a corresponding character set.\n/// \nexport class CharacterRange {\n public static Define = (\n start: string,\n end: string,\n excludes: string[] | CharacterSet = []\n ): CharacterRange => new CharacterRange(start, end, excludes);\n\n private _correspondingCharSet: CharacterSet = new CharacterSet();\n private _excludes = new Set();\n\n constructor(\n private _start: string,\n private _end: string,\n excludes: string[] | CharacterSet = []\n ) {\n if (excludes instanceof CharacterSet) {\n this._excludes = excludes.set;\n } else {\n for (const item of excludes) {\n this._excludes.add(item);\n }\n }\n }\n\n get start(): string {\n return this._start;\n }\n\n get end(): string {\n return this._end;\n }\n\n /// \n /// Returns a character set instance corresponding to the character range\n /// represented by the current instance.\n /// \n /// \n /// The internal character set is created once and cached in memory.\n /// \n /// The char set.\n public readonly ToCharacterSet = (): CharacterSet => {\n if (this._correspondingCharSet.set.size === 0) {\n for (\n let ii = this.start.charCodeAt(0), c;\n ii <= this.end.charCodeAt(0);\n ii += 1\n ) {\n c = String.fromCharCode(ii);\n if (!this._excludes.has(c)) {\n this._correspondingCharSet.AddCharacters(c);\n }\n }\n }\n\n return this._correspondingCharSet;\n };\n}\n","import { InkObject } from \"./Object\";\nimport { Path } from \"./Path\";\nimport { Container } from \"./Container\";\nimport { throwNullException } from \"./NullException\";\n\nexport class ChoicePoint extends InkObject {\n public _pathOnChoice: Path | null = null;\n public hasCondition: boolean = false;\n public hasStartContent: boolean = false;\n public hasChoiceOnlyContent: boolean = false;\n public isInvisibleDefault: boolean = false;\n public onceOnly: boolean = true;\n\n constructor(onceOnly: boolean = true) {\n super();\n this.onceOnly = onceOnly;\n }\n get pathOnChoice(): Path | null {\n if (this._pathOnChoice != null && this._pathOnChoice.isRelative) {\n let choiceTargetObj = this.choiceTarget;\n if (choiceTargetObj) {\n this._pathOnChoice = choiceTargetObj.path;\n }\n }\n return this._pathOnChoice;\n }\n set pathOnChoice(value: Path | null) {\n this._pathOnChoice = value;\n }\n get choiceTarget(): Container | null {\n if (this._pathOnChoice === null)\n return throwNullException(\"ChoicePoint._pathOnChoice\");\n return this.ResolvePath(this._pathOnChoice).container;\n }\n get pathStringOnChoice(): string {\n if (this.pathOnChoice === null)\n return throwNullException(\"ChoicePoint.pathOnChoice\");\n return this.CompactPathString(this.pathOnChoice);\n }\n set pathStringOnChoice(value: string) {\n this.pathOnChoice = new Path(value);\n }\n get flags(): number {\n let flags = 0;\n if (this.hasCondition) flags |= 1;\n if (this.hasStartContent) flags |= 2;\n if (this.hasChoiceOnlyContent) flags |= 4;\n if (this.isInvisibleDefault) flags |= 8;\n if (this.onceOnly) flags |= 16;\n return flags;\n }\n set flags(value: number) {\n this.hasCondition = (value & 1) > 0;\n this.hasStartContent = (value & 2) > 0;\n this.hasChoiceOnlyContent = (value & 4) > 0;\n this.isInvisibleDefault = (value & 8) > 0;\n this.onceOnly = (value & 16) > 0;\n }\n public toString(): string {\n if (this.pathOnChoice === null)\n return throwNullException(\"ChoicePoint.pathOnChoice\");\n // int? targetLineNum = DebugLineNumberOfPath (pathOnChoice);\n let targetLineNum = null;\n let targetString = this.pathOnChoice.toString();\n\n if (targetLineNum != null) {\n targetString = \" line \" + targetLineNum + \"(\" + targetString + \")\";\n }\n\n return \"Choice: -> \" + targetString;\n }\n}\n","import { Path } from \"./Path\";\nimport { Container } from \"./Container\";\nimport { InkObject } from \"./Object\";\n\nexport class Pointer {\n public container: Container | null = null;\n public index: number = -1;\n\n constructor();\n constructor(container: Container | null, index: number);\n constructor() {\n if (arguments.length === 2) {\n this.container = arguments[0];\n this.index = arguments[1];\n }\n }\n\n public Resolve(): InkObject | null {\n if (this.index < 0) return this.container;\n if (this.container == null) return null;\n if (this.container.content.length == 0) return this.container;\n if (this.index >= this.container.content.length) return null;\n\n return this.container.content[this.index];\n }\n\n get isNull(): boolean {\n return this.container == null;\n }\n\n get path(): Path | null {\n if (this.isNull) return null;\n\n if (this.index >= 0)\n return this.container!.path.PathByAppendingComponent(\n new Path.Component(this.index)\n );\n else return this.container!.path;\n }\n\n public toString(): string {\n if (!this.container) return \"Ink Pointer (null)\";\n\n return (\n \"Ink Pointer -> \" +\n this.container.path.toString() +\n \" -- index \" +\n this.index\n );\n }\n\n // This method does not exist in the original C# code, but is here to maintain the\n // value semantics of Pointer.\n public copy(): Pointer {\n return new Pointer(this.container, this.index);\n }\n\n public static StartOf(container: Container | null): Pointer {\n return new Pointer(container, 0);\n }\n\n public static get Null(): Pointer {\n return new Pointer(null, -1);\n }\n}\n","import { Path } from \"./Path\";\nimport { PushPopType } from \"./PushPop\";\nimport { StringBuilder } from \"./StringBuilder\";\nimport { InkObject } from \"./Object\";\nimport { Pointer } from \"./Pointer\";\nimport { Container } from \"./Container\";\nimport { throwNullException } from \"./NullException\";\n\nexport class Divert extends InkObject {\n get targetPath() {\n if (this._targetPath != null && this._targetPath.isRelative) {\n let targetObj = this.targetPointer.Resolve();\n if (targetObj) {\n this._targetPath = targetObj.path;\n }\n }\n\n return this._targetPath;\n }\n set targetPath(value: Path | null) {\n this._targetPath = value;\n this._targetPointer = Pointer.Null;\n }\n\n public _targetPath: Path | null = null;\n\n get targetPointer() {\n if (this._targetPointer.isNull) {\n let targetObj = this.ResolvePath(this._targetPath).obj;\n\n if (this._targetPath === null)\n return throwNullException(\"this._targetPath\");\n if (this._targetPath.lastComponent === null)\n return throwNullException(\"this._targetPath.lastComponent\");\n\n if (this._targetPath.lastComponent.isIndex) {\n if (targetObj === null) return throwNullException(\"targetObj\");\n this._targetPointer.container =\n targetObj.parent instanceof Container ? targetObj.parent : null;\n this._targetPointer.index = this._targetPath.lastComponent.index;\n } else {\n this._targetPointer = Pointer.StartOf(\n targetObj instanceof Container ? targetObj : null\n );\n }\n }\n\n return this._targetPointer.copy();\n }\n\n public _targetPointer: Pointer = Pointer.Null;\n\n get targetPathString() {\n if (this.targetPath == null) return null;\n\n return this.CompactPathString(this.targetPath);\n }\n set targetPathString(value: string | null) {\n if (value == null) {\n this.targetPath = null;\n } else {\n this.targetPath = new Path(value);\n }\n }\n\n public variableDivertName: string | null = null;\n get hasVariableTarget() {\n return this.variableDivertName != null;\n }\n\n public pushesToStack: boolean = false;\n public stackPushType: PushPopType = 0;\n\n public isExternal: boolean = false;\n public externalArgs: number = 0;\n\n public isConditional: boolean = false;\n\n constructor(stackPushType?: PushPopType) {\n super();\n this.pushesToStack = false;\n\n if (typeof stackPushType !== \"undefined\") {\n this.pushesToStack = true;\n this.stackPushType = stackPushType;\n }\n }\n\n public Equals(obj: Divert | null) {\n let otherDivert = obj;\n if (otherDivert instanceof Divert) {\n if (this.hasVariableTarget == otherDivert.hasVariableTarget) {\n if (this.hasVariableTarget) {\n return this.variableDivertName == otherDivert.variableDivertName;\n } else {\n if (this.targetPath === null)\n return throwNullException(\"this.targetPath\");\n return this.targetPath.Equals(otherDivert.targetPath);\n }\n }\n }\n return false;\n }\n\n public toString() {\n if (this.hasVariableTarget) {\n return \"Divert(variable: \" + this.variableDivertName + \")\";\n } else if (this.targetPath == null) {\n return \"Divert(null)\";\n } else {\n let sb = new StringBuilder();\n\n let targetStr = this.targetPath.toString();\n // int? targetLineNum = DebugLineNumberOfPath (targetPath);\n let targetLineNum = null;\n if (targetLineNum != null) {\n targetStr = \"line \" + targetLineNum;\n }\n\n sb.Append(\"Divert\");\n\n if (this.isConditional) sb.Append(\"?\");\n\n if (this.pushesToStack) {\n if (this.stackPushType == PushPopType.Function) {\n sb.Append(\" function\");\n } else {\n sb.Append(\" tunnel\");\n }\n }\n\n sb.Append(\" -> \");\n sb.Append(this.targetPathString);\n\n sb.Append(\" (\");\n sb.Append(targetStr);\n sb.Append(\")\");\n\n return sb.toString();\n }\n }\n}\n","export enum SymbolType {\n Knot = 0,\n List = 1,\n ListItem = 2,\n Var = 3,\n SubFlowAndWeave = 4,\n Arg = 5,\n Temp = 6,\n}\n","import { InkObject } from \"./Object\";\n\nexport class VariableAssignment extends InkObject {\n public readonly variableName: string | null;\n public readonly isNewDeclaration: boolean;\n public isGlobal: boolean;\n\n constructor(variableName: string | null, isNewDeclaration: boolean) {\n super();\n this.variableName = variableName || null;\n this.isNewDeclaration = !!isNewDeclaration;\n this.isGlobal = false;\n }\n\n public toString(): string {\n return \"VarAssign to \" + this.variableName;\n }\n}\n","import { ChoicePoint } from \"../../../engine/ChoicePoint\";\nimport { Container as RuntimeContainer } from \"../../../engine/Container\";\nimport { ContentList } from \"./ContentList\";\nimport { ControlCommand as RuntimeControlCommand } from \"../../../engine/ControlCommand\";\nimport { Divert as RuntimeDivert } from \"../../../engine/Divert\";\nimport { DivertTargetValue } from \"../../../engine/Value\";\nimport { INamedContent } from \"../../../engine/INamedContent\";\nimport { IWeavePoint } from \"./IWeavePoint\";\nimport { ParsedObject } from \"./Object\";\nimport { InkObject as RuntimeObject } from \"../../../engine/Object\";\nimport { Path as RuntimePath } from \"../../../engine/Path\";\nimport { Story } from \"./Story\";\nimport { SymbolType } from \"./SymbolType\";\nimport { VariableAssignment as RuntimeVariableAssignment } from \"../../../engine/VariableAssignment\";\nimport { Expression } from \"./Expression/Expression\";\nimport { Identifier } from \"./Identifier\";\n\nexport class Choice extends ParsedObject implements IWeavePoint, INamedContent {\n private _condition: Expression | null = null;\n private _innerContentContainer: RuntimeContainer | null = null;\n private _outerContainer: RuntimeContainer | null = null;\n private _runtimeChoice: ChoicePoint | null = null;\n get runtimeChoice(): ChoicePoint {\n if (!this._runtimeChoice) {\n throw new Error();\n }\n\n return this._runtimeChoice;\n }\n\n private _returnToR1: DivertTargetValue | null = null;\n private _returnToR2: DivertTargetValue | null = null;\n private _r1Label: RuntimeContainer | null = null;\n private _r2Label: RuntimeContainer | null = null;\n private _divertToStartContentOuter: RuntimeDivert | null = null;\n private _divertToStartContentInner: RuntimeDivert | null = null;\n private _startContentRuntimeContainer: RuntimeContainer | null = null;\n\n public startContent: ContentList;\n public choiceOnlyContent: ContentList;\n public innerContent: ContentList;\n public identifier?: Identifier;\n get name() {\n return this.identifier?.name || null;\n }\n public onceOnly: boolean;\n public isInvisibleDefault: boolean = false;\n public indentationDepth: number;\n public hasWeaveStyleInlineBrackets: boolean = false;\n\n get condition() {\n return this._condition;\n }\n\n set condition(value) {\n this._condition = value;\n if (value) {\n this.AddContent(value as ParsedObject);\n }\n }\n\n // Required for IWeavePoint interface\n // Choice's target container. Used by weave to append any extra\n // nested weave content into.\n get runtimeContainer() {\n return this._innerContentContainer;\n }\n\n get innerContentContainer() {\n return this._innerContentContainer;\n }\n\n get containerForCounting() {\n return this._innerContentContainer;\n }\n\n // Override runtimePath to point to the Choice's target content (after it's chosen),\n // as opposed to the default implementation which would point to the choice itself\n // (or it's outer container), which is what runtimeObject is.\n get runtimePath(): RuntimePath {\n if (!this.innerContentContainer || !this.innerContentContainer.path) {\n throw new Error();\n }\n\n return this.innerContentContainer.path;\n }\n\n constructor(\n startContent: ContentList,\n choiceOnlyContent: ContentList,\n innerContent: ContentList\n ) {\n super();\n\n this.startContent = startContent;\n this.choiceOnlyContent = choiceOnlyContent;\n this.innerContent = innerContent;\n this.indentationDepth = 1;\n\n if (startContent) {\n this.AddContent(this.startContent);\n }\n\n if (choiceOnlyContent) {\n this.AddContent(this.choiceOnlyContent);\n }\n\n if (innerContent) {\n this.AddContent(this.innerContent);\n }\n\n this.onceOnly = true; // default\n }\n\n get typeName(): string {\n return \"Choice\";\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject => {\n this._outerContainer = new RuntimeContainer();\n\n // Content names for different types of choice:\n // * start content [choice only content] inner content\n // * start content -> divert\n // * start content\n // * [choice only content]\n\n // Hmm, this structure has become slightly insane!\n //\n // [\n // EvalStart\n // assign $r = $r1 -- return target = return label 1\n // BeginString\n // -> s\n // [(r1)] -- return label 1 (after start content)\n // EndString\n // BeginString\n // ... choice only content\n // EndEval\n // Condition expression\n // choice: -> \"c-0\"\n // (s) = [\n // start content\n // -> r -- goto return label 1 or 2\n // ]\n // ]\n //\n // in parent's container: (the inner content for the choice)\n //\n // (c-0) = [\n // EvalStart\n // assign $r = $r2 -- return target = return label 2\n // EndEval\n // -> s\n // [(r2)] -- return label 1 (after start content)\n // inner content\n // ]\n //\n\n this._runtimeChoice = new ChoicePoint(this.onceOnly);\n this._runtimeChoice.isInvisibleDefault = this.isInvisibleDefault;\n\n if (this.startContent || this.choiceOnlyContent || this.condition) {\n this._outerContainer.AddContent(RuntimeControlCommand.EvalStart());\n }\n\n // Start content is put into a named container that's referenced both\n // when displaying the choice initially, and when generating the text\n // when the choice is chosen.\n if (this.startContent) {\n // Generate start content and return\n // - We can't use a function since it uses a call stack element, which would\n // put temporary values out of scope. Instead we manually divert around.\n // - $r is a variable divert target contains the return point\n this._returnToR1 = new DivertTargetValue();\n this._outerContainer.AddContent(this._returnToR1);\n\n const varAssign = new RuntimeVariableAssignment(\"$r\", true);\n this._outerContainer.AddContent(varAssign);\n\n // Mark the start of the choice text generation, so that the runtime\n // knows where to rewind to to extract the content from the output stream.\n this._outerContainer.AddContent(RuntimeControlCommand.BeginString());\n\n this._divertToStartContentOuter = new RuntimeDivert();\n this._outerContainer.AddContent(this._divertToStartContentOuter);\n\n // Start content itself in a named container\n this._startContentRuntimeContainer =\n this.startContent.GenerateRuntimeObject() as RuntimeContainer;\n this._startContentRuntimeContainer.name = \"s\";\n\n // Effectively, the \"return\" statement - return to the point specified by $r\n const varDivert = new RuntimeDivert();\n varDivert.variableDivertName = \"$r\";\n this._startContentRuntimeContainer.AddContent(varDivert);\n\n // Add the container\n this._outerContainer.AddToNamedContentOnly(\n this._startContentRuntimeContainer\n );\n\n // This is the label to return to\n this._r1Label = new RuntimeContainer();\n this._r1Label.name = \"$r1\";\n this._outerContainer.AddContent(this._r1Label);\n\n this._outerContainer.AddContent(RuntimeControlCommand.EndString());\n\n this._runtimeChoice.hasStartContent = true;\n }\n\n // Choice only content - mark the start, then generate it directly into the outer container\n if (this.choiceOnlyContent) {\n this._outerContainer.AddContent(RuntimeControlCommand.BeginString());\n\n const choiceOnlyRuntimeContent =\n this.choiceOnlyContent.GenerateRuntimeObject() as RuntimeContainer;\n this._outerContainer.AddContentsOfContainer(choiceOnlyRuntimeContent);\n\n this._outerContainer.AddContent(RuntimeControlCommand.EndString());\n\n this._runtimeChoice.hasChoiceOnlyContent = true;\n }\n\n // Generate any condition for this choice\n if (this.condition) {\n this.condition.GenerateIntoContainer(this._outerContainer);\n this._runtimeChoice.hasCondition = true;\n }\n\n if (this.startContent || this.choiceOnlyContent || this.condition) {\n this._outerContainer.AddContent(RuntimeControlCommand.EvalEnd());\n }\n\n // Add choice itself\n this._outerContainer.AddContent(this._runtimeChoice);\n\n // Container that choice points to for when it's chosen\n this._innerContentContainer = new RuntimeContainer();\n\n // Repeat start content by diverting to its container\n if (this.startContent) {\n // Set the return point when jumping back into the start content\n // - In this case, it's the $r2 point, within the choice content \"c\".\n this._returnToR2 = new DivertTargetValue();\n this._innerContentContainer.AddContent(RuntimeControlCommand.EvalStart());\n this._innerContentContainer.AddContent(this._returnToR2);\n this._innerContentContainer.AddContent(RuntimeControlCommand.EvalEnd());\n const varAssign = new RuntimeVariableAssignment(\"$r\", true);\n this._innerContentContainer.AddContent(varAssign);\n\n // Main divert into start content\n this._divertToStartContentInner = new RuntimeDivert();\n this._innerContentContainer.AddContent(this._divertToStartContentInner);\n\n // Define label to return to\n this._r2Label = new RuntimeContainer();\n this._r2Label.name = \"$r2\";\n this._innerContentContainer.AddContent(this._r2Label);\n }\n\n // Choice's own inner content\n if (this.innerContent) {\n const innerChoiceOnlyContent =\n this.innerContent.GenerateRuntimeObject() as RuntimeContainer;\n this._innerContentContainer.AddContentsOfContainer(\n innerChoiceOnlyContent\n );\n }\n\n if (this.story.countAllVisits) {\n this._innerContentContainer.visitsShouldBeCounted = true;\n }\n\n this._innerContentContainer.countingAtStartOnly = true;\n\n return this._outerContainer;\n };\n\n public ResolveReferences(context: Story): void {\n // Weave style choice - target own content container\n if (this._innerContentContainer) {\n this.runtimeChoice.pathOnChoice = this._innerContentContainer.path;\n\n if (this.onceOnly) {\n this._innerContentContainer.visitsShouldBeCounted = true;\n }\n }\n\n if (this._returnToR1) {\n if (!this._r1Label) {\n throw new Error();\n }\n\n this._returnToR1.targetPath = this._r1Label.path;\n }\n\n if (this._returnToR2) {\n if (!this._r2Label) {\n throw new Error();\n }\n\n this._returnToR2.targetPath = this._r2Label.path;\n }\n\n if (this._divertToStartContentOuter) {\n if (!this._startContentRuntimeContainer) {\n throw new Error();\n }\n\n this._divertToStartContentOuter.targetPath =\n this._startContentRuntimeContainer.path;\n }\n\n if (this._divertToStartContentInner) {\n if (!this._startContentRuntimeContainer) {\n throw new Error();\n }\n\n this._divertToStartContentInner.targetPath =\n this._startContentRuntimeContainer.path;\n }\n\n super.ResolveReferences(context);\n\n if (this.identifier && (this.identifier?.name || \"\").length > 0) {\n context.CheckForNamingCollisions(\n this as ParsedObject,\n this.identifier,\n SymbolType.SubFlowAndWeave\n );\n }\n }\n\n public readonly toString = () => {\n if (this.choiceOnlyContent !== null) {\n return `* ${this.startContent}[${this.choiceOnlyContent}]...`;\n }\n\n return `* ${this.startContent}...`;\n };\n}\n","export class StringParserElement {\n public static _uniqueIdCounter: number = 1000;\n\n public characterIndex: number = 0;\n public characterInLineIndex: number = 0;\n public lineIndex: number = 0;\n public reportedErrorInScope: boolean = false;\n public uniqueId: number = 0;\n public customFlags: number = 0;\n\n public readonly CopyFrom = (fromElement: StringParserElement): void => {\n StringParserElement._uniqueIdCounter++;\n this.uniqueId = StringParserElement._uniqueIdCounter;\n this.characterIndex = fromElement.characterIndex;\n this.characterInLineIndex = fromElement.characterInLineIndex;\n this.lineIndex = fromElement.lineIndex;\n this.customFlags = fromElement.customFlags;\n this.reportedErrorInScope = false;\n };\n\n // Squash is used when succeeding from a rule,\n // so only the state information we wanted to carry forward is\n // retained. e.g. characterIndex and lineIndex are global,\n // however uniqueId is specific to the individual rule,\n // and likewise, custom flags are designed for the temporary\n // state of the individual rule too.\n public readonly SquashFrom = (fromElement: StringParserElement): void => {\n this.characterIndex = fromElement.characterIndex;\n this.characterInLineIndex = fromElement.characterInLineIndex;\n this.lineIndex = fromElement.lineIndex;\n this.reportedErrorInScope = fromElement.reportedErrorInScope;\n this.customFlags = fromElement.customFlags;\n };\n}\n","import { StringParserElement } from \"./StringParserElement\";\n\nexport class StringParserState {\n private _stack: StringParserElement[] = [];\n private _numElements: number = 0;\n\n get currentElement(): StringParserElement {\n return this._stack[this._numElements - 1];\n }\n\n get lineIndex(): number {\n return this.currentElement.lineIndex;\n }\n\n set lineIndex(value: number) {\n this.currentElement.lineIndex = value;\n }\n\n get characterIndex(): number {\n return this.currentElement.characterIndex;\n }\n\n set characterIndex(value: number) {\n this.currentElement.characterIndex = value;\n }\n\n get characterInLineIndex(): number {\n return this.currentElement.characterInLineIndex;\n }\n\n set characterInLineIndex(value: number) {\n this.currentElement.characterInLineIndex = value;\n }\n\n get customFlags(): number {\n return this.currentElement.customFlags;\n }\n\n set customFlags(value: number) {\n this.currentElement.customFlags = value;\n }\n\n get errorReportedAlreadyInScope(): boolean {\n return this.currentElement.reportedErrorInScope;\n }\n\n get stackHeight(): number {\n return this._numElements;\n }\n\n constructor() {\n const kExpectedMaxStackDepth = 200;\n for (let i = 0; i < kExpectedMaxStackDepth; i++) {\n this._stack[i] = new StringParserElement();\n }\n this._numElements = 1;\n }\n\n public readonly StringParserState = (): void => {\n const kExpectedMaxStackDepth: number = 200;\n this._stack = new Array(kExpectedMaxStackDepth);\n\n for (let ii = 0; ii < kExpectedMaxStackDepth; ++ii) {\n this._stack[ii] = new StringParserElement();\n }\n\n this._numElements = 1;\n };\n\n public readonly Push = (): number => {\n if (this._numElements >= this._stack.length && this._numElements > 0) {\n throw new Error(\"Stack overflow in parser state.\");\n }\n\n const prevElement = this._stack[this._numElements - 1];\n const newElement = this._stack[this._numElements];\n this._numElements++;\n\n newElement.CopyFrom(prevElement);\n\n return newElement.uniqueId;\n };\n\n public readonly Pop = (expectedRuleId: number): void => {\n if (this._numElements == 1) {\n throw new Error(\n \"Attempting to remove final stack element is illegal! Mismatched Begin/Succceed/Fail?\"\n );\n }\n\n if (this.currentElement.uniqueId != expectedRuleId) {\n throw new Error(\n \"Mismatched rule IDs while Poping - do you have mismatched Begin/Succeed/Fail?\"\n );\n }\n\n // Restore state\n this._numElements -= 1;\n };\n\n public Peek = (expectedRuleId: number) => {\n if (this.currentElement.uniqueId != expectedRuleId) {\n throw new Error(\n \"Mismatched rule IDs while Peeking - do you have mismatched Begin/Succeed/Fail?\"\n );\n }\n\n return this._stack[this._numElements - 1];\n };\n\n public readonly PeekPenultimate = (): StringParserElement | null => {\n if (this._numElements >= 2) {\n return this._stack[this._numElements - 2];\n }\n\n return null;\n };\n\n // Reduce stack height while maintaining currentElement\n // Remove second last element: i.e. \"squash last two elements together\"\n // Used when succeeding from a rule (and ONLY when succeeding, since\n // the state of the top element is retained).\n public readonly Squash = (): void => {\n if (this._numElements < 2) {\n throw new Error(\n \"Attempting to remove final stack element is illegal! Mismatched Begin/Succceed/Fail?\"\n );\n }\n\n const penultimateEl = this._stack[this._numElements - 2];\n const lastEl = this._stack[this._numElements - 1];\n\n penultimateEl.SquashFrom(lastEl);\n\n this._numElements -= 1;\n };\n\n public readonly NoteErrorReported = (): void => {\n for (const el of this._stack) {\n el.reportedErrorInScope = true;\n }\n };\n}\n","import { CharacterSet } from \"../CharacterSet\";\nimport { ParsedObject } from \"../ParsedHierarchy/Object\";\nimport { StringParserState } from \"./StringParserState\";\nimport { StringParserElement } from \"./StringParserElement\";\n\nexport const ParseSuccess = Symbol(\"ParseSuccessStruct\");\n\nexport type ParseRule = () => ParseRuleReturn;\n\nexport type ParseRuleReturn =\n | object\n | string\n | null\n | number\n | (typeof StringParser)[\"ParseSuccess\"];\n\nexport type SpecificParseRule = T;\n\nexport class StringParser {\n public ParseRule: ParseRule | null = null;\n\n public static readonly ParseSuccess: typeof ParseSuccess = ParseSuccess;\n public static readonly numbersCharacterSet = new CharacterSet(\"0123456789\");\n\n private _chars: string[];\n\n public errorHandler:\n | null\n | ((\n message: string,\n index: number,\n lineIndex?: number,\n isWarning?: boolean\n ) => void) = null;\n public state: StringParserState;\n public hadError: boolean = false;\n\n constructor(str: string) {\n const strPreProc = this.PreProcessInputString(str);\n this.state = new StringParserState();\n\n if (str) {\n this._chars = strPreProc.split(\"\");\n } else {\n this._chars = [];\n }\n\n this.inputString = strPreProc;\n }\n\n get currentCharacter(): string {\n if (this.index >= 0 && this.remainingLength > 0) {\n return this._chars[this.index];\n }\n\n return \"0\";\n }\n\n // Don't do anything by default, but provide ability for subclasses\n // to manipulate the string before it's used as input (converted to a char array)\n public PreProcessInputString(str: string): string {\n return str;\n }\n\n //--------------------------------\n // Parse state\n //--------------------------------\n\n public readonly BeginRule = (): number => this.state.Push();\n\n public readonly FailRule = (expectedRuleId: number): ParseRuleReturn => {\n this.state.Pop(expectedRuleId);\n return null;\n };\n\n public readonly CancelRule = (expectedRuleId: number): void => {\n this.state.Pop(expectedRuleId);\n };\n\n public readonly SucceedRule = (\n expectedRuleId: number,\n result: ParseRuleReturn = null\n ): ParseRuleReturn => {\n // Get state at point where this rule stared evaluating\n const stateAtSucceedRule = this.state.Peek(expectedRuleId);\n const stateAtBeginRule = this.state.PeekPenultimate();\n\n // Allow subclass to receive callback\n if (this.RuleDidSucceed) {\n this.RuleDidSucceed(result, stateAtBeginRule, stateAtSucceedRule);\n }\n\n // Flatten state stack so that we maintain the same values,\n // but remove one level in the stack.\n this.state.Squash();\n\n let finalResult: ParseRuleReturn = result;\n if (finalResult === null) {\n finalResult = StringParser.ParseSuccess;\n }\n\n return finalResult;\n };\n\n public RuleDidSucceed?: (\n result: ParseRuleReturn,\n startState: StringParserElement | null,\n endState: StringParserElement\n ) => void;\n\n public readonly Expect = (\n rule: ParseRule,\n message: string | null = null,\n recoveryRule: ParseRule | null = null\n ): ParseRuleReturn => {\n let result: ParseRuleReturn = this.ParseObject(rule);\n if (result === null) {\n if (message === null) {\n message = rule.name;\n }\n\n let butSaw: string;\n const lineRemainder: string = this.LineRemainder();\n if (lineRemainder === null || lineRemainder.length === 0) {\n butSaw = \"end of line\";\n } else {\n butSaw = `'${lineRemainder}'`;\n }\n\n this.Error(`Expected ${message} but saw ${butSaw}`);\n\n if (recoveryRule !== null) {\n result = recoveryRule();\n }\n }\n\n return result;\n };\n\n public Error = (message: string, isWarning: boolean = false): void => {\n this.ErrorOnLine(message, this.lineIndex + 1, isWarning);\n };\n\n public readonly ErrorWithParsedObject = (\n message: string,\n result: ParsedObject,\n isWarning: boolean = false\n ): void => {\n this.ErrorOnLine(\n message,\n result.debugMetadata ? result.debugMetadata.startLineNumber : -1,\n isWarning\n );\n };\n\n public readonly ErrorOnLine = (\n message: string,\n lineNumber: number,\n isWarning: boolean\n ): void => {\n if (!this.state.errorReportedAlreadyInScope) {\n const errorType = isWarning ? \"Warning\" : \"Error\";\n\n if (!this.errorHandler) {\n throw new Error(`${errorType} on line ${lineNumber}: ${message}`);\n } else {\n this.errorHandler(message, this.index, lineNumber - 1, isWarning);\n }\n\n this.state.NoteErrorReported();\n }\n\n if (!isWarning) {\n this.hadError = true;\n }\n };\n\n public readonly Warning = (message: string): void =>\n this.Error(message, true);\n\n get endOfInput(): boolean {\n return this.index >= this._chars.length;\n }\n\n get remainingString(): string {\n return this._chars\n .slice(this.index, this.index + this.remainingLength)\n .join(\"\");\n }\n\n public readonly LineRemainder = (): string =>\n this.Peek(() => this.ParseUntilCharactersFromString(\"\\n\\r\")) as string;\n\n get remainingLength() {\n return this._chars.length - this.index;\n }\n\n public inputString: string;\n\n get lineIndex() {\n return this.state.lineIndex;\n }\n\n set lineIndex(value: number) {\n this.state.lineIndex = value;\n }\n\n set characterInLineIndex(value: number) {\n this.state.characterInLineIndex = value;\n }\n\n get characterInLineIndex() {\n return this.state.characterInLineIndex;\n }\n\n get index(): number {\n // If we want subclass parsers to be able to set the index directly,\n // then we would need to know what the lineIndex of the new\n // index would be - would we have to step through manually\n // counting the newlines to do so?\n return this.state.characterIndex;\n }\n\n set index(value: number) {\n this.state.characterIndex = value;\n }\n\n public readonly SetFlag = (flag: number, trueOrFalse: boolean): void => {\n if (trueOrFalse) {\n this.state.customFlags |= flag;\n } else {\n this.state.customFlags &= ~flag;\n }\n };\n\n public readonly GetFlag = (flag: number): boolean =>\n Boolean(this.state.customFlags & flag);\n\n //--------------------------------\n // Structuring\n //--------------------------------\n\n public ParseObject = (rule: ParseRule): ParseRuleReturn => {\n const ruleId: number = this.BeginRule();\n const stackHeightBefore = this.state.stackHeight;\n const result = rule();\n\n if (stackHeightBefore !== this.state.stackHeight) {\n throw new Error(\"Mismatched Begin/Fail/Succeed rules\");\n }\n\n if (result === null) {\n return this.FailRule(ruleId);\n }\n\n this.SucceedRule(ruleId, result);\n\n return result;\n };\n\n public readonly Parse = (\n rule: SpecificParseRule\n ): ParseRuleReturn => {\n const ruleId: number = this.BeginRule();\n\n const result: ParseRuleReturn = rule();\n if (result === null) {\n this.FailRule(ruleId);\n return null;\n }\n\n this.SucceedRule(ruleId, result);\n\n return result;\n };\n\n public readonly OneOf = (array: ParseRule[]): ParseRuleReturn => {\n for (const rule of array) {\n const result = this.ParseObject(rule);\n if (result !== null) {\n return result;\n }\n }\n\n return null;\n };\n\n public readonly OneOrMore = (rule: ParseRule): ParseRuleReturn[] | null => {\n const results: ParseRuleReturn[] = [];\n let result: ParseRuleReturn = null;\n\n do {\n result = this.ParseObject(rule);\n if (result !== null) {\n results.push(result);\n }\n } while (result !== null);\n\n if (results.length > 0) {\n return results;\n }\n\n return null;\n };\n\n public readonly Optional =\n (rule: ParseRule): ParseRule =>\n () => {\n const result = this.ParseObject(rule);\n if (result === null) return StringParser.ParseSuccess;\n return result;\n };\n\n // Return ParseSuccess instead the real result so that it gets excluded\n // from result arrays (e.g. Interleave)\n public readonly Exclude =\n (rule: ParseRule): ParseRule =>\n () =>\n this.ParseObject(rule) && StringParser.ParseSuccess;\n\n // Combination of both of the above\n public readonly OptionalExclude =\n (rule: ParseRule): ParseRule =>\n () => {\n this.ParseObject(rule);\n return StringParser.ParseSuccess;\n };\n\n // Convenience method for creating more readable ParseString rules that can be combined\n // in other structuring rules (like OneOf etc)\n // e.g. OneOf(String(\"one\"), String(\"two\"))\n public readonly String =\n (str: string): ParseRule =>\n () =>\n this.ParseString(str);\n\n private readonly TryAddResultToList = (\n result: ParseRuleReturn,\n list: T[],\n flatten: boolean = true\n ): void => {\n if (result === StringParser.ParseSuccess) {\n return;\n }\n\n if (flatten && Array.isArray(result)) {\n const resultCollection = result as ParseRuleReturn[];\n if (resultCollection !== null) {\n for (const obj of resultCollection) {\n list.push(obj as any);\n }\n\n return;\n }\n }\n\n list.push(result as any);\n };\n\n public readonly Interleave = (\n ruleA: ParseRule,\n ruleB: ParseRule,\n untilTerminator: ParseRule | null = null,\n flatten: boolean = true\n ): T[] => {\n const ruleId: number = this.BeginRule();\n const results: T[] = [];\n\n // First outer padding\n const firstA = this.ParseObject(ruleA);\n if (firstA === null) {\n return this.FailRule(ruleId) as any;\n } else {\n this.TryAddResultToList(firstA, results, flatten);\n }\n\n let lastMainResult: ParseRuleReturn | null = null;\n let outerResult: ParseRuleReturn | null = null;\n do {\n // \"until\" condition hit?\n if (untilTerminator !== null && this.Peek(untilTerminator) !== null) {\n break;\n }\n\n // Main inner\n lastMainResult = this.ParseObject(ruleB);\n if (lastMainResult === null) {\n break;\n } else {\n this.TryAddResultToList(lastMainResult, results, flatten);\n }\n\n // Outer result (i.e. last A in ABA)\n outerResult = null;\n if (lastMainResult !== null) {\n outerResult = this.ParseObject(ruleA);\n\n if (outerResult === null) {\n break;\n } else {\n this.TryAddResultToList(outerResult, results, flatten);\n }\n }\n\n // Stop if there are no results, or if both are the placeholder \"ParseSuccess\" (i.e. Optional success rather than a true value)\n } while (\n (lastMainResult !== null || outerResult !== null) &&\n !(\n (lastMainResult as any) === StringParser.ParseSuccess &&\n outerResult == StringParser.ParseSuccess\n ) &&\n this.remainingLength > 0\n );\n\n if (results.length === 0) {\n return this.FailRule(ruleId) as T[];\n }\n\n return this.SucceedRule(ruleId, results) as T[];\n };\n\n //--------------------------------\n // Basic string parsing\n //--------------------------------\n\n public readonly ParseString = (str: string): string | null => {\n if (str.length > this.remainingLength) {\n return null;\n }\n\n const ruleId: number = this.BeginRule();\n\n // Optimisation from profiling:\n // Store in temporary local variables\n // since they're properties that would have to access\n // the rule stack every time otherwise.\n let i: number = this.index;\n let cli: number = this.characterInLineIndex;\n let li: number = this.lineIndex;\n\n let success: boolean = true;\n for (let tempIdx = 0; tempIdx < str.length; tempIdx += 1) {\n const c = str[tempIdx];\n\n if (this._chars[i] !== c) {\n success = false;\n break;\n }\n if (c === \"\\n\") {\n li++;\n cli = -1;\n }\n\n i++;\n cli++;\n }\n\n this.index = i;\n this.characterInLineIndex = cli;\n this.lineIndex = li;\n\n if (success) {\n return this.SucceedRule(ruleId, str) as any;\n }\n\n return this.FailRule(ruleId) as any;\n };\n\n public readonly ParseSingleCharacter = (): string => {\n if (this.remainingLength > 0) {\n const c = this._chars[this.index];\n if (c === \"\\n\") {\n this.lineIndex += 1;\n this.characterInLineIndex = -1;\n }\n\n this.index += 1;\n this.characterInLineIndex += 1;\n\n return c;\n }\n\n return \"0\";\n };\n\n public readonly ParseUntilCharactersFromString = (\n str: string,\n maxCount: number = -1\n ): string | null => this.ParseCharactersFromString(str, false, maxCount);\n\n public readonly ParseUntilCharactersFromCharSet = (\n charSet: CharacterSet,\n maxCount: number = -1\n ): string | null => this.ParseCharactersFromCharSet(charSet, false, maxCount);\n\n public readonly ParseCharactersFromString = (\n str: string,\n maxCountOrShouldIncludeStrChars: boolean | number = -1,\n maxCount: number = -1\n ): string | null => {\n const charSet = new CharacterSet(str);\n if (typeof maxCountOrShouldIncludeStrChars === \"number\") {\n return this.ParseCharactersFromCharSet(\n charSet,\n true,\n maxCountOrShouldIncludeStrChars\n );\n }\n\n return this.ParseCharactersFromCharSet(\n charSet,\n maxCountOrShouldIncludeStrChars,\n maxCount\n );\n };\n\n public readonly ParseCharactersFromCharSet = (\n charSet: CharacterSet,\n shouldIncludeChars: boolean = true,\n maxCount: number = -1\n ): string | null => {\n if (maxCount === -1) {\n maxCount = Number.MAX_SAFE_INTEGER;\n }\n\n const startIndex: number = this.index;\n\n // Optimisation from profiling:\n // Store in temporary local variables\n // since they're properties that would have to access\n // the rule stack every time otherwise.\n let ii: number = this.index;\n let cli: number = this.characterInLineIndex;\n let li: number = this.lineIndex;\n let count: number = 0;\n while (\n ii < this._chars.length &&\n charSet.set.has(this._chars[ii]) === shouldIncludeChars &&\n count < maxCount\n ) {\n if (this._chars[ii] === \"\\n\") {\n li += 1;\n cli = -1;\n }\n\n ii += 1;\n cli += 1;\n count += 1;\n }\n\n this.index = ii;\n this.characterInLineIndex = cli;\n this.lineIndex = li;\n\n const lastCharIndex: number = this.index;\n if (lastCharIndex > startIndex) {\n return this._chars.slice(startIndex, this.index).join(\"\");\n }\n\n return null;\n };\n\n public readonly Peek = (rule: ParseRule): ParseRuleReturn => {\n const ruleId: number = this.BeginRule();\n const result: ParseRuleReturn = rule();\n this.CancelRule(ruleId);\n\n return result;\n };\n\n public ParseUntil(\n stopRule: ParseRule,\n pauseCharacters: CharacterSet | null = null,\n endCharacters: CharacterSet | null = null\n ): string {\n const ruleId: number = this.BeginRule();\n const pauseAndEnd: CharacterSet = new CharacterSet();\n if (pauseCharacters !== null) {\n pauseAndEnd.set = new Set([\n ...pauseAndEnd.set.values(),\n ...pauseCharacters.set.values(),\n ]);\n }\n\n if (endCharacters !== null) {\n pauseAndEnd.set = new Set([\n ...pauseAndEnd.set.values(),\n ...endCharacters.set.values(),\n ]);\n }\n\n let parsedString = \"\";\n let ruleResultAtPause: ParseRuleReturn | null = null;\n\n // Keep attempting to parse strings up to the pause (and end) points.\n // - At each of the pause points, attempt to parse according to the rule\n // - When the end point is reached (or EOF), we're done\n do {\n // TODO: Perhaps if no pause or end characters are passed, we should check *every* character for stopRule?\n const partialParsedString: string | null =\n this.ParseUntilCharactersFromCharSet(pauseAndEnd);\n\n if (partialParsedString) {\n parsedString += partialParsedString;\n }\n\n // Attempt to run the parse rule at this pause point\n ruleResultAtPause = this.Peek(stopRule);\n\n // Rule completed - we're done\n if (ruleResultAtPause !== null) {\n break;\n } else {\n if (this.endOfInput) {\n break;\n }\n\n // Reached a pause point, but rule failed. Step past and continue parsing string\n const pauseCharacter: string = this.currentCharacter;\n if (\n pauseCharacters !== null &&\n pauseCharacters.set.has(pauseCharacter)\n ) {\n parsedString += pauseCharacter;\n if (pauseCharacter === \"\\n\") {\n this.lineIndex += 1;\n this.characterInLineIndex = -1;\n }\n\n this.index += 1;\n this.characterInLineIndex += 1;\n\n continue;\n } else {\n break;\n }\n }\n } while (true);\n\n if (parsedString.length > 0) {\n return this.SucceedRule(ruleId, String(parsedString)) as string;\n }\n\n return this.FailRule(ruleId) as string;\n }\n\n // No need to Begin/End rule since we never parse a newline, so keeping oldIndex is good enough\n public readonly ParseInt = (): number | null => {\n const oldIndex: number = this.index;\n const oldCharacterInLineIndex: number = this.characterInLineIndex;\n const negative: boolean = this.ParseString(\"-\") !== null;\n\n // Optional whitespace\n this.ParseCharactersFromString(\" \\t\");\n\n const parsedString = this.ParseCharactersFromCharSet(\n StringParser.numbersCharacterSet\n );\n if (parsedString === null) {\n // Roll back and fail\n this.index = oldIndex;\n this.characterInLineIndex = oldCharacterInLineIndex;\n\n return null;\n }\n\n let parsedInt: number;\n if (!Number.isNaN(Number(parsedString))) {\n parsedInt = Number(parsedString);\n return negative ? -parsedInt : parsedInt;\n }\n\n this.Error(\n \"Failed to read integer value: \" +\n parsedString +\n \". Perhaps it's out of the range of acceptable numbers ink supports? (\" +\n Number.MIN_SAFE_INTEGER +\n \" to \" +\n Number.MAX_SAFE_INTEGER +\n \")\"\n );\n\n return null;\n };\n\n // No need to Begin/End rule since we never parse a newline, so keeping oldIndex is good enough\n public readonly ParseFloat = (): number | null => {\n const oldIndex: number = this.index;\n const oldCharacterInLineIndex: number = this.characterInLineIndex;\n\n const leadingInt: number | null = this.ParseInt();\n if (leadingInt !== null) {\n if (this.ParseString(\".\") !== null) {\n const afterDecimalPointStr = this.ParseCharactersFromCharSet(\n StringParser.numbersCharacterSet\n );\n\n return Number(`${leadingInt}.${afterDecimalPointStr}`);\n }\n }\n\n // Roll back and fail\n this.index = oldIndex;\n this.characterInLineIndex = oldCharacterInLineIndex;\n\n return null;\n };\n\n public readonly ParseNewline = (): string => {\n const ruleId: number = this.BeginRule();\n\n // Optional \\r, definite \\n to support Windows (\\r\\n) and Mac/Unix (\\n)\n // 2nd May 2016: Always collapse \\r\\n to just \\n\n this.ParseString(\"\\r\");\n\n if (this.ParseString(\"\\n\") === null) {\n return this.FailRule(ruleId) as string;\n }\n\n return this.SucceedRule(ruleId, \"\\n\") as string;\n };\n}\n","import { CharacterSet } from \"./CharacterSet\";\nimport { StringParser } from \"./StringParser/StringParser\";\n\n/// \n/// Pre-pass before main ink parser runs. It actually performs two main tasks:\n/// - comment elimination to simplify the parse rules in the main parser\n/// - Conversion of Windows line endings (\\r\\n) to the simpler Unix style (\\n), so\n/// we don't have to worry about them later.\n/// \nexport class CommentEliminator extends StringParser {\n public _commentOrNewlineStartCharacter = new CharacterSet(\"/\\r\\n\");\n public _commentBlockEndCharacter = new CharacterSet(\"*\");\n public _newlineCharacters = new CharacterSet(\"\\n\\r\");\n\n public readonly Process = (): string => {\n // Make both comments and non-comments optional to handle trivial empty file case (or *only* comments)\n const stringList: string[] = this.Interleave(\n this.Optional(this.CommentsAndNewlines),\n this.Optional(this.MainInk)\n );\n\n if (stringList !== null) {\n return stringList.join(\"\");\n } else {\n return \"\";\n }\n };\n\n public readonly MainInk = () =>\n this.ParseUntil(\n this.CommentsAndNewlines,\n this._commentOrNewlineStartCharacter,\n null\n );\n\n public readonly CommentsAndNewlines = () => {\n let newLines: string[] = this.Interleave(\n this.Optional(this.ParseNewline),\n this.Optional(this.ParseSingleComment)\n );\n\n if (newLines !== null) {\n return newLines.join(\"\");\n }\n\n return null;\n };\n\n // Valid comments always return either an empty string or pure newlines,\n // which we want to keep so that line numbers stay the same\n public readonly ParseSingleComment = () =>\n this.OneOf([this.EndOfLineComment, this.BlockComment]);\n\n public readonly EndOfLineComment = () => {\n if (this.ParseString(\"//\") === null) {\n return null;\n }\n\n this.ParseUntilCharactersFromCharSet(this._newlineCharacters);\n\n return \"\";\n };\n\n public readonly BlockComment = () => {\n if (this.ParseString(\"/*\") === null) {\n return null;\n }\n\n const startLineIndex: number = this.lineIndex;\n const commentResult = this.ParseUntil(\n this.String(\"*/\"),\n this._commentBlockEndCharacter,\n null\n );\n\n if (!this.endOfInput) {\n this.ParseString(\"*/\");\n }\n\n // Count the number of lines that were inside the block, and replicate them as newlines\n // so that the line indexing still works from the original source\n if (commentResult != null) {\n return \"\\n\".repeat(this.lineIndex - startLineIndex);\n }\n\n // No comment at all\n return null;\n };\n\n public PreProcessInputString(str: string): string {\n return str;\n }\n}\n","import { ConditionalSingleBranch } from \"./ConditionalSingleBranch\";\nimport { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { ControlCommand as RuntimeControlCommand } from \"../../../../engine/ControlCommand\";\nimport { Expression } from \"../Expression/Expression\";\nimport { ParsedObject } from \"../Object\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\nimport { Story } from \"../Story\";\n\nexport class Conditional extends ParsedObject {\n private _reJoinTarget: RuntimeControlCommand | null = null;\n\n constructor(\n public initialCondition: Expression,\n public branches: ConditionalSingleBranch[]\n ) {\n super();\n\n if (this.initialCondition) {\n this.AddContent(this.initialCondition);\n }\n\n if (this.branches !== null) {\n this.AddContent(this.branches);\n }\n }\n\n get typeName(): string {\n return \"Conditional\";\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject => {\n const container = new RuntimeContainer();\n\n // Initial condition\n if (this.initialCondition) {\n container.AddContent(this.initialCondition.runtimeObject);\n }\n\n // Individual branches\n for (const branch of this.branches) {\n const branchContainer = branch.runtimeObject;\n container.AddContent(branchContainer);\n }\n\n // If it's a switch-like conditional, each branch\n // will have a \"duplicate\" operation for the original\n // switched value. If there's no final else clause\n // and we fall all the way through, we need to clean up.\n // (An else clause doesn't dup but it *does* pop)\n if (\n this.initialCondition !== null &&\n this.branches[0].ownExpression !== null &&\n !this.branches[this.branches.length - 1].isElse\n ) {\n container.AddContent(RuntimeControlCommand.PopEvaluatedValue());\n }\n\n // Target for branches to rejoin to\n this._reJoinTarget = RuntimeControlCommand.NoOp();\n container.AddContent(this._reJoinTarget);\n\n return container;\n };\n\n public ResolveReferences(context: Story): void {\n const pathToReJoin = this._reJoinTarget!.path;\n\n for (const branch of this.branches) {\n if (!branch.returnDivert) {\n throw new Error();\n }\n\n branch.returnDivert.targetPath = pathToReJoin;\n }\n\n super.ResolveReferences(context);\n }\n}\n","import { ParsedObject } from \"./Object\";\nimport { InkObject as RuntimeObject } from \"../../../engine/Object\";\nimport { StringValue } from \"../../../engine/Value\";\n\nexport class Text extends ParsedObject {\n constructor(public text: string) {\n super();\n }\n get typeName(): string {\n return \"Text\";\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject =>\n new StringValue(this.text);\n\n public readonly toString = (): string => this.text;\n}\n","import { Expression } from \"../Expression/Expression\";\nimport { ParsedObject } from \"../Object\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\nimport { Story } from \"../Story\";\nimport { SymbolType } from \"../SymbolType\";\nimport { Identifier } from \"../Identifier\";\n\nexport class ConstantDeclaration extends ParsedObject {\n get constantName(): string | undefined {\n return this.constantIdentifier?.name;\n }\n public constantIdentifier: Identifier;\n\n private _expression: Expression | null = null;\n\n get expression(): Expression {\n if (!this._expression) {\n throw new Error();\n }\n\n return this._expression;\n }\n\n constructor(name: Identifier, assignedExpression: Expression) {\n super();\n\n this.constantIdentifier = name;\n\n // Defensive programming in case parsing of assignedExpression failed\n if (assignedExpression) {\n this._expression = this.AddContent(assignedExpression) as Expression;\n }\n }\n\n get typeName(): string {\n return \"CONST\";\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject | null => {\n // Global declarations don't generate actual procedural\n // runtime objects, but instead add a global variable to the story itself.\n // The story then initialises them all in one go at the start of the game.\n return null;\n };\n\n public ResolveReferences(context: Story) {\n super.ResolveReferences(context);\n context.CheckForNamingCollisions(\n this,\n this.constantIdentifier,\n SymbolType.Var\n );\n }\n}\n","export enum FlowLevel {\n Story, // 0\n Knot, // 1\n Stitch, // 2\n // not actually a FlowBase, but used for diverts\n WeavePoint, // 3\n}\n","import { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { INamedContent } from \"../../../../engine/INamedContent\";\nimport { IWeavePoint } from \"../IWeavePoint\";\nimport { ParsedObject } from \"../Object\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\nimport { Story } from \"../Story\";\nimport { SymbolType } from \"../SymbolType\";\nimport { Identifier } from \"../Identifier\";\n\nexport class Gather extends ParsedObject implements INamedContent, IWeavePoint {\n get name(): string | null {\n return this.identifier?.name || null;\n }\n public identifier?: Identifier;\n\n get runtimeContainer(): RuntimeContainer {\n return this.runtimeObject as RuntimeContainer;\n }\n\n constructor(\n identifier: Identifier | null,\n public readonly indentationDepth: number\n ) {\n super();\n\n if (identifier) this.identifier = identifier;\n }\n\n get typeName(): string {\n return \"Gather\";\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject => {\n const container = new RuntimeContainer();\n container.name = this.name;\n\n if (this.story.countAllVisits) {\n container.visitsShouldBeCounted = true;\n }\n\n container.countingAtStartOnly = true;\n\n // A gather can have null content, e.g. it's just purely a line with \"-\"\n if (this.content) {\n for (const c of this.content) {\n container.AddContent(c.runtimeObject);\n }\n }\n\n return container;\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n\n if (this.identifier && (this.identifier.name || \"\").length > 0) {\n context.CheckForNamingCollisions(\n this,\n this.identifier,\n SymbolType.SubFlowAndWeave\n );\n }\n }\n\n public readonly toString = (): string =>\n `- ${this.identifier?.name ? \"(\" + this.identifier?.name + \")\" : \"gather\"}`;\n}\n","import { asOrNull, filterUndef } from \"../../../engine/TypeAssertion\";\nimport { FlowBase } from \"./Flow/FlowBase\";\nimport { FlowLevel } from \"./Flow/FlowLevel\";\nimport { Identifier } from \"./Identifier\";\nimport { ParsedObject } from \"./Object\";\nimport { Weave } from \"./Weave\";\n\nexport class Path {\n private _baseTargetLevel: FlowLevel | null;\n private components: Identifier[] | null;\n\n get baseTargetLevel() {\n if (this.baseLevelIsAmbiguous) {\n return FlowLevel.Story;\n }\n\n return this._baseTargetLevel;\n }\n\n get baseLevelIsAmbiguous(): boolean {\n return !this._baseTargetLevel;\n }\n\n get firstComponent(): string | null {\n if (this.components == null || !this.components.length) {\n return null;\n }\n\n return this.components[0].name;\n }\n\n get numberOfComponents(): number {\n return this.components ? this.components.length : 0;\n }\n\n private _dotSeparatedComponents: string | null = null;\n\n get dotSeparatedComponents(): string {\n if (this._dotSeparatedComponents == null) {\n this._dotSeparatedComponents = (this.components ? this.components : [])\n .map((c) => c.name)\n .filter(filterUndef)\n .join(\".\");\n }\n return this._dotSeparatedComponents;\n }\n\n constructor(\n argOne: FlowLevel | Identifier[] | Identifier,\n argTwo?: Identifier[]\n ) {\n if (Object.values(FlowLevel).includes(argOne as FlowLevel)) {\n this._baseTargetLevel = argOne as FlowLevel;\n this.components = argTwo || [];\n } else if (Array.isArray(argOne)) {\n this._baseTargetLevel = null;\n this.components = argOne || [];\n } else {\n this._baseTargetLevel = null;\n this.components = [argOne as Identifier];\n }\n }\n\n get typeName(): string {\n return \"Path\";\n }\n\n public readonly toString = (): string => {\n if (this.components === null || this.components.length === 0) {\n if (this.baseTargetLevel === FlowLevel.WeavePoint) {\n return \"-> \";\n }\n\n return \"\";\n }\n\n return `-> ${this.dotSeparatedComponents}`;\n };\n\n public readonly ResolveFromContext = (\n context: ParsedObject\n ): ParsedObject | null => {\n if (this.components == null || this.components.length == 0) {\n return null;\n }\n\n // Find base target of path from current context. e.g.\n // ==> BASE.sub.sub\n let baseTargetObject = this.ResolveBaseTarget(context);\n if (baseTargetObject === null) {\n return null;\n }\n\n // Given base of path, resolve final target by working deeper into hierarchy\n // e.g. ==> base.mid.FINAL\n if (this.components.length > 1) {\n return this.ResolveTailComponents(baseTargetObject);\n }\n\n return baseTargetObject;\n };\n\n // Find the root object from the base, i.e. root from:\n // root.sub1.sub2\n public readonly ResolveBaseTarget = (\n originalContext: ParsedObject\n ): ParsedObject | null => {\n const firstComp = this.firstComponent;\n\n // Work up the ancestry to find the node that has the named object\n let ancestorContext: ParsedObject | null = originalContext;\n while (ancestorContext) {\n // Only allow deep search when searching deeper from original context.\n // Don't allow search upward *then* downward, since that's searching *everywhere*!\n // Allowed examples:\n // - From an inner gather of a stitch, you should search up to find a knot called 'x'\n // at the root of a story, but not a stitch called 'x' in that knot.\n // - However, from within a knot, you should be able to find a gather/choice\n // anywhere called 'x'\n // (that latter example is quite loose, but we allow it)\n const deepSearch: boolean = ancestorContext === originalContext;\n\n const foundBase = this.GetChildFromContext(\n ancestorContext,\n firstComp,\n null,\n deepSearch\n );\n\n if (foundBase) {\n return foundBase;\n }\n\n ancestorContext = ancestorContext.parent;\n }\n\n return null;\n };\n\n // Find the final child from path given root, i.e.:\n // root.sub.finalChild\n public readonly ResolveTailComponents = (\n rootTarget: ParsedObject\n ): ParsedObject | null => {\n let foundComponent: ParsedObject | null = rootTarget;\n\n if (!this.components) return null;\n\n for (let ii = 1; ii < this.components.length; ++ii) {\n const compName = this.components[ii].name;\n\n let minimumExpectedLevel: FlowLevel;\n let foundFlow = asOrNull(foundComponent, FlowBase);\n if (foundFlow !== null) {\n minimumExpectedLevel = (foundFlow.flowLevel + 1) as FlowLevel;\n } else {\n minimumExpectedLevel = FlowLevel.WeavePoint;\n }\n\n foundComponent = this.GetChildFromContext(\n foundComponent,\n compName,\n minimumExpectedLevel\n );\n\n if (foundComponent === null) {\n break;\n }\n }\n\n return foundComponent;\n };\n\n // See whether \"context\" contains a child with a given name at a given flow level\n // Can either be a named knot/stitch (a FlowBase) or a weave point within a Weave (Choice or Gather)\n // This function also ignores any other object types that are neither FlowBase nor Weave.\n // Called from both ResolveBase (force deep) and ResolveTail for the individual components.\n public readonly GetChildFromContext = (\n context: ParsedObject,\n childName: string | null,\n minimumLevel: FlowLevel | null,\n forceDeepSearch: boolean = false\n ): ParsedObject | null => {\n // null childLevel means that we don't know where to find it\n const ambiguousChildLevel: boolean = minimumLevel === null;\n\n // Search for WeavePoint within Weave\n const weaveContext = asOrNull(context, Weave);\n if (\n childName &&\n weaveContext !== null &&\n (ambiguousChildLevel || minimumLevel === FlowLevel.WeavePoint)\n ) {\n return weaveContext.WeavePointNamed(childName) as ParsedObject;\n }\n\n // Search for content within Flow (either a sub-Flow or a WeavePoint)\n let flowContext = asOrNull(context, FlowBase);\n if (childName && flowContext !== null) {\n // When searching within a Knot, allow a deep searches so that\n // named weave points (choices and gathers) can be found within any stitch\n // Otherwise, we just search within the immediate object.\n const shouldDeepSearch =\n forceDeepSearch || flowContext.flowLevel === FlowLevel.Knot;\n\n return flowContext.ContentWithNameAtLevel(\n childName,\n minimumLevel,\n shouldDeepSearch\n );\n }\n\n return null;\n };\n}\n","import { Expression } from \"./Expression/Expression\";\nimport { ParsedObject } from \"./Object\";\nimport { Container as RuntimeContainer } from \"../../../engine/Container\";\nimport { ControlCommand as RuntimeControlCommand } from \"../../../engine/ControlCommand\";\nimport { InkObject as RuntimeObject } from \"../../../engine/Object\";\nimport { Void } from \"../../../engine/Void\";\n\nexport class ReturnType extends ParsedObject {\n public returnedExpression: Expression | null = null;\n\n constructor(returnedExpression: Expression | null = null) {\n super();\n\n if (returnedExpression) {\n this.returnedExpression = this.AddContent(\n returnedExpression\n ) as Expression;\n }\n }\n\n get typeName(): string {\n return \"ReturnType\";\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject => {\n const container = new RuntimeContainer();\n\n if (this.returnedExpression) {\n // Evaluate expression\n container.AddContent(this.returnedExpression.runtimeObject);\n } else {\n // Return Runtime.Void when there's no expression to evaluate\n // (This evaluation will just add the Void object to the evaluation stack)\n container.AddContent(RuntimeControlCommand.EvalStart());\n container.AddContent(new Void());\n container.AddContent(RuntimeControlCommand.EvalEnd());\n }\n\n // Then pop the call stack\n // (the evaluated expression will leave the return value on the evaluation stack)\n container.AddContent(RuntimeControlCommand.PopFunction());\n\n return container;\n };\n}\n","// import { FlowBase } from './FlowBase';\n\nexport function ClosestFlowBase(obj: any): any | null {\n let ancestor = obj.parent;\n while (ancestor) {\n if (ancestor.hasOwnProperty(\"iamFlowbase\") && ancestor.iamFlowbase()) {\n return ancestor as any;\n }\n\n ancestor = ancestor.parent;\n }\n\n return null;\n}\n","import { DebugMetadata } from \"../../../engine/DebugMetadata\";\n\nexport class Identifier {\n public name: string;\n public debugMetadata: DebugMetadata | null = null;\n\n constructor(name: string) {\n this.name = name;\n }\n\n get typeName(): string {\n return \"Identifier\";\n }\n\n public static Done(): Identifier {\n return new Identifier(\"DONE\");\n }\n\n public readonly toString = (): string => this.name || \"undefined identifer\";\n}\n","import { Argument } from \"../Argument\";\nimport { Choice } from \"../Choice\";\nimport { Divert } from \"../Divert/Divert\";\nimport { DivertTarget } from \"../Divert/DivertTarget\";\nimport { FlowLevel } from \"./FlowLevel\";\nimport { Gather } from \"../Gather/Gather\";\nimport { INamedContent } from \"../../../../engine/INamedContent\";\n// import { Knot } from '../Knot';\nimport { ParsedObject } from \"../Object\";\nimport { Path } from \"../Path\";\nimport { ReturnType } from \"../ReturnType\";\nimport { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { Divert as RuntimeDivert } from \"../../../../engine/Divert\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\nimport { VariableAssignment as RuntimeVariableAssignment } from \"../../../../engine/VariableAssignment\";\n//import { Story } from '../Story';\nimport { SymbolType } from \"../SymbolType\";\nimport { VariableAssignment } from \"../Variable/VariableAssignment\";\nimport { Weave } from \"../Weave\";\nimport { ClosestFlowBase } from \"./ClosestFlowBase\";\nimport { Identifier } from \"../Identifier\";\nimport { asOrNull } from \"../../../../engine/TypeAssertion\";\n\ntype VariableResolveResult = {\n found: boolean;\n isGlobal: boolean;\n isArgument: boolean;\n isTemporary: boolean;\n ownerFlow: FlowBase;\n};\n\n// Base class for Knots and Stitches\nexport abstract class FlowBase extends ParsedObject implements INamedContent {\n public abstract readonly flowLevel: FlowLevel;\n\n public _rootWeave: Weave | null = null;\n public _subFlowsByName: Map = new Map();\n public _startingSubFlowDivert: RuntimeDivert | null = null;\n public _startingSubFlowRuntime: RuntimeObject | null = null;\n public _firstChildFlow: FlowBase | null = null;\n public variableDeclarations: Map = new Map();\n\n get hasParameters() {\n return this.args !== null && this.args.length > 0;\n }\n\n get subFlowsByName() {\n return this._subFlowsByName;\n }\n\n get typeName(): string {\n if (this.isFunction) {\n return \"Function\";\n }\n\n return String(this.flowLevel);\n }\n\n get name(): string | null {\n return this.identifier?.name || null;\n }\n\n public identifier: Identifier | null = null;\n public args: Argument[] | null = null;\n\n constructor(\n identifier: Identifier | null,\n topLevelObjects: ParsedObject[] | null = null,\n args: Argument[] | null = null,\n public readonly isFunction: boolean = false,\n isIncludedStory: boolean = false\n ) {\n super();\n\n this.identifier = identifier;\n this.args = args;\n\n if (topLevelObjects === null) {\n topLevelObjects = [];\n }\n\n // Used by story to add includes\n this.PreProcessTopLevelObjects(topLevelObjects);\n\n topLevelObjects = this.SplitWeaveAndSubFlowContent(\n topLevelObjects,\n this.GetType() == \"Story\" && !isIncludedStory\n );\n\n this.AddContent(topLevelObjects);\n }\n\n public iamFlowbase = () => true;\n\n public readonly SplitWeaveAndSubFlowContent = (\n contentObjs: ParsedObject[],\n isRootStory: boolean\n ): ParsedObject[] => {\n const weaveObjs: ParsedObject[] = [];\n const subFlowObjs: ParsedObject[] = [];\n\n this._subFlowsByName = new Map();\n\n for (const obj of contentObjs) {\n const subFlow = asOrNull(obj, FlowBase);\n if (subFlow) {\n if (this._firstChildFlow === null) {\n this._firstChildFlow = subFlow;\n }\n\n subFlowObjs.push(obj);\n if (subFlow.identifier?.name) {\n this._subFlowsByName.set(subFlow.identifier?.name, subFlow);\n }\n } else {\n weaveObjs.push(obj);\n }\n }\n\n // Implicit final gather in top level story for ending without warning that you run out of content\n if (isRootStory) {\n weaveObjs.push(\n new Gather(null, 1),\n new Divert(new Path(Identifier.Done()))\n );\n }\n\n const finalContent: ParsedObject[] = [];\n\n if (weaveObjs.length > 0) {\n this._rootWeave = new Weave(weaveObjs, 0);\n finalContent.push(this._rootWeave);\n }\n\n if (subFlowObjs.length > 0) {\n finalContent.push(...subFlowObjs);\n }\n return finalContent;\n };\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n public PreProcessTopLevelObjects(_: ParsedObject[]): void {\n // empty by default, used by Story to process included file references\n }\n\n public VariableResolveResult?: VariableResolveResult | null | undefined;\n\n public ResolveVariableWithName = (\n varName: string,\n fromNode: ParsedObject\n ): VariableResolveResult => {\n const result: VariableResolveResult = {} as any;\n\n // Search in the stitch / knot that owns the node first\n const ownerFlow = fromNode === null ? this : ClosestFlowBase(fromNode);\n\n if (ownerFlow) {\n // Argument\n if (ownerFlow.args !== null) {\n for (const arg of ownerFlow.args) {\n if (arg.identifier?.name === varName) {\n result.found = true;\n result.isArgument = true;\n result.ownerFlow = ownerFlow;\n return result;\n }\n }\n }\n\n // Temp\n if (\n ownerFlow !== this.story &&\n ownerFlow.variableDeclarations.has(varName)\n ) {\n result.found = true;\n result.ownerFlow = ownerFlow;\n result.isTemporary = true;\n\n return result;\n }\n }\n\n // Global\n if (this.story.variableDeclarations.has(varName)) {\n result.found = true;\n result.ownerFlow = this.story;\n result.isGlobal = true;\n\n return result;\n }\n\n result.found = false;\n\n return result;\n };\n\n public AddNewVariableDeclaration = (varDecl: VariableAssignment): void => {\n const varName = varDecl.variableName;\n if (this.variableDeclarations.has(varName)) {\n const varab = this.variableDeclarations.get(varName)!;\n let prevDeclError = \"\";\n const debugMetadata = varab.debugMetadata;\n if (debugMetadata) {\n prevDeclError = ` (${varab.debugMetadata})`;\n }\n\n this.Error(\n `found declaration variable '${varName}' that was already declared${prevDeclError}`,\n varDecl,\n false\n );\n\n return;\n }\n\n this.variableDeclarations.set(varDecl.variableName, varDecl);\n };\n\n public ResolveWeavePointNaming = (): void => {\n // Find all weave points and organise them by name ready for\n // diverting. Also detect naming collisions.\n if (this._rootWeave) {\n this._rootWeave.ResolveWeavePointNaming();\n }\n\n for (const [, value] of this._subFlowsByName) {\n if (value.hasOwnProperty(\"ResolveWeavePointNaming\")) {\n value.ResolveWeavePointNaming();\n }\n }\n };\n\n public readonly GenerateRuntimeObject = (): RuntimeObject => {\n let foundReturn: ReturnType | null = null;\n if (this.isFunction) {\n this.CheckForDisallowedFunctionFlowControl();\n } else if (\n this.flowLevel === FlowLevel.Knot ||\n this.flowLevel === FlowLevel.Stitch\n ) {\n // Non-functon: Make sure knots and stitches don't attempt to use Return statement\n foundReturn = this.Find(ReturnType)();\n\n if (foundReturn !== null) {\n this.Error(\n `Return statements can only be used in knots that are declared as functions: == function ${this.identifier} ==`,\n foundReturn\n );\n }\n }\n\n const container = new RuntimeContainer();\n container.name = this.identifier?.name as string;\n\n if (this.story.countAllVisits) {\n container.visitsShouldBeCounted = true;\n }\n\n this.GenerateArgumentVariableAssignments(container);\n\n // Run through content defined for this knot/stitch:\n // - First of all, any initial content before a sub-stitch\n // or any weave content is added to the main content container\n // - The first inner knot/stitch is automatically entered, while\n // the others are only accessible by an explicit divert\n // - The exception to this rule is if the knot/stitch takes\n // parameters, in which case it can't be auto-entered.\n // - Any Choices and Gathers (i.e. IWeavePoint) found are\n // processsed by GenerateFlowContent.\n let contentIdx: number = 0;\n while (this.content !== null && contentIdx < this.content.length) {\n const obj: ParsedObject = this.content[contentIdx];\n\n // Inner knots and stitches\n if (obj instanceof FlowBase) {\n const childFlow: FlowBase = obj;\n const childFlowRuntime = childFlow.runtimeObject;\n\n // First inner stitch - automatically step into it\n // 20/09/2016 - let's not auto step into knots\n if (\n contentIdx === 0 &&\n !childFlow.hasParameters &&\n this.flowLevel === FlowLevel.Knot\n ) {\n this._startingSubFlowDivert = new RuntimeDivert();\n container.AddContent(this._startingSubFlowDivert);\n this._startingSubFlowRuntime = childFlowRuntime;\n }\n\n // Check for duplicate knots/stitches with same name\n const namedChild = childFlowRuntime as RuntimeObject & INamedContent;\n const existingChild: INamedContent | null =\n container.namedContent.get(namedChild.name!) || null;\n\n if (existingChild) {\n const errorMsg = `${this.GetType()} already contains flow named '${\n namedChild.name\n }' (at ${(existingChild as any as RuntimeObject).debugMetadata})`;\n this.Error(errorMsg, childFlow);\n }\n\n container.AddToNamedContentOnly(namedChild);\n } else if (obj) {\n // Other content (including entire Weaves that were grouped in the constructor)\n // At the time of writing, all FlowBases have a maximum of one piece of \"other content\"\n // and it's always the root Weave\n container.AddContent(obj.runtimeObject);\n }\n\n contentIdx += 1;\n }\n\n // CHECK FOR FINAL LOOSE ENDS!\n // Notes:\n // - Functions don't need to terminate - they just implicitly return\n // - If return statement was found, don't continue finding warnings for missing control flow,\n // since it's likely that a return statement has been used instead of a ->-> or something,\n // or the writer failed to mark the knot as a function.\n // - _rootWeave may be null if it's a knot that only has stitches\n if (\n this.flowLevel !== FlowLevel.Story &&\n !this.isFunction &&\n this._rootWeave !== null &&\n foundReturn === null\n ) {\n this._rootWeave.ValidateTermination(this.WarningInTermination);\n }\n\n return container;\n };\n\n public readonly GenerateArgumentVariableAssignments = (\n container: RuntimeContainer\n ): void => {\n if (this.args === null || this.args.length === 0) {\n return;\n }\n\n // Assign parameters in reverse since they'll be popped off the evaluation stack\n // No need to generate EvalStart and EvalEnd since there's nothing being pushed\n // back onto the evaluation stack.\n for (let ii = this.args.length - 1; ii >= 0; --ii) {\n const paramName = this.args[ii].identifier?.name || null;\n const assign = new RuntimeVariableAssignment(paramName, true);\n container.AddContent(assign);\n }\n };\n\n public readonly ContentWithNameAtLevel = (\n name: string,\n level: FlowLevel | null = null,\n deepSearch: boolean = false\n ): ParsedObject | null => {\n // Referencing self?\n if (level === this.flowLevel || level === null) {\n if (name === this.identifier?.name) {\n return this;\n }\n }\n\n if (level === FlowLevel.WeavePoint || level === null) {\n let weavePointResult: ParsedObject | null = null;\n\n if (this._rootWeave) {\n weavePointResult = this._rootWeave.WeavePointNamed(\n name\n ) as ParsedObject;\n if (weavePointResult) {\n return weavePointResult;\n }\n }\n\n // Stop now if we only wanted a result if it's a weave point?\n if (level === FlowLevel.WeavePoint) {\n return deepSearch ? this.DeepSearchForAnyLevelContent(name) : null;\n }\n }\n\n // If this flow would be incapable of containing the requested level, early out\n // (e.g. asking for a Knot from a Stitch)\n if (level !== null && level < this.flowLevel) {\n return null;\n }\n\n let subFlow: FlowBase | null = this._subFlowsByName.get(name) || null;\n\n if (subFlow && (level === null || level === subFlow.flowLevel)) {\n return subFlow;\n }\n\n return deepSearch ? this.DeepSearchForAnyLevelContent(name) : null;\n };\n\n public readonly DeepSearchForAnyLevelContent = (name: string) => {\n const weaveResultSelf = this.ContentWithNameAtLevel(\n name,\n FlowLevel.WeavePoint,\n false\n );\n\n if (weaveResultSelf) {\n return weaveResultSelf;\n }\n\n for (const [, value] of this._subFlowsByName) {\n const deepResult = value.ContentWithNameAtLevel(name, null, true);\n\n if (deepResult) {\n return deepResult;\n }\n }\n\n return null;\n };\n\n public ResolveReferences(context: any): void {\n if (this._startingSubFlowDivert) {\n if (!this._startingSubFlowRuntime) {\n throw new Error();\n }\n\n this._startingSubFlowDivert.targetPath =\n this._startingSubFlowRuntime.path;\n }\n\n super.ResolveReferences(context);\n\n // Check validity of parameter names\n if (this.args !== null) {\n for (const arg of this.args) {\n context.CheckForNamingCollisions(\n this,\n arg.identifier,\n SymbolType.Arg,\n \"argument\"\n );\n }\n\n // Separately, check for duplicate arugment names, since they aren't Parsed.Objects,\n // so have to be checked independently.\n for (let ii = 0; ii < this.args.length; ii += 1) {\n for (let jj = ii + 1; jj < this.args.length; jj += 1) {\n if (\n this.args[ii].identifier?.name == this.args[jj].identifier?.name\n ) {\n this.Error(\n `Multiple arguments with the same name: '${this.args[ii].identifier}'`\n );\n }\n }\n }\n }\n\n // Check naming collisions for knots and stitches\n if (this.flowLevel !== FlowLevel.Story) {\n // Weave points aren't FlowBases, so this will only be knot or stitch\n const symbolType =\n this.flowLevel === FlowLevel.Knot\n ? SymbolType.Knot\n : SymbolType.SubFlowAndWeave;\n\n context.CheckForNamingCollisions(this, this.identifier, symbolType);\n }\n }\n\n public readonly CheckForDisallowedFunctionFlowControl = (): void => {\n // if (!(this instanceof Knot)) { // cannont use Knot here because of circular dependancy\n if (this.flowLevel !== FlowLevel.Knot) {\n this.Error(\n \"Functions cannot be stitches - i.e. they should be defined as '== function myFunc ==' rather than internal to another knot.\"\n );\n }\n\n // Not allowed sub-flows\n for (const [key, value] of this._subFlowsByName) {\n this.Error(\n `Functions may not contain stitches, but saw '${key}' within the function '${this.identifier}'`,\n value\n );\n }\n\n if (!this._rootWeave) {\n throw new Error();\n }\n\n const allDiverts = this._rootWeave.FindAll(Divert)();\n for (const divert of allDiverts) {\n if (!divert.isFunctionCall && !(divert.parent instanceof DivertTarget)) {\n this.Error(\n `Functions may not contain diverts, but saw '${divert}'`,\n divert\n );\n }\n }\n\n const allChoices = this._rootWeave.FindAll(Choice)();\n for (const choice of allChoices) {\n this.Error(\n `Functions may not contain choices, but saw '${choice}'`,\n choice\n );\n }\n };\n\n public readonly WarningInTermination = (terminatingObject: ParsedObject) => {\n let message: string =\n \"Apparent loose end exists where the flow runs out. Do you need a '-> DONE' statement, choice or divert?\";\n if (terminatingObject.parent === this._rootWeave && this._firstChildFlow) {\n message = `${message} Note that if you intend to enter '${this._firstChildFlow.identifier}' next, you need to divert to it explicitly.`;\n }\n\n const terminatingDivert = asOrNull(terminatingObject, Divert);\n if (terminatingDivert && terminatingDivert.isTunnel) {\n message += ` When final tunnel to '${terminatingDivert.target} ->' returns it won't have anywhere to go.`;\n }\n\n this.Warning(message, terminatingObject);\n };\n\n public readonly toString = (): string =>\n `${this.typeName} '${this.identifier}'`;\n}\n","import { Container as RuntimeContainer } from \"../../../engine/Container\";\nimport { ParsedObject } from \"./Object\";\nimport { InkObject as RuntimeObject } from \"../../../engine/Object\";\nimport { Text } from \"./Text\";\nimport { asOrNull } from \"../../../engine/TypeAssertion\";\n\nexport class ContentList extends ParsedObject {\n public dontFlatten: boolean = false;\n\n get runtimeContainer(): RuntimeContainer {\n return this.runtimeObject as RuntimeContainer;\n }\n\n constructor(objects?: ParsedObject[], ...moreObjects: ParsedObject[]) {\n super();\n\n if (objects) {\n this.AddContent(objects);\n }\n\n if (moreObjects) {\n this.AddContent(moreObjects);\n }\n }\n\n get typeName(): string {\n return \"ContentList\";\n }\n\n public readonly TrimTrailingWhitespace = (): void => {\n for (let ii = this.content.length - 1; ii >= 0; --ii) {\n const text = asOrNull(this.content[ii], Text);\n if (text === null) {\n break;\n }\n\n text.text = text.text.replace(new RegExp(/[ \\t]/g), \"\");\n if (text.text.length === 0) {\n this.content.splice(ii, 1);\n } else {\n break;\n }\n }\n };\n\n public readonly GenerateRuntimeObject = (): RuntimeObject => {\n const container = new RuntimeContainer();\n if (this.content !== null) {\n for (const obj of this.content) {\n const contentObjRuntime = obj.runtimeObject;\n\n // Some objects (e.g. author warnings) don't generate runtime objects\n if (contentObjRuntime) {\n container.AddContent(contentObjRuntime);\n }\n }\n }\n\n if (this.dontFlatten) {\n this.story.DontFlattenContainer(container);\n }\n\n return container;\n };\n\n public toString = (): string => `ContentList(${this.content.join(\", \")})`;\n}\n","import { InkObject } from \"./Object\";\nimport { Path } from \"./Path\";\n\nexport class VariableReference extends InkObject {\n public name: string | null;\n public pathForCount: Path | null = null;\n\n get containerForCount() {\n if (this.pathForCount === null) return null;\n return this.ResolvePath(this.pathForCount).container;\n }\n get pathStringForCount() {\n if (this.pathForCount === null) return null;\n\n return this.CompactPathString(this.pathForCount);\n }\n set pathStringForCount(value: string | null) {\n if (value === null) this.pathForCount = null;\n else this.pathForCount = new Path(value);\n }\n\n constructor(name: string | null = null) {\n super();\n this.name = name;\n }\n\n public toString() {\n if (this.name != null) {\n return \"var(\" + this.name + \")\";\n } else {\n let pathStr = this.pathStringForCount;\n return \"read_count(\" + pathStr + \")\";\n }\n }\n}\n","import { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { ContentList } from \"../ContentList\";\nimport { Expression } from \"../Expression/Expression\";\nimport { FlowBase } from \"../Flow/FlowBase\";\nimport { ParsedObject } from \"../Object\";\nimport { Path } from \"../Path\";\nimport { Story } from \"../Story\";\nimport { VariableReference as RuntimeVariableReference } from \"../../../../engine/VariableReference\";\nimport { Weave } from \"../Weave\";\nimport { Identifier } from \"../Identifier\";\nimport { asOrNull, filterUndef } from \"../../../../engine/TypeAssertion\";\n\nexport class VariableReference extends Expression {\n private _runtimeVarRef: RuntimeVariableReference | null = null;\n\n // - Normal variables have a single item in their \"path\"\n // - Knot/stitch names for read counts are actual dot-separated paths\n // (though this isn't actually used at time of writing)\n // - List names are dot separated: listName.itemName (or just itemName)\n get name() {\n return this.path.join(\".\");\n }\n\n get path(): string[] {\n return this.pathIdentifiers.map((id) => id.name!).filter(filterUndef);\n }\n\n get identifier(): Identifier | null {\n if (!this.pathIdentifiers || this.pathIdentifiers.length == 0) {\n return null;\n }\n const name = this.path.join(\".\");\n const id = new Identifier(name);\n\n return id;\n }\n\n // Only known after GenerateIntoContainer has run\n public isConstantReference: boolean = false;\n public isListItemReference: boolean = false;\n\n get runtimeVarRef() {\n return this._runtimeVarRef;\n }\n\n constructor(public readonly pathIdentifiers: Identifier[]) {\n super();\n }\n\n get typeName(): string {\n return \"ref\";\n }\n\n public readonly GenerateIntoContainer = (\n container: RuntimeContainer\n ): void => {\n let constantValue: Expression | null | undefined = this.story.constants.get(\n this.name\n );\n\n // If it's a constant reference, just generate the literal expression value\n // It's okay to access the constants at code generation time, since the\n // first thing the ExportRuntime function does it search for all the constants\n // in the story hierarchy, so they're all available.\n if (constantValue) {\n constantValue.GenerateConstantIntoContainer(container);\n this.isConstantReference = true;\n\n return;\n }\n\n this._runtimeVarRef = new RuntimeVariableReference(this.name);\n\n // List item reference?\n // Path might be to a list (listName.listItemName or just listItemName)\n if (this.path.length === 1 || this.path.length === 2) {\n let listItemName: string = \"\";\n let listName: string = \"\";\n\n if (this.path.length === 1) {\n listItemName = this.path[0];\n } else {\n listName = this.path[0];\n listItemName = this.path[1];\n }\n\n const listItem = this.story.ResolveListItem(listName, listItemName, this);\n\n if (listItem) {\n this.isListItemReference = true;\n }\n }\n\n container.AddContent(this._runtimeVarRef);\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n\n // Work is already done if it's a constant or list item reference\n if (this.isConstantReference || this.isListItemReference) {\n return;\n }\n\n // Is it a read count?\n const parsedPath = new Path(this.pathIdentifiers);\n const targetForCount: ParsedObject | null =\n parsedPath.ResolveFromContext(this);\n if (targetForCount) {\n if (!targetForCount.containerForCounting) {\n throw new Error();\n }\n\n targetForCount.containerForCounting.visitsShouldBeCounted = true;\n\n // If this is an argument to a function that wants a variable to be\n // passed by reference, then the Parsed.Divert will have generated a\n // Runtime.VariablePointerValue instead of allowing this object\n // to generate its RuntimeVariableReference. This only happens under\n // error condition since we shouldn't be passing a read count by\n // reference, but we don't want it to crash!\n if (this._runtimeVarRef === null) {\n return;\n }\n\n this._runtimeVarRef.pathForCount = targetForCount.runtimePath;\n this._runtimeVarRef.name = null;\n\n // Check for very specific writer error: getting read count and\n // printing it as content rather than as a piece of logic\n // e.g. Writing {myFunc} instead of {myFunc()}\n let targetFlow = asOrNull(targetForCount, FlowBase);\n if (targetFlow && targetFlow.isFunction) {\n // Is parent context content rather than logic?\n if (\n this.parent instanceof Weave ||\n this.parent instanceof ContentList ||\n this.parent instanceof FlowBase\n ) {\n this.Warning(\n `'${targetFlow.identifier}' being used as read count rather than being called as function. Perhaps you intended to write ${targetFlow.identifier}()`\n );\n }\n }\n\n return;\n }\n\n // Couldn't find this multi-part path at all, whether as a divert\n // target or as a list item reference.\n if (this.path.length > 1) {\n let errorMsg = `Could not find target for read count: ${parsedPath}`;\n if (this.path.length <= 2) {\n errorMsg += `, or couldn't find list item with the name ${this.path.join(\n \",\"\n )}`;\n }\n\n this.Error(errorMsg);\n\n return;\n }\n\n if (!context.ResolveVariableWithName(this.name, this).found) {\n this.Error(`Unresolved variable: ${this.name}`, this);\n }\n }\n\n public readonly toString = (): string => `{${this.path.join(\".\")}}`;\n}\n","import { Container as RuntimeContainer } from \"../../../engine/Container\";\nimport { ControlCommand as RuntimeControlCommand } from \"../../../engine/ControlCommand\";\nimport { Divert } from \"./Divert/Divert\";\nimport { Divert as RuntimeDivert } from \"../../../engine/Divert\";\nimport { DivertTarget } from \"./Divert/DivertTarget\";\nimport { Expression } from \"./Expression/Expression\";\nimport { InkList as RuntimeInkList } from \"../../../engine/InkList\";\nimport { ListValue } from \"../../../engine/Value\";\nimport { NativeFunctionCall } from \"../../../engine/NativeFunctionCall\";\nimport { NumberExpression } from \"./Expression/NumberExpression\";\nimport { Path } from \"./Path\";\nimport { Story } from \"./Story\";\nimport { StringValue } from \"../../../engine/Value\";\nimport { VariableReference } from \"./Variable/VariableReference\";\nimport { Identifier } from \"./Identifier\";\nimport { asOrNull } from \"../../../engine/TypeAssertion\";\n\nexport class FunctionCall extends Expression {\n public static readonly IsBuiltIn = (name: string): boolean => {\n if (NativeFunctionCall.CallExistsWithName(name)) {\n return true;\n }\n\n return (\n name === \"CHOICE_COUNT\" ||\n name === \"TURNS_SINCE\" ||\n name === \"TURNS\" ||\n name === \"RANDOM\" ||\n name === \"SEED_RANDOM\" ||\n name === \"LIST_VALUE\" ||\n name === \"LIST_RANDOM\" ||\n name === \"READ_COUNT\"\n );\n };\n\n private _proxyDivert: Divert;\n get proxyDivert(): Divert {\n return this._proxyDivert;\n }\n private _divertTargetToCount: DivertTarget | null = null;\n private _variableReferenceToCount: VariableReference | null = null;\n\n get name(): string {\n return (this._proxyDivert.target as Path).firstComponent || \"\";\n }\n\n get args(): Expression[] {\n return this._proxyDivert.args;\n }\n\n get runtimeDivert(): RuntimeDivert {\n return this._proxyDivert.runtimeDivert;\n }\n\n get isChoiceCount(): boolean {\n return this.name === \"CHOICE_COUNT\";\n }\n\n get isTurns(): boolean {\n return this.name === \"TURNS\";\n }\n\n get isTurnsSince(): boolean {\n return this.name === \"TURNS_SINCE\";\n }\n\n get isRandom(): boolean {\n return this.name === \"RANDOM\";\n }\n\n get isSeedRandom(): boolean {\n return this.name === \"SEED_RANDOM\";\n }\n\n get isListRange(): boolean {\n return this.name === \"LIST_RANGE\";\n }\n\n get isListRandom(): boolean {\n return this.name === \"LIST_RANDOM\";\n }\n\n get isReadCount(): boolean {\n return this.name === \"READ_COUNT\";\n }\n\n public shouldPopReturnedValue: boolean = false;\n\n constructor(functionName: Identifier, args: Expression[]) {\n super();\n\n this._proxyDivert = new Divert(new Path(functionName), args);\n this._proxyDivert.isFunctionCall = true;\n this.AddContent(this._proxyDivert);\n }\n\n get typeName(): string {\n return \"FunctionCall\";\n }\n\n public readonly GenerateIntoContainer = (\n container: RuntimeContainer\n ): void => {\n const foundList = this.story.ResolveList(this.name);\n\n let usingProxyDivert: boolean = false;\n\n if (this.isChoiceCount) {\n if (this.args.length > 0) {\n this.Error(\"The CHOICE_COUNT() function shouldn't take any arguments\");\n }\n\n container.AddContent(RuntimeControlCommand.ChoiceCount());\n } else if (this.isTurns) {\n if (this.args.length > 0) {\n this.Error(\"The TURNS() function shouldn't take any arguments\");\n }\n\n container.AddContent(RuntimeControlCommand.Turns());\n } else if (this.isTurnsSince || this.isReadCount) {\n const divertTarget = asOrNull(this.args[0], DivertTarget);\n const variableDivertTarget = asOrNull(this.args[0], VariableReference);\n\n if (\n this.args.length !== 1 ||\n (divertTarget === null && variableDivertTarget === null)\n ) {\n this.Error(\n `The ${this.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)`\n );\n return;\n }\n\n if (divertTarget) {\n this._divertTargetToCount = divertTarget;\n this.AddContent(this._divertTargetToCount);\n\n this._divertTargetToCount.GenerateIntoContainer(container);\n } else if (variableDivertTarget) {\n this._variableReferenceToCount = variableDivertTarget;\n this.AddContent(this._variableReferenceToCount);\n\n this._variableReferenceToCount.GenerateIntoContainer(container);\n }\n\n if (this.isTurnsSince) {\n container.AddContent(RuntimeControlCommand.TurnsSince());\n } else {\n container.AddContent(RuntimeControlCommand.ReadCount());\n }\n } else if (this.isRandom) {\n if (this.args.length !== 2) {\n this.Error(\n \"RANDOM should take 2 parameters: a minimum and a maximum integer\"\n );\n }\n\n // We can type check single values, but not complex expressions\n for (let ii = 0; ii < this.args.length; ii += 1) {\n const num = asOrNull(this.args[ii], NumberExpression);\n if (num && !num.isInt()) {\n const paramName: string = ii === 0 ? \"minimum\" : \"maximum\";\n this.Error(`RANDOM's ${paramName} parameter should be an integer`);\n }\n\n this.args[ii].GenerateIntoContainer(container);\n }\n\n container.AddContent(RuntimeControlCommand.Random());\n } else if (this.isSeedRandom) {\n if (this.args.length !== 1) {\n this.Error(\"SEED_RANDOM should take 1 parameter - an integer seed\");\n }\n\n const num = asOrNull(this.args[0], NumberExpression);\n if (num && !num.isInt()) {\n this.Error(\"SEED_RANDOM's parameter should be an integer seed\");\n }\n\n this.args[0].GenerateIntoContainer(container);\n\n container.AddContent(RuntimeControlCommand.SeedRandom());\n } else if (this.isListRange) {\n if (this.args.length !== 3) {\n this.Error(\n \"LIST_RANGE should take 3 parameters - a list, a min and a max\"\n );\n }\n\n for (let ii = 0; ii < this.args.length; ii += 1) {\n this.args[ii].GenerateIntoContainer(container);\n }\n\n container.AddContent(RuntimeControlCommand.ListRange());\n } else if (this.isListRandom) {\n if (this.args.length !== 1) {\n this.Error(\"LIST_RANDOM should take 1 parameter - a list\");\n }\n\n this.args[0].GenerateIntoContainer(container);\n\n container.AddContent(RuntimeControlCommand.ListRandom());\n } else if (NativeFunctionCall.CallExistsWithName(this.name)) {\n const nativeCall = NativeFunctionCall.CallWithName(this.name);\n if (nativeCall.numberOfParameters !== this.args.length) {\n let msg = `${FunctionCall.name} should take ${nativeCall.numberOfParameters} parameter`;\n if (nativeCall.numberOfParameters > 1) {\n msg += \"s\";\n }\n\n this.Error(msg);\n }\n\n for (let ii = 0; ii < this.args.length; ii += 1) {\n this.args[ii].GenerateIntoContainer(container);\n }\n\n container.AddContent(NativeFunctionCall.CallWithName(this.name));\n } else if (foundList !== null) {\n if (this.args.length > 1) {\n this.Error(\n \"Can currently only construct a list from one integer (or an empty list from a given list definition)\"\n );\n }\n\n // List item from given int\n if (this.args.length === 1) {\n container.AddContent(new StringValue(this.name));\n this.args[0].GenerateIntoContainer(container);\n container.AddContent(RuntimeControlCommand.ListFromInt());\n } else {\n // Empty list with given origin.\n const list = new RuntimeInkList();\n list.SetInitialOriginName(this.name);\n container.AddContent(new ListValue(list));\n }\n } else {\n // Normal function call\n container.AddContent(this._proxyDivert.runtimeObject);\n usingProxyDivert = true;\n }\n\n // Don't attempt to resolve as a divert if we're not doing a normal function call\n if (!usingProxyDivert) {\n this.content.splice(this.content.indexOf(this._proxyDivert), 1);\n }\n\n // Function calls that are used alone on a tilda-based line:\n // ~ func()\n // Should tidy up any returned value from the evaluation stack,\n // since it's unused.\n if (this.shouldPopReturnedValue) {\n container.AddContent(RuntimeControlCommand.PopEvaluatedValue());\n }\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n\n // If we aren't using the proxy divert after all (e.g. if\n // it's a native function call), but we still have arguments,\n // we need to make sure they get resolved since the proxy divert\n // is no longer in the content array.\n if (!this.content.includes(this._proxyDivert) && this.args !== null) {\n for (const arg of this.args) {\n arg.ResolveReferences(context);\n }\n }\n\n if (this._divertTargetToCount) {\n const divert = this._divertTargetToCount.divert;\n const attemptingTurnCountOfVariableTarget =\n divert.runtimeDivert.variableDivertName != null;\n\n if (attemptingTurnCountOfVariableTarget) {\n this.Error(\n `When getting the TURNS_SINCE() of a variable target, remove the '->' - i.e. it should just be TURNS_SINCE(${divert.runtimeDivert.variableDivertName})`\n );\n\n return;\n }\n\n const targetObject = divert.targetContent;\n if (targetObject === null) {\n if (!attemptingTurnCountOfVariableTarget) {\n this.Error(\n `Failed to find target for TURNS_SINCE: '${divert.target}'`\n );\n }\n } else {\n if (!targetObject.containerForCounting) {\n throw new Error();\n }\n\n targetObject.containerForCounting.turnIndexShouldBeCounted = true;\n }\n } else if (this._variableReferenceToCount) {\n const runtimeVarRef = this._variableReferenceToCount.runtimeVarRef;\n if (!runtimeVarRef) {\n throw new Error();\n }\n\n if (runtimeVarRef.pathForCount !== null) {\n this.Error(\n `Should be '${FunctionCall.name}'(-> '${this._variableReferenceToCount.name}). Usage without the '->' only makes sense for variable targets.`\n );\n }\n }\n }\n\n public readonly toString = (): string => {\n const strArgs = this.args.join(\", \");\n return `${this.name}(${strArgs})`;\n };\n}\n","import { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { Expression } from \"./Expression\";\nimport { NativeFunctionCall } from \"../../../../engine/NativeFunctionCall\";\n\nexport class MultipleConditionExpression extends Expression {\n get subExpressions(): Expression[] {\n return this.content as Expression[];\n }\n\n constructor(conditionExpressions: Expression[]) {\n super();\n\n this.AddContent(conditionExpressions);\n }\n\n get typeName(): string {\n return \"MultipleConditionExpression\";\n }\n\n public readonly GenerateIntoContainer = (\n container: RuntimeContainer\n ): void => {\n // A && B && C && D\n // => (((A B &&) C &&) D &&) etc\n let isFirst: boolean = true;\n for (const conditionExpr of this.subExpressions) {\n conditionExpr.GenerateIntoContainer(container);\n\n if (!isFirst) {\n container.AddContent(NativeFunctionCall.CallWithName(\"&&\"));\n }\n\n isFirst = false;\n }\n };\n}\n","import { BinaryExpression } from \"../Expression/BinaryExpression\";\nimport { Choice } from \"../Choice\";\nimport { Conditional } from \"../Conditional/Conditional\";\nimport { ConditionalSingleBranch } from \"../Conditional/ConditionalSingleBranch\";\nimport { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { ParsedObject } from \"../Object\";\nimport { Divert } from \"./Divert\";\nimport { Divert as RuntimeDivert } from \"../../../../engine/Divert\";\nimport { DivertTargetValue } from \"../../../../engine/Value\";\nimport { Expression } from \"../Expression/Expression\";\nimport { FlowBase } from \"../Flow/FlowBase\";\nimport { FunctionCall } from \"../FunctionCall\";\nimport { MultipleConditionExpression } from \"../Expression/MultipleConditionExpression\";\nimport { Story } from \"../Story\";\nimport { VariableReference } from \"../Variable/VariableReference\";\nimport { asOrNull } from \"../../../../engine/TypeAssertion\";\n\nexport class DivertTarget extends Expression {\n private _runtimeDivert: RuntimeDivert | null = null;\n get runtimeDivert(): RuntimeDivert {\n if (!this._runtimeDivert) {\n throw new Error();\n }\n\n return this._runtimeDivert;\n }\n\n private _runtimeDivertTargetValue: DivertTargetValue | null = null;\n get runtimeDivertTargetValue(): DivertTargetValue {\n if (!this._runtimeDivertTargetValue) {\n throw new Error();\n }\n\n return this._runtimeDivertTargetValue;\n }\n\n public divert: Divert;\n\n constructor(divert: Divert) {\n super();\n\n this.divert = this.AddContent(divert) as Divert;\n }\n\n get typeName(): string {\n return \"DivertTarget\";\n }\n\n public readonly GenerateIntoContainer = (\n container: RuntimeContainer\n ): void => {\n this.divert.GenerateRuntimeObject();\n\n this._runtimeDivert = this.divert.runtimeDivert as RuntimeDivert;\n this._runtimeDivertTargetValue = new DivertTargetValue();\n\n container.AddContent(this.runtimeDivertTargetValue);\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n\n if (this.divert.isDone || this.divert.isEnd) {\n this.Error(\n `Can't use -> DONE or -> END as variable divert targets`,\n this\n );\n\n return;\n }\n\n let usageContext: ParsedObject | null = this;\n while (usageContext && usageContext instanceof Expression) {\n let badUsage: boolean = false;\n let foundUsage: boolean = false;\n\n const usageParent: any = (usageContext as Expression).parent;\n if (usageParent instanceof BinaryExpression) {\n // Only allowed to compare for equality\n\n const binaryExprParent = usageParent;\n if (\n binaryExprParent.opName !== \"==\" &&\n binaryExprParent.opName !== \"!=\"\n ) {\n badUsage = true;\n } else {\n if (\n !(\n binaryExprParent.leftExpression instanceof DivertTarget ||\n binaryExprParent.leftExpression instanceof VariableReference\n )\n ) {\n badUsage = true;\n } else if (\n !(\n binaryExprParent.rightExpression instanceof DivertTarget ||\n binaryExprParent.rightExpression instanceof VariableReference\n )\n ) {\n badUsage = true;\n }\n }\n\n foundUsage = true;\n } else if (usageParent instanceof FunctionCall) {\n const funcCall = usageParent;\n if (!funcCall.isTurnsSince && !funcCall.isReadCount) {\n badUsage = true;\n }\n\n foundUsage = true;\n } else if (usageParent instanceof Expression) {\n badUsage = true;\n foundUsage = true;\n } else if (usageParent instanceof MultipleConditionExpression) {\n badUsage = true;\n foundUsage = true;\n } else if (\n usageParent instanceof Choice &&\n (usageParent as Choice).condition === usageContext\n ) {\n badUsage = true;\n foundUsage = true;\n } else if (\n usageParent instanceof Conditional ||\n usageParent instanceof ConditionalSingleBranch\n ) {\n badUsage = true;\n foundUsage = true;\n }\n\n if (badUsage) {\n this.Error(\n `Can't use a divert target like that. Did you intend to call '${this.divert.target}' as a function: likeThis(), or check the read count: likeThis, with no arrows?`,\n this\n );\n }\n\n if (foundUsage) {\n break;\n }\n\n usageContext = usageParent;\n }\n\n // Example ink for this case:\n //\n // VAR x = -> blah\n //\n // ...which means that \"blah\" is expected to be a literal stitch target rather\n // than a variable name. We can't really intelligently recover from this (e.g. if blah happens to\n // contain a divert target itself) since really we should be generating a variable reference\n // rather than a concrete DivertTarget, so we list it as an error.\n if (this.runtimeDivert.hasVariableTarget) {\n if (!this.divert.target) {\n throw new Error();\n }\n\n this.Error(\n `Since '${this.divert.target.dotSeparatedComponents}' is a variable, it shouldn't be preceded by '->' here.`\n );\n }\n\n // Main resolve\n this.runtimeDivert.targetPath &&\n (this.runtimeDivertTargetValue.targetPath =\n this.runtimeDivert.targetPath);\n\n // Tell hard coded (yet variable) divert targets that they also need to be counted\n // TODO: Only detect DivertTargets that are values rather than being used directly for\n // read or turn counts. Should be able to detect this by looking for other uses of containerForCounting\n let targetContent = this.divert.targetContent;\n if (targetContent !== null) {\n let target = targetContent.containerForCounting;\n if (target !== null) {\n // Purpose is known: used directly in TURNS_SINCE(-> divTarg)\n const parentFunc = asOrNull(this.parent, FunctionCall);\n if (parentFunc && parentFunc.isTurnsSince) {\n target.turnIndexShouldBeCounted = true;\n } else {\n // Unknown purpose, count everything\n target.visitsShouldBeCounted = true;\n target.turnIndexShouldBeCounted = true;\n }\n }\n\n // Unfortunately not possible:\n // https://github.com/inkle/ink/issues/538\n //\n // VAR func = -> double\n //\n // === function double(ref x)\n // ~ x = x * 2\n //\n // Because when generating the parameters for a function\n // to be called, it needs to know ahead of time when\n // compiling whether to pass a variable reference or value.\n //\n let targetFlow = asOrNull(targetContent, FlowBase);\n if (targetFlow != null && targetFlow.args !== null) {\n for (const arg of targetFlow.args) {\n if (arg.isByReference) {\n this.Error(\n `Can't store a divert target to a knot or function that has by-reference arguments ('${targetFlow.identifier}' has 'ref ${arg.identifier}').`\n );\n }\n }\n }\n }\n }\n\n // Equals override necessary in order to check for CONST multiple definition equality\n public readonly Equals = (obj: ParsedObject): boolean => {\n const otherDivTarget = asOrNull(obj, DivertTarget);\n if (\n !otherDivTarget ||\n !this.divert.target ||\n !otherDivTarget.divert.target\n ) {\n return false;\n }\n\n const targetStr = this.divert.target.dotSeparatedComponents;\n const otherTargetStr = otherDivTarget.divert.target.dotSeparatedComponents;\n\n return targetStr === otherTargetStr;\n };\n}\n","import { Argument } from \"../Argument\";\nimport { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { ControlCommand as RuntimeControlCommand } from \"../../../../engine/ControlCommand\";\nimport { Divert as RuntimeDivert } from \"../../../../engine/Divert\";\nimport { DivertTarget } from \"./DivertTarget\";\nimport { Expression } from \"../Expression/Expression\";\nimport { FlowBase } from \"../Flow/FlowBase\";\nimport { FunctionCall } from \"../FunctionCall\";\nimport { ParsedObject } from \"../Object\";\nimport { Path } from \"../Path\";\nimport { Path as RuntimePath } from \"../../../../engine/Path\";\nimport { PushPopType } from \"../../../../engine/PushPop\";\nimport { Story } from \"../Story\";\nimport { VariablePointerValue } from \"../../../../engine/Value\";\nimport { VariableReference } from \"../Variable/VariableReference\";\nimport { ClosestFlowBase } from \"../Flow/ClosestFlowBase\";\nimport { asOrNull } from \"../../../../engine/TypeAssertion\";\n\nexport class Divert extends ParsedObject {\n public readonly args: Expression[] = [];\n\n public readonly target: Path | null = null;\n public targetContent: ParsedObject | null = null;\n private _runtimeDivert: RuntimeDivert | null = null;\n get runtimeDivert(): RuntimeDivert {\n if (!this._runtimeDivert) {\n throw new Error();\n }\n\n return this._runtimeDivert;\n }\n\n set runtimeDivert(value: RuntimeDivert) {\n this._runtimeDivert = value;\n }\n\n public isFunctionCall: boolean = false;\n public isEmpty: boolean = false;\n public isTunnel: boolean = false;\n public isThread: boolean = false;\n\n get isEnd(): boolean {\n return Boolean(this.target && this.target.dotSeparatedComponents === \"END\");\n }\n\n get isDone(): boolean {\n return Boolean(\n this.target && this.target.dotSeparatedComponents === \"DONE\"\n );\n }\n\n constructor(target?: Path | null | undefined, args?: Expression[]) {\n super();\n\n if (target) {\n this.target = target;\n }\n\n if (args) {\n this.args = args;\n this.AddContent(args);\n }\n }\n\n get typeName(): string {\n return \"Divert\";\n }\n\n public readonly GenerateRuntimeObject = () => {\n // End = end flow immediately\n // Done = return from thread or instruct the flow that it's safe to exit\n if (this.isEnd) {\n return RuntimeControlCommand.End();\n } else if (this.isDone) {\n return RuntimeControlCommand.Done();\n }\n\n this.runtimeDivert = new RuntimeDivert();\n\n // Normally we resolve the target content during the\n // Resolve phase, since we expect all runtime objects to\n // be available in order to find the final runtime path for\n // the destination. However, we need to resolve the target\n // (albeit without the runtime target) early so that\n // we can get information about the arguments - whether\n // they're by reference - since it affects the code we\n // generate here.\n this.ResolveTargetContent();\n\n this.CheckArgumentValidity();\n\n // Passing arguments to the knot\n const requiresArgCodeGen = this.args !== null && this.args.length > 0;\n if (\n requiresArgCodeGen ||\n this.isFunctionCall ||\n this.isTunnel ||\n this.isThread\n ) {\n const container = new RuntimeContainer();\n\n // Generate code for argument evaluation\n // This argument generation is coded defensively - it should\n // attempt to generate the code for all the parameters, even if\n // they don't match the expected arguments. This is so that the\n // parameter objects themselves are generated correctly and don't\n // get into a state of attempting to resolve references etc\n // without being generated.\n if (requiresArgCodeGen) {\n // Function calls already in an evaluation context\n if (!this.isFunctionCall) {\n container.AddContent(RuntimeControlCommand.EvalStart());\n }\n\n let targetArguments: Argument[] | null = null;\n if (this.targetContent) {\n targetArguments = (this.targetContent as FlowBase).args;\n }\n\n for (let ii = 0; ii < this.args.length; ++ii) {\n const argToPass: Expression = this.args[ii];\n let argExpected: Argument | null = null;\n if (targetArguments && ii < targetArguments.length) {\n argExpected = targetArguments[ii];\n }\n\n // Pass by reference: argument needs to be a variable reference\n if (argExpected && argExpected.isByReference) {\n const varRef = asOrNull(argToPass, VariableReference);\n if (!varRef) {\n this.Error(\n `Expected variable name to pass by reference to 'ref ${argExpected.identifier}' but saw ${argToPass}`\n );\n\n break;\n }\n\n // Check that we're not attempting to pass a read count by reference\n const targetPath = new Path(varRef.pathIdentifiers);\n const targetForCount: ParsedObject | null =\n targetPath.ResolveFromContext(this);\n if (targetForCount) {\n this.Error(\n `can't pass a read count by reference. '${\n targetPath.dotSeparatedComponents\n }' is a knot/stitch/label, but '${\n this.target!.dotSeparatedComponents\n }' requires the name of a VAR to be passed.`\n );\n\n break;\n }\n\n const varPointer = new VariablePointerValue(varRef.name);\n container.AddContent(varPointer);\n } else {\n // Normal value being passed: evaluate it as normal\n argToPass.GenerateIntoContainer(container);\n }\n }\n\n // Function calls were already in an evaluation context\n if (!this.isFunctionCall) {\n container.AddContent(RuntimeControlCommand.EvalEnd());\n }\n }\n\n // Starting a thread? A bit like a push to the call stack below... but not.\n // It sort of puts the call stack on a thread stack (argh!) - forks the full flow.\n if (this.isThread) {\n container.AddContent(RuntimeControlCommand.StartThread());\n } else if (this.isFunctionCall || this.isTunnel) {\n // If this divert is a function call, tunnel, we push to the call stack\n // so we can return again\n this.runtimeDivert.pushesToStack = true;\n this.runtimeDivert.stackPushType = this.isFunctionCall\n ? PushPopType.Function\n : PushPopType.Tunnel;\n }\n\n // Jump into the \"function\" (knot/stitch)\n container.AddContent(this.runtimeDivert);\n\n return container;\n }\n\n // Simple divert\n return this.runtimeDivert;\n };\n\n // When the divert is to a target that's actually a variable name\n // rather than an explicit knot/stitch name, try interpretting it\n // as such by getting the variable name.\n public readonly PathAsVariableName = () =>\n this.target ? this.target.firstComponent : null;\n\n public readonly ResolveTargetContent = (): void => {\n if (this.isEmpty || this.isEnd) {\n return;\n }\n\n if (this.targetContent === null) {\n // Is target of this divert a variable name that will be de-referenced\n // at runtime? If so, there won't be any further reference resolution\n // we can do at this point.\n let variableTargetName = this.PathAsVariableName();\n if (variableTargetName !== null) {\n const flowBaseScope = asOrNull(ClosestFlowBase(this), FlowBase);\n if (flowBaseScope) {\n const resolveResult = flowBaseScope.ResolveVariableWithName(\n variableTargetName,\n this\n );\n\n if (resolveResult.found) {\n // Make sure that the flow was typed correctly, given that we know that this\n // is meant to be a divert target\n if (\n resolveResult.isArgument &&\n resolveResult.ownerFlow &&\n resolveResult.ownerFlow.args\n ) {\n let argument = resolveResult.ownerFlow.args.find(\n (a) => a.identifier?.name == variableTargetName\n );\n\n if (argument && !argument.isDivertTarget) {\n this.Error(\n `Since '${argument.identifier}' is used as a variable divert target (on ${this.debugMetadata}), it should be marked as: -> ${argument.identifier}`,\n resolveResult.ownerFlow\n );\n }\n }\n\n this.runtimeDivert.variableDivertName = variableTargetName;\n return;\n }\n }\n }\n\n if (!this.target) {\n throw new Error();\n }\n\n this.targetContent = this.target.ResolveFromContext(this);\n }\n };\n\n public ResolveReferences(context: Story): void {\n if (this.isEmpty || this.isEnd || this.isDone) {\n return;\n } else if (!this.runtimeDivert) {\n throw new Error();\n }\n\n if (this.targetContent) {\n this.runtimeDivert.targetPath = this.targetContent.runtimePath;\n }\n\n // Resolve children (the arguments)\n super.ResolveReferences(context);\n\n // May be null if it's a built in function (e.g. TURNS_SINCE)\n // or if it's a variable target.\n let targetFlow = asOrNull(this.targetContent, FlowBase);\n if (targetFlow) {\n if (!targetFlow.isFunction && this.isFunctionCall) {\n super.Error(\n `${targetFlow.identifier} hasn't been marked as a function, but it's being called as one. Do you need to declare the knot as '== function ${targetFlow.identifier} =='?`\n );\n } else if (\n targetFlow.isFunction &&\n !this.isFunctionCall &&\n !(this.parent instanceof DivertTarget)\n ) {\n super.Error(\n targetFlow.identifier +\n \" can't be diverted to. It can only be called as a function since it's been marked as such: '\" +\n targetFlow.identifier +\n \"(...)'\"\n );\n }\n }\n\n // Check validity of target content\n const targetWasFound = this.targetContent !== null;\n let isBuiltIn: boolean = false;\n let isExternal: boolean = false;\n\n if (!this.target) {\n throw new Error();\n } else if (this.target.numberOfComponents === 1) {\n if (!this.target.firstComponent) {\n throw new Error();\n }\n\n // BuiltIn means TURNS_SINCE, CHOICE_COUNT, RANDOM or SEED_RANDOM\n isBuiltIn = FunctionCall.IsBuiltIn(this.target.firstComponent);\n\n // Client-bound function?\n isExternal = context.IsExternal(this.target.firstComponent);\n\n if (isBuiltIn || isExternal) {\n if (!this.isFunctionCall) {\n super.Error(\n `${this.target.firstComponent} must be called as a function: ~ ${this.target.firstComponent}()`\n );\n }\n\n if (isExternal) {\n this.runtimeDivert.isExternal = true;\n if (this.args !== null) {\n this.runtimeDivert.externalArgs = this.args.length;\n }\n\n this.runtimeDivert.pushesToStack = false;\n this.runtimeDivert.targetPath = new RuntimePath(\n this.target.firstComponent\n );\n\n this.CheckExternalArgumentValidity(context);\n }\n\n return;\n }\n }\n\n // Variable target?\n if (this.runtimeDivert.variableDivertName != null) {\n return;\n }\n\n if (!targetWasFound && !isBuiltIn && !isExternal) {\n this.Error(`target not found: '${this.target}'`);\n }\n }\n\n // Returns false if there's an error\n public readonly CheckArgumentValidity = (): void => {\n if (this.isEmpty) {\n return;\n }\n\n // Argument passing: Check for errors in number of arguments\n let numArgs = 0;\n if (this.args !== null && this.args.length > 0) {\n numArgs = this.args.length;\n }\n\n // Missing content?\n // Can't check arguments properly. It'll be due to some\n // other error though, so although there's a problem and\n // we report false, we don't need to report a specific error.\n // It may also be because it's a valid call to an external\n // function, that we check at the resolve stage.\n if (this.targetContent === null) {\n return;\n }\n\n const targetFlow = asOrNull(this.targetContent, FlowBase);\n\n // No error, crikey!\n if (numArgs === 0 && (targetFlow === null || !targetFlow.hasParameters)) {\n return;\n } else if (targetFlow === null && numArgs > 0) {\n this.Error(\n \"target needs to be a knot or stitch in order to pass arguments\"\n );\n return;\n } else if (\n targetFlow !== null &&\n (targetFlow.args === null || (!targetFlow.args && numArgs > 0))\n ) {\n this.Error(`target (${targetFlow.name}) doesn't take parameters`);\n return;\n } else if (this.parent instanceof DivertTarget) {\n if (numArgs > 0) {\n this.Error(`can't store arguments in a divert target variable`);\n }\n\n return;\n }\n\n const paramCount = targetFlow!.args!.length;\n if (paramCount !== numArgs) {\n let butClause: string;\n if (numArgs === 0) {\n butClause = \"but there weren't any passed to it\";\n } else if (numArgs < paramCount) {\n butClause = `but only got ${numArgs}`;\n } else {\n butClause = `but got ${numArgs}`;\n }\n\n this.Error(\n `to '${\n targetFlow!.identifier\n }' requires ${paramCount} arguments, ${butClause}`\n );\n\n return;\n }\n\n // Light type-checking for divert target arguments\n for (let ii = 0; ii < paramCount; ++ii) {\n const flowArg: Argument = targetFlow!.args![ii];\n const divArgExpr: Expression = this.args[ii];\n\n // Expecting a divert target as an argument, let's do some basic type checking\n if (flowArg.isDivertTarget) {\n // Not passing a divert target or any kind of variable reference?\n let varRef = asOrNull(divArgExpr, VariableReference);\n if (!(divArgExpr instanceof DivertTarget) && varRef === null) {\n this.Error(\n `Target '${\n targetFlow!.identifier\n }' expects a divert target for the parameter named -> ${\n flowArg.identifier\n } but saw ${divArgExpr}`,\n divArgExpr\n );\n } else if (varRef) {\n // Passing 'a' instead of '-> a'?\n // i.e. read count instead of divert target\n // Unfortunately have to manually resolve here since we're still in code gen\n const knotCountPath = new Path(varRef.pathIdentifiers);\n const targetForCount: ParsedObject | null =\n knotCountPath.ResolveFromContext(varRef);\n if (targetForCount) {\n this.Error(\n `Passing read count of '${knotCountPath.dotSeparatedComponents}' instead of a divert target. You probably meant '${knotCountPath}'`\n );\n }\n }\n }\n }\n\n if (targetFlow === null) {\n this.Error(\n \"Can't call as a function or with arguments unless it's a knot or stitch\"\n );\n return;\n }\n\n return;\n };\n\n public readonly CheckExternalArgumentValidity = (context: Story): void => {\n const externalName: string | null = this.target\n ? this.target.firstComponent\n : null;\n const external = context.externals.get(externalName as string);\n if (!external) {\n throw new Error(\"external not found\");\n }\n\n const externalArgCount: number = external.argumentNames.length;\n let ownArgCount = 0;\n if (this.args) {\n ownArgCount = this.args.length;\n }\n\n if (ownArgCount !== externalArgCount) {\n this.Error(\n `incorrect number of arguments sent to external function '${externalName}'. Expected ${externalArgCount} but got ${ownArgCount}`\n );\n }\n };\n\n public Error(\n message: string,\n source: ParsedObject | null = null,\n isWarning: boolean = false\n ): void {\n // Could be getting an error from a nested Divert\n if (source !== this && source) {\n super.Error(message, source);\n return;\n }\n\n if (this.isFunctionCall) {\n super.Error(`Function call ${message}`, source, isWarning);\n } else {\n super.Error(`Divert ${message}`, source, isWarning);\n }\n }\n\n public toString = (): string => {\n let returnString = \"\";\n if (this.target !== null) {\n returnString += this.target.toString();\n } else {\n return \"-> \";\n }\n\n if (this.isTunnel) {\n returnString += \" ->\";\n }\n if (this.isFunctionCall) {\n returnString += \" ()\";\n }\n\n return returnString;\n };\n}\n","import { Divert as RuntimeDivert } from \"../../../../engine/Divert\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\n\nexport class GatherPointToResolve {\n constructor(\n public divert: RuntimeDivert,\n public targetRuntimeObj: RuntimeObject\n ) {}\n}\n","import { Divert as RuntimeDivert } from \"../../../../engine/Divert\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\n\nexport class SequenceDivertToResolve {\n constructor(\n public divert: RuntimeDivert,\n public targetContent: RuntimeObject\n ) {}\n}\n","export enum SequenceType {\n Stopping = 1, // default\n Cycle = 2,\n Shuffle = 4,\n Once = 8,\n}\n","import { ContentList } from \"../ContentList\";\nimport { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { ControlCommand as RuntimeControlCommand } from \"../../../../engine/ControlCommand\";\nimport { Divert as RuntimeDivert } from \"../../../../engine/Divert\";\nimport { IntValue } from \"../../../../engine/Value\";\nimport { NativeFunctionCall } from \"../../../../engine/NativeFunctionCall\";\nimport { ParsedObject } from \"../Object\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\nimport { SequenceDivertToResolve } from \"./SequenceDivertToResolve\";\nimport { SequenceType } from \"./SequenceType\";\nimport { Story } from \"../Story\";\nimport { Weave } from \"../Weave\";\n\nexport class Sequence extends ParsedObject {\n private _sequenceDivertsToResolve: SequenceDivertToResolve[] = [];\n\n public sequenceElements: ParsedObject[];\n\n constructor(\n elementContentLists: ContentList[],\n public readonly sequenceType: SequenceType\n ) {\n super();\n\n this.sequenceType = sequenceType;\n this.sequenceElements = [];\n\n for (const elementContentList of elementContentLists) {\n const contentObjs = elementContentList.content;\n let seqElObject: ParsedObject | null = null;\n\n // Don't attempt to create a weave for the sequence element\n // if the content list is empty. Weaves don't like it!\n if (contentObjs === null || contentObjs.length === 0) {\n seqElObject = elementContentList;\n } else {\n seqElObject = new Weave(contentObjs);\n }\n\n this.sequenceElements.push(seqElObject);\n this.AddContent(seqElObject);\n }\n }\n\n get typeName(): string {\n return \"Sequence\";\n }\n\n // Generate runtime code that looks like:\n //\n // chosenIndex = MIN(sequence counter, num elements) e.g. for \"Stopping\"\n // if chosenIndex == 0, divert to s0\n // if chosenIndex == 1, divert to s1 [etc]\n //\n // - s0:\n // \n // divert to no-op\n // - s1:\n // \n // divert to no-op\n // - s2:\n // empty branch if using \"once\"\n // divert to no-op\n //\n // no-op\n //\n public readonly GenerateRuntimeObject = (): RuntimeObject => {\n const container = new RuntimeContainer();\n container.visitsShouldBeCounted = true;\n container.countingAtStartOnly = true;\n\n this._sequenceDivertsToResolve = [];\n\n // Get sequence read count\n container.AddContent(RuntimeControlCommand.EvalStart());\n container.AddContent(RuntimeControlCommand.VisitIndex());\n\n const once: boolean = (this.sequenceType & SequenceType.Once) > 0;\n const cycle: boolean = (this.sequenceType & SequenceType.Cycle) > 0;\n const stopping: boolean = (this.sequenceType & SequenceType.Stopping) > 0;\n const shuffle: boolean = (this.sequenceType & SequenceType.Shuffle) > 0;\n\n let seqBranchCount = this.sequenceElements.length;\n if (once) {\n seqBranchCount += 1;\n }\n\n // Chosen sequence index:\n // - Stopping: take the MIN(read count, num elements - 1)\n // - Once: take the MIN(read count, num elements)\n // (the last one being empty)\n if (stopping || once) {\n //var limit = stopping ? seqBranchCount-1 : seqBranchCount;\n container.AddContent(new IntValue(seqBranchCount - 1));\n container.AddContent(NativeFunctionCall.CallWithName(\"MIN\"));\n } else if (cycle) {\n // - Cycle: take (read count % num elements)\n container.AddContent(new IntValue(this.sequenceElements.length));\n container.AddContent(NativeFunctionCall.CallWithName(\"%\"));\n }\n\n // Shuffle\n if (shuffle) {\n // Create point to return to when sequence is complete\n const postShuffleNoOp = RuntimeControlCommand.NoOp();\n\n // When visitIndex == lastIdx, we skip the shuffle\n if (once || stopping) {\n // if( visitIndex == lastIdx ) -> skipShuffle\n const lastIdx = stopping\n ? this.sequenceElements.length - 1\n : this.sequenceElements.length;\n\n container.AddContent(RuntimeControlCommand.Duplicate());\n container.AddContent(new IntValue(lastIdx));\n container.AddContent(NativeFunctionCall.CallWithName(\"==\"));\n\n const skipShuffleDivert = new RuntimeDivert();\n skipShuffleDivert.isConditional = true;\n container.AddContent(skipShuffleDivert);\n\n this.AddDivertToResolve(skipShuffleDivert, postShuffleNoOp);\n }\n\n // This one's a bit more complex! Choose the index at runtime.\n let elementCountToShuffle = this.sequenceElements.length;\n if (stopping) {\n elementCountToShuffle -= 1;\n }\n\n container.AddContent(new IntValue(elementCountToShuffle));\n container.AddContent(RuntimeControlCommand.SequenceShuffleIndex());\n if (once || stopping) {\n container.AddContent(postShuffleNoOp);\n }\n }\n\n container.AddContent(RuntimeControlCommand.EvalEnd());\n\n // Create point to return to when sequence is complete\n const postSequenceNoOp = RuntimeControlCommand.NoOp();\n\n // Each of the main sequence branches, and one extra empty branch if\n // we have a \"once\" sequence.\n for (let elIndex = 0; elIndex < seqBranchCount; elIndex += 1) {\n // This sequence element:\n // if( chosenIndex == this index ) divert to this sequence element\n // duplicate chosen sequence index, since it'll be consumed by \"==\"\n container.AddContent(RuntimeControlCommand.EvalStart());\n container.AddContent(RuntimeControlCommand.Duplicate());\n container.AddContent(new IntValue(elIndex));\n container.AddContent(NativeFunctionCall.CallWithName(\"==\"));\n container.AddContent(RuntimeControlCommand.EvalEnd());\n\n // Divert branch for this sequence element\n const sequenceDivert = new RuntimeDivert();\n sequenceDivert.isConditional = true;\n container.AddContent(sequenceDivert);\n\n let contentContainerForSequenceBranch: RuntimeContainer;\n\n // Generate content for this sequence element\n if (elIndex < this.sequenceElements.length) {\n const el = this.sequenceElements[elIndex];\n contentContainerForSequenceBranch =\n el.runtimeObject as RuntimeContainer;\n } else {\n // Final empty branch for \"once\" sequences\n contentContainerForSequenceBranch = new RuntimeContainer();\n }\n\n contentContainerForSequenceBranch.name = `s${elIndex}`;\n contentContainerForSequenceBranch.InsertContent(\n RuntimeControlCommand.PopEvaluatedValue(),\n 0\n );\n\n // When sequence element is complete, divert back to end of sequence\n const seqBranchCompleteDivert = new RuntimeDivert();\n contentContainerForSequenceBranch.AddContent(seqBranchCompleteDivert);\n container.AddToNamedContentOnly(contentContainerForSequenceBranch);\n\n // Save the diverts for reference resolution later (in ResolveReferences)\n this.AddDivertToResolve(\n sequenceDivert,\n contentContainerForSequenceBranch\n );\n this.AddDivertToResolve(seqBranchCompleteDivert, postSequenceNoOp);\n }\n\n container.AddContent(postSequenceNoOp);\n\n return container;\n };\n\n public readonly AddDivertToResolve = (\n divert: RuntimeDivert,\n targetContent: RuntimeObject\n ) => {\n this._sequenceDivertsToResolve.push(\n new SequenceDivertToResolve(divert, targetContent)\n );\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n\n for (const toResolve of this._sequenceDivertsToResolve) {\n toResolve.divert.targetPath = toResolve.targetContent.path;\n }\n }\n}\n","import { Container as RuntimeContainer } from \"../../../engine/Container\";\nimport { ControlCommand as RuntimeControlCommand } from \"../../../engine/ControlCommand\";\nimport { Divert } from \"./Divert/Divert\";\nimport { Divert as RuntimeDivert } from \"../../../engine/Divert\";\nimport { DivertTargetValue } from \"../../../engine/Value\";\nimport { ParsedObject } from \"./Object\";\nimport { InkObject as RuntimeObject } from \"../../../engine/Object\";\nimport { Story } from \"./Story\";\nimport { Void } from \"../../../engine/Void\";\nimport { asOrNull } from \"../../../engine/TypeAssertion\";\nimport { VariableReference } from \"../../../engine/VariableReference\";\n\nexport class TunnelOnwards extends ParsedObject {\n private _overrideDivertTarget: DivertTargetValue | null = null;\n\n private _divertAfter: Divert | null = null;\n get divertAfter() {\n return this._divertAfter;\n }\n\n set divertAfter(value) {\n this._divertAfter = value;\n if (this._divertAfter) {\n this.AddContent(this._divertAfter);\n }\n }\n\n get typeName(): string {\n return \"TunnelOnwards\";\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject => {\n const container = new RuntimeContainer();\n\n // Set override path for tunnel onwards (or nothing)\n container.AddContent(RuntimeControlCommand.EvalStart());\n\n if (this.divertAfter) {\n // Generate runtime object's generated code and steal the arguments runtime code\n const returnRuntimeObj = this.divertAfter.GenerateRuntimeObject();\n const returnRuntimeContainer = returnRuntimeObj as RuntimeContainer;\n if (returnRuntimeContainer) {\n // Steal all code for generating arguments from the divert\n const args = this.divertAfter.args;\n if (args !== null && args.length > 0) {\n // Steal everything betwen eval start and eval end\n let evalStart = -1;\n let evalEnd = -1;\n for (\n let ii = 0;\n ii < returnRuntimeContainer.content.length;\n ii += 1\n ) {\n const cmd = returnRuntimeContainer.content[\n ii\n ] as RuntimeControlCommand;\n if (cmd) {\n if (\n evalStart == -1 &&\n cmd.commandType === RuntimeControlCommand.CommandType.EvalStart\n ) {\n evalStart = ii;\n } else if (\n cmd.commandType === RuntimeControlCommand.CommandType.EvalEnd\n ) {\n evalEnd = ii;\n }\n }\n }\n\n for (let ii = evalStart + 1; ii < evalEnd; ii += 1) {\n const obj = returnRuntimeContainer.content[ii];\n obj.parent = null; // prevent error of being moved between owners\n container.AddContent(returnRuntimeContainer.content[ii]);\n }\n }\n }\n // Supply the divert target for the tunnel onwards target, either variable or more commonly, the explicit name\n // var returnDivertObj = returnRuntimeObj as Runtime.Divert;\n let returnDivertObj = asOrNull(returnRuntimeObj, RuntimeDivert);\n if (returnDivertObj != null && returnDivertObj.hasVariableTarget) {\n let runtimeVarRef = new VariableReference(\n returnDivertObj.variableDivertName\n );\n container.AddContent(runtimeVarRef);\n } else {\n this._overrideDivertTarget = new DivertTargetValue();\n container.AddContent(this._overrideDivertTarget);\n }\n } else {\n // No divert after tunnel onwards\n container.AddContent(new Void());\n }\n\n container.AddContent(RuntimeControlCommand.EvalEnd());\n container.AddContent(RuntimeControlCommand.PopTunnel());\n\n return container;\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n\n if (this.divertAfter && this.divertAfter.targetContent) {\n this._overrideDivertTarget!.targetPath =\n this.divertAfter.targetContent.runtimePath;\n }\n }\n\n public toString = (): string => {\n return ` -> ${this._divertAfter}`;\n };\n}\n","import { InkListItem, SerializedInkListItem } from \"./InkList\";\nimport { TryGetResult } from \"./TryGetResult\";\n\nexport class ListDefinition {\n public _name: string;\n public _items: Map | null;\n public _itemNameToValues: Map;\n\n constructor(name: string, items: Map | null) {\n this._name = name || \"\";\n this._items = null;\n this._itemNameToValues = items || new Map();\n }\n get name() {\n return this._name;\n }\n get items() {\n if (this._items == null) {\n this._items = new Map();\n for (let [key, value] of this._itemNameToValues) {\n let item = new InkListItem(this.name, key);\n this._items.set(item.serialized(), value);\n }\n }\n\n return this._items;\n }\n\n public ValueForItem(item: InkListItem) {\n if (!item.itemName) return 0;\n\n let intVal = this._itemNameToValues.get(item.itemName);\n if (typeof intVal !== \"undefined\") return intVal;\n else return 0;\n }\n public ContainsItem(item: InkListItem) {\n if (!item.itemName) return false;\n if (item.originName != this.name) return false;\n\n return this._itemNameToValues.has(item.itemName);\n }\n public ContainsItemWithName(itemName: string) {\n return this._itemNameToValues.has(itemName);\n }\n public TryGetItemWithValue(\n val: number,\n /* out */ item: InkListItem\n ): TryGetResult {\n for (let [key, value] of this._itemNameToValues) {\n if (value == val) {\n item = new InkListItem(this.name, key);\n return { result: item, exists: true };\n }\n }\n\n item = InkListItem.Null;\n return { result: item, exists: false };\n }\n\n public TryGetValueForItem(\n item: InkListItem,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n /* out */ intVal: number\n ): TryGetResult {\n if (!item.itemName) return { result: 0, exists: false };\n let value = this._itemNameToValues.get(item.itemName);\n\n if (!value) return { result: 0, exists: false };\n return { result: value, exists: true };\n }\n}\n","import { InkList as RuntimeInkList } from \"../../../../engine/InkList\";\nimport { InkListItem as RuntimeInkListItem } from \"../../../../engine/InkList\";\nimport { ListDefinition as RuntimeListDefinition } from \"../../../../engine/ListDefinition\";\nimport { ListElementDefinition } from \"./ListElementDefinition\";\nimport { ListValue } from \"../../../../engine/Value\";\nimport { ParsedObject } from \"../Object\";\nimport { Story } from \"../Story\";\nimport { SymbolType } from \"../SymbolType\";\nimport { VariableAssignment } from \"../Variable/VariableAssignment\";\nimport { Identifier } from \"../Identifier\";\n\nexport class ListDefinition extends ParsedObject {\n public identifier: Identifier | null = null;\n public variableAssignment: VariableAssignment | null = null;\n\n get typeName() {\n return \"ListDefinition\";\n }\n\n private _elementsByName: Map | null = null;\n\n get runtimeListDefinition(): RuntimeListDefinition {\n const allItems: Map = new Map();\n for (const e of this.itemDefinitions) {\n if (!allItems.has(e.name!)) {\n allItems.set(e.name!, e.seriesValue);\n } else {\n this.Error(\n `List '${this.identifier}' contains duplicate items called '${e.name}'`\n );\n }\n }\n\n return new RuntimeListDefinition(this.identifier?.name || \"\", allItems);\n }\n\n public readonly ItemNamed = (\n itemName: string\n ): ListElementDefinition | null => {\n if (this._elementsByName === null) {\n this._elementsByName = new Map();\n\n for (const el of this.itemDefinitions) {\n this._elementsByName.set(el.name!, el);\n }\n }\n\n const foundElement = this._elementsByName.get(itemName) || null;\n\n return foundElement;\n };\n\n constructor(public itemDefinitions: ListElementDefinition[]) {\n super();\n\n let currentValue = 1;\n for (const e of this.itemDefinitions) {\n if (e.explicitValue !== null) {\n currentValue = e.explicitValue;\n }\n\n e.seriesValue = currentValue;\n\n currentValue += 1;\n }\n\n this.AddContent(itemDefinitions as any);\n }\n\n public readonly GenerateRuntimeObject = (): ListValue => {\n const initialValues = new RuntimeInkList();\n for (const itemDef of this.itemDefinitions) {\n if (itemDef.inInitialList) {\n const item = new RuntimeInkListItem(\n this.identifier?.name || null,\n itemDef.name || null\n );\n initialValues.Add(item, itemDef.seriesValue);\n }\n }\n\n // Set origin name, so\n initialValues.SetInitialOriginName(this.identifier?.name || \"\");\n\n return new ListValue(initialValues);\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n context.CheckForNamingCollisions(this, this.identifier!, SymbolType.List);\n }\n}\n","import { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { Expression } from \"../Expression/Expression\";\nimport { FlowBase } from \"../Flow/FlowBase\";\nimport { ClosestFlowBase } from \"../Flow/ClosestFlowBase\";\nimport { ListDefinition } from \"../List/ListDefinition\";\nimport { ParsedObject } from \"../Object\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\nimport { Story } from \"../Story\";\nimport { SymbolType } from \"../SymbolType\";\nimport { VariableAssignment as RuntimeVariableAssignment } from \"../../../../engine/VariableAssignment\";\nimport { VariableReference } from \"./VariableReference\";\nimport { Identifier } from \"../Identifier\";\nimport { asOrNull } from \"../../../../engine/TypeAssertion\";\n\nexport class VariableAssignment extends ParsedObject {\n private _runtimeAssignment: RuntimeVariableAssignment | null = null;\n\n get variableName(): string {\n return this.variableIdentifier.name!;\n }\n public readonly variableIdentifier: Identifier;\n public readonly expression: Expression | null = null;\n public readonly listDefinition: ListDefinition | null = null;\n public readonly isGlobalDeclaration: boolean;\n public readonly isNewTemporaryDeclaration: boolean;\n\n get typeName() {\n if (this.isNewTemporaryDeclaration) {\n return \"temp\";\n } else if (this.isGlobalDeclaration) {\n if (this.listDefinition !== null) {\n return \"LIST\";\n }\n return \"VAR\";\n }\n\n return \"variable assignment\";\n }\n\n get isDeclaration(): boolean {\n return this.isGlobalDeclaration || this.isNewTemporaryDeclaration;\n }\n\n constructor({\n assignedExpression,\n isGlobalDeclaration,\n isTemporaryNewDeclaration,\n listDef,\n variableIdentifier,\n }: {\n readonly assignedExpression?: Expression;\n readonly isGlobalDeclaration?: boolean;\n readonly isTemporaryNewDeclaration?: boolean;\n readonly listDef?: ListDefinition;\n readonly variableIdentifier: Identifier;\n }) {\n super();\n\n this.variableIdentifier = variableIdentifier;\n this.isGlobalDeclaration = Boolean(isGlobalDeclaration);\n this.isNewTemporaryDeclaration = Boolean(isTemporaryNewDeclaration);\n\n // Defensive programming in case parsing of assignedExpression failed\n if (listDef instanceof ListDefinition) {\n this.listDefinition = this.AddContent(listDef) as ListDefinition;\n this.listDefinition.variableAssignment = this;\n\n // List definitions are always global\n this.isGlobalDeclaration = true;\n } else if (assignedExpression) {\n this.expression = this.AddContent(assignedExpression) as Expression;\n }\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject | null => {\n let newDeclScope: FlowBase | null | undefined = null;\n if (this.isGlobalDeclaration) {\n newDeclScope = this.story;\n } else if (this.isNewTemporaryDeclaration) {\n newDeclScope = ClosestFlowBase(this);\n }\n\n if (newDeclScope) {\n newDeclScope.AddNewVariableDeclaration(this);\n }\n\n // Global declarations don't generate actual procedural\n // runtime objects, but instead add a global variable to the story itself.\n // The story then initialises them all in one go at the start of the game.\n if (this.isGlobalDeclaration) {\n return null;\n }\n\n const container = new RuntimeContainer();\n\n // The expression's runtimeObject is actually another nested container\n if (this.expression) {\n container.AddContent(this.expression.runtimeObject);\n } else if (this.listDefinition) {\n container.AddContent(this.listDefinition.runtimeObject);\n }\n\n this._runtimeAssignment = new RuntimeVariableAssignment(\n this.variableName,\n this.isNewTemporaryDeclaration\n );\n\n container.AddContent(this._runtimeAssignment);\n\n return container;\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n\n // List definitions are checked for conflicts separately\n if (this.isDeclaration && this.listDefinition === null) {\n context.CheckForNamingCollisions(\n this,\n this.variableIdentifier,\n this.isGlobalDeclaration ? SymbolType.Var : SymbolType.Temp\n );\n }\n\n // Initial VAR x = [intialValue] declaration, not re-assignment\n if (this.isGlobalDeclaration) {\n const variableReference = asOrNull(this.expression, VariableReference);\n if (\n variableReference &&\n !variableReference.isConstantReference &&\n !variableReference.isListItemReference\n ) {\n this.Error(\n \"global variable assignments cannot refer to other variables, only literal values, constants and list items\"\n );\n }\n }\n\n if (!this.isNewTemporaryDeclaration) {\n const resolvedVarAssignment = context.ResolveVariableWithName(\n this.variableName,\n this\n );\n\n if (!resolvedVarAssignment.found) {\n if (this.variableName in this.story.constants) {\n this.Error(\n `Can't re-assign to a constant (do you need to use VAR when declaring '${this.variableName}'?)`,\n this\n );\n } else {\n this.Error(\n `Variable could not be found to assign to: '${this.variableName}'`,\n this\n );\n }\n }\n\n // A runtime assignment may not have been generated if it's the initial global declaration,\n // since these are hoisted out and handled specially in Story.ExportRuntime.\n if (this._runtimeAssignment) {\n this._runtimeAssignment.isGlobal = resolvedVarAssignment.isGlobal;\n }\n }\n }\n\n public readonly toString = (): string =>\n `${\n this.isGlobalDeclaration\n ? \"VAR\"\n : this.isNewTemporaryDeclaration\n ? \"~ temp\"\n : \"\"\n } ${this.variableName}`;\n}\n","import { AuthorWarning } from \"./AuthorWarning\";\nimport { Choice } from \"./Choice\";\nimport { Conditional } from \"./Conditional/Conditional\";\nimport { ConstantDeclaration } from \"./Declaration/ConstantDeclaration\";\nimport { Container as RuntimeContainer } from \"../../../engine/Container\";\nimport { Divert } from \"./Divert/Divert\";\nimport { Divert as RuntimeDivert } from \"../../../engine/Divert\";\nimport { DivertTarget } from \"./Divert/DivertTarget\";\nimport { FlowBase } from \"./Flow/FlowBase\";\nimport { Gather } from \"./Gather/Gather\";\nimport { GatherPointToResolve } from \"./Gather/GatherPointToResolve\";\nimport { IWeavePoint } from \"./IWeavePoint\";\nimport { ParsedObject } from \"./Object\";\nimport { InkObject as RuntimeObject } from \"../../../engine/Object\";\nimport { Sequence } from \"./Sequence/Sequence\";\nimport { Story } from \"./Story\";\nimport { Text } from \"./Text\";\nimport { TunnelOnwards } from \"./TunnelOnwards\";\nimport { VariableAssignment } from \"./Variable/VariableAssignment\";\nimport { asOrNull } from \"../../../engine/TypeAssertion\";\n\ntype BadTerminationHandler = (terminatingObj: ParsedObject) => void;\n\n// Used by the FlowBase when constructing the weave flow from\n// a flat list of content objects.\nexport class Weave extends ParsedObject {\n // Containers can be chained as multiple gather points\n // get created as the same indentation level.\n // rootContainer is always the first in the chain, while\n // currentContainer is the latest.\n get rootContainer(): RuntimeContainer {\n if (!this._rootContainer) {\n this._rootContainer = this.GenerateRuntimeObject();\n }\n\n return this._rootContainer;\n }\n\n // Keep track of previous weave point (Choice or Gather)\n // at the current indentation level:\n // - to add ordinary content to be nested under it\n // - to add nested content under it when it's indented\n // - to remove it from the list of loose ends when\n // - it has indented content since it's no longer a loose end\n // - it's a gather and it has a choice added to it\n public previousWeavePoint: IWeavePoint | null = null;\n public addContentToPreviousWeavePoint: boolean = false;\n\n // Used for determining whether the next Gather should auto-enter\n public hasSeenChoiceInSection: boolean = false;\n\n public currentContainer: RuntimeContainer | null = null;\n public baseIndentIndex: number;\n\n private _unnamedGatherCount: number = 0;\n private _choiceCount: number = 0;\n private _rootContainer: RuntimeContainer | null = null;\n private _namedWeavePoints: Map = new Map();\n get namedWeavePoints() {\n return this._namedWeavePoints;\n }\n\n // Loose ends are:\n // - Choices or Gathers that need to be joined up\n // - Explicit Divert to gather points (i.e. \"->\" without a target)\n public looseEnds: IWeavePoint[] = [];\n\n public gatherPointsToResolve: GatherPointToResolve[] = [];\n\n get lastParsedSignificantObject(): ParsedObject | null {\n if (this.content.length === 0) {\n return null;\n }\n\n // Don't count extraneous newlines or VAR/CONST declarations,\n // since they're \"empty\" statements outside of the main flow.\n let lastObject: ParsedObject | null = null;\n for (let ii = this.content.length - 1; ii >= 0; --ii) {\n lastObject = this.content[ii];\n\n let lastText = asOrNull(lastObject, Text);\n if (lastText && lastText.text === \"\\n\") {\n continue;\n }\n\n if (this.IsGlobalDeclaration(lastObject)) {\n continue;\n }\n\n break;\n }\n\n const lastWeave = asOrNull(lastObject, Weave);\n if (lastWeave) {\n lastObject = lastWeave.lastParsedSignificantObject;\n }\n\n return lastObject;\n }\n\n constructor(cont: ParsedObject[], indentIndex: number = -1) {\n super();\n\n if (indentIndex == -1) {\n this.baseIndentIndex = this.DetermineBaseIndentationFromContent(cont);\n } else {\n this.baseIndentIndex = indentIndex;\n }\n\n this.AddContent(cont);\n\n this.ConstructWeaveHierarchyFromIndentation();\n }\n\n get typeName(): string {\n return \"Weave\";\n }\n\n public readonly ResolveWeavePointNaming = (): void => {\n const namedWeavePoints = [\n ...this.FindAll(Gather)(\n (w) => !(w.name === null || w.name === undefined)\n ),\n ...this.FindAll(Choice)(\n (w) => !(w.name === null || w.name === undefined)\n ),\n ];\n this._namedWeavePoints = new Map();\n\n for (const weavePoint of namedWeavePoints) {\n // Check for weave point naming collisions\n const existingWeavePoint: IWeavePoint | null | undefined =\n this.namedWeavePoints.get(weavePoint.identifier?.name || \"\");\n\n if (existingWeavePoint) {\n const typeName =\n existingWeavePoint instanceof Gather ? \"gather\" : \"choice\";\n const existingObj: ParsedObject = existingWeavePoint;\n\n this.Error(\n `A ${typeName} with the same label name '${\n weavePoint.name\n }' already exists in this context on line ${\n existingObj.debugMetadata\n ? existingObj.debugMetadata.startLineNumber\n : \"NO DEBUG METADATA AVAILABLE\"\n }`,\n weavePoint as ParsedObject\n );\n }\n if (weavePoint.identifier?.name) {\n this.namedWeavePoints.set(weavePoint.identifier?.name, weavePoint);\n }\n }\n };\n\n public readonly ConstructWeaveHierarchyFromIndentation = (): void => {\n // Find nested indentation and convert to a proper object hierarchy\n // (i.e. indented content is replaced with a Weave object that contains\n // that nested content)\n let contentIdx = 0;\n while (contentIdx < this.content.length) {\n const obj: ParsedObject = this.content[contentIdx];\n\n // Choice or Gather\n if (obj instanceof Choice || obj instanceof Gather) {\n const weavePoint: IWeavePoint = obj;\n const weaveIndentIdx = weavePoint.indentationDepth - 1;\n\n // Inner level indentation - recurse\n if (weaveIndentIdx > this.baseIndentIndex) {\n // Step through content until indent jumps out again\n let innerWeaveStartIdx = contentIdx;\n while (contentIdx < this.content.length) {\n const innerWeaveObj =\n asOrNull(this.content[contentIdx], Choice) ||\n asOrNull(this.content[contentIdx], Gather);\n if (innerWeaveObj !== null) {\n const innerIndentIdx = innerWeaveObj.indentationDepth - 1;\n if (innerIndentIdx <= this.baseIndentIndex) {\n break;\n }\n }\n\n contentIdx += 1;\n }\n\n const weaveContentCount = contentIdx - innerWeaveStartIdx;\n const weaveContent = this.content.slice(\n innerWeaveStartIdx,\n innerWeaveStartIdx + weaveContentCount\n );\n\n this.content.splice(innerWeaveStartIdx, weaveContentCount);\n\n const weave = new Weave(weaveContent, weaveIndentIdx);\n this.InsertContent(innerWeaveStartIdx, weave);\n\n // Continue iteration from this point\n contentIdx = innerWeaveStartIdx;\n }\n }\n\n contentIdx += 1;\n }\n };\n\n // When the indentation wasn't told to us at construction time using\n // a choice point with a known indentation level, we may be told to\n // determine the indentation level by incrementing from our closest ancestor.\n public readonly DetermineBaseIndentationFromContent = (\n contentList: ParsedObject[]\n ): number => {\n for (const obj of contentList) {\n if (obj instanceof Choice || obj instanceof Gather) {\n return obj.indentationDepth - 1;\n }\n }\n\n // No weave points, so it doesn't matter\n return 0;\n };\n\n public readonly GenerateRuntimeObject = (): RuntimeContainer => {\n this._rootContainer = new RuntimeContainer();\n this.currentContainer = this._rootContainer;\n this.looseEnds = [];\n this.gatherPointsToResolve = [];\n\n // Iterate through content for the block at this level of indentation\n // - Normal content is nested under Choices and Gathers\n // - Blocks that are further indented cause recursion\n // - Keep track of loose ends so that they can be diverted to Gathers\n for (const obj of this.content) {\n // Choice or Gather\n if (obj instanceof Choice || obj instanceof Gather) {\n this.AddRuntimeForWeavePoint(obj as IWeavePoint);\n } else {\n // Non-weave point\n if (obj instanceof Weave) {\n // Nested weave\n const weave = obj;\n this.AddRuntimeForNestedWeave(weave);\n this.gatherPointsToResolve.splice(\n 0,\n 0,\n ...weave.gatherPointsToResolve\n );\n } else {\n // Other object\n // May be complex object that contains statements - e.g. a multi-line conditional\n this.AddGeneralRuntimeContent(obj.runtimeObject);\n }\n }\n }\n\n // Pass any loose ends up the hierarhcy\n this.PassLooseEndsToAncestors();\n\n return this._rootContainer;\n };\n\n // Found gather point:\n // - gather any loose ends\n // - set the gather as the main container to dump new content in\n public readonly AddRuntimeForGather = (gather: Gather): void => {\n // Determine whether this Gather should be auto-entered:\n // - It is auto-entered if there were no choices in the last section\n // - A section is \"since the previous gather\" - so reset now\n const autoEnter = !this.hasSeenChoiceInSection;\n this.hasSeenChoiceInSection = false;\n\n const gatherContainer = gather.runtimeContainer;\n\n if (!gather.name) {\n // Use disallowed character so it's impossible to have a name collision\n gatherContainer.name = `g-${this._unnamedGatherCount}`;\n this._unnamedGatherCount += 1;\n }\n\n if (autoEnter) {\n if (!this.currentContainer) {\n throw new Error();\n }\n\n // Auto-enter: include in main content\n this.currentContainer.AddContent(gatherContainer);\n } else {\n // Don't auto-enter:\n // Add this gather to the main content, but only accessible\n // by name so that it isn't stepped into automatically, but only via\n // a divert from a loose end.\n this.rootContainer.AddToNamedContentOnly(gatherContainer);\n }\n\n // Consume loose ends: divert them to this gather\n for (const looseEndWeavePoint of this.looseEnds) {\n const looseEnd = looseEndWeavePoint as ParsedObject;\n\n // Skip gather loose ends that are at the same level\n // since they'll be handled by the auto-enter code below\n // that only jumps into the gather if (current runtime choices == 0)\n if (looseEnd instanceof Gather) {\n const prevGather = looseEnd;\n if (prevGather.indentationDepth == gather.indentationDepth) {\n continue;\n }\n }\n\n let divert: RuntimeDivert | null = null;\n if (looseEnd instanceof Divert) {\n divert = looseEnd.runtimeObject as RuntimeDivert;\n } else {\n divert = new RuntimeDivert();\n const looseWeavePoint = looseEnd as IWeavePoint;\n if (!looseWeavePoint.runtimeContainer) {\n throw new Error();\n }\n\n looseWeavePoint.runtimeContainer.AddContent(divert);\n }\n\n // Pass back knowledge of this loose end being diverted\n // to the FlowBase so that it can maintain a list of them,\n // and resolve the divert references later\n this.gatherPointsToResolve.push(\n new GatherPointToResolve(divert, gatherContainer)\n );\n }\n\n this.looseEnds = [];\n\n // Replace the current container itself\n this.currentContainer = gatherContainer;\n };\n\n public readonly AddRuntimeForWeavePoint = (weavePoint: IWeavePoint): void => {\n // Current level Gather\n if (weavePoint instanceof Gather) {\n this.AddRuntimeForGather(weavePoint);\n }\n\n // Current level choice\n else if (weavePoint instanceof Choice) {\n if (!this.currentContainer) {\n throw new Error();\n }\n\n // Gathers that contain choices are no longer loose ends\n // (same as when weave points get nested content)\n if (this.previousWeavePoint instanceof Gather) {\n this.looseEnds.splice(\n this.looseEnds.indexOf(this.previousWeavePoint),\n 1\n );\n }\n\n // Add choice point content\n const choice = weavePoint; //, Choice);\n\n this.currentContainer.AddContent(choice.runtimeObject);\n if (!choice.innerContentContainer) {\n throw new Error();\n } //guaranteed not to happen\n\n // Add choice's inner content to self\n choice.innerContentContainer.name = `c-${this._choiceCount}`;\n this.currentContainer.AddToNamedContentOnly(choice.innerContentContainer);\n this._choiceCount += 1;\n\n this.hasSeenChoiceInSection = true;\n }\n\n // Keep track of loose ends\n this.addContentToPreviousWeavePoint = false; // default\n if (this.WeavePointHasLooseEnd(weavePoint)) {\n this.looseEnds.push(weavePoint);\n\n const looseChoice = asOrNull(weavePoint, Choice);\n if (looseChoice) {\n this.addContentToPreviousWeavePoint = true;\n }\n }\n\n this.previousWeavePoint = weavePoint;\n };\n\n // Add nested block at a greater indentation level\n public readonly AddRuntimeForNestedWeave = (nestedResult: Weave): void => {\n // Add this inner block to current container\n // (i.e. within the main container, or within the last defined Choice/Gather)\n this.AddGeneralRuntimeContent(nestedResult.rootContainer);\n\n // Now there's a deeper indentation level, the previous weave point doesn't\n // count as a loose end (since it will have content to go to)\n if (this.previousWeavePoint !== null) {\n this.looseEnds.splice(this.looseEnds.indexOf(this.previousWeavePoint), 1);\n\n this.addContentToPreviousWeavePoint = false;\n }\n };\n\n // Normal content gets added into the latest Choice or Gather by default,\n // unless there hasn't been one yet.\n public readonly AddGeneralRuntimeContent = (content: RuntimeObject): void => {\n // Content is allowed to evaluate runtimeObject to null\n // (e.g. AuthorWarning, which doesn't make it into the runtime)\n if (content === null) {\n return;\n }\n\n if (this.addContentToPreviousWeavePoint) {\n if (\n !this.previousWeavePoint ||\n !this.previousWeavePoint.runtimeContainer\n ) {\n throw new Error();\n }\n\n this.previousWeavePoint.runtimeContainer.AddContent(content);\n } else {\n if (!this.currentContainer) {\n throw new Error();\n }\n\n this.currentContainer.AddContent(content);\n }\n };\n\n public readonly PassLooseEndsToAncestors = () => {\n if (this.looseEnds.length === 0) {\n return;\n }\n\n // Search for Weave ancestor to pass loose ends to for gathering.\n // There are two types depending on whether the current weave\n // is separated by a conditional or sequence.\n // - An \"inner\" weave is one that is directly connected to the current\n // weave - i.e. you don't have to pass through a conditional or\n // sequence to get to it. We're allowed to pass all loose ends to\n // one of these.\n // - An \"outer\" weave is one that is outside of a conditional/sequence\n // that the current weave is nested within. We're only allowed to\n // pass gathers (i.e. 'normal flow') loose ends up there, not normal\n // choices. The rule is that choices have to be diverted explicitly\n // by the author since it's ambiguous where flow should go otherwise.\n //\n // e.g.:\n //\n // - top <- e.g. outer weave\n // {true:\n // * choice <- e.g. inner weave\n // * * choice 2\n // more content <- e.g. current weave\n // * choice 2\n // }\n // - more of outer weave\n //\n let closestInnerWeaveAncestor: Weave | null = null;\n let closestOuterWeaveAncestor: Weave | null = null;\n\n // Find inner and outer ancestor weaves as defined above.\n let nested = false;\n for (\n let ancestor = this.parent;\n ancestor !== null;\n ancestor = ancestor.parent\n ) {\n // Found ancestor?\n const weaveAncestor = asOrNull(ancestor, Weave);\n if (weaveAncestor) {\n if (!nested && closestInnerWeaveAncestor === null) {\n closestInnerWeaveAncestor = weaveAncestor;\n }\n\n if (nested && closestOuterWeaveAncestor === null) {\n closestOuterWeaveAncestor = weaveAncestor;\n }\n }\n\n // Weaves nested within Sequences or Conditionals are\n // \"sealed\" - any loose ends require explicit diverts.\n if (ancestor instanceof Sequence || ancestor instanceof Conditional) {\n nested = true;\n }\n }\n\n // No weave to pass loose ends to at all?\n if (\n closestInnerWeaveAncestor === null &&\n closestOuterWeaveAncestor === null\n ) {\n return;\n }\n\n // Follow loose end passing logic as defined above\n for (let ii = this.looseEnds.length - 1; ii >= 0; ii -= 1) {\n const looseEnd = this.looseEnds[ii];\n let received = false;\n\n if (nested) {\n // This weave is nested within a conditional or sequence:\n // - choices can only be passed up to direct ancestor (\"inner\") weaves\n // - gathers can be passed up to either, but favour the closer (inner) weave\n // if there is one\n if (looseEnd instanceof Choice && closestInnerWeaveAncestor !== null) {\n closestInnerWeaveAncestor.ReceiveLooseEnd(looseEnd);\n received = true;\n } else if (!(looseEnd instanceof Choice)) {\n const receivingWeave =\n closestInnerWeaveAncestor || closestOuterWeaveAncestor;\n if (receivingWeave !== null) {\n receivingWeave.ReceiveLooseEnd(looseEnd);\n received = true;\n }\n }\n } else {\n // No nesting, all loose ends can be safely passed up\n if (closestInnerWeaveAncestor?.hasOwnProperty(\"ReceiveLooseEnd\")) {\n closestInnerWeaveAncestor!.ReceiveLooseEnd(looseEnd);\n }\n received = true;\n }\n\n if (received) {\n this.looseEnds.splice(ii, 1);\n }\n }\n };\n\n public readonly ReceiveLooseEnd = (childWeaveLooseEnd: IWeavePoint): void => {\n this.looseEnds.push(childWeaveLooseEnd);\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n\n // Check that choices nested within conditionals and sequences are terminated\n if (this.looseEnds !== null && this.looseEnds.length > 0) {\n let isNestedWeave = false;\n for (\n let ancestor = this.parent;\n ancestor !== null;\n ancestor = ancestor.parent\n ) {\n if (ancestor instanceof Sequence || ancestor instanceof Conditional) {\n isNestedWeave = true;\n break;\n }\n }\n\n if (isNestedWeave) {\n this.ValidateTermination(this.BadNestedTerminationHandler);\n }\n }\n\n for (const gatherPoint of this.gatherPointsToResolve) {\n gatherPoint.divert.targetPath = gatherPoint.targetRuntimeObj.path;\n }\n\n this.CheckForWeavePointNamingCollisions();\n }\n\n public readonly WeavePointNamed = (name: string): IWeavePoint | null => {\n if (!this.namedWeavePoints) {\n return null;\n }\n\n let weavePointResult: IWeavePoint | null | undefined =\n this.namedWeavePoints.get(name);\n if (weavePointResult) {\n return weavePointResult;\n }\n\n return null;\n };\n\n // Global VARs and CONSTs are treated as \"outside of the flow\"\n // when iterating over content that follows loose ends\n public readonly IsGlobalDeclaration = (obj: ParsedObject) => {\n const varAss = asOrNull(obj, VariableAssignment);\n if (varAss && varAss.isGlobalDeclaration && varAss.isDeclaration) {\n return true;\n }\n\n const constDecl = asOrNull(obj, ConstantDeclaration);\n if (constDecl) {\n return true;\n }\n\n return false;\n };\n\n // While analysing final loose ends, we look to see whether there\n // are any diverts etc which choices etc divert from\n public readonly ContentThatFollowsWeavePoint = (\n weavePoint: IWeavePoint\n ): ParsedObject[] => {\n const returned = [];\n const obj = weavePoint as ParsedObject;\n\n // Inner content first (e.g. for a choice)\n if (obj.content !== null) {\n for (const contentObj of obj.content) {\n // Global VARs and CONSTs are treated as \"outside of the flow\"\n if (this.IsGlobalDeclaration(contentObj)) {\n continue;\n }\n\n returned.push(contentObj);\n }\n }\n\n const parentWeave = asOrNull(obj.parent, Weave);\n if (parentWeave === null) {\n throw new Error(\"Expected weave point parent to be weave?\");\n }\n\n const weavePointIdx = parentWeave.content.indexOf(obj);\n for (let ii = weavePointIdx + 1; ii < parentWeave.content.length; ii += 1) {\n const laterObj = parentWeave.content[ii];\n\n // Global VARs and CONSTs are treated as \"outside of the flow\"\n if (this.IsGlobalDeclaration(laterObj)) {\n continue;\n }\n\n // End of the current flow\n // if (laterObj instanceof IWeavePoint) // cannot test on interface in ts\n if (laterObj instanceof Choice || laterObj instanceof Gather) {\n break;\n }\n\n // Other weaves will be have their own loose ends\n if (laterObj instanceof Weave) {\n break;\n }\n\n returned.push(laterObj);\n }\n\n return returned;\n };\n\n public readonly ValidateTermination = (\n badTerminationHandler: BadTerminationHandler\n ): void => {\n // Don't worry if the last object in the flow is a \"TODO\",\n // even if there are other loose ends in other places\n if (this.lastParsedSignificantObject instanceof AuthorWarning) {\n return;\n }\n\n // By now, any sub-weaves will have passed loose ends up to the root weave (this).\n // So there are 2 possible situations:\n // - There are loose ends from somewhere in the flow.\n // These aren't necessarily \"real\" loose ends - they're weave points\n // that don't connect to any lower weave points, so we just\n // have to check that they terminate properly.\n // - This weave is just a list of content with no actual weave points,\n // so we just need to check that the list of content terminates.\n\n const hasLooseEnds: boolean =\n this.looseEnds !== null && this.looseEnds.length > 0;\n\n if (hasLooseEnds) {\n for (const looseEnd of this.looseEnds) {\n const looseEndFlow = this.ContentThatFollowsWeavePoint(looseEnd);\n this.ValidateFlowOfObjectsTerminates(\n looseEndFlow,\n looseEnd as ParsedObject,\n badTerminationHandler\n );\n }\n } else {\n // No loose ends... is there any inner weaving at all?\n // If not, make sure the single content stream is terminated correctly\n //\n // If there's any actual weaving, assume that content is\n // terminated correctly since we would've had a loose end otherwise\n for (const obj of this.content) {\n if (obj instanceof Choice || obj instanceof Divert) {\n return;\n }\n }\n\n // Straight linear flow? Check it terminates\n this.ValidateFlowOfObjectsTerminates(\n this.content,\n this,\n badTerminationHandler\n );\n }\n };\n\n readonly BadNestedTerminationHandler: BadTerminationHandler = (\n terminatingObj\n ) => {\n let conditional: Conditional | null = null;\n for (\n let ancestor = terminatingObj.parent;\n ancestor !== null;\n ancestor = ancestor.parent\n ) {\n if (ancestor instanceof Sequence || ancestor instanceof Conditional) {\n conditional = asOrNull(ancestor, Conditional);\n break;\n }\n }\n\n let errorMsg =\n \"Choices nested in conditionals or sequences need to explicitly divert afterwards.\";\n\n // Tutorialise proper choice syntax if this looks like a single choice within a condition, e.g.\n // { condition:\n // * choice\n // }\n if (conditional !== null) {\n let numChoices = conditional.FindAll(Choice)().length;\n if (numChoices === 1) {\n errorMsg = `Choices with conditions should be written: '* {condition} choice'. Otherwise, ${errorMsg.toLowerCase()}`;\n }\n }\n\n this.Error(errorMsg, terminatingObj);\n };\n\n public readonly ValidateFlowOfObjectsTerminates = (\n objFlow: ParsedObject[],\n defaultObj: ParsedObject,\n badTerminationHandler: BadTerminationHandler\n ) => {\n let terminated = false;\n let terminatingObj: ParsedObject = defaultObj;\n for (const flowObj of objFlow) {\n const divert = flowObj.Find(Divert)(\n (d) =>\n !d.isThread &&\n !d.isTunnel &&\n !d.isFunctionCall &&\n !(d.parent instanceof DivertTarget)\n );\n\n if (divert !== null) {\n terminated = true;\n }\n\n if (flowObj.Find(TunnelOnwards)() != null) {\n terminated = true;\n break;\n }\n\n terminatingObj = flowObj;\n }\n\n if (!terminated) {\n // Author has left a note to self here - clearly we don't need\n // to leave them with another warning since they know what they're doing.\n if (terminatingObj instanceof AuthorWarning) {\n return;\n }\n\n badTerminationHandler(terminatingObj);\n }\n };\n\n public readonly WeavePointHasLooseEnd = (\n weavePoint: IWeavePoint\n ): boolean => {\n // No content, must be a loose end.\n if (weavePoint.content === null) {\n return true;\n }\n\n // If a weave point is diverted from, it doesn't have a loose end.\n // Detect a divert object within a weavePoint's main content\n // Work backwards since we're really interested in the end,\n // although it doesn't actually make a difference!\n // (content after a divert will simply be inaccessible)\n for (let ii = weavePoint.content.length - 1; ii >= 0; --ii) {\n let innerDivert = asOrNull(weavePoint.content[ii], Divert);\n if (innerDivert) {\n const willReturn =\n innerDivert.isThread ||\n innerDivert.isTunnel ||\n innerDivert.isFunctionCall;\n if (!willReturn) {\n return false;\n }\n }\n }\n\n return true;\n };\n\n // Enforce rule that weave points must not have the same\n // name as any stitches or knots upwards in the hierarchy\n public readonly CheckForWeavePointNamingCollisions = (): void => {\n if (!this.namedWeavePoints) {\n return;\n }\n\n const ancestorFlows = [];\n for (const obj of this.ancestry) {\n const flow = asOrNull(obj, FlowBase);\n if (flow) {\n ancestorFlows.push(flow);\n } else {\n break;\n }\n }\n\n for (const [weavePointName, weavePoint] of this.namedWeavePoints) {\n for (const flow of ancestorFlows) {\n // Shallow search\n const otherContentWithName =\n flow.ContentWithNameAtLevel(weavePointName);\n if (otherContentWithName && otherContentWithName !== weavePoint) {\n const errorMsg = `${weavePoint.GetType()} '${weavePointName}' has the same label name as a ${otherContentWithName.GetType()} (on ${\n otherContentWithName.debugMetadata\n })`;\n this.Error(errorMsg, weavePoint);\n }\n }\n }\n };\n}\n","import { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { ControlCommand as RuntimeControlCommand } from \"../../../../engine/ControlCommand\";\nimport { Divert as RuntimeDivert } from \"../../../../engine/Divert\";\nimport { Expression } from \"../Expression/Expression\";\nimport { ParsedObject } from \"../Object\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\nimport { NativeFunctionCall } from \"../../../../engine/NativeFunctionCall\";\nimport { StringValue } from \"../../../../engine/Value\";\nimport { Story } from \"../Story\";\nimport { Text } from \"../Text\";\nimport { Weave } from \"../Weave\";\nimport { asOrNull } from \"../../../../engine/TypeAssertion\";\n\nexport class ConditionalSingleBranch extends ParsedObject {\n public _contentContainer: RuntimeContainer | null = null;\n public _conditionalDivert: RuntimeDivert | null = null;\n public _ownExpression: Expression | null = null;\n public _innerWeave: Weave | null = null;\n // bool condition, e.g.:\n // { 5 == 4:\n // - the true branch\n // - the false branch\n // }\n public isTrueBranch: boolean = false;\n\n // When each branch has its own expression like a switch statement,\n // this is non-null. e.g.\n // { x:\n // - 4: the value of x is four (ownExpression is the value 4)\n // - 3: the value of x is three\n // }\n get ownExpression() {\n return this._ownExpression;\n }\n\n set ownExpression(value) {\n this._ownExpression = value;\n if (this._ownExpression) {\n this.AddContent(this._ownExpression);\n }\n }\n\n // In the above example, match equality of x with 4 for the first branch.\n // This is as opposed to simply evaluating boolean equality for each branch,\n // example when shouldMatchEquality is FALSE:\n // {\n // 3 > 2: This will happen\n // 2 > 3: This won't happen\n // }\n public matchingEquality: boolean = false;\n\n public isElse: boolean = false;\n public isInline: boolean = false;\n\n public returnDivert: RuntimeDivert | null = null;\n\n constructor(content?: ParsedObject[] | null | undefined) {\n super();\n\n // Branches are allowed to be empty\n if (content) {\n this._innerWeave = new Weave(content);\n this.AddContent(this._innerWeave);\n }\n }\n\n get typeName(): string {\n return \"ConditionalSingleBranch\";\n }\n\n // Runtime content can be summarised as follows:\n // - Evaluate an expression if necessary to branch on\n // - Branch to a named container if true\n // - Divert back to main flow\n // (owner Conditional is in control of this target point)\n public readonly GenerateRuntimeObject = (): RuntimeObject => {\n // Check for common mistake, of putting \"else:\" instead of \"- else:\"\n if (this._innerWeave) {\n for (const c of this._innerWeave.content) {\n const text = asOrNull(c, Text);\n if (text) {\n // Don't need to trim at the start since the parser handles that already\n if (text.text.startsWith(\"else:\")) {\n this.Warning(\n \"Saw the text 'else:' which is being treated as content. Did you mean '- else:'?\",\n text\n );\n }\n }\n }\n }\n\n const container = new RuntimeContainer();\n\n // Are we testing against a condition that's used for more than just this\n // branch? If so, the first thing we need to do is replicate the value that's\n // on the evaluation stack so that we don't fully consume it, in case other\n // branches need to use it.\n const duplicatesStackValue: boolean = this.matchingEquality && !this.isElse;\n\n if (duplicatesStackValue) {\n container.AddContent(RuntimeControlCommand.Duplicate());\n }\n\n this._conditionalDivert = new RuntimeDivert();\n\n // else clause is unconditional catch-all, otherwise the divert is conditional\n this._conditionalDivert.isConditional = !this.isElse;\n\n // Need extra evaluation?\n if (!this.isTrueBranch && !this.isElse) {\n const needsEval: boolean = this.ownExpression !== null;\n if (needsEval) {\n container.AddContent(RuntimeControlCommand.EvalStart());\n }\n\n if (this.ownExpression) {\n this.ownExpression.GenerateIntoContainer(container);\n }\n\n // Uses existing duplicated value\n if (this.matchingEquality) {\n container.AddContent(NativeFunctionCall.CallWithName(\"==\"));\n }\n\n if (needsEval) {\n container.AddContent(RuntimeControlCommand.EvalEnd());\n }\n }\n\n // Will pop from stack if conditional\n container.AddContent(this._conditionalDivert);\n\n this._contentContainer = this.GenerateRuntimeForContent();\n this._contentContainer.name = \"b\";\n\n // Multi-line conditionals get a newline at the start of each branch\n // (as opposed to the start of the multi-line conditional since the condition\n // may evaluate to false.)\n if (!this.isInline) {\n this._contentContainer.InsertContent(new StringValue(\"\\n\"), 0);\n }\n\n if (duplicatesStackValue || (this.isElse && this.matchingEquality)) {\n this._contentContainer.InsertContent(\n RuntimeControlCommand.PopEvaluatedValue(),\n 0\n );\n }\n\n container.AddToNamedContentOnly(this._contentContainer);\n\n this.returnDivert = new RuntimeDivert();\n this._contentContainer.AddContent(this.returnDivert);\n\n return container;\n };\n\n public readonly GenerateRuntimeForContent = (): RuntimeContainer => {\n // Empty branch - create empty container\n if (this._innerWeave === null) {\n return new RuntimeContainer();\n }\n\n return this._innerWeave.rootContainer;\n };\n\n public ResolveReferences(context: Story): void {\n if (!this._conditionalDivert || !this._contentContainer) {\n throw new Error();\n }\n\n this._conditionalDivert.targetPath = this._contentContainer.path;\n super.ResolveReferences(context);\n }\n}\n","export enum CustomFlags {\n ParsingString = 0x1,\n TagActive = 0x2,\n}\n","export class DebugMetadata {\n public startLineNumber: number = 0;\n public endLineNumber: number = 0;\n public startCharacterNumber: number = 0;\n public endCharacterNumber: number = 0;\n public fileName: string | null = null;\n public sourceName: string | null = null;\n\n public Merge(dm: DebugMetadata) {\n let newDebugMetadata = new DebugMetadata();\n\n newDebugMetadata.fileName = this.fileName;\n newDebugMetadata.sourceName = this.sourceName;\n\n if (this.startLineNumber < dm.startLineNumber) {\n newDebugMetadata.startLineNumber = this.startLineNumber;\n newDebugMetadata.startCharacterNumber = this.startCharacterNumber;\n } else if (this.startLineNumber > dm.startLineNumber) {\n newDebugMetadata.startLineNumber = dm.startLineNumber;\n newDebugMetadata.startCharacterNumber = dm.startCharacterNumber;\n } else {\n newDebugMetadata.startLineNumber = this.startLineNumber;\n newDebugMetadata.startCharacterNumber = Math.min(\n this.startCharacterNumber,\n dm.startCharacterNumber\n );\n }\n\n if (this.endLineNumber > dm.endLineNumber) {\n newDebugMetadata.endLineNumber = this.endLineNumber;\n newDebugMetadata.endCharacterNumber = this.endCharacterNumber;\n } else if (this.endLineNumber < dm.endLineNumber) {\n newDebugMetadata.endLineNumber = dm.endLineNumber;\n newDebugMetadata.endCharacterNumber = dm.endCharacterNumber;\n } else {\n newDebugMetadata.endLineNumber = this.endLineNumber;\n newDebugMetadata.endCharacterNumber = Math.max(\n this.endCharacterNumber,\n dm.endCharacterNumber\n );\n }\n\n return newDebugMetadata;\n }\n\n public toString() {\n if (this.fileName !== null) {\n return `line ${this.startLineNumber} of ${this.fileName}\"`;\n } else {\n return \"line \" + this.startLineNumber;\n }\n }\n}\n","import { INamedContent } from \"../../../../engine/INamedContent\";\nimport { ParsedObject } from \"../Object\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\nimport { Identifier } from \"../Identifier\";\n\nexport class ExternalDeclaration extends ParsedObject implements INamedContent {\n public get name(): string | null {\n return this.identifier?.name || null;\n }\n\n constructor(\n public readonly identifier: Identifier,\n public readonly argumentNames: string[]\n ) {\n super();\n }\n\n get typeName(): string {\n return \"EXTERNAL\";\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject | null => {\n this.story.AddExternal(this);\n\n // No runtime code exists for an external, only metadata\n return null;\n };\n\n public toString(): string {\n return `EXTERNAL ${this.identifier?.name}`;\n }\n}\n","import { Argument } from \"./ParsedHierarchy/Argument\";\nimport { Identifier } from \"./ParsedHierarchy/Identifier\";\n\nexport class FlowDecl {\n constructor(\n public readonly name: Identifier,\n public readonly args: Argument[],\n public readonly isFunction: boolean\n ) {}\n}\n","import { ParsedObject } from \"./Object\";\nimport { InkObject as RuntimeObject } from \"../../../engine/Object\";\n\nexport class Wrap extends ParsedObject {\n constructor(private _objToWrap: T) {\n super();\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject => this._objToWrap;\n}\n","import { Glue as RuntimeGlue } from \"../../../engine/Glue\";\nimport { Wrap } from \"./Wrap\";\n\nexport class Glue extends Wrap {\n constructor(glue: RuntimeGlue) {\n super(glue);\n }\n\n get typeName(): string {\n return \"Glue\";\n }\n}\n","import { InkObject } from \"./Object\";\n\nexport class Glue extends InkObject {\n public toString() {\n return \"Glue\";\n }\n}\n","import { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { ContentList } from \"../ContentList\";\nimport { Expression } from \"./Expression\";\nimport { FlowBase } from \"../Flow/FlowBase\";\nimport { NativeFunctionCall } from \"../../../../engine/NativeFunctionCall\";\nimport { IntValue } from \"../../../../engine/Value\";\nimport { Story } from \"../Story\";\nimport { VariableAssignment as RuntimeVariableAssignment } from \"../../../../engine/VariableAssignment\";\nimport { VariableReference as RuntimeVariableReference } from \"../../../../engine/VariableReference\";\nimport { Weave } from \"../Weave\";\nimport { Identifier } from \"../Identifier\";\n\nexport class IncDecExpression extends Expression {\n private _runtimeAssignment: RuntimeVariableAssignment | null = null;\n\n public isInc: boolean;\n public expression: Expression | null = null;\n\n constructor(\n public readonly varIdentifier: Identifier | null,\n isIncOrExpression: boolean | Expression,\n isInc?: boolean\n ) {\n super();\n\n if (isIncOrExpression instanceof Expression) {\n this.expression = isIncOrExpression;\n this.AddContent(this.expression);\n this.isInc = Boolean(isInc);\n } else {\n this.isInc = isIncOrExpression as boolean;\n }\n }\n\n get typeName(): string {\n return \"IncDecExpression\";\n }\n\n public readonly GenerateIntoContainer = (\n container: RuntimeContainer\n ): void => {\n // x = x + y\n // ^^^ ^ ^ ^\n // 4 1 3 2\n // Reverse polish notation: (x 1 +) (assign to x)\n\n // 1.\n container.AddContent(\n new RuntimeVariableReference(this.varIdentifier?.name || null)\n );\n\n // 2.\n // - Expression used in the form ~ x += y\n // - Simple version: ~ x++\n if (this.expression) {\n this.expression.GenerateIntoContainer(container);\n } else {\n container.AddContent(new IntValue(1));\n }\n\n // 3.\n container.AddContent(\n NativeFunctionCall.CallWithName(this.isInc ? \"+\" : \"-\")\n );\n\n // 4.\n this._runtimeAssignment = new RuntimeVariableAssignment(\n this.varIdentifier?.name || null,\n false\n );\n container.AddContent(this._runtimeAssignment);\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n\n const varResolveResult = context.ResolveVariableWithName(\n this.varIdentifier?.name || \"\",\n this\n );\n\n if (!varResolveResult.found) {\n this.Error(\n `variable for ${this.incrementDecrementWord} could not be found: '${this.varIdentifier}' after searching: {this.descriptionOfScope}`\n );\n }\n\n if (!this._runtimeAssignment) {\n throw new Error();\n }\n\n this._runtimeAssignment.isGlobal = varResolveResult.isGlobal;\n\n if (\n !(this.parent instanceof Weave) &&\n !(this.parent instanceof FlowBase) &&\n !(this.parent instanceof ContentList)\n ) {\n this.Error(`Can't use ${this.incrementDecrementWord} as sub-expression`);\n }\n }\n\n get incrementDecrementWord(): \"increment\" | \"decrement\" {\n if (this.isInc) {\n return \"increment\";\n }\n\n return \"decrement\";\n }\n\n public readonly toString = (): string => {\n if (this.expression) {\n return `${this.varIdentifier?.name}${this.isInc ? \" += \" : \" -= \"}${\n this.expression\n }`;\n }\n\n return `${this.varIdentifier?.name}` + (this.isInc ? \"++\" : \"--\");\n };\n}\n","import { ParsedObject } from \"./Object\";\nimport { InkObject as RuntimeObject } from \"../../../engine/Object\";\nimport { Story } from \"./Story\";\n\nexport class IncludedFile extends ParsedObject {\n constructor(public readonly includedStory: Story | null) {\n super();\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject | null => {\n // Left to the main story to process\n return null;\n };\n\n get typeName(): string {\n return \"IncludedFile\";\n }\n}\n","export class InfixOperator {\n constructor(\n public readonly type: string,\n public readonly precedence: number,\n public readonly requireWhitespace: boolean\n ) {}\n\n public readonly toString = (): string => this.type;\n}\n","import { Argument } from \"./Argument\";\nimport { FlowBase } from \"./Flow/FlowBase\";\nimport { FlowLevel } from \"./Flow/FlowLevel\";\nimport { Identifier } from \"./Identifier\";\nimport { ParsedObject } from \"./Object\";\nimport { Story } from \"./Story\";\n\nexport class Knot extends FlowBase {\n get flowLevel(): FlowLevel {\n return FlowLevel.Knot;\n }\n\n constructor(\n name: Identifier,\n topLevelObjects: ParsedObject[],\n args: Argument[],\n isFunction: boolean\n ) {\n super(name, topLevelObjects, args, isFunction);\n }\n\n get typeName(): string {\n return this.isFunction ? \"Function\" : \"Knot\";\n }\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n\n let parentStory = this.story;\n\n // Enforce rule that stitches must not have the same\n // name as any knots that exist in the story\n for (const stitchName in this.subFlowsByName) {\n const knotWithStitchName = parentStory.ContentWithNameAtLevel(\n stitchName,\n FlowLevel.Knot,\n false\n );\n\n if (knotWithStitchName) {\n const stitch = this.subFlowsByName.get(stitchName);\n const errorMsg = `Stitch '${\n stitch ? stitch.name : \"NO STITCH FOUND\"\n }' has the same name as a knot (on ${\n knotWithStitchName.debugMetadata\n })`;\n this.Error(errorMsg, stitch);\n }\n }\n }\n}\n","import { Container as RuntimeContainer } from \"../../../../engine/Container\";\nimport { Expression } from \"../Expression/Expression\";\nimport { InkList as RuntimeInkList } from \"../../../../engine/InkList\";\nimport { InkListItem as RuntimeInkListItem } from \"../../../../engine/InkList\";\nimport { ListElementDefinition } from \"./ListElementDefinition\";\nimport { ListValue } from \"../../../../engine/Value\";\nimport { Identifier } from \"../Identifier\";\n\nexport class List extends Expression {\n constructor(public readonly itemIdentifierList: Identifier[]) {\n super();\n }\n\n get typeName(): string {\n return \"List\";\n }\n\n public readonly GenerateIntoContainer = (\n container: RuntimeContainer\n ): void => {\n const runtimeRawList = new RuntimeInkList();\n\n if (this.itemIdentifierList != null) {\n for (const itemIdentifier of this.itemIdentifierList) {\n const nameParts = itemIdentifier?.name?.split(\".\") || [];\n\n let listName: string | null = null;\n let listItemName: string = \"\";\n if (nameParts.length > 1) {\n listName = nameParts[0];\n listItemName = nameParts[1];\n } else {\n listItemName = nameParts[0];\n }\n\n const listItem = this.story.ResolveListItem(\n listName,\n listItemName,\n this\n ) as ListElementDefinition;\n\n if (listItem === null) {\n if (listName === null) {\n this.Error(\n `Could not find list definition that contains item '${itemIdentifier}'`\n );\n } else {\n this.Error(`Could not find list item ${itemIdentifier}`);\n }\n } else {\n if (listItem.parent == null) {\n this.Error(\n `Could not find list definition for item ${itemIdentifier}`\n );\n return;\n }\n if (!listName) {\n listName = listItem.parent.identifier?.name || null;\n }\n\n const item = new RuntimeInkListItem(listName, listItem.name || null);\n\n if (runtimeRawList.has(item.serialized())) {\n this.Warning(`Duplicate of item '${itemIdentifier}' in list.`);\n } else {\n runtimeRawList.Add(item, listItem.seriesValue);\n }\n }\n }\n }\n\n container.AddContent(new ListValue(runtimeRawList));\n };\n}\n","import { ListDefinition } from \"./ListDefinition\";\nimport { ParsedObject } from \"../Object\";\nimport { InkObject as RuntimeObject } from \"../../../../engine/Object\";\nimport { Story } from \"../Story\";\nimport { SymbolType } from \"../SymbolType\";\nimport { Identifier } from \"../Identifier\";\n\nexport class ListElementDefinition extends ParsedObject {\n public seriesValue: number = 0;\n\n public parent: ListDefinition | null = null;\n\n get fullName(): string {\n const parentList = this.parent;\n if (parentList === null) {\n throw new Error(\"Can't get full name without a parent list.\");\n }\n\n return `${parentList.identifier?.name}.${this.name}`;\n }\n\n get typeName(): string {\n return \"ListElement\";\n }\n\n get name(): string | null {\n return this.indentifier?.name || null;\n }\n\n constructor(\n public readonly indentifier: Identifier,\n public readonly inInitialList: boolean,\n public readonly explicitValue: number | null = null\n ) {\n super();\n this.parent = this.parent as ListDefinition;\n }\n\n public readonly GenerateRuntimeObject = (): RuntimeObject => {\n throw new Error(\"Not implemented.\");\n };\n\n public ResolveReferences(context: Story): void {\n super.ResolveReferences(context);\n context.CheckForNamingCollisions(\n this,\n this.indentifier,\n SymbolType.ListItem\n );\n }\n\n public readonly toString = (): string => this.fullName;\n}\n","export enum StatementLevel {\n InnerBlock,\n Stitch,\n Knot,\n Top,\n}\n","import { Argument } from \"./Argument\";\nimport { FlowBase } from \"./Flow/FlowBase\";\nimport { FlowLevel } from \"./Flow/FlowLevel\";\nimport { Identifier } from \"./Identifier\";\nimport { ParsedObject } from \"./Object\";\n\nexport class Stitch extends FlowBase {\n get flowLevel(): FlowLevel {\n return FlowLevel.Stitch;\n }\n\n constructor(\n name: Identifier,\n topLevelObjects: ParsedObject[],\n args: Argument[],\n isFunction: boolean\n ) {\n super(name, topLevelObjects, args, isFunction);\n }\n\n get typeName(): string {\n return \"Stitch\";\n }\n\n // Fixes TS issue with not being able to access the prototype via `super` in functions\n // attached to the class as properties.\n private baseToString = this.toString;\n\n public toString = (): string => {\n return `${\n this.parent !== null ? this.parent + \" > \" : \"\"\n }${this.baseToString()}`;\n };\n}\n","import { InkObject } from \"./Object\";\n\n// New version of tags is dynamic - it constructs the tags\n// at runtime based on BeginTag and EndTag control commands.\n// Plain text that's in the output stream is turned into tags\n// when you do story.currentTags.\n// The only place this is used is when flattening tags down\n// to string in advance, during dynamic string generation if\n// there's a tag embedded in it. See how ControlCommand.EndString\n// is implemented in Story.cs for more details + comment\nexport class Tag extends InkObject {\n public readonly text: string;\n\n constructor(tagText: string) {\n super();\n this.text = tagText.toString() || \"\";\n }\n\n public toString(): string {\n return \"# \" + this.text;\n }\n}\n","import { Path } from \"./Path\";\nimport { CallStack } from \"./CallStack\";\nimport { throwNullException } from \"./NullException\";\nimport { InkObject } from \"./Object\";\n\nexport class Choice extends InkObject {\n public text: string = \"\";\n public index: number = 0;\n public threadAtGeneration: CallStack.Thread | null = null;\n public sourcePath: string = \"\";\n public targetPath: Path | null = null;\n public isInvisibleDefault: boolean = false;\n public tags: string[] | null = null;\n public originalThreadIndex: number = 0;\n\n get pathStringOnChoice(): string {\n if (this.targetPath === null)\n return throwNullException(\"Choice.targetPath\");\n return this.targetPath.toString();\n }\n set pathStringOnChoice(value: string) {\n this.targetPath = new Path(value);\n }\n}\n","import { InkListItem } from \"./InkList\";\nimport { ListValue } from \"./Value\";\nimport { ListDefinition } from \"./ListDefinition\";\nimport { TryGetResult } from \"./TryGetResult\";\nimport { throwNullException } from \"./NullException\";\n\nexport class ListDefinitionsOrigin {\n protected _lists: Map;\n protected _allUnambiguousListValueCache: Map;\n\n constructor(lists: ListDefinition[]) {\n this._lists = new Map();\n this._allUnambiguousListValueCache = new Map();\n\n for (let list of lists) {\n this._lists.set(list.name, list);\n\n for (let [key, val] of list.items) {\n let item = InkListItem.fromSerializedKey(key);\n let listValue = new ListValue(item, val);\n\n if (!item.itemName) {\n throw new Error(\"item.itemName is null or undefined.\");\n }\n\n this._allUnambiguousListValueCache.set(item.itemName, listValue);\n this._allUnambiguousListValueCache.set(item.fullName, listValue);\n }\n }\n }\n get lists(): ListDefinition[] {\n let listOfLists: ListDefinition[] = [];\n\n for (let [, value] of this._lists) {\n listOfLists.push(value);\n }\n\n return listOfLists;\n }\n public TryListGetDefinition(\n name: string | null,\n /* out */ def: ListDefinition | null\n ): TryGetResult {\n if (name === null) {\n return { result: def, exists: false };\n }\n // initially, this function returns a boolean and the second parameter is an out.\n let definition = this._lists.get(name);\n if (!definition) return { result: def, exists: false };\n\n return { result: definition, exists: true };\n }\n public FindSingleItemListWithName(name: string | null) {\n if (name === null) {\n return throwNullException(\"name\");\n }\n let val = this._allUnambiguousListValueCache.get(name);\n\n if (typeof val !== \"undefined\") {\n return val;\n }\n\n return null;\n }\n}\n","import { Container } from \"./Container\";\nimport {\n Value,\n IntValue,\n FloatValue,\n StringValue,\n DivertTargetValue,\n VariablePointerValue,\n ListValue,\n BoolValue,\n} from \"./Value\";\nimport { Glue } from \"./Glue\";\nimport { ControlCommand } from \"./ControlCommand\";\nimport { PushPopType } from \"./PushPop\";\nimport { Divert } from \"./Divert\";\nimport { ChoicePoint } from \"./ChoicePoint\";\nimport { VariableReference } from \"./VariableReference\";\nimport { VariableAssignment } from \"./VariableAssignment\";\nimport { NativeFunctionCall } from \"./NativeFunctionCall\";\nimport { Void } from \"./Void\";\nimport { Tag } from \"./Tag\";\nimport { Path } from \"./Path\";\nimport { Choice } from \"./Choice\";\nimport { ListDefinition } from \"./ListDefinition\";\nimport { ListDefinitionsOrigin } from \"./ListDefinitionsOrigin\";\nimport { InkListItem, InkList } from \"./InkList\";\nimport { InkObject } from \"./Object\";\nimport { asOrNull } from \"./TypeAssertion\";\nimport { throwNullException } from \"./NullException\";\nimport { SimpleJson } from \"./SimpleJson\";\n\nexport class JsonSerialisation {\n public static JArrayToRuntimeObjList(\n jArray: any[],\n skipLast: boolean = false\n ) {\n let count = jArray.length;\n if (skipLast) count--;\n\n let list: InkObject[] = [];\n\n for (let i = 0; i < count; i++) {\n let jTok = jArray[i];\n let runtimeObj = this.JTokenToRuntimeObject(jTok);\n if (runtimeObj === null) {\n return throwNullException(\"runtimeObj\");\n }\n list.push(runtimeObj);\n }\n\n return list;\n }\n\n public static WriteDictionaryRuntimeObjs(\n writer: SimpleJson.Writer,\n dictionary: Map\n ) {\n writer.WriteObjectStart();\n for (let [key, value] of dictionary) {\n writer.WritePropertyStart(key);\n this.WriteRuntimeObject(writer, value);\n writer.WritePropertyEnd();\n }\n writer.WriteObjectEnd();\n }\n\n public static WriteListRuntimeObjs(\n writer: SimpleJson.Writer,\n list: InkObject[]\n ) {\n writer.WriteArrayStart();\n for (let value of list) {\n this.WriteRuntimeObject(writer, value);\n }\n writer.WriteArrayEnd();\n }\n\n public static WriteIntDictionary(\n writer: SimpleJson.Writer,\n dict: Map\n ) {\n writer.WriteObjectStart();\n for (let [key, value] of dict) {\n writer.WriteIntProperty(key, value);\n }\n writer.WriteObjectEnd();\n }\n\n public static WriteRuntimeObject(\n writer: SimpleJson.Writer,\n obj: InkObject\n ): void {\n let container = asOrNull(obj, Container);\n if (container) {\n this.WriteRuntimeContainer(writer, container);\n return;\n }\n\n let divert = asOrNull(obj, Divert);\n if (divert) {\n let divTypeKey = \"->\";\n if (divert.isExternal) {\n divTypeKey = \"x()\";\n } else if (divert.pushesToStack) {\n if (divert.stackPushType == PushPopType.Function) {\n divTypeKey = \"f()\";\n } else if (divert.stackPushType == PushPopType.Tunnel) {\n divTypeKey = \"->t->\";\n }\n }\n\n let targetStr;\n if (divert.hasVariableTarget) {\n targetStr = divert.variableDivertName;\n } else {\n targetStr = divert.targetPathString;\n }\n\n writer.WriteObjectStart();\n writer.WriteProperty(divTypeKey, targetStr);\n\n if (divert.hasVariableTarget) {\n writer.WriteProperty(\"var\", true);\n }\n\n if (divert.isConditional) {\n writer.WriteProperty(\"c\", true);\n }\n\n if (divert.externalArgs > 0) {\n writer.WriteIntProperty(\"exArgs\", divert.externalArgs);\n }\n\n writer.WriteObjectEnd();\n return;\n }\n\n let choicePoint = asOrNull(obj, ChoicePoint);\n if (choicePoint) {\n writer.WriteObjectStart();\n writer.WriteProperty(\"*\", choicePoint.pathStringOnChoice);\n writer.WriteIntProperty(\"flg\", choicePoint.flags);\n writer.WriteObjectEnd();\n return;\n }\n\n let boolVal = asOrNull(obj, BoolValue);\n if (boolVal) {\n writer.WriteBool(boolVal.value);\n return;\n }\n\n let intVal = asOrNull(obj, IntValue);\n if (intVal) {\n writer.WriteInt(intVal.value);\n return;\n }\n\n let floatVal = asOrNull(obj, FloatValue);\n if (floatVal) {\n writer.WriteFloat(floatVal.value);\n return;\n }\n\n let strVal = asOrNull(obj, StringValue);\n if (strVal) {\n if (strVal.isNewline) {\n writer.Write(\"\\n\", false);\n } else {\n writer.WriteStringStart();\n writer.WriteStringInner(\"^\");\n writer.WriteStringInner(strVal.value);\n writer.WriteStringEnd();\n }\n return;\n }\n\n let listVal = asOrNull(obj, ListValue);\n if (listVal) {\n this.WriteInkList(writer, listVal);\n return;\n }\n\n let divTargetVal = asOrNull(obj, DivertTargetValue);\n if (divTargetVal) {\n writer.WriteObjectStart();\n if (divTargetVal.value === null) {\n return throwNullException(\"divTargetVal.value\");\n }\n writer.WriteProperty(\"^->\", divTargetVal.value.componentsString);\n writer.WriteObjectEnd();\n\n return;\n }\n\n let varPtrVal = asOrNull(obj, VariablePointerValue);\n if (varPtrVal) {\n writer.WriteObjectStart();\n writer.WriteProperty(\"^var\", varPtrVal.value);\n writer.WriteIntProperty(\"ci\", varPtrVal.contextIndex);\n writer.WriteObjectEnd();\n return;\n }\n\n let glue = asOrNull(obj, Glue);\n if (glue) {\n writer.Write(\"<>\");\n return;\n }\n\n let controlCmd = asOrNull(obj, ControlCommand);\n if (controlCmd) {\n writer.Write(\n JsonSerialisation._controlCommandNames[controlCmd.commandType]\n );\n return;\n }\n\n let nativeFunc = asOrNull(obj, NativeFunctionCall);\n if (nativeFunc) {\n let name = nativeFunc.name;\n\n if (name == \"^\") name = \"L^\";\n\n writer.Write(name);\n return;\n }\n\n let varRef = asOrNull(obj, VariableReference);\n if (varRef) {\n writer.WriteObjectStart();\n let readCountPath = varRef.pathStringForCount;\n if (readCountPath != null) {\n writer.WriteProperty(\"CNT?\", readCountPath);\n } else {\n writer.WriteProperty(\"VAR?\", varRef.name);\n }\n\n writer.WriteObjectEnd();\n return;\n }\n\n let varAss = asOrNull(obj, VariableAssignment);\n if (varAss) {\n writer.WriteObjectStart();\n\n let key = varAss.isGlobal ? \"VAR=\" : \"temp=\";\n writer.WriteProperty(key, varAss.variableName);\n\n // Reassignment?\n if (!varAss.isNewDeclaration) writer.WriteProperty(\"re\", true);\n\n writer.WriteObjectEnd();\n\n return;\n }\n\n let voidObj = asOrNull(obj, Void);\n if (voidObj) {\n writer.Write(\"void\");\n return;\n }\n\n let tag = asOrNull(obj, Tag);\n if (tag) {\n writer.WriteObjectStart();\n writer.WriteProperty(\"#\", tag.text);\n writer.WriteObjectEnd();\n return;\n }\n\n let choice = asOrNull(obj, Choice);\n if (choice) {\n this.WriteChoice(writer, choice);\n return;\n }\n\n throw new Error(\"Failed to convert runtime object to Json token: \" + obj);\n }\n\n public static JObjectToDictionaryRuntimeObjs(jObject: Record) {\n let dict: Map = new Map();\n\n for (let key in jObject) {\n if (jObject.hasOwnProperty(key)) {\n let inkObject = this.JTokenToRuntimeObject(jObject[key]);\n if (inkObject === null) {\n return throwNullException(\"inkObject\");\n }\n dict.set(key, inkObject);\n }\n }\n\n return dict;\n }\n\n public static JObjectToIntDictionary(jObject: Record) {\n let dict: Map = new Map();\n for (let key in jObject) {\n if (jObject.hasOwnProperty(key)) {\n dict.set(key, parseInt(jObject[key]));\n }\n }\n return dict;\n }\n\n public static JTokenToRuntimeObject(token: any): InkObject | null {\n if (\n (typeof token === \"number\" && !isNaN(token)) ||\n typeof token === \"boolean\"\n ) {\n return Value.Create(token);\n }\n\n if (typeof token === \"string\") {\n let str = token.toString();\n\n // String value\n let firstChar = str[0];\n if (firstChar == \"^\") return new StringValue(str.substring(1));\n else if (firstChar == \"\\n\" && str.length == 1)\n return new StringValue(\"\\n\");\n\n // Glue\n if (str == \"<>\") return new Glue();\n\n // Control commands (would looking up in a hash set be faster?)\n for (let i = 0; i < JsonSerialisation._controlCommandNames.length; ++i) {\n let cmdName = JsonSerialisation._controlCommandNames[i];\n if (str == cmdName) {\n return new ControlCommand(i);\n }\n }\n\n // Native functions\n if (str == \"L^\") str = \"^\";\n if (NativeFunctionCall.CallExistsWithName(str))\n return NativeFunctionCall.CallWithName(str);\n\n // Pop\n if (str == \"->->\") return ControlCommand.PopTunnel();\n else if (str == \"~ret\") return ControlCommand.PopFunction();\n\n // Void\n if (str == \"void\") return new Void();\n }\n\n if (typeof token === \"object\" && !Array.isArray(token)) {\n let obj = token as Record;\n let propValue;\n\n // Divert target value to path\n if (obj[\"^->\"]) {\n propValue = obj[\"^->\"];\n return new DivertTargetValue(new Path(propValue.toString()));\n }\n\n // VariablePointerValue\n if (obj[\"^var\"]) {\n propValue = obj[\"^var\"];\n let varPtr = new VariablePointerValue(propValue.toString());\n if (\"ci\" in obj) {\n propValue = obj[\"ci\"];\n varPtr.contextIndex = parseInt(propValue);\n }\n return varPtr;\n }\n\n // Divert\n let isDivert = false;\n let pushesToStack = false;\n let divPushType = PushPopType.Function;\n let external = false;\n if ((propValue = obj[\"->\"])) {\n isDivert = true;\n } else if ((propValue = obj[\"f()\"])) {\n isDivert = true;\n pushesToStack = true;\n divPushType = PushPopType.Function;\n } else if ((propValue = obj[\"->t->\"])) {\n isDivert = true;\n pushesToStack = true;\n divPushType = PushPopType.Tunnel;\n } else if ((propValue = obj[\"x()\"])) {\n isDivert = true;\n external = true;\n pushesToStack = false;\n divPushType = PushPopType.Function;\n }\n\n if (isDivert) {\n let divert = new Divert();\n divert.pushesToStack = pushesToStack;\n divert.stackPushType = divPushType;\n divert.isExternal = external;\n\n let target = propValue.toString();\n\n if ((propValue = obj[\"var\"])) divert.variableDivertName = target;\n else divert.targetPathString = target;\n\n divert.isConditional = !!obj[\"c\"];\n\n if (external) {\n if ((propValue = obj[\"exArgs\"]))\n divert.externalArgs = parseInt(propValue);\n }\n\n return divert;\n }\n\n // Choice\n if ((propValue = obj[\"*\"])) {\n let choice = new ChoicePoint();\n choice.pathStringOnChoice = propValue.toString();\n\n if ((propValue = obj[\"flg\"])) choice.flags = parseInt(propValue);\n\n return choice;\n }\n\n // Variable reference\n if ((propValue = obj[\"VAR?\"])) {\n return new VariableReference(propValue.toString());\n } else if ((propValue = obj[\"CNT?\"])) {\n let readCountVarRef = new VariableReference();\n readCountVarRef.pathStringForCount = propValue.toString();\n return readCountVarRef;\n }\n\n // Variable assignment\n let isVarAss = false;\n let isGlobalVar = false;\n if ((propValue = obj[\"VAR=\"])) {\n isVarAss = true;\n isGlobalVar = true;\n } else if ((propValue = obj[\"temp=\"])) {\n isVarAss = true;\n isGlobalVar = false;\n }\n if (isVarAss) {\n let varName = propValue.toString();\n let isNewDecl = !obj[\"re\"];\n let varAss = new VariableAssignment(varName, isNewDecl);\n varAss.isGlobal = isGlobalVar;\n return varAss;\n }\n if (obj[\"#\"] !== undefined) {\n propValue = obj[\"#\"];\n return new Tag(propValue.toString());\n }\n\n // List value\n if ((propValue = obj[\"list\"])) {\n // var listContent = (Dictionary)propValue;\n let listContent = propValue as Record;\n let rawList = new InkList();\n if ((propValue = obj[\"origins\"])) {\n // var namesAsObjs = (List