I’ve been trying to get Dan for a while now to make some changes to ChoiceScript to match the particular dialect that I use. I wrote Choice of Robots and Choice of Alexandria using these changes, and now I’m writing Choice of Magics this way. These discussions with Dan typically take place in a vacuum, where I’ve got my arguments and he’s got his, and mostly they don’t go anywhere. But it occurred to me that others don’t even really know that I write ChoiceScript with a few different features added, and maybe telling you about the changes could inspire you to hack ChoiceScript yourself, or (I wish) clamor that my changes be merged into the main branch.
The main two features of the way that I write ChoiceScript are fewer gotos and using arguments for gosub. I see both of these as really nice for transforming complex ChoiceScript into less complex ChoiceScript. I’ll tell you about each and why and how I did it; if you’re motivated and know computer science, you can replicate them yourself, and if you’re motivated and don’t know computer science, I suppose you can ask ChoiceScript to support them.
*Fewer gotos. ChoiceScript is unique as a language in requiring gotos (or returns) to get out of if/else statements. My dialect looks like this:
*if autonomy >= 50 "I have an idea," says ${robot_name}. *else "What should we do?" asks ${robot_name}. $!{rhe} looks *if autonomy >= 50 excited. *else worried.
In this dialect, it’s assumed that if and else will fall out to the next line with the same indentation as the “if.” I find that using less gotos like this achieves a few things. One, I’m not stopping to think of label names every few seconds. Two, genuinely meaningful jumps that go someplace entirely different in the code stand out more; a goto always means something unusual is going on, as it does in other languages.
*if robot_dead *comment Skip ahead to after the robot leaves *goto someLaterScene *elseif robot_happy "Hello," ${robot_name} brightly. *else Your robot looks upset. How do you respond?
This change is IIRC fairly easy to accomplish if you want to modify scene.js – just comment out the part of the if-handling code where it gives the error when you don’t have a goto.
Doing the same thing for choices is slightly trickier. fake_choice can do one level like this, but not nested levels; by default, ChoiceScript doesn’t keep track of where to jump to in the outer fake_choices if you pop out of an inner one. So I modified the interpreter to remember what those line numbers to jump to are, when exiting choice code. So my choices don’t end in goto either; they always are assumed to go to the next line at the same indent as the *choice. Same reason - I want a goto to really signify we’re going somewhere weird. (Not coming up with labels is also nice.)
Dan’s fairly convinced that requiring the gotos prevents bugs. I’m not so sure - Python works very much this way, and all other languages have some way of signaling that the if/else is done that doesn’t require goto, like “endif” or a close curly brace. Most languages really try hard to avoid goto.
*Arguments. ChoiceScript doesn’t have full-fledged “functions” in the sense of other computer languages - pieces of code that take some inputs (“arguments”) and perform some activities based on their input. The closest thing it has are subroutines with *gosub, which jumps and returns like a function, but can’t take any arguments.
I found this unfortunate in Choice of Robots, because I wanted to report variable changes that always matched the text. (For example, “Autonomy++” for a gain of two autonomy.) Doing these things independently seemed like it would allow for bugs where one change happened, but another was reported - printing two pluses but only gaining one autonomy, for example. Furthermore, I wasn’t totally sure that the reporting of stat changes would survive the editorial process, and I wouldn’t want to go through the whole game and change how they were all reported.
So instead, I modified gosub so that it would take “arguments.” It was hacky because I didn’t expect to merge to the main ChoiceScript branch, but essentially, if I wrote something like
*gosub bump “autonomy” 2
then a variable arg0 would be set to the string “autonomy,” the variable arg1 would be set to 2, and I’d jump to a gosub called “bump” that would set the variable mentioned in the string to the value in the variable, and also report the change by putting down a number of +'s or -'s equal to the change. If I wanted to change a bump, I would change only one number. If I wanted to change how the stat changes were reported, I could do it in one place. (Well, a few places, since either gosub_scene didn’t work at the time or I wasn’t aware of it.)
I admit, that’s about all I’ve used said functions for, but it’s quite handy for that, as all my stat changes would be a lot more cumbersome without it.
None of these changes required a whole lot of code, but they do require a bit of picking at the code every time I bother to update my ChoiceScript, which means that happens basically once per game I write. Still, I find these features indispensable.
I hope you found this interesting, and you’re now thinking not just about how ChoiceScript is, but how it could be.