Compare commits
3 Commits
| 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 input = Sys.stdin();
|
||||||
|
|
||||||
var debug = false;
|
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();
|
var path = input.readLine();
|
||||||
path = if (path.length == 0) {
|
path = if (path.length == 0) {
|
||||||
"examples/main.hank";
|
"examples/main/main.hank";
|
||||||
} else {
|
} else {
|
||||||
if (path.charAt(0) == '*') {
|
if (path.charAt(0) == '*') {
|
||||||
debug = true;
|
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);
|
story.loadScript(path);
|
||||||
var frame = StoryFrame.Finished;
|
var frame = StoryFrame.Finished;
|
||||||
do {
|
do {
|
||||||
|
|||||||
80
src/Story.hx
80
src/Story.hx
@@ -721,7 +721,17 @@ class Story {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var availableChoices = [for (choice in collectChoicesToDisplay()) choice.text];
|
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
|
// Execute gathers by updating the choice depth and continuing from that point
|
||||||
case Gather(label, depth, nextPartType):
|
case Gather(label, depth, nextPartType):
|
||||||
@@ -867,20 +877,8 @@ class Story {
|
|||||||
case Some(target):
|
case Some(target):
|
||||||
divert(target);
|
divert(target);
|
||||||
case None:
|
case None:
|
||||||
// Otherwise, find the line where the choice taken occurs
|
// Otherwise, follow the choice's branch.
|
||||||
for (i in currentLineIdx...scriptLines.length) {
|
gotoChoiceBranch(choiceTaken);
|
||||||
switch (scriptLines[i].type) {
|
|
||||||
case DeclareChoice(choice):
|
|
||||||
if (choice.id == choiceTaken.id) {
|
|
||||||
gotoLine(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move to the line following this choice.
|
|
||||||
stepLine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the choice's index to the transcript
|
// Log the choice's index to the transcript
|
||||||
@@ -889,6 +887,23 @@ class Story {
|
|||||||
return choiceTaken.text;
|
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.
|
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.
|
@return If a suitable gather is found, Empty. Otherwise, an Error frame.
|
||||||
@@ -974,6 +989,35 @@ class Story {
|
|||||||
return choices;
|
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
|
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
|
// Check that the choice hasn't been chosen before if it is a one-time-only
|
||||||
if (choicesEliminated.indexOf(choice.id) == -1) {
|
if (choicesEliminated.indexOf(choice.id) == -1) {
|
||||||
// fill the choice's h expressions after removing the flag expression
|
// 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 {
|
} else {
|
||||||
// debugTrace('can\'t display "${choice.text}" because it has expired.');
|
// debugTrace('can\'t display "${choice.text}" because it has expired.');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class StoryTestCase extends utest.Test {
|
|||||||
try {
|
try {
|
||||||
frame = story.nextFrame();
|
frame = story.nextFrame();
|
||||||
} catch (e: Dynamic) {
|
} catch (e: Dynamic) {
|
||||||
trace('Error at ${story.lastLineID}');
|
trace('Error at ${story.lastLineID} while validating ${transcriptFile}');
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@@ -69,13 +69,19 @@ class StoryTestCase extends utest.Test {
|
|||||||
continue;
|
continue;
|
||||||
} else if (StringTools.startsWith(line, ">>>")) {
|
} else if (StringTools.startsWith(line, ">>>")) {
|
||||||
// Make the choice given.
|
// Make the choice given.
|
||||||
var output = story.choose(Std.parseInt(StringTools.trim(line.substr(3))));
|
try {
|
||||||
if (debugPrints) {
|
var output = story.choose(Std.parseInt(StringTools.trim(line.substr(3))));
|
||||||
trace(output);
|
if (debugPrints) {
|
||||||
}
|
trace(output);
|
||||||
if (fullTranscript || transcriptLines.length > i+1) {
|
}
|
||||||
// Assert that its output equals the transcript's expected choice output.
|
if (fullTranscript || transcriptLines.length > i+1) {
|
||||||
Assert.equals(transcriptLines[++i], output);
|
// 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, "#")) {
|
} else if (StringTools.startsWith(line, "#")) {
|
||||||
// Allow comments in a transcript that need not be validated in any way
|
// Allow comments in a transcript that need not be validated in any way
|
||||||
|
|||||||
Reference in New Issue
Block a user