Compare commits
3 Commits
nested-alt
...
fallback
| Author | SHA1 | Date | |
|---|---|---|---|
| 843e01e888 | |||
| 712852673a | |||
| e67f68a51b |
18
examples/fallback/main.hank
Normal file
18
examples/fallback/main.hank
Normal file
@@ -0,0 +1,18 @@
|
||||
-> start
|
||||
|
||||
== start
|
||||
* Sometimes
|
||||
* you try everything
|
||||
* and everything
|
||||
* and nothing seems to get you anywhere.
|
||||
* -> end
|
||||
- -> start
|
||||
|
||||
== end
|
||||
This is what fallback choices are for!
|
||||
* One other thing -> end
|
||||
* ->
|
||||
You can put content in a fallback choice using the "choice then arrow" syntax.
|
||||
|
||||
|
||||
/* TODO test the situation where we want to allow multiple fallbacks, each one having a different condition */
|
||||
24
examples/fallback/test1.hlog
Normal file
24
examples/fallback/test1.hlog
Normal file
@@ -0,0 +1,24 @@
|
||||
* Sometimes
|
||||
* you try everything
|
||||
* and everything
|
||||
* and nothing seems to get you anywhere.
|
||||
>>> 3
|
||||
and nothing seems to get you anywhere.
|
||||
* Sometimes
|
||||
* you try everything
|
||||
* and everything
|
||||
>>> 0
|
||||
Sometimes
|
||||
* you try everything
|
||||
* and everything
|
||||
>>> 1
|
||||
and everything
|
||||
* you try everything
|
||||
>>> 0
|
||||
you try everything
|
||||
This is what fallback choices are for!
|
||||
* One other thing
|
||||
>>> 0
|
||||
One other thing
|
||||
This is what fallback choices are for!
|
||||
You can put content in a fallback choice using the "choice then arrow" syntax.
|
||||
@@ -5,10 +5,10 @@ class RunStoryDemo {
|
||||
var input = Sys.stdin();
|
||||
|
||||
var debug = false;
|
||||
trace("Enter a path to a hank story file. Prepend with * for debug prints. (default is examples/main.hank): ");
|
||||
trace("Enter a path to a hank story file. Prepend with * for debug prints. (default is examples/main/main.hank): ");
|
||||
var path = input.readLine();
|
||||
path = if (path.length == 0) {
|
||||
"examples/main.hank";
|
||||
"examples/main/main.hank";
|
||||
} else {
|
||||
if (path.charAt(0) == '*') {
|
||||
debug = true;
|
||||
@@ -18,7 +18,7 @@ class RunStoryDemo {
|
||||
}
|
||||
}
|
||||
|
||||
var story: Story = new Story(false, StringTools.replace(path, '.hank', '.hog'));
|
||||
var story: Story = new Story(false, StringTools.replace(path, '.hank', '.hlog'));
|
||||
story.loadScript(path);
|
||||
var frame = StoryFrame.Finished;
|
||||
do {
|
||||
|
||||
80
src/Story.hx
80
src/Story.hx
@@ -721,7 +721,17 @@ class Story {
|
||||
}
|
||||
|
||||
var availableChoices = [for (choice in collectChoicesToDisplay()) choice.text];
|
||||
return if (availableChoices.length > 0) HasChoices(availableChoices) else Error("Ran out of available choices.");
|
||||
if (availableChoices.length > 0) {
|
||||
return HasChoices(availableChoices);
|
||||
}
|
||||
else {
|
||||
var fallback = collectFallbackChoice();
|
||||
if (fallback != null) {
|
||||
return chooseFallbackChoice(fallback);
|
||||
} else {
|
||||
throw 'Ran out of available choices at ${currentLineMapKey()}.';
|
||||
}
|
||||
}
|
||||
|
||||
// Execute gathers by updating the choice depth and continuing from that point
|
||||
case Gather(label, depth, nextPartType):
|
||||
@@ -867,20 +877,8 @@ class Story {
|
||||
case Some(target):
|
||||
divert(target);
|
||||
case None:
|
||||
// Otherwise, find the line where the choice taken occurs
|
||||
for (i in currentLineIdx...scriptLines.length) {
|
||||
switch (scriptLines[i].type) {
|
||||
case DeclareChoice(choice):
|
||||
if (choice.id == choiceTaken.id) {
|
||||
gotoLine(i);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the line following this choice.
|
||||
stepLine();
|
||||
// Otherwise, follow the choice's branch.
|
||||
gotoChoiceBranch(choiceTaken);
|
||||
}
|
||||
|
||||
// Log the choice's index to the transcript
|
||||
@@ -889,6 +887,23 @@ class Story {
|
||||
return choiceTaken.text;
|
||||
}
|
||||
|
||||
private function gotoChoiceBranch(choiceTaken: Choice) {
|
||||
// find the line where the choice taken occurs
|
||||
for (i in currentLineIdx...scriptLines.length) {
|
||||
switch (scriptLines[i].type) {
|
||||
case DeclareChoice(choice):
|
||||
if (choice.id == choiceTaken.id) {
|
||||
gotoLine(i);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Move to the line following this choice.
|
||||
stepLine();
|
||||
}
|
||||
|
||||
/**
|
||||
Search for the next gather that applies to the current choice depth, and follow it.
|
||||
@return If a suitable gather is found, Empty. Otherwise, an Error frame.
|
||||
@@ -974,6 +989,35 @@ class Story {
|
||||
return choices;
|
||||
}
|
||||
|
||||
private function collectFallbackChoice() {
|
||||
var choices = collectRawChoices();
|
||||
for (choice in choices) {
|
||||
if (choice.text.length == 0) {
|
||||
return choice;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
Return the first frame following a fallback choice, pushing the flow forward down that branch.
|
||||
**/
|
||||
private function chooseFallbackChoice(choice: Choice): StoryFrame {
|
||||
switch (choice.divertTarget) {
|
||||
case Some(target):
|
||||
// "choice then arrow syntax" should simply evaluate the lines following this choice
|
||||
if (target.length == 0) {
|
||||
gotoChoiceBranch(choice);
|
||||
return nextFrame();
|
||||
} else {
|
||||
return divert(target);
|
||||
}
|
||||
default:
|
||||
throw 'Syntax error in fallback choice: no ->';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Check if a choice's display condition is satisfied
|
||||
**/
|
||||
@@ -1044,7 +1088,11 @@ class Story {
|
||||
// Check that the choice hasn't been chosen before if it is a one-time-only
|
||||
if (choicesEliminated.indexOf(choice.id) == -1) {
|
||||
// fill the choice's h expressions after removing the flag expression
|
||||
choices.push(choiceToDisplay(choice, chosen));
|
||||
var displayChoice = choiceToDisplay(choice, chosen);
|
||||
// Don't display fallback choices (choices with no text)
|
||||
if (StringTools.trim(displayChoice.text.split('->')[0]).length > 0) {
|
||||
choices.push(displayChoice);
|
||||
}
|
||||
} else {
|
||||
// debugTrace('can\'t display "${choice.text}" because it has expired.');
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class StoryTestCase extends utest.Test {
|
||||
try {
|
||||
frame = story.nextFrame();
|
||||
} catch (e: Dynamic) {
|
||||
trace('Error at ${story.lastLineID}');
|
||||
trace('Error at ${story.lastLineID} while validating ${transcriptFile}');
|
||||
|
||||
throw e;
|
||||
}
|
||||
@@ -69,13 +69,19 @@ class StoryTestCase extends utest.Test {
|
||||
continue;
|
||||
} else if (StringTools.startsWith(line, ">>>")) {
|
||||
// Make the choice given.
|
||||
var output = story.choose(Std.parseInt(StringTools.trim(line.substr(3))));
|
||||
if (debugPrints) {
|
||||
trace(output);
|
||||
}
|
||||
if (fullTranscript || transcriptLines.length > i+1) {
|
||||
// Assert that its output equals the transcript's expected choice output.
|
||||
Assert.equals(transcriptLines[++i], output);
|
||||
try {
|
||||
var output = story.choose(Std.parseInt(StringTools.trim(line.substr(3))));
|
||||
if (debugPrints) {
|
||||
trace(output);
|
||||
}
|
||||
if (fullTranscript || transcriptLines.length > i+1) {
|
||||
// Assert that its output equals the transcript's expected choice output.
|
||||
Assert.equals(transcriptLines[++i], output);
|
||||
}
|
||||
} catch (e: Dynamic) {
|
||||
trace('Error at ${story.lastLineID} while validating ${transcriptFile}');
|
||||
|
||||
throw e;
|
||||
}
|
||||
} else if (StringTools.startsWith(line, "#")) {
|
||||
// Allow comments in a transcript that need not be validated in any way
|
||||
|
||||
Reference in New Issue
Block a user