Lesson 4: Switches



Yes, we're going to learn about switches. Switches allow you to choose paths for which window you display. Later, we will find that switches are what you write your game code in.

A switch statement allows you to check variables and make decisions based on those actions. Variables are names you give numbers whose value _varies_ over time. If you've taken algebra, you know what variables are: X, Y, that kind of thing. We'll talk more about them very soon.

A switch statement looks like this:

#switch 4:1

Switches end with a RETURN or a GOTO statement. Unlike windows, switches are executed invisible to the user. You can use them to manipulate things without the user ever knowing. For example a user crossing over an invisible trigger in a map could set up a whole cast of NPCs somewhere else in the level. At the same time switches can be used to prepare things for a window, or to carry out a command that was indicated by a CHOICE command. Remember, switches are code, and windows are what you see in the window. Let's start looking at commands that start showing you the power of this FULLY OPERATIONAL scripting language!


SET
Ah. Now we're talking. In switches, you can SET variables. Unlike other languages, you don't have to declare the variables. All undeclared variables are assumed to be 0. A variable name can be any name made out of letters and numbers. You can use underscores too. (There are ways to use other characters, but we'll talk about that WAY later!) Let's stick to simple names for now. In a lot of languages, you name functions (what we call switches and windows) starting with a capital letter, then you capitalize each word within. That's called InterCaps, for Internal Capitals. Apple does this a lot, with names like AppleWorks, and so on.
Anyway, variables are usually started with lower case letters, and are either all lowercase, or have intercaps. This is a dumb little style point, but if you use a consistent style, it helps when trying to look over your code. Anyway, below we are going to SET a variable called "talkedToGrumpos". Here we go!

So, say you talked to Grumpos, and if you have, you can now talk to Fleenbot. You could do the statement:

set talkedToGrumpos = 1

Now, this variable never existed before, but now it does, and it has a value of 1. Any variables that are non-zero after your APE script executes will still exist, and will be saved with the game's save file. So, you really can't assume a variable will be zero--because you might have set it the last time you ran this script!

Okay, you can also do computations in a SET statement. (By the way, set statements are also called "assignment" statements, as they assign a value to a variable.)

Here's one where we do a computation:

set x = 12 * 16
set y = -1 * (3 - 1 + (4 / 2))

We will talk about more advanced operators later.
(Programmers, there are no bitwise operations in the language.)

You can also use variables in these expressions:

set actualY = (yt - 1) * tileSize + yPartial

Okay, now the big secret of set, is you actually don't have to use the word SET! You can just do this!

x = y * z

works just fine!


However, most people say that you "set" a variable when you give it a value so that's why you learned about it that way first!

When your program quits, you can set a variable to zero (which will mean it will go away and not be saved in the save game file). There is a command that does this explicitly: unset. You can use unset to set a variable to zero:

unset tempX

It's just another way to do set something to zero.

tempX = 0


Okay, we don't just have normal variables, we also have a thing called an "array", which is a set of indexed related values.
Arrays look like this:

A[44] = 2

We have two sizes: A[] through D[] have 10,000 elements, so you can look at A[0] through A[9999]. E[] through Z[] have 256 elements, so you can look at Q[0] through Q[99]. Note from creaper... Shouldn't this be q[254]? You can use variables to index arrays, but you CAN'T use equations. To use a variable to index an array, enclose it in dollar signs:

index = index + 1
value = A[$index$]

This is very useful for checking a lot of related things in a row. If you have to process a set of numbers or a set of events, put them in an array. For instance, you could use X[] and Y[] as the coordinates of each point in a path for an object to travel on.

Well, once you have variables, how do you check their values and make decisions? It's the magic word "IF".

IF

The IF command allows you to check the value of any number of things, and then do something based on whether those comparisons were true or not.

This is how you use it to go somewhere else:

if (currentX == 10) goto 30:3


Note that when doing comparisons, they must be surrounded by parentheses, and the test for equality is represented by TWO equals signs. This is a convention from the C programming language. (You can actually use just one equals sign, but two is a format nicety.)

There are a number of different comparisons you can make:

if (currentX != 10) goto 30:3 not equal
if (currentX > 10) goto 30:3 greater than
if (currentX < 10) goto 30:3 less than
if (currentX >= 10) goto 30:3 greater than or equal to
if (currentX <= 10) goto 30:3 less than or equal to
if (currentX) goto 30:3 non-zero (same as currentX != 0)

You can compare multiple things in an IF statement. You can check if this AND that are true, and if this OR that are true. Both things have to be true when you use AND, but only one of the things have to be true for the OR, like you'd expect. You can also check to see if only one or the other are true, which is called Exclusive-OR (XOR).

AND is represented by this: &&
OR is represented by this: ||
XOR is represented by this: ^^

This may be a little confusing, so let's see some examples:

if (currentX > 16 && currentY > 16) goto 30:3

So both of these things have to be true for you to go to that window.

if (currentX < 16 || currentX > 320) goto 30:3

So if currentX is less than 16 OR it is greater than 320, we'll go to the window 30:3.

if (x < 40 ^^ y < 40) goto 30:3

This checks to see if only x or only y is less than forty, so we'll only go to that window if we're in the lower left or upper right quadrant of an 80 x 80 space. We won't go there if we're in the upper left quadrant (because they are BOTH less than 40), and we won't go there if we are in the lower right quadrant (because both
x and y will not be less than forty). Only when one of them and not the other one is true, will the expression be true.
Now, this is not often used, and there isn't even a logical exclusive-OR in C, but we have one, so there ya go.
(Again, programmers, there are no bitwise operations in APE.)

Now, what if you want to do something different if the comparison is false? If could do a whole separate IF statement, checking exactly the opposite, but that is stinky. So, we have the ELSE statement.

ELSE
You use else to say, if the comparison is true, do that, but otherwise, do this.

if (playerDied) goto 30:3 go to player died routine
else counter = counter + 1 count how long player stays alive

Now, you can do complex code inside an IF or inside an ELSE. All you have to do is enclose it with "curly brackets"--"{}". So, here's if and else with the curly brackets in use.

if (playerDied)
{
deathCounter = deathCounter - 1
dyingAnimCounter = dyingAnimCounter + 1
if (dyingAnimCounter > 6) dyingAnimCounter = 1
}
else
{
playerAnimCounter = playerAnimCounter + 1
if (playerAnimCounter > 4) playerAnimCounter = 1
}
<next statement goes here>

Okay, we got a little fancy here! Not only did we put code sections inside curly brackets, we put an IF statement inside that section. Putting an IF inside a clause of an IF is called having "nested IFs". It's like one is nesting inside the other.
Anyway, in the above, if playerDied is non-zero, the first section of code will be executed. If it IS zero, then the second section (the else section) will get executed. Then execution continues with the <next statement> after the else.

There's another different way to have a subsection of code, and it is very powerful. It is called GOSUB.

GOSUB
This allows you to execute a WHOLE OTHER SWITCH, then return to the switch you're in and keep going. In programming languages, this other section of code is called a "subroutine": a routine that is a small routine that is either useful to many different routines, useful to this routine many times, or perhaps this routine has
gotten so big, it is hard to understand, so breaking the code into sensible chunks will make it WAY more understandable.

IMPORTANT!
Here is another reason to use many subroutine switches:
Switch statements can only have 30 statements executable in any path through them. So, if you start counting with the first statement in a switch, no matter what IFs are true or what way the things are set, if there is a way to get through that switch that executes 31 statements before GOTOing somewhere, APE will FREAK. So be careful not to make routines too long.

So, since a GOSUB takes you to another switch, it is the perfect way to cheat that 30 statement limitation.

Okay, so GOSUB:

GOSUB 30:3
<next statement>

This will execute the switch 30:3, then return with whatever it changed, and execute the next statement.

This is so useful and a key to programming, because it allows you to write useful chunks of code that every part of the program can use. This also reduces bugs, because otherwise you'd have to copy that same section of code and paste it in every place you want to use it! Now imagine if you've pasted it fifty places--and
there's a bug in it! Now you have FIFTY bugs in your program instead of one!

Well, up till now, we've done GOTOs and GOSUBs to numbers. But that's not very mnemonic--you have to remember what those numbers mean. Soooo, we have something super-useful in Flow called the "DEFINE" statement.

DEFINE
This is not really a command in the language, as a command for the APE compiler, the thing that compiles the APE script. It tells the APE compiler to do string replacements in the file before trying to compile it into APE code. Let's look at how we use it:

#define POOPER "20001"
#define DRAW "10"


#switch POOPER:DRAW - Sorry, kinda hard to display a variable(yellow) and args(light blue) at the sametime!


So, instead of having to type: #switch 20001:10 we have something that's much more readable. But, the above defines have a problem. Look at this:

#define POOPER "20001"
#define DRAW "10"
#define DRAWPOOPER "11"

#switch POOPER:DRAWPOOPER

If I have a routine "DRAWPOOPER", DPARSE is going to find all the defines, then first replace "POOPER" everywhere, so we'll get:

#define POOPER "20001"
#
define DRAW "10"
#
define DRAWPOOPER "11"

#
switch 20001:DRAW20001

This is not what we want. So, you should include weird characters in the define, so this doesn't accidentally happen. You can use any characters you want. Squirrel uses dollar signs. I use dollar signs for string replaces, and percent signs for number replaces. It doesn't matter. All the following work:

#define %POOPER "20001"
#define $FuncA "10"
#define |VALUEB| "11"
#define !@#$%Goop%$#@! "String"
#define $PLocCheck "x = pooperX
y = pooperY
gosub 20001:10" He is trying to trick us here. Notice the start quotation and end quotation. These three lines are all one string, the $PLocCheck define

It just goes until it finds a quote. You can't have spaces in the name, though. Note that when I used it to replace code, I couldn't use the more defines inside it--you can't have nested DEFINEs. AND doing macros like that can be bad. Why? Because you can only have SIXTEEN statements in a switch, so if you use a bunch of
macros like this, you run the risk of putting in too many statements without know it. So chill on that, or name them things like 3PLOCCHECK, so you know how many lines they introduce!

TIP #1:
Putting the defines for all your Window and Switch defines at the start of your file will make sure you don't accidentally define them with the same value.

TIP #2:
It's usually easier to replace the whole bank:entry pair with one define, like this:

#define @PooperMain "20001:1"

TIP #3
You can use defines to give the arrays A[] through Z[] nicer names!
Watch this:

#define EnemyX[ "E["

Anywhere in the code APE sees something like this:

EnemyX[1] = 1

It will replace it with this:

E[1] = 1

Cool, huh?

REMEMBER: DEFINEs are all done before you program is compiled. You can't use them for changing things at runtime, and you can't DEFINEthings inside other DEFINE statements!

One last thing you can do in switches, which opens up the power of built-in commands!

CONSOLE
This allows you to issues console commands from inside your Flow(APE) program. You can do things that modify the window, that modify the level you're in, or that do internal things.

The most useful one is "ECHO", which sends a line of text to the console:

console "echo We are in MovePooper!"

The echo command is exceptionally helpfull for troubleshooting your APE programs. For example, if you are curious if a switch is being properly called, put an echo statement in it, and when you run your program, drop down the console. If you don't see it then you can assume that switch hasn't been called. Since the
console is cluttered, it helps to make echo statements stand out like this:

console "echo *|*|*| -- If you see this the switch worked! -- |*|*|*"

If you want to use quotes in the echo command, use \" like this:

console "echo bind x \"invoke 1:1\""

This sends the following to the console text output display:
bind x "invoke 1:1"

You can actually print out variables with echo, but you have to define them, for example:

console "echo pooperX = %f", pooperX

Where we wanted pooper to appear we put %f, this stands for float, which is the type of the variable. Technically pooperX was an interger, but since this is just an echo, overkill isn't going to hurt. After the end " we put a , followed by the variable we wanted to appear where %f is in the console.
So if pooperX is equal to 3, you will see on the console:
pooperX = 3

The types of variables are:
%f - floating point number (A number with a decimal place)
%d - an interger (A whole number with no decimal places.)
%s - A string (A varaible containing text.)

You can chain several of these together at once. Let's assume the following variables:
test1 = 50
test$ = "Hello"
test2 = 5

console "echo %s, I have %d jellybeans. Would you like to win %d of them?", test$, test1, test2

This would display the following in the console.

"Hello, I have 50 jellybeans. Would you like to win 5 of them?"


You cannot put an equation in there to evaluate, only a variable.
You also cannot directly echo an array variable So, set another temp variable to the element's value, and echo that:

temp = A[$index$]
console "echo A[index] = %f", temp

(Note: in windows, you can also print variables in BODY statements! Just do this:

body "pooperX = %d", pooperX

It's that easy!)

Additionally as the varaible updates, so will the body statement, so this can be a great way to troubleshoot a variable in your program.
We'll list more console commands in later lessons.

There's another command you can set in a window.

FLAGS

This allows you to set certain flags, determining how the window operates. You use it like this:
flags <flagname>

Here are the current usable flags you can set:

flags passive
This creates a window with no cursor, that can't be clicked away with the mouse. This is good for a HUD or something you want to hold infront of the user for awhile.

flags passive2D
This creates a window with a 2D cursor, but the clicking the mouse STILL won't dismiss the window. This is nice for an interface. That way you can control the areas of interaction on the interface. For example, all the worldskill mini-games are passive2D windows, as well as the hive minigame.

Okay, that's basically all the commands for switches. Here's a simple switch, with defines, variables, and so on.

// DEFINES

#define %Lesson4 "4"
#define %TalkToJoe "1"
#define %JoeSaying1 "10"
#define %JoeSaying2 "20"
#define %JoeSaying3 "30"
#define %JoeSaying4 "40"

//CODE

#switch %Lesson4:%TalkToJoe
joeCounter = joeCounter + 1
if (joeCounter > 4) joeCounter = 1
if (joeCounter == 1) goto %Lesson4:%JoeSaying1
else if (joeCounter == 2) goto %Lesson4:%JoeSaying2
else if (joeCounter == 3) goto %Lesson4:%JoeSaying3
else goto %Lesson4:%JoeSaying4

#window %Lesson4:%JoeSaying1
title "Joe"
body "ZOWIE! Joe knows the counter is %d.", joeCounter

#window %Lesson4:%JoeSaying2
title "Joe"
body "BOOM! Joe knows the counter is %d.", joeCounter

#window %Lesson4:%JoeSaying3
title "Joe"
body "WHAM! Joe knows the counter is %d.", joeCounter

#window %Lesson4:%JoeSaying4
title "Joe"
body "PLOP! Joe knows the counter is %d.", joeCounter

 

Compile and load with the console command:
MAP battlesize
LOADAPE Lesson4
INVOKE 4:1

Each time you INVOKE 4:1, it will print out a different saying, and tell you the number of the counter.

On to Lesson 5, where we learn that windows are even cooler than we thought!