Implicit control flow is an optional modification to the way ChoiceScript behaves. It changes ChoiceScript's requirements by removing the need for *finish, *goto, *goto_scene, or *ending at the end of *choice, *elseif, and *else.
Devblog update: https://forum.choiceofgames.com/t/new-choicescript-features-implicit-control-flow-gosub-parameters/30042
Usage[]
To use implicit control flow, simply add the following to your *create variable section in startup.txt:
*create implicit_control_flow true
(You can also *set implicit_control_flow true or false in other places in your scene files.)
Features[]
Usually, in ChoiceScript, the code below would produce an error:
*temp player_gender "Male" Coming inside, your friend greets you. *if (player_gender = "Female") "Hey girl!" *elseif (player_gender = "Nonbinary") "Hey pal!" *else "Hey man!" Still smilling, your friend continues, "How're you today?"
The error produced here would be:
"It is illegal to fall in to an *else statement; you must *goto or *finish before the end of the indented block."
As seen in the previous *choice, *elseif, and *else pages, ChoiceScript requires that all of these commands have either a *finish, *goto, *goto_scene, or *ending at the end of the code block; otherwise, the code will throw up an error.
So to correct that, the following changes would be needed:
*temp player_gender "Male" Coming inside, your friend greets you. *if (player_gender = "Female") "Hey girl!" *goto smilling_friend *elseif (player_gender = "Nonbinary") "Hey pal!" *goto smilling_friend *else "Hey man!" *goto smilling_friend *label smilling_friend Still smilling, your friend continues, "How're you today?"
As seen here, if you want to simply proceed down the file, the use of several *gotos is quite redundant. Games that feature heavy use of *if, *elseif, and *else might end up with dozens or more of these extra *gotos and *labels just to proceed through simple *if/*elseif/*else sequences.
By using implicit control flow, however, the requirement for *gotos and *labels is removed. Our very first code example would not flag any errors and would proceed down the code without requiring the extra *gotos and *labels.
When implicit control flow is enabled, *choice works basically the same as *fake_choice, except you can also freely nest a *choice inside another *choice with implicit control flow.
Notes[]
Implicit control flow is convenient, but ChoiceScript's original behavior can help find unintended bugs.
*choice #Be very naughty. Santa refuses to give you a present. #Be mostly nice. Santa gives you a present reluctantly. #Be as nice as can be. Santa gives you a present enthusiastically. Inside the gift box is a video game!
In the following example, the *choice will behave as a *fake_choice; all options will proceed down the code. If choice #1 is selected, it will mention that Santa refused to give you a present—but because the code continues down, you will still receive the video game.
Therefore, if the author forgets to include a different branch for choice #1, the previous error message could remind them to check their branching.
Another advantage of implicit control flow is that branches are immediately apparent because of using *goto; any #options that do not branch will not have a *goto following.
To use the Santa example, with original CS, you would need:
*choice #Be very naughty. Santa refuses to give you a present. *goto coal #Be mostly nice. Santa gives you a present reluctantly. *goto present #Be as nice as can be. Santa gives you a present enthusiastically. *goto present *label present Inside the gift box is a video game! *finish *label coal You get a lump of coal instead. *finish
With implicit control flow, the same passage could read:
*choice #Be very naughty. Santa refuses to give you a present. *goto coal #Be mostly nice. Santa gives you a present reluctantly. #Be as nice as can be. Santa gives you a present enthusiastically. Inside the gift box is a video game! *finish *label coal You get a lump of coal instead. *finish
It's immediately visible that #option A branches, but #B and #C fall directly through.
Known Bug[]
While implicit control flow seems like a powerful tool, there's a specific scenario where it doesn't function well.
*label haha *choice #varA *set varB true text *if varB #varB text *finish *goto haha
The expected interpretation of that code would be the player is given an only choice #varA upon the first visit of "haha". Picking #varA would loop the player back to the beginning of "haha," but now they can pick either #varA again or the now-revealed #varB.
However, CS throws an error when the player picks #varA: "It is illegal to fall out of a *choice statement; you must *goto or *finish before the end of the indented block." Removing the command *set varB true, however, fixes the error, although the player will now be trapped in an infinite limbo of #varA. A simple fix would be moving the *goto hehe inside the body of #varA, though this begs up the question, "why bother using implicit control flow at all?"
Comparison[]
Here is a simple visual comparison with ICF on and off:
| More commands / functions | |
| Choice | *choice, *fake_choice, *disable_reuse, *hide_reuse, *allow_reuse, *selectable_if |
|---|---|
| Variable | *create, *temp, *set, Arithmetic operators, *delete, *input_number, *input_text, *print, *rand |
| Conditional | *if, *elseif, *else, Multireplace |
| Goto | *label, *goto, *goto_scene, *goto_random_scene, *gosub, *gosub_scene, *finish |
| Formatting | Bold text, Italic text, *image, *line_break, *page_break, *link, *stat_chart |
| Miscellaneous | *comment, *scene_list, *title, *author, *achieve, *achievement, *check_achievements, *bug, *ending, *more_games, *share_this_game, *show_password, *script, Implicit Control Flow |