What are HamsterSpeak flow control commands?

Jump to navigation Jump to search

The term "flow control" originated a long time ago when some guy thought of a program or a script to be a river, flowing from one command to the next one. In this case, flow control commands make the river change direction.

begin and end[edit]

Technically, begin and end are not flow control. However, they are very important to flow control, as they define a "block". Every other flow control command attaches to one or more blocks, so they know which parts of the script to skip.

Further, every script you write is surrounded in begin and end, as the whole script is basically a gigantic block.

Finally, ( is a mnemonic for begin, and ) is a mnemonic for end. Look familiar? Yup, even parameters to functions are blocks.


if ( condition ) then ( true stuff ) else ( false stuff )

if is a common flow control command. It checks condition to see whether it's true or false. If it's any value other than false (which is equal to 0), meaning any non-zero value, it'll run the first block (true stuff), and totally ignore the second block (false stuff) as if it didn't exist. Otherwise, (if the condition is equal to false) it'll run the second block and ignore the first.

It is entirely possible to leave out either of the blocks if you don't want anything to run if it's true/false:

if ( check tag( tag:seen bob ) ) then ( create npc ( 1, 2, 3 ) ) #show bob here
if ( check tag( tag:got key ) ) else ( create npc ( 2, 3, 4 ) ) #create a door


elseif(...) is a shorthand for else ( if(...) which lets you omit a ( ) pair:

if (inventory (item:apple)) then (
  show text box (10)  # "Apples! My favourite!"
) else if (inventory (item:orange)) then (
  show text box (11)  # "My, what a tasty looking orange..."


while ( condition ) do ( commands )

There are two loop commands, and while is one of them. It will run the set of commands in the do block while condition is true. If ever condition is false, it stops looping, and continues with the rest of the script.

while (check tag (tag: seen bob) == false) do ( wait(1) ) #halt the script until we see bob

Often, condition will be modified in some way inside the loop, or externally in another script, to avoid an infinite loop. However, it's perfectly legal to specify a constant (say, true) as the condition, in which case it'll either loop forever (if it's non-zero) or never run (if it is zero).

What good is an infinite loop, you ask? Well, sometimes it's easier to exit from the middle of the loop, so you'll want to use one of the other flow control commands (specifically, break).


for ( counter, start, finish, step) do ( commands )

This is the second looping command. If you need to loop over a series of numbers (1, 2, 3...), then for is a lot easier than using while.

counter is a variable that you must supply. start is the number you want to start counting with, and finish is the last such number. step (which is optional, and defaults to 1) tells how large to increment counter while it's counting from start to finish. Examples are in order:

#assuming i is a variable defined somewhere
for (i, 1, 10, 1) do ( stuff ) #i will be 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

for (i, 1, 10) do ( stuff ) #same thing, counter defaults to 1

for (i, 1, 10, 2) do ( stuff ) #i will be 1, 3, 5, 7, 9

for (i, 2, 10, 2) do ( stuff ) #i will be 2, 4, 6, 8, 10

for (i, 10, 1, -1) do ( stuff ) #i will be 10, 9, 8, 7, 6, 5, 4, 3, 2, 1

And so forth. Note that if you need to count backwards, you have to use a negative step. Otherwise, it won't work.


This is a special kind of block used by while and for. It is also used behind the scenes for scripts. It is only mentioned here, because commands below affect the running of the do block specifically.


This is not a loop or other type of block like the above commands. This is actually a flow control command. Anyway, this will cause the inner-most do block to exit, skipping to the next command after it. If you want to abort more than one do, use something like break(2).

Oh, and, here's an example of an infinite loop (now that we know how one would exit it):

variable (i)
while (true) do, begin
 i += 1
 if (i == 52) then (break)

(No, it doesn't do anything useful. But, it does nothing faster than the equivalent for loop)


Continue is for use inside for and while loops, alot like break. It also does something different inside switch. Continue causes flow to skip to the bottom of the current (innermost) do block, but not to exit it. This means that for and while will continue looping, except you've skipped a single loop through. Like break, you can specify how many do's to skip to the bottom with an argument: eg. continue(2).

# show backdrops 1, 2, 4, 5
count := 0
for(i,1,5) do, begin
 if(i==3) then (continue)
 show backdrop(i)
# here's a harder example:
variable(i, j, count)
count := 0
for(i,1,2) do, begin
 for(j,1,3) do, begin
  if(i==1 && j==2) then (continue (2))
  count += 1
show value(count)
# count incremented for i=1,j=1; i=2,j=1; i=2,j=2; i=2,j=3;
# count == 4

exit script[edit]

This is kind of a special break that will exit the whole script. Whatever value you've set with return (or 0 if you haven't) will be returned to the calling script.

exit returning[edit]

For the lazy scripter in all of us, exit returning combines exit script and return into one command. You can set the return value and exit the script all at once:

script, foo, begin
 exit returning (42)
 show text box (1) #will never be shown!


switch is a replacement for using a whole lot of if statements. The base form is:

switch (<expression>) do, begin
  case (<expression(s)>) do (<statements>)
  [else (<statements>)]

You may have as many case () do () lines as you wish.

When interpreted, <expression> within switch is evaluated to return a value. Then each <expression> within each case block is evaluated in order and checked against the value. If it matches, then the corresponding do block is entered. Once the do block is run, the switch is exited (unless a continue was encountered, see below). No do blocks are run unless they produce a match.

Each case () can contain multiple expressions, these are all evaluated for a match.

case (a, b, c) do (...)


case (a)
case (b)
case (c) do (...)

are identical.

However, the do () following a case is optional unless it is empty. The following are equivalent:

switch(v) do(
  case(-1) do()  # Do nothing
  case(0) do(
    show textbox(1)
  case(1, 2) do(
    show textbox(2)

switch(v) do(
  case(-1)   do()  # Must still put a do() here, otherwise this is merged with case(0)!
  case(0)    show textbox(1)
  case(1, 2) show textbox(2)

The optional else(...) clause (alternatively written as case(else) ... is entered if no cases matched the expression. It must be last inside the switch block.

In C by default switch falls through, so explicit breaks are needed, in HS the default is to not fall through, so explicit continues are needed.

Here's some C code:

switch (foo) {
  case 1:
  case 2:
  case 3:
  case 4:
    printf("Two to Four\n");
  case 5:
  case 6:
    printf("Five or Six\n");
    printf("Number not recognised\n");

Here's what it would look like in HamsterSpeak:

switch (foo) do, begin
  case (1) do (
  case (2, 3, 4) do (
    $0="Two to Four"
  case (5) do (
  case (6) do (
    $0="Five or Six"
  else (
    $0="Number not recognised"

Notice the continue.

Of course, instead of

  case (2, 3, 4) do (
    $0="Two to Four"

you could write

  case (2) do (continue)
  case (3) do (continue)
  case (4) do (
    $0="Two to Four"

but this is less efficient and less clear.

A break inside a do causes the switch block to be immediately exited.


May occur inside a switch block only. No other use.