From SQF to Enforce Script – Arma Reforger
Welcome to Enfusion!
While Arma 3 is powered by SQF, Arma Reforger uses Enforce Script as language, which is syntactically close to C#.
SQF is a succession of Scripting Commands processed in a certain order to give you a certain access or reference to the engine objects themselves (e.g player).
It is easy to access, read and process at the cost of performance.
Enforce Script is an Object-Oriented Programming language (OOP language) which as this designation suggests is based on objects.
The language is way closer to the engine and is inspired by C++, which allows scripters more possibilities, but also more responsibilities - a script can make or break one's game experience.
It is a stricter language that allows for a wider, stronger interaction with the engine.
Similarities
Let's start with the similarities between the languages… because they are very different, the list will actually be a short one.
SQF | Enforce Script |
---|---|
And that's it! As this example already shows, even the variable declaration syntax has changed. Now, let's see the main differences on first sight!
Main Differences
If-Then-Else
Definitely the most important point of this document. SQF being keyword-based, it needs to "bridge" the "if" condition to the "then" value, using the then keyword. Enforce Script uses if-else structure like any recent language.
SQF | Enforce Script |
---|---|
if (myPlayer.GetHealth() > 0) // no more 'then'!
{
Print("I am alive!");
}
if (myPlayer.GetHealth() > 0)
Print("I am alive!"); // a short version is possible |
The same applies for other code structures (while-do, switch-do, for-do, etc), see Code Flow below.
The else part supports else if (note: not elseif) statements:
SQF | Enforce Script |
---|---|
if (myPlayer.GetHealth() > 0)
{
Print("I am alive!");
}
else if (myPlayer.GetHealth() < 10) // no more "else { if }" structure
{
Print("That's a lot of damage");
} |
Code Flow
for
SQF | Enforce Script |
---|---|
// Enforce Script has only one type of for loop
// statement, condition and step are separated by semicolons
for (int i; i < 5; i++) // i is automatically initialised to 0, but "int i = 0" could also be used
{
} |
foreach
SQF | Enforce Script |
---|---|
// foreach's syntax is changed and follows today's standards
// the code to be run is placed after the keyword
// beware of the syntax: currentIndex and currentElement are separated with ","
// whereas currentElement and the array being iterated over are separated with ":"
foreach (int currentIndex, string currentElement : items)
{
Print(currentIndex);
Print(currentElement);
}
// or, if the index is not required
foreach (string currentElement : items)
{
Print(currentElement);
} |
while
SQF | Enforce Script |
---|---|
// Condition check in while loop is done in regular () brackets
while (i < 10)
{
i++; // this will increment i by 1
} |
switch
SQF | Enforce Script |
---|---|
switch (i) do
{
case 0: { systemChat "case 0" }; // if i is 0, code in brackets is executed and switch is exited automatically
case 1: { systemChat "case 1" };
default { systemChat "default" }; // default does -not- use ":" like case does
}; |
switch (i)
{
case 0:
Print("case 0");
break;
case 1:
Print("case 1");
break;
default: // default -does- use ":" just like case does
Print("default");
break;
} |
Others
SQF | Enforce Script |
---|---|
// SPECIAL CASES
i = if (isScript) then { 1 } else { 0 }; // if-then-else returning value, not possible in Enforce Script
if (isScript) exitWith {}; // exitWith breaks from the current -scope-
// it will exit the script only if at its root
// lazy evaluation: by default, all statements in condition field are evaluated
// to prevent this behaviour the second statement should be wrapped in code brackets {}
// if (!isNil "_value" && _value > 10) then // wrong as "_value > 10" will be evaluated
if (!isNil "_value" && { _value > 10 }) then
{
hint "_value exists and is greater than 10";
}; |
// SPECIAL CASES
// an if statement can avoid brackets for only one line of code
if (isScript)
break; // this will exit a while loop
if (isScript)
return 0; // this will exit the current method and return 0
// "value.GetValue() > 10" will not be evaluated if "value != null" returns false
if (value != null && value.GetValue() > 10)
{
Print("value exists and is greater than 10");
} |
Case Sensitivity
Everything is case-sensitive in Enforce Script, unlike SQF:
SQF | Enforce Script |
---|---|
string myValue = "Hello there";
Print(MYVALUE); // error: MYVALUE is undefined
IF (true) // error: "IF" is an unknown operator. "if" is the proper casing
{
Print("it's true!");
}
if (myValue == "HELLO THERE")
{
// will NOT work - "Hello there" is different from "HELLO THERE"
} |
Typed Variables
In SQF, a variable can change its type on the go according to value assignation. In Enforce Script, a variable has one type and this type cannot be changed during the variable's lifetime.
SQF | Enforce Script |
---|---|
string myValue = "Hello there"; // myValue is a string
myValue = 5; // error: cannot convert an int to string
myValue = true; // error: cannot convert a bool to string |
Position
A position is the location of an object. A position is composed of X, Y and Z values, X being the West → East axis, Y being the Floor → Sky axis, and Z being the South → North axis. A position is called origin in Enfusion.
Real Virtuality uses the [X, Z, Y] format (a vector pointing up being [0, 0, 1]) whereas Enfusion uses { X, Y, Z } (a vector pointing up being { 0, 1, 0 }).
SQF | Enforce Script |
---|---|
Array
In SQF, an array is a list that can contain any type of data including sub-arrays, and can be expanded at will. In Enforce Script, there are two types of array: static (extremely fast but of static size) and dynamic (size can change); also, an array can only contain one type of data.
SQF | Enforce Script |
---|---|
array<int> myArray = {}; // dynamic array
myArray.Insert(0); // array is { 0 }
myArray.Insert(true); // array is { 0, 1 } - true is 1
myArray.Insert("2"); // error: an array cannot contain a different type
myArray[0] = 1; // sets the first element to 1
// array duplication
array<int> duplicate = {};
duplicate.Copy(myArray); // does not work for ref items
// ref array duplication
array<ref SCR_Class> refDuplicate = {};
foreach (SCR_Class item : myArray)
{
refDuplicate.Insert(item);
}
// a helper is provided
array<ref SCR_Class> refDuplicate = SCR_ArrayHelper<SCR_Class>.GetCopy(myArray);
// new array type: static (size) array
int myArray[3]; // automatically initialised to the type's default value (here { 0, 0, 0 })
myArray[0] = 1; // { 1, 0, 0 }
myArray[1] = 2; // { 1, 2, 0 }
myArray[2] = 3; // { 1, 2, 3 } |
Data Types
Including vectors, enums, sets, classes of course and others! Find all the new data types on their dedicated page.
SQF | Enforce Script |
---|---|
private _position = [50, 50, 0]; // an array
private _number1 = 42; // a Number, a.k.a float
private _number2 = 5.5; // a Number, a.k.a float
_number1 = _number1 + 1;
_number2 = _number2 - 1;
private _hashmap = createHashMap;
_hashmap set [1, "oops"];
_hashmap set [2, "two"];
_hashmap set ["two", true];
_hashmap set [1, "one"];
private _value = nil;
if (isNil "_value") then { hint "_value is not defined" };
private _value = objectParent player;
if (isNull _value) then { hint "player has vehicle" }; |
vector position = { 50, 0, 50 };
int number1 = 42;
float number2 = 5.5;
number1++;
number2--;
map<int, string> hashmap = new map<int, string>(); // types are set in stone
hashmap.Insert(1, "oops");
hashmap.Insert(2, "two");
// no Enforce Script equivalent
hashmap.Set(1, "one"); // Set() is to update a value - it can create it too, but Insert() is faster
set<string> stringSet = new set<string>();
stringSet.Insert("value1");
stringSet.Insert("value2");
stringSet.Insert("value1"); // returns false as value1 already exists
// there is no isNil equivalent in Enforce Script as a variable is either defined or not
// its content can still be null as seen below
MyClass myInstance;
if (myInstance == null)
Print("myInstance is null");
// alternatively
if (!myInstance)
Print("myInstance is null"); |
Object-Oriented Programming
SQF is a scripting language based on sequences of expressions set in scripts. Enforce Script is Object-Oriented Programming (OOP) meaning that the code is placed inside declared objects.
SQF | Enforce Script |
---|---|
// a method is a class' function
class MyClass
{
int Sum(int value1, int value2)
{
return value1 + value2;
}
// a different method signature, yet named the same
string Sum(string value1, string value2)
{
return value1 + value2;
}
}
// further in code
MyClass myInstance = new MyClass();
int result = myInstance.Sum(5, 3);
string result = myInstance.Sum("Hello ", "there");
string result = myInstance.Sum("Hello ", 3); // does not work - no such string/int signature, only string/string |
SQF | Enforce Script |
---|---|
class MyClass
{
void PrintMessage()
{
Print("Thread A 1/2");
GetGame().GetCallqueue().CallLater(PrintOtherMessage, 1000); // in milliseconds
// thread Thread_PrintOtherMessage(); // thread usage is not recommended
Print("Thread A 2/2");
}
void PrintOtherMessage()
{
Print("CallQueue later");
}
void Thread_PrintOtherMessage()
{
Print("Thread B 1/2");
Sleep(1000);
Print("Thread B 2/2");
}
}
// further in code
MyClass myInstance = new MyClass();
myInstance.PrintMessage(); |