Script Structure

Pfhortran has a few instructions that actually alter how the script executes in real time. The simplest example of such an instruction is jump, which changes the next line of execution to the line (or label) specified. However, a few other similar instructions exist, and this section details their meaning and use.

Back in the day before procedural languages, everyone used some flavor of assembly programming to write their programs. Assembly is the closest human perceivable abstraction of machine language, the computer's internal 1's and 0's. As such, assembly programming requires a lot of very low level operations but executes very very quickly. Pfhortran's code structure is somewhat similar to classic assembly code because it does not yet support complex arguments to instructions like high-level languages do (i.e. in many languages, x = 5 would set the value of 5 to the variable x, but in Pfhortran you have to say Set x, 5).

Given this similarity, it makes sense that Pfhortran has borrowed a few programming concepts from assembly code. One of assembly concepts that has been incorporated into Pfhortran is Call/Return-style label jumping. The idea is that rather than jumping to a label, you "call" it with the Call instruction. When you are ready to return to the point you were at before the call, issuing the Return instruction will take your right back. For example:

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

    Call check_nums
    if_= nums, 5, done
    teleport_player 34

done: end


    check_nums: if_= nums, 5, reset_nums
    add nums, 1
    return
    reset_nums: set nums, 0
    return



Here is what this script does: every idle iteration, the check_nums label is called. Execution moves to the check_nums line and continues. If the nums variable is equal to 5, code jumps to the reset_nums label. Otherwise, 1 is added to nums and the code returns to line 4. At reset_nums, the nums variable is set to 4 before the code returns to the fourth line. At line 4, the nums variable is checked to see if it is equal to 5. If so, execution jumps to done, which ends the procedure. Otherwise, the player is teleported to polygon 34.

What does that mean for the game? The nums variable is going to count from 0 to 5, each time teleporting the player back to polygon 34. Once nums reaches 5, it will be reset to zero and the loop will continue. I have no idea why anyone would want to do this to a player (as it will effectively trap him on polygon 34), but hopefully it explains how call/return style labels can be used effectively.

NOTE: Up until Pfhortran version 0.4, only one part of the script could be executing at a single time. Now that procedures exist, two or more different parts of the script (for example the idle procedure and the tag_switch procedure) can be running at the same time. This means problems for the call/return statements, as they were not written with multiple scripts in mind. The problem is that when a label is called, it's return line number is stored in a list of numbers called a stack. When the return statement is called, the number at the top of the stack is removed and execution returns to that point. The problem is that with multiple scripts executing at the same time, it is possible that the return statement will receive a line number meant for a different script. To put it another way, if two procedures both call parts of code, it is possible that when the first procedure returns, it will get the return line number associated with the second procedure. This could lead to some serious code anomalies, which are always bad. To avoid such problems, simply limit the use of call / return to one procedure. Or, make sure all your call/return code is contained within a block (described below) which will also solve the problem.

Another concept that changes the way code executes is the block. Normally, Pfhortran executes one instruction per frame, but this often proves too slow. Blocks solve this problem by sectioning off a "block" of code to be executed all at once within a single frame. This is very useful for creating fast effects and for taking care of code that is not immediately visible to the player.

Blocks are defined with the Block_Start and Block_End instructions. ALL BLOCKS MUST HAVE A BLOCK_END INSTRUCTION OR ALEPH ONE WILL CRASH. The code in between Block_Start and Block_End will all be executed in a single frame.

01
02
03
04
05
06
07
08
09
10
11
12
13
_procedure idle

    if_!= vacMode, true, skip

    wait_ticks 5

    block_start
    get_oxygen oxy
    subtract oxy, 20
    set_oxygen oxy
    block_end

skip: end



The addition of Block_Start and Block_End to this procedure has declared all the grunt variable work (lines 8 - 10) should be executed in a single frame. This will make the script run faster, as the entire idle loop can be executed in 4 frames (previously it took 6 frames).

NOTE: Blocks can often be used to make Pfhortran scripts very speedy, but they must be used with care. Blocks must always end (see the all-caps warning above). You may NOT call waiting instructions like wait_ticks or wait_for_path during a block. These instructions take more than one frame to execute, so it doesn't make since that you would want to do them in a single frame. You also may NOT nest blocks. This means you absolutely cannot have a block within a block. I can almost promise you that doing this will cause Aleph One to crash, or at the very least execute differently than you expected.