Files
hank/hank-godot/scripts/Story.gd
Nat Quayle Nelson b0a3c0b3d9
Some checks failed
/ test (push) Failing after 41s
Make hank work in GDScript
2025-10-30 21:42:19 -05:00

779 lines
21 KiB
GDScript

class_name Story
var hInterface: HInterface
var insertionHooks: Variant
var random: Random
var ast: Array[Variant]
var exprIndex: int
var storyTree: StoryNode
var viewCounts: Variant
var nodeScopes: Array[StoryNode]
var altInstances: Variant = haxe_ds_ObjectMap.new()
var parser: HParser
var embedMode: EmbedMode.EmbedMode = EmbedMode.EmbedMode.Tunnel
var embeddedBlocks: Array[Story] = []
var parent: Variant = { "_index": 1 }
var choicesTaken: Array[int] = []
var weaveDepth: int = 0
var storedFrame: Variant = { "_index": 1 }
func _init(r: Random, p: HParser, ast2: Array[Variant], st: StoryNode, sc: Array[StoryNode], vc: Variant, hi: HInterface) -> void:
self.insertionHooks = haxe_ds_StringMap.new()
self.random = r
self.parser = p
self.ast = ast2
self.storyTree = st
self.nodeScopes = sc
self.viewCounts = vc
self.hInterface = hi
func currentFile() -> String:
return self.ast[0].get("position").file
func embeddedStory(h: String) -> Story:
var ast2: Array[Variant] = self.parser.parseString(h)
var story: Story = Story.new(self.random, self.parser, ast2, self.storyTree, self.nodeScopes, self.viewCounts, self.hInterface)
story.exprIndex = 0
story.parent = { "_index": 0, "v": self }
return story
func storyFork(t: String, readonly: bool = false) -> Story:
var tempMap
if (readonly):
tempMap = self.storyTree.createViewCounts()
else:
tempMap = self.viewCounts
var story: Story = Story.new(self.random, self.parser, self.ast, self.storyTree, self.nodeScopes, tempMap, self.hInterface)
if (self.embedMode == EmbedMode.EmbedMode.Thread):
story.parent = { "_index": 0, "v": self }
story.divertTo(t)
return story
func addVariable(name: String, val) -> void:
self.hInterface.addVariable(name, val)
func runRootIncludedHaxe(rootFile: String) -> void:
var i: int = 0
while (i < ASTExtension.findFile(self.ast, rootFile)):
var file: String = self.ast[i].get("position").file
if true:
var _g: Variant = self.ast[i].get("expr")
match (_g._index):
7:
var _g2: String = _g.haxe
if true:
var h: String = _g2
if true:
self.hInterface.runEmbeddedHaxe(h, self.nodeScopes)
i += 1
8:
var _g2: String = _g.haxe
if true:
var h: String = _g2
if true:
self.hInterface.runEmbeddedHaxe(h, self.nodeScopes)
i += 1
_:
i = ASTExtension.findEOF(self.ast, file) + 1
func nextFrame() -> Variant:
if true:
var _g: Variant = self.storedFrame
if (_g._index == 0):
var _g2: Variant = _g.v
var f: Variant = _g2
self.storedFrame = { "_index": 1 }
return f
else:
pass
while (self.embeddedBlocks.size() > 0):
if true:
var _g: EmbedMode.EmbedMode = self.embedMode
match (((_g as Variant) as int)):
0:
var nf: Variant = self.embeddedBlocks[0].nextFrame()
if (nf == { "_index": 2 }):
var _this: Array[Story] = self.embeddedBlocks
var x: Story = self.embeddedBlocks[0]
var index: int = _this.find(x)
if (index >= 0):
_this.remove_at(index)
true
else:
false
else:
return nf
1:
var idx: int = 0
while (idx < self.embeddedBlocks.size()):
var nf: Variant = self.embeddedBlocks[idx].nextFrame()
match (nf._index):
1:
var _g2: Array[String] = nf.choices
var _g3: Array[Variant] = nf.tags
idx += 1
continue
2:
idx += 1
continue
_:
return nf
break
if (self.exprIndex >= self.ast.size()):
if (self.embedMode == EmbedMode.EmbedMode.Thread):
return self.nextChoiceFrame()
return { "_index": 2 }
var rootNf: Variant = self.processExpr(self.ast[self.exprIndex].get("expr"))
if (self.embedMode == EmbedMode.EmbedMode.Thread):
if (rootNf == { "_index": 2 }):
return self.nextChoiceFrame()
return rootNf
func processExpr(expr: Variant) -> Variant:
match (expr._index):
1:
var _g: Output = expr.o
var output: Output = _g
self.exprIndex += 1
var text: String = StringTools.trim(output.format(self, self.hInterface, self.random, self.altInstances, self.nodeScopes, false))
if (self.exprIndex < self.ast.size()):
var nextExpr: Variant = self.ast[self.exprIndex].get("expr")
var nextIdx: int = self.exprIndex
match (nextExpr._index):
2:
var _g2: Array[String] = nextExpr.targets
if true:
var targets: Array[String] = _g2
nextIdx = self.indexOf(targets[0])
3:
var _g2: String = nextExpr.target
if true:
var target: String = _g2
nextIdx = self.indexOf(target)
_:
pass
if (nextIdx < self.ast.size()):
nextExpr = self.ast[nextIdx].get("expr")
if (nextExpr._index == 9):
var _g2: Variant = nextExpr.label
var _g3: int = nextExpr.depth
var _g4: Variant = nextExpr.expr
var _exp: Variant = _g4
nextExpr = _exp
else:
pass
if (nextExpr._index == 1):
var _g2: Output = nextExpr.o
var output2: Output = _g2
if (output2.startsWithGlue()):
text = Output.appendNextText(self, text + " ", Output.GLUE_ERROR)
else:
pass
return self.finalTextProcessing(text)
2:
var _g: Array[String] = expr.targets
if (_g.size() == 1):
var _g2: String = _g[0]
if (_g2 == ""):
self.exprIndex += 1
else:
var oneTarget: String = _g2
self.divertTo(oneTarget)
return self.nextFrame()
else:
var targets: Array[String] = _g
if true:
var _g2 = targets.pop_back()
if true:
var target = _g2
if (target != ""):
self.divertTo(target)
else:
if (_g2 == null):
assert(false, str("No divert targets!"))
else:
if (_g2 == ""):
self.exprIndex += 1
if true:
var _g2: int = 0
while (_g2 < targets.size()):
var target: String = targets[_g2]
_g2 += 1
var fork: Story = self.storyFork(target, false)
self.embeddedBlocks.push_back(fork)
return self.nextFrame()
3:
var _g: String = expr.target
if true:
var target: String = _g
if true:
self.exprIndex += 1
self.embedMode = EmbedMode.EmbedMode.Thread
self.embeddedBlocks.push_back(self.storyFork(target, false))
var nf: Variant = self.nextFrame()
return nf
7:
var _g: String = expr.haxe
if true:
var h: String = _g
if true:
self.exprIndex += 1
self.hInterface.runEmbeddedHaxe(h, self.nodeScopes)
return self.nextFrame()
8:
var _g: String = expr.haxe
if true:
var h: String = _g
if true:
self.exprIndex += 1
self.hInterface.runEmbeddedHaxe(h, self.nodeScopes)
return self.nextFrame()
9:
var _g: Variant = expr.label
var _g1: int = expr.depth
var _g2: Variant = expr.expr
if true:
var label: Variant = _g
var depth: int = _g1
var nextExpr: Variant = _g2
if true:
match (label._index):
0:
var _g3: String = label.v
if true:
var l: String = _g3
if true:
var node: StoryNode = self.resolveNodeInScope(l, null)[0]
if true:
var _g4: StoryNode = node
var _g5: Variant = self.viewCounts
if true:
var v: int = _g5.__get(_g4) + 1
_g5.__set(_g4, v)
v
1:
pass
self.weaveDepth = depth
self.exprIndex += 1
return self.nextFrame()
10:
var _g: Variant = expr.c
var choice: Variant = _g
if (choice.get("depth") > self.weaveDepth):
self.weaveDepth = choice.get("depth")
else:
if (choice.get("depth") < self.weaveDepth):
self.gotoNextGather()
return self.nextFrame()
return self.nextChoiceFrame()
_:
haxe_Log.trace.call("" + str(expr) + " is not implemented", {
"fileName": "../hank/Story.hx",
"lineNumber": 336,
"className": "hank.Story",
"methodName": "processExpr"
})
return { "_index": 2 }
return { "_index": 2 }
func nextChoiceFrame() -> Variant:
var choices: Array[Variant] = self.availableChoices()
var tempArray
if true:
var _g: Array[String] = ([] as Array[String])
if true:
var _g1: int = 0
while (_g1 < choices.size()):
var choiceInfo: Variant = choices[_g1]
_g1 += 1
_g.push_back(choiceInfo.get("output").format(self, self.hInterface, self.random, self.altInstances, self.nodeScopes, false))
tempArray = _g
var tempArray1
if true:
var _g: Array[Variant] = ([] as Array[Variant])
if true:
var _g1: int = 0
while (_g1 < choices.size()):
var choiceInfo: Variant = choices[_g1]
_g1 += 1
_g.push_back(choiceInfo.get("tags"))
tempArray1 = _g
var tags: Array[Variant] = tempArray1
if (tempArray.size() > 0):
return self.finalChoiceProcessing(tempArray, tags)
else:
var fallback: Variant = self.fallbackChoice()
if true:
var _g: Variant = fallback.get("choiceInfo").get("divertTarget")
if (_g._index == 0):
var _g2: String = _g.v
var t: String = _g2
if (t.length() > 0):
var fallbackText: String = self.evaluateChoice(fallback.get("choiceInfo"))
if (fallbackText.length() > 0):
assert(false, str("For some reason a fallback choice evaluated to text!"))
return self.nextFrame()
else:
self.exprIndex = fallback.get("index") + 1
self.weaveDepth = fallback.get("choiceInfo").get("depth") + 1
return self.nextFrame()
else:
self.exprIndex = fallback.get("index") + 1
self.weaveDepth = fallback.get("choiceInfo").get("depth") + 1
return self.nextFrame()
return null
func traceChoiceArray(choices: Array[Variant]) -> void:
var _g: int = 0
while (_g < choices.size()):
var choiceInfo: Variant = choices[_g]
_g += 1
var tempRight
var _this: Array[String] = choiceInfo.get("tags")
var result: String = ""
var len: int = _this.size()
var _g2: int = 0
var _g1: int = len
while (_g2 < _g1):
var tempNumber
_g2 += 1
tempNumber = _g2 - 1
var i: int = tempNumber
var tempString
if (i == len - 1):
tempString = ""
else:
tempString = " #"
result += str(_this[i]) + (tempString)
tempRight = result
haxe_Log.trace.call("" + ChoiceExtension.toString(choiceInfo) + ": #" + tempRight, {
"fileName": "../hank/Story.hx",
"lineNumber": 374,
"className": "hank.Story",
"methodName": "traceChoiceArray"
})
haxe_Log.trace.call("---", {
"fileName": "../hank/Story.hx",
"lineNumber": 376,
"className": "hank.Story",
"methodName": "traceChoiceArray"
})
func availableChoices() -> Array[Variant]:
var choices: Array[Variant] = []
if (self.embedMode == EmbedMode.EmbedMode.Thread):
var idx: int = 0
if true:
var _g: int = 0
var _g1: Array[Story] = self.embeddedBlocks
while (_g < _g1.size()):
var thread: Story = _g1[_g]
_g += 1
choices = (choices + thread.availableChoices())
idx += 1
var tempRight
if true:
var _g: Variant = self.ast[self.exprIndex].get("expr")
if (_g._index == 10):
var _g2: Variant = _g.c
tempRight = true
else:
tempRight = false
if (self.exprIndex < self.ast.size() && tempRight):
var allChoiceInfo: Array[Variant] = ASTExtension.collectChoices(self.ast, self.exprIndex, self.weaveDepth).get("choices")
if true:
var _g: int = 0
while (_g < allChoiceInfo.size()):
var choiceInfo: Variant = allChoiceInfo[_g]
_g += 1
if (self.choicesTaken.find(choiceInfo.get("id")) == -1 || !choiceInfo.get("onceOnly")):
if true:
var _g2: Variant = choiceInfo.get("condition")
match (_g2._index):
0:
var _g3: String = _g2.v
if true:
var expr: String = _g3
if (!self.hInterface.cond(expr, self.nodeScopes)):
continue
1:
pass
if (!choiceInfo.get("output").isEmpty()):
choices.push_back(choiceInfo)
return choices
func fallbackChoice() -> Variant:
var choiceInfo: Variant = ASTExtension.collectChoices(self.ast, self.exprIndex, self.weaveDepth)
var lastChoice: Variant = choiceInfo.get("choices")[choiceInfo.get("choices").size() - 1]
if (lastChoice.get("output").isEmpty()):
return {
"choiceInfo": lastChoice,
"index": choiceInfo.get("fallbackIndex")
}
else:
assert(false, str("there is no fallback choice!"))
return null
func gotoNextGather() -> void:
var gatherIndex: int = ASTExtension.findNextGather(self.ast, self.currentFile(), self.exprIndex + 1, self.weaveDepth)
if (gatherIndex == -1):
assert(false, str("Ran out of choice content, and there is no gather"))
self.exprIndex = gatherIndex
func resolveNodeInScope(label: String, whichScope = null) -> Array[StoryNode]:
if (whichScope == null):
whichScope = self.nodeScopes
var targetParts: Array[String] = Array(Array(label.split(".")), Variant.Type.TYPE_STRING, "", null)
var newScopes: Array[StoryNode] = ([] as Array[StoryNode])
var _g: int = 0
var _g1: int = whichScope.size()
while (_g < _g1):
var tempNumber
_g += 1
tempNumber = _g - 1
var i: int = tempNumber
var scope: StoryNode = whichScope[i]
if true:
var _g2: Variant = scope.resolve(targetParts[0])
match (_g2._index):
0:
var _g3: StoryNode = _g2.v
var node: StoryNode = _g3
newScopes = whichScope.slice(i)
newScopes.insert(0, node)
null
var _g4: int = 0
var _g5: Array[String] = targetParts.slice(1)
while (_g4 < _g5.size()):
var part: String = _g5[_g4]
_g4 += 1
var scope2: StoryNode = newScopes[0]
if true:
var _g6: Variant = scope2.resolve(part)
match (_g6._index):
0:
var _g7: StoryNode = _g6.v
var innerNode: StoryNode = _g7
newScopes.insert(0, innerNode)
null
1:
break
break
1:
pass
return newScopes
func resolveScopes(target: String) -> Array[StoryNode]:
var tempArray
if (StringTools.startsWith(target, "@")):
var parts: Array[String] = Array(Array(target.split(".")), Variant.Type.TYPE_STRING, "", null)
var root: Array[StoryNode] = self.hInterface.getVariable(parts[0].substr(1))
if (parts.size() > 1):
var tempString
var _this: Array[String] = parts.slice(1)
var result: String = ""
var len: int = _this.size()
var _g: int = 0
var _g1: int = len
while (_g < _g1):
var tempNumber
_g += 1
tempNumber = _g - 1
var i: int = tempNumber
var tempString1
if (i == len - 1):
tempString1 = ""
else:
tempString1 = "."
result += str(_this[i]) + (tempString1)
tempString = result
var subTarget: String = tempString
tempArray = self.resolveNodeInScope(subTarget, root)
else:
tempArray = root
else:
tempArray = self.resolveNodeInScope(target, null)
if (tempArray == null || tempArray.size() == 0):
assert(false, str("Divert target not found: " + target))
return tempArray
func indexOf(target: String) -> int:
var disposableFork: Story = self.storyFork(target, true)
return disposableFork.exprIndex
func divertTo(target: String) -> void:
if (target.length() == 0):
return
if true:
var _g: Variant = self.parent
if (_g._index == 0):
var _g2: Story = _g.v
var p: Story = _g2
if (p.embedMode == EmbedMode.EmbedMode.Tunnel):
p.embeddedBlocks = ([] as Array[Story])
p.divertTo(target)
self.exprIndex = self.ast.size()
return
else:
pass
else:
pass
var newScopes: Array[StoryNode] = self.resolveScopes(target)
var targetIdx: int = newScopes[0].astIndex
self.exprIndex = targetIdx
var target2: StoryNode = newScopes[0]
self.weaveDepth = 0
if true:
var _g: Variant = self.ast[self.exprIndex].get("expr")
match (_g._index):
4:
var _g2: String = _g.name
if true:
var _g3: StoryNode = target2
var _g1: Variant = self.viewCounts
if true:
var v: int = _g1.__get(_g3) + 1
_g1.__set(_g3, v)
v
self.exprIndex += 1
if true:
var _g3: Variant = self.ast[self.exprIndex].get("expr")
if (_g3._index == 5):
var _g4: String = _g3.name
var label: String = _g4
var firstStitch: StoryNode = self.resolveNodeInScope(label, newScopes)[0]
if true:
var _g5: StoryNode = firstStitch
var _g1: Variant = self.viewCounts
if true:
var v: int = _g1.__get(_g5) + 1
_g1.__set(_g5, v)
v
self.exprIndex += 1
else:
pass
self.weaveDepth = 0
5:
var _g2: String = _g.name
if true:
var _g3: StoryNode = target2
var _g1: Variant = self.viewCounts
if true:
var v: int = _g1.__get(_g3) + 1
_g1.__set(_g3, v)
v
var enclosingKnot: StoryNode = newScopes[1]
if (self.nodeScopes.find(enclosingKnot) == -1):
var _g3: StoryNode = enclosingKnot
var _g1: Variant = self.viewCounts
if true:
var v: int = _g1.__get(_g3) + 1
_g1.__set(_g3, v)
v
self.exprIndex += 1
self.weaveDepth = 0
10:
var _g2: Variant = _g.c
var c: Variant = _g2
self.storedFrame = { "_index": 0, "v": self.finalTextProcessing(self.evaluateChoice(c)) }
return
_:
pass
self.nodeScopes = newScopes
func choose(choiceIndex: int) -> String:
var nf: Variant = self.nextFrame()
var tempBool
if (nf._index == 1):
var _g: Array[String] = nf.choices
var _g2: Array[Variant] = nf.tags
tempBool = true
else:
tempBool = false
if (!(tempBool)):
assert(false, str("Trying to make a choice when next frame is " + str(nf)))
if (self.embeddedBlocks.size() > 0 && self.embedMode == EmbedMode.EmbedMode.Tunnel):
return self.embeddedBlocks[0].choose(choiceIndex)
else:
var output: String = self.evaluateChoice(self.availableChoices()[choiceIndex])
if (self.embedMode == EmbedMode.EmbedMode.Thread):
self.embedMode = EmbedMode.EmbedMode.Tunnel
self.embeddedBlocks = ([] as Array[Story])
return output
func evaluateChoice(choice: Variant) -> String:
if true:
var _g: Variant = choice.get("label")
match (_g._index):
0:
var _g2: String = _g.v
var l: String = _g2
var tempMaybeStoryNode
if true:
var _g3: Array[StoryNode] = self.resolveNodeInScope(l, null)
if (_g3.size() == 0):
tempMaybeStoryNode = self.storyTree.nodeForChoice(choice.get("id"))
else:
var nodePath: Array[StoryNode] = _g3
tempMaybeStoryNode = nodePath[0]
var node = tempMaybeStoryNode
if true:
var _g3 = node
var _g1: Variant = self.viewCounts
if true:
var v: int = _g1.__get(_g3) + 1
_g1.__set(_g3, v)
v
1:
pass
self.weaveDepth = choice.get("depth") + 1
if (choice.get("onceOnly")):
self.choicesTaken.push_back(choice.get("id"))
if true:
var _g: Variant = choice.get("divertTarget")
match (_g._index):
0:
var _g2: String = _g.v
var t: String = _g2
self.divertTo(t)
1:
self.exprIndex = ASTExtension.indexOfChoice(self.ast, choice.get("id")) + 1
var output: String = choice.get("output").format(self, self.hInterface, self.random, self.altInstances, self.nodeScopes, true)
return self.finalChoiceOutputProcessing(output)
func runEmbeddedHank(h: String) -> void:
self.embedMode = EmbedMode.EmbedMode.Tunnel
self.embeddedBlocks.push_back(self.embeddedStory(h))
func removeDoubleSpaces(t: String) -> String:
var intermediate: String = t
while (intermediate.find(" ") != -1):
intermediate = StringTools.replace(intermediate, " ", " ")
return intermediate
func finalTextProcessing(t: String) -> Variant:
if (t.length() > 0):
return { "_index": 0, "text": StringTools.trim(self.removeDoubleSpaces(t)) }
else:
return self.nextFrame()
func finalChoiceOutputProcessing(t: String) -> String:
return StringTools.trim(self.removeDoubleSpaces(t))
func finalChoiceProcessing(choices: Array[String], tags: Array[Variant]) -> Variant:
var _g: Array[String] = ([] as Array[String])
var _g1: int = 0
while (_g1 < choices.size()):
var c: String = choices[_g1]
_g1 += 1
_g.push_back(StringTools.trim(self.removeDoubleSpaces(c)))
return { "_index": 1, "choices": _g, "tags": tags }
func formatForInsertion(value) -> String:
if (value == null):
assert(false, str("Trying to format null for insertion!"))
return str(value)
func run(showText, showChoices, finish) -> void:
var _gthis: Story = self
var tempFunction
if true:
var _g: Story = self
tempFunction = func() -> void:
return _g.run(showText, showChoices, finish)
if true:
var _g: Variant = self.nextFrame()
match (_g._index):
0:
var _g2: String = _g.text
var text: String = _g2
showText.call(text, func() -> void:
tempFunction.call())
1:
var _g1: Array[String] = _g.choices
var _g2: Array[Variant] = _g.tags
var choices: Array[String] = _g1
var tags: Array[Variant] = _g2
showChoices.call(choices, tags, func(choiceIndex: int) -> void:
_gthis.choose(choiceIndex)
tempFunction.call())
2:
finish.call()
static func FromAST(script: String, ast2: Array[Variant], randomSeed = null) -> Story:
var random2: Random = Random.new(randomSeed)
var storyTree2: StoryNode = StoryNode.FromAST(ast2)
var nodeScopes2: Array[StoryNode] = ([storyTree2] as Array[StoryNode])
var viewCounts2: Variant = storyTree2.createViewCounts()
var hInterface2: HInterface = HInterface.new(storyTree2, viewCounts2)
var story: Story = Story.new(random2, HParser.new(), ast2, storyTree2, nodeScopes2, viewCounts2, hInterface2)
hInterface2.setStory(story)
hInterface2.addVariable("story", story)
story.runRootIncludedHaxe(script)
story.exprIndex = ASTExtension.findFile(ast2, script)
return story
static func FromFile(script: String, files = null, randomSeed = null) -> Story:
var parser2: HParser = HParser.new()
var ast2: Array[Variant] = parser2.parseFile(script, files, false)
return Story.FromAST(script, ast2, randomSeed)