Thanks to a big code contribution from @kgold, there are new features to try in the latest version of ChoiceScript up on GitHub: implicit control flow and *gosub
parameters.
Implicit control flow
Normally, you have to use a *goto
before the end of a *choice
#option
or before an *else
, or you’ll get errors like this:
It is illegal to fall out of a *choice statement; you must *goto or *finish before the end of the indented block.
It is illegal to fall in to an *else statement; you must *goto or *finish before the end of the indented block.
With implicit control flow, those errors go away; ChoiceScript will just skip over subsequent #options
or *else
blocks automatically.
Implicit control flow is off by default. To use it, you have to *create implicit_control_flow true
in startup.txt
. (You can also *set implicit_control_flow true
or false
in other places in your scene files.)
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.
Implicit control flow is convenient, but it can also make it harder to find bugs, like this one.
*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 this example of implicit control flow, you still get a present even if you’re very naughty. It can be hard to catch this bug when you have long #options
that fill a few screens of text.
ChoiceScript’s traditional approach (“explicit control flow”) forces you to add a *goto
before #Be mostly nice
; if you give the label a good name, like *label present
, the error should be easier to find, like this:
*choice
#Be very naughty.
Santa refuses to give you a present.
*goto present
#Be mostly nice.
Santa gives you a present reluctantly.
*goto present
#Be as nice as can be.
Santa gives you a present enthusiastically.
*label present
Inside the gift box is a video game!
This bug may be easier to find, because “refuses to give you a present” is right next to *goto present
which is clearly wrong.
It’s possible that everybody will love ICF so much that we’ll enable it by default at some point. It certainly makes it easier to get your ideas down on the page; we’ll see if it comes at the expense of having more bugs.
*gosub
parameters and *params
This one’s for the programmer-ish folks out there. Now you can write code like this:
*gosub visit "Dracula" "garlic"
*label visit
*params
You go to visit ${param_1} and you bring ${param_2} with you.
*return
After the *gosub
label, you can include any number of parameters. When you use the *params
command, it sets temps named param_1
, param_2
, etc. for each parameter. (It also sets a param_count
temp with the number of parameters; in this case, param_count
would be 2.)
Since param_1
and param_2
are not very good names, you might be tempted to write some code like this:
*params
*temp person param_1
*temp gift param_2
We anticipated that; you can just write the names of the parameters after *params
and we’ll set the temps for you, like this:
*label visit
*params person gift
You go to visit ${person} and you bring ${gift} with you.
*return
You can also use parameters with *gosub_scene
. *gosub_scene
already allows you to optionally specify a label, e.g. *gosub_scene travel visit
so if you want to pass in parameters to *gosub_scene
, you must specify a label name. *gosub_scene visit "Dracula"
won’t use a parameter; it will try to *gosub
the label “Dracula”.
Programmer-ish people should note that parameters are just ordinary *temp
s and are scoped to the entire file, not the subroutine. Thus, if you *gosub
within a *gosub
, param_1
can and will be changed in the second subroutine and will not be restored when you *return
.
However, *gosub_scene
defines a new scope for *temp
, so if you want your *params
to be scoped to the subroutine, (for example if you want to use recursion,) you can use *gosub_scene
instead of *gosub
.
Here’s an example of a naive recursive Fibonacci subroutine. (Note that it can only work with *gosub_scene
; it wouldn’t work with *gosub
because of parameter scopes.)
*create return 0
*gosub_scene startup fib 6
${return}
*finish
*label fib
*params n
*if n < 2
*set return 1
*return
*gosub_scene startup fib (n-1)
*temp prev return
*gosub_scene startup fib (n-2)
*set return +prev
*return
Let us know if you run into bugs with these new features!