Comrades in Arms Discussion Board

Full Version: The FHQ Mini Scripting Guide
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Introduction

A lot of people see scripting as an arcane thing that no one can fully understand, and while it's true that scripting topics can go from the trivial to the extreme, most practical scripting used by mission designers is actually pretty simple.

The daunting part is always that, when faced with an SQF code, and without the knowledge of what exactly it is you are seeing, everything looks arcane and incomprehensible. But at it's core, it's rather simple.

I will use this thread as sort of mini scripting guide. The goal is to give you just enough scripting knowledge to do simple scripts that allow you to do what you need them to do within your mission. It's goal is not an exhaustive programming course, and with what I present here, you might not be able to write complex code, but again, this is not the intention.


Script organization

Scripts are usually organized in files ending in ".sqf".  When such a file is executed, the script interpreter in Arma starts executing the commands in the file starting with the first command, and then going on to the next, and the next, and so on. This is an important thing to note, and even though it might sound trivial, it's the essence of how a script works.

Some scripts, like init.sqf, are executed automatically at the start of the mission. A full table of all those "automatic" scripts can be found here, but for our purposes, we will stick to init.sqf as the root of all scripting in Arma. If you need something initialized, this is where to put it. You can call other scripts from within init.sqf using certain script commands, which we will touch on later.

Another way of executing script commands is from within the editor's console. This is useful for testing small script pieces and/or verifying the result of script commands.

[Image: 1vpMt1L.jpg]


Anything entered into the Execute field can be executed either locally (LOCAL EXEC), on the server (SERVER EXEC, multiplayer only) or on all clients and the server (GLOBAL EXEC, multiplayer only).

The Watch fields can be filled with code that produces a result, and the result will be displayed below the field.


To summarize what this chapter introduced:
  • Scripts are kept in files ending in "sqf", which are read and executed sequentially
  • There are script files that are executed automatically at mission start, like init.sqf
  • The Console is a handy way of testing scripts, and verifying the result of certain script pieces
Data Types 

Every piece of data is expressed as one of the existing data types. You can think of data types as a classification of the data. For example, you do know that 1.254 is a number, while "Hello World" is a string of characters.

SQF knows several data type, some more frequently used, some less so.

The most commonly used data types are:
  • Number
  • String
  • Boolean
  • Object
  • Code
  • Arrays
There are some data types that are specific to certain situations, like group, or task, but these are basically special cases of objects and we'll only glance at them

Numbers

Number are exactly what you would think they are. 1 is a number. 3.1415 is a number. -1000 is a number. Very large or small numbers can be written as powers of ten by using 'E' or 'e': 1e3 is 1 times ten to the third power (1000), while 2e-3 is 2 times 10 to the power of -3 (0.002). Note that SQF does not make any distinction between whole numbers and floating point numbers.

Strings

Strings are squences of characters, enclosed in either double (") or single (') quotes. "This is a string" is an example of a double-quoted string. 'This is a single-quoted string' is another example of a (this time single-quoted) string. Note that a double quote must be closed by another double quote and vice versa. So, "This is not a string' is not correct.

Strings are used as names and for other purposes.

Booleans

A boolean is a truth value. It can either be true, or false. Their use is mainly for conditions (see later chapter), but just like numbers ans strings can be used in other expressions. We'll be talking about this in detail later. 

Objects

Objects are "handles" for anything in game. The player is an object. Any vehicle is an object. Enemy AI are objects. Basically, everything that manifests itself in the game world is an object, even triggers and modules (Exception of this rule: Markers are not objects).

A special object is defined as well, the "null object", which represents nothin. In SQF, it's expressed as objNull.

Code

Code is anything you can execute. In SQF, code has it's own data type. It is written as a sequence of statements enclosed in curly braces. Examples for code data types:
  • {hint "XXX";}
  • {_a = 0;}
We'll look at this more closely later, when we discuss functions.

Arrays

Finally, we have arrays. An array is a special datatype in that it's a collection of multiple items of data of varying types. Arrays are written as comma separated lists of items, enclosed in square brackets [].

The order in which items are listed is significant. The first item is always item number 0, the second one is 1, and the last one is the n-1st item (if there are n item in an array).

For beginners, this is probably the most difficult data type to understand, so let's see a few examples:
  • [1, 2, 3] - This is an array consisting of three elements, the numbers 1, 2 and 3
  • ["task", player, 4.5, ["A", "B"]] - This is an array consisting of four elements, the first one being a string ("task"), the second an object (player), the third a number (4.5) and the last one an array consisting of two strings, "A" and "B". 
  • [] - This is also an array, consisting of no elements at all (an empty array)
Arrays are vital for passing data between functions. They can theoretically be any size, but the engine limits them to 10 million items maximum.


Arrays can be manipulated in a number of ways. We'll look at them in an extra chapter.


As mentioned above, some data types are rarely used. For example, tasks, briefings and groups are data types in their own right, comparable to objects. For groups, there's even the same null group, "grpNull"


To summarize:
  • A data type represents an item of a specific kind
  • Numbers represent real numbers
  • Strings are sequences of characters
  • Booleans are truth values
  • Code can be executed
  • Arrays are collection of multiple data items of different kind
Variables

As we've seen in the previous chapter, data can be represented in SQF as different types. To store data, we need to make sure we can retrieve it again when we need it. To easily do it, we tie data to a name. This is called a variable.

A variable is simply a named data item of a distinct data type. To define a variable, we have to assign it a type:

myVariable = 1;

This definition does several things: It declares a variable called "myVariable", gives it the value of 1, and also determines that it's data type is a number. Unlike other programming languages, the data type itself can change, though.

myVariable = "A";

This reassigns the value of myVariable to be "A", i.e. a string.

A variable can take the place of any data item. Thus

myVariable = 10;
myOtherVariable = 100;

myOtherVariable = myOtherVariable + myVariable;

assigns myOtherVariable the value 110.

Naming

Variable names can consist of characters (a - z, A- Z), numbers (0 - 9) and the underscore character ("_"). They must NOT start with a number, though. As such, these a valid names:

myVariable
MyVaRiAbLe
some_variable0
_someVariable

And these are invalid

0xxx
-ky
some-illegal-variable-name

Scope

Variables starting with an underscore character ("_") are considered local, while characters starting with a letter are considered global. A global variable is known on the current client or server anywhere, not only in the currently executed script, but also in other scripts. On the other hand, local variables are only known in the current scope.  The current scope is either the current script file, the current function, or the current control structure. We'll discuss these things later. Suffice to say for now that local variables are not known outside the currently running script, while global variables are known everywhere on the current machine.

Network considerations

Variables as such are only visible on the current machine. That is, even if you define a global variable "myGlobalVariable" on your client, it will ONLY be visible on this client, not on any other client. To make it known beyond the boundaries of your current machine, you can broadcast it via the publicVariable command:

myVariable = 1; publicVariable "myVariable";

This distributes the variable among all clients and server in the current game session. Note, though, that this ONLY transfers the current state.

So, after executing the above line, myVariable will be known on any client/server and will have the value 1 on each.

Now, if you execute 

myVariable = myVariable + 1;

on a client, then the variable will be 2 on that client but 1 everywhere else. This is an important point when working with networks.

Arrays

Arrays are handled somewhat differently. They are not copied and modification of one modifies the other.

For example:

array1 = ["A", "B", "C"];
array2 = array1;

array1 set [1, "D"]; // Set = array set[x, value] sets element number x to value

will make array1have the value ["A", "D", "C"], and attay2 hast the same elements. This is a so called assignment by reference in contrast to an assignment by value as in the other cases

To make a real copy, use

array2 = +array1;


To summarize:
  • Variables are named containers for data types
  • Variable names start with letters or the underscore character
  • Global variables are known outside their current scope, private variabls can are not
  • To make a variable known on other machines, use the publicVariable command
  • Arrays are copied by reference, not by value, unless explicitly requested
Statements

Up to now, we were talking about data. But scripting means writing something that operates on that data. This is where statements come in.

Basically, a statement is one single line of script that "does something".

The following line is a statment:

_test = 0;

This statement is a simple assignment. The value 0 is assigned to the variable _test. A statement is always ended with a semicolon (";") character. If you forget the semicolon, the game will complain about a script error.

Multiple statements can be written on the same line, or on different lines:

_object = player;
_object moveInDriver _car; _object assignAsDriver _car;

The script can also be written like this:

_object = player; _object moveInDriver _car; _object assignAsDriver _car;

Or like this:

_object = player;
_object moveInDriver _car;
_object assignAsDriver _car;

The important thing is the order: A script always executes one statement at a time. So the _object = player; would be executed first, followed by the second statment, and the third statement. In the next chapter, we'll learn how to deviate from that order (with the use of conditional execution).

Remember the code datatype that we discussed briefly in the section about data types ? The code datatype is nothing else but a sequence of statements that are assigned to a variable. For example:

_code = {_object = player; _object moveInDriver _car; _object assignAsDriver _car;};

The code in curly braces is not executed directly, but written into a variable. Note: The same rules apply to code written as data as normal code: A Semicolon must end every statement, and the line can be broken in any way. So the above could be written as

_code = {
  _object = player;
  _object moveInDriver _car;
  _object assignAsDriver _car;
};

or even

_code = {
  _object
  = 
  player;
  _object
  moveInDriver
  _car;
  _object
  assignAsDriver
  _car;
};

if you are feeling funky.

We will go into more detail on how to use code in variables when we discuss functions (including passing values into the function), but for now, suffice to say that the code in _code can be executed by

call _code;

To summarize:
  • Every statement must be ended by a semicolon;
  • Statments are executed in the order they appear in the script
  • A series of statements can be assigned to a variable, and executed via the call command
Flow control, part 1

In itself, an unbroken sequence of statments can not do much. Sometimes, you need to react to certain conditions being present, and branch out into different parts of your script.

This is where flow control comes in. Flow control encompasses everything that changes the flow of statements. It allows you to execute different sequences of statements when a condition is fulfilled, or repeat statements a defined (or undefined) number of times.

Conditions

Most flow control works with conditions, which is why we first take a look at those. A condition is something that always evaluates to either true or false (in other words, a boolean data item).

A few examples will make this clearer. The follwoing are conditions
  • _a > 1 - True if the value of _a is greater than 1
  • _a != 2 - True if the value of _a is not equal to 1
  • _a == 3 - True if the value of _a is exactly 3. Not the double equals sign. "==" is used to differentiate from the pure assignement, "=".
  • alive player - true if the player is alive
  • not alive player - true if the player is NOT alive
  • _a >= 3 and _a <= 5 - true if the value of _a is between _3 and _5 (so, greater or equal to 3 and less than or equal to 5)
As you can see, conditions can be negated by prefixing them with a "not" operator, and conditions can be combined with operators "and" or "or". "not", "and" and "or" can be written as "!", "&&" and "||" as well. You will find this way of writing in some scripts, like this:
  • _a >= 3 && _a <= 5
  • !alive player
It's useful to keep this in mind when reading foreign scripts, but I would strongly suggest to stick to the words "not", "and" and "or" to make the conditions more readable.

if a and b are conditions, then:
  • a and b is true when both a AND b are true
  • a or b is true when either a OR b is true
There are many more operators that are useful for conditions, but we will look at those in a later chapter when we dissect existing scripts to see how they work.

To summarize:
  • Conditions always evaluate to true or false, i.e. a boolean value
  • Condition values can be negated with "not"
  • Conditions can be tied to more complex conditions by the use of the "and" and "or" operators.
Flow Control, part 2

The most fundamental flow control is deciding which of two possible code blocks to execute. This is done with the "if" statement. In it's simplest form, it tests a condition, and, if true, executes a piece of code

if (condition) then {
    // code to execute
};

The following are working examples of if statements:
  • if (_a < 0) then {
      player sideChat "_a is less than zero";
    };
  • if (_x == 0) then {
      _y = 1;
    };
The first example check if _a is less than zero, and if it is, executes the code following it (it shows "_a is less than zero" on a players side chat).

The second example checks whether _x is zero, and sets _y to 1 if it is. Note that the block following then is not executed if the condition is false, which means that in case _x is not zero, _y will be undefined.

The second form of an if statement tests a condition, and then executes one of two possible code blocks depending on the result:

if (condition) then {
   // code to run when condition is true
} else {
   // code to run when condition is false
};

Examples of this:
  • if (alive player) then {
       player setPos _somePosition;
    } else {
       player setPos _someOtherPosition;
    };
  • max = 0;
    if (_a > _b) then {   max = _a} else {   max = _b;}
The first example move the player to _somePosition if he's alive, and _someOtherPosition if he's dead. The second example first sets max to 0 (so it's defined), then checks whether _a is greater than _b. If it is, the value of _a is assigned to max, otherwise, the value of _b is assigned to max.

if ... then and if ... then ... else statements allow for a wide variety of possibilities. In fact, this and the while construct discussed in the next chapter are actually all that is needed. Any other constructs are purely "cosmetically" and just make the script easier to read.

SQF scripting allows another use of the if statement that is uncommon and not usually found in other languages: The if statement can be used in an assignment.

You can often find this in scripts in a case like this:
allUnits = (if (isMultiplayer) then {playableUnits} else {switchableUnits});

playableUnits returns all playable units in a multiplayer game, but has no effect in single player. Conversely, switchableUnits shows all units the player can switch to in a single player match, but returns an empty list in multiplayer.

The above line, then, checks if the game is in multiplayer (if (isMultiplayer)) and if it is returns playableUnits, else it retuns switchableUnits. The result is assigned to allUnits.

To recapture:
  • An if ... then statment checks a condition, and if the condition is true, runs a block of code
  • An if  ... then ... else statement is an extended version that checks a condition, and runs the first piece of code if the condition is true (the "then" branch) and the second piece of code (the "else" branch) when the condition is false.
  • A special form of if ... then ... else can be used in assignments to assign values depending on a condition