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 |
---|---|
<enforce> // comments /* comment blocks
bool booleanValue = true; float floatValue1 = 5; float floatValue2 = 5.5; string stringValue = "Hello there"; </syntaxhighlight> |
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 |
---|---|
<enforce> if (myPlayer.GetHealth() > 0) // no more 'then'! { Print("I am alive!"); }; </syntaxhighlight> |
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 |
---|---|
<enforce> if (myPlayer.GetHealth() > 0) // no more 'then'! { Print("I am alive!"); } else if (myPlayer.GetHealth() < -90) { Print("That's a lot of damage"); } </syntaxhighlight> |
Code Flow
for
SQF | Enforce Script |
---|---|
<enforce> // 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 { }; </syntaxhighlight> |
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; |
<enforce> // 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); }; </syntaxhighlight> |
while
SQF | Enforce Script |
---|---|
<enforce> // Condition check in while loop is done in regular () brackets while (i < 10) { i++; // this will increment i by 1 }; </syntaxhighlight> |
switch
SQF | Enforce Script |
---|---|
<enforce> 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 }; </syntaxhighlight> |
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";
}; |
<enforce> //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
|
Case Sensitivity
Everything is case-sensitive in Enforce Script, unlike SQF:
SQF | Enforce Script |
---|---|
<enforce> 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" }; </syntaxhighlight> |
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 |
---|---|
<enforce> 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 </syntaxhighlight> |
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 |
---|---|
<enforce> vector northVector = "0 0 1"; vector westVector = "-1 0 0"; vector upVector = "0 1 0"; </syntaxhighlight> |
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 |
---|---|
<enforce> 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; </syntaxhighlight> |
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" }; |
<enforce> 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"); </syntaxhighlight> |
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 |
---|---|
<enforce> // 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 </syntaxhighlight> |
SQF | Enforce Script |
---|---|
<enforce> 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(); </syntaxhighlight> |