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) - learn more about SQF here.
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!");
}; |
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) // no more 'then'!
{
Print("I am alive!");
}
else if (myPlayer.GetHealth() < -90)
{
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 already initialised to 0
{
}; |
foreach
SQF | Enforce Script |
---|---|
// forEach loop in SQF is very convenient by selecting current index and current variables automatically
{
hint str _forEachIndex; // current index of the array is accessible by _forEachIndex
hint _x; // _x is automatically selected variable representing iterated element in the array
} forEach _items; |
// 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 divided with ",",
// but currentElement and array used in by ":"
foreach (int currentIndex, string currentElement : items)
{
Print(currentIndex);
Print(currentElement);
};
// or, without the index need
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)
{
case 0: { continue; }; // continue exits the current scope
// but keeps evaluating other cases below
case 1: { break; }; // the break keyword is used to exit the switch or loop structure
default: {}; // default does use ":" like case does
}; |
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 {}; // if that breaks from current scope
// lazy evaluation: by default, all statements in condition field are evaluated
// to prevent this behaviour the second statement should be wrapped in code brackets {}
// wrong as "_value > 10" will be evaluated
// if (!isNil "_value" && _value > 10) then
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 and dynamic; also, an array can only contain one type of data.
SQF | Enforce Script |
---|---|
array<int> myArray = {}; // dynamic array
myArray.Insert(0); // OK
myArray.Insert(true); // OK - true is 1
myArray.Insert("2"); // error: an array cannot contain a different type
myArray[0] = 1;
// array duplication
array<int> duplicate = {};
duplicate.Copy(myArray);
// new array type: static (size) array
int myArray[3] = { 0, 0, 0 };
myArray[0] = 1;
myArray[1] = 2;
myArray[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 Enscript 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. Enscript 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 value 1 + 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(); |