This commit is contained in:
2019-06-06 10:34:31 -06:00
parent 075f001107
commit b99e3e6050
8 changed files with 116 additions and 18 deletions

View File

@@ -0,0 +1,30 @@
~ var target=null;
-> start
== start
Mind: {if (unreachable_knot.choice2) "blown" else "not blown"}
{sequence:
~target = ->unreachable_knot.choice1;|
~target = ->unreachable_knot.choice2;|
~target = ->unreachable_knot.choice3;
}
-> @target
== unreachable_knot
This text should be like a ninja. Completely silent.
* (choice1) Choice 1 [Ninja!] has been made.
The content following choice 1 will be played out.
** Including deeper choices!
** Beep Boop
* (choice2) Choice 2 has been made.
* (choice3) [Ninja!]
-> finish
- ->start
== finish
Is your mind blown yet?

View File

@@ -0,0 +1,10 @@
Mind: not blown
Choice 1 has been made.
The content following choice 1 will be played out.
* Including deeper choices!
* Beep Boop
> 1: Including deeper choices!
Mind: not blown
Choice 2 has been made.
Mind: blown
Is your mind blown yet?

View File

@@ -36,8 +36,9 @@ class Alt {
public static function parse(buffer: HankBuffer): Option<Alt> {
var rawExpr = buffer.findNestedExpression('{', '}').unwrap().checkValue();
// trace(rawExpr);
var expr = rawExpr.substr(1, rawExpr.length-2);
var expr = rawExpr.substr(1, rawExpr.length-2).ltrim();
// trace (expr);
// Sequences are the default behavior
var behavior = Sequence;
@@ -45,14 +46,22 @@ class Alt {
if (expr.startsWith(prefix)) {
expr = expr.substr(prefix.length).trim();
behavior = behaviorMap[prefix];
break; // <-- Finally figured that one out.
}
}
// trace (behavior);
var outputsBuffer = HankBuffer.Dummy(expr);
var eachOutputExpr = outputsBuffer.rootSplit('|');
if (eachOutputExpr.length == 1) {
return None; // If no pipe is present, it's not an alt
}
var outputs = [for(outputExpr in eachOutputExpr) Output.parse(HankBuffer.Dummy(outputExpr), true)];
// There can't be newlines preceding the arms of alt expressions, or everything following the newline will disappear -- hence ltrim() below.
var outputs = [for(outputExpr in eachOutputExpr) Output.parse(HankBuffer.Dummy(outputExpr.ltrim()), true)];
// trace(outputs);
buffer.take(rawExpr.length);
return Some(new Alt(behavior, outputs));

View File

@@ -42,7 +42,7 @@ class HankInterp extends Interp {
}
// TODO Divert target variables are just StoryNode values
case EUnop("->", true, e):
trace(e);
// trace(e);
var targetWithDots = '';
var trailingDot = false;
while (true) {
@@ -50,6 +50,7 @@ class HankInterp extends Interp {
case EIdent(lastTarget):
targetWithDots = lastTarget + '.' + targetWithDots;
if (trailingDot) targetWithDots = targetWithDots.substr(0,targetWithDots.length-1);
// trace('final target is $targetWithDots');
var node = story.resolveNodeInScope(targetWithDots);
trace (node);
return node;

View File

@@ -11,7 +11,8 @@ import hank.Alt.AltInstance;
enum OutputType {
Text(t: String); // Pure text that is always displayed
AltExpression(a: Alt);
HExpression(h: String); // An embedded Haxe expression whose value will be inserted
HExpression(h: String); // A Haxe expression inside braces whose value will be inserted
HCall(h: String); // An embedded Haxe expression preceded by ~. Will not be inserted
InlineDivert(t: String); // A divert statement on the same line as an output sequence.
ToggleOutput(o: Output, invert: Bool); // Output that will sometimes be displayed (i.e. [bracketed] section in a choice text or the section following the bracketed section)
}
@@ -78,11 +79,20 @@ class Output {
if (peekLine.length < endSegment) {
var text = buffer.takeLine().unwrap();
if (text.length > 0) {
if (text.ltrim().startsWith('~'))
parts.push(HCall(text.ltrim().substr(1)));
else
parts.push(Text(text));
}
break;
} else {
var text = buffer.take(endSegment);
// If text starts with ~, it's actually a silent HCall.
if (text.ltrim().startsWith('~'))
parts.push(HCall(text.ltrim().substr(1)));
else
parts.push(Text(text));
}
} else {
@@ -168,6 +178,8 @@ class Output {
diverted = false; // Clear the diverted flag
// trace(parts);
for (part in parts) {
switch (part) {
case Text(t):
@@ -196,7 +208,9 @@ class Output {
else {
fullOutput += value;
}
case HCall(h):
hInterface.runEmbeddedHaxe(h,scope);
// Don't append anything
case InlineDivert(t):
// follow the divert. If the next expression is an output, concatenate the pieces together. Otherwise, terminate formatting
story.divertTo(t);

View File

@@ -297,6 +297,8 @@ class Story {
var newScopes = if (target.startsWith("@")) hInterface.getVariable(target.substr(1)) else resolveNodeInScope(target);
// trace('$target is $newScopes');
var targetIdx = newScopes[0].astIndex;
if (targetIdx == -1) {
@@ -361,8 +363,16 @@ class Story {
// if the choice has a label, increment its view count
switch (choice.label) {
case Some(l):
var node = resolveNodeInScope(l)[0];
viewCounts[node] += 1;
var node = switch (resolveNodeInScope(l)) {
case []:
// the choice is being diverted to, which means it's out of scope. Find it from the StoryTree's choice map another way
storyTree.nodeForChoice(choice.id);
case nodePath:
nodePath[0];
};
viewCounts[node] += 1;
case None:
}
weaveDepth = choice.depth + 1;

View File

@@ -138,6 +138,9 @@ class StoryTestCase extends utest.Test {
// Make the choice given, and check for expected output.
line = line.substr(1).ltrim();
var firstColonIdx = line.indexOf(':');
if (firstColonIdx == -1) {
throw 'Choice line in transcript does not specify expected output: > $line';
}
var index = Std.parseInt(line.substr(0, firstColonIdx))-1;
var expectedOutput = line.substr(firstColonIdx+1).trim();
// trace('expecting: ${expectedOutput}');

View File

@@ -7,7 +7,14 @@ import hank.LogUtil.watch;
class StoryNode {
public var astIndex(default, null): Int;
var children: Map<String, StoryNode> = new Map();
/**
The root StoryNode maps all choice IDs to the choices' nodes, so that when diverting to choices, we can easily resolve out-of-scope targets to increment their view counts
*/
var choiceNodes: Map<Int, StoryNode> = new Map();
public function nodeForChoice(id: Int) {
return choiceNodes[id];
}
public function toString() {
return '{Node@${astIndex}: ${[for(key in children.keys()) key]}}';
}
@@ -68,7 +75,27 @@ class StoryNode {
var node = new StoryNode(exprIndex);
lastKnot.addChild(name, node);
lastStitch = node;
case EGather(Some(name), _, _) | EChoice({id: _, onceOnly: _, label: Some(name), condition: _, depth: _, output: _, divertTarget: _}):
case EGather(Some(name), _, _):
leafNode(exprIndex, root, lastKnot, lastStitch, name);
case EChoice({id: choiceId, onceOnly: _, label: Some(name), condition: _, depth: _, output: _, divertTarget: _}):
var choiceNode = leafNode(exprIndex, root, lastKnot, lastStitch, name);
// keep a map of choiceId to node
root.choiceNodes[choiceId] = choiceNode;
default:
}
exprIndex++;
}
return root;
}
static function leafNode(exprIndex, root, lastKnot, lastStitch, name) {
var node = new StoryNode(exprIndex);
if (lastKnot == null) {
// watch(root);
@@ -80,12 +107,6 @@ class StoryNode {
// watch(lastStitch);
lastStitch.addChild(name, node);
}
default:
}
exprIndex++;
}
return root;
}
return node;
}
}