Variable Management

Variables are an important element to complicated functionality in Pfhortran. Unlike many programming languages, Pfhortran only has one variable type, which can be used in the place of any normal operand.

For those of you not familiar with the concept of variables, let me clarify. A variable is a way of storing information that may change as the script executes. For example, if you wanted to create a script that would slowly raise the players life, you could use a variable to keep track of how much life the player had and to figure out how much more you should add. Variables are used in place of numbers or mnemonics, and you are free to change their values at any time. If variables still make little sense, read the following carefully and try fooling around with the scripts yourself... it should become clear with a little bit of experimentation.

All variables should be defined in the init procedure. Defining a variable is accomplished using the define instruction, followed by a starting value for the variable. The define instruction is uses labels in a unique way: rather than naming the line the label appears on, a label followed by the define instruction names a variable. For example, we could define a variable named "bob" that had a starting value of 5 by entering the following into out init procedure script:

bob: define 5



Pfhortran provides several instructions to manipulate variables. See the Variable Management section of the Pfhortran Instructions chapter for a complete listing.

Let's take a look at some code that uses variables.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
_procedure init

    oxy: define 0
    get_oxygen oxy

end


_procedure idle

    wait_ticks 10
    subtract oxy, 5
    set_oxygen oxy

end



The above script simulates a vacuum. The init procedure initializes a single variable called "oxy" and starts it's value off at zero. Then it gets the player's current oxygen value and puts that number into oxy with the get_oxygen instruction on line 4. All variables in Pfhortran are "global", meaning that a variable defined in one procedure can be used in another. Thats a good thing, too, because in the idle portion of this script, we slowly decrease the player's oxygen supply by subtracting 5 from oxy every 10 ticks. This script is a loop because, as mentioned before in the Procedures section, the idle procedure will restart after reaching the end instruction.

However, in simulating a vacuum this script has some problems. First of all, it only considers the player's oxygen level once... in the init procedure. What if the player were to fall in some water or find an oxygen recharger during the level? The player's oxygen level would change, but the above script would not realize it because the oxy variable is not constantly updated. If, for instance, the player recharged their oxygen to full, the oxygen level would be reset to it's former low state by the script after 10 ticks had passed. To fix this, we need to make sure that the oxy variable is updated every time we change the player's oxygen level.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
_procedure init

    oxy: define 0

end


_procedure idle

    wait_ticks 10
    get_oxygen oxy
    subtract oxy, 5
    set_oxygen oxy

end



The only change made was to move the get_oxygen instruction into the init procedure loop. This forces the oxy variable to update before it does any subtraction, so that when the set_oxygen instruction executes, oxy is the current oxygen level - 5. This modified script will correctly drain the player's oxygen supply quickly, but it is still not very convincing in creating a vacuum level... it just duplicates that Marathon can already do.

So let's change this script so that it makes use of Pfhortran's extended functionality. Let's say our level is on a ship that has a hanger which is sealed off by an air lock because the hanger itself has no air. Through flipping various switches, the player is able to open the air lock door to the hanger an go "outside". In real life, of course, this would depressurize the ship and cause it to implode, but for our purposes let's just pretend that all the air escapes and the level becomes a vacuum as soon as the door opens. Now were are talking about a some dynamic changes to the game environment that could never be accomplished with the original Marathon. This is progress!

Before we get into the actual script, a few things about the map itself should be clarified. Pfhortran has instructions for controlling tags, so let's make the air lock door a tagged door. In fact, let's say it is tag 1 and the tag can only be triggered by a tag switch. Now that those details have been decided, let's write the script.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
_procedure init

    oxy: define 0
    vacMode: define false
    tag: define 0

end

_procedure tag_switch

    get_my_value tag
    if_= tag, 1, vacTime
    jump finish
    vacTime: set vacMode, true

finish: end

_procedure idle

    if_!= vacMode, true, skip

    wait_ticks 10
    get_oxygen oxy
    subtract oxy, 5
    set_oxygen oxy

skip: end



Ok, we have added quite a lot. First of all, the init procedure now has two new variable declarations: a variable called "vacMode" defined as false, and a variable called "tag" defined as zero. We'll use these variables in making our script dynamic.

The second major change is the addition of the "tag_switch" procedure. This procedure executes every time a tag switch is thrown. Procedures all come with a "which" variable that defines "which" game element the procedure refers to. In this case, the "which" variable for the tag_switch procedure will contain the number of the tag that has changed. We can read the "which" variable by using the get_my_value instruction. This is exactly what we have done on line 11, and since we have passed the tag variable as an operand, the number of the tag that has been changed will be put into the tag variable. Next, we compare the tag variable with the value 1 to see if tag 1 was the tag that changed. The third operand on line 12 is the name of a label, "vacTime". If the tag variable is equal to 1, execution will jump to the "vacTime" label--line 14. Line 14 sets the value of our other variable, vacMode, to true. Otherwise if the tag variable is not equal to 1, execution will continue to the next line, the jump instruction, which will force execution to jump to the "finish" line--line 16--and end the procedure. The final result of this procedure is that the vacMode variable will be changed to true if a switch controlling tag 1 is thrown.

Finally, the idle procedure loops over and over again. Line 20 uses another conditional statement, this time if_!= (if not equal). This line says that if the vacMode variable is not equal to the value "true", jump to the line "skip". We can see that the line "skip" is line 27, the end of the procedure. If vacMode has not been set to true, the procedure will end, and, since it is an idle procedure, repeat. On the other hand, if vacMode does equal true, the script will continue and line 22 will be executed. At this point we are back to our previous vacuum script: just get the oxygen and lower it every ten ticks. We have, with this new and modified script, created a fairly convincing vacuum simulation that will turn on when the door associated with tag 1 is opened.

Is there room for further improvement of this script? Naturally! We can make the script faster and more efficient by simplifying the tag_switch procedure.

09
10
11
12
13
_procedure tag_switch

    get_tag_state 1, vacMode

end



This modification works exactly the same as the last script did, but with only one line of code. Since both the vacMode variable and tags only use two states (true and false), line 11 will set vacMode to false when tag 1 is off and true when it is on. Not only does this reproduce the code we had before, it gives us the ability to turn our vacuum simulation off by closing the door, which would have previously had no effect.

Variables can be used to make very intricate scripts, and it is well worth taking the time to master them. By using variables effectively, one can add randomness or sequence to an otherwise static game world, and thereby increase the depth of game play.