From SQF to Enforce Script – Arma Reforger
Lou Montana (talk | contribs) m (Text replacement - "{{HashLink" to "{{Link") |
Lou Montana (talk | contribs) m (Remove after class' semicolons) |
||
Line 2: | Line 2: | ||
Welcome to '''Enfusion'''! | Welcome to '''Enfusion'''! | ||
While ''' | While '''{{GameCategory|arma3|link= y}}''' is powered by '''[[SQF Syntax|SQF]]''', '''{{GameCategory|armaR|link= y}}''' uses '''[[:Category:Arma Reforger/Modding/Tutorials/Scripting|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 | '''SQF''' is a succession of [[:Category:Scripting Commands|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. | It is easy to access, read and process at the cost of performance. | ||
Line 75: | Line 75: | ||
{ | { | ||
Print("I am alive!"); | Print("I am alive!"); | ||
} | } | ||
</enforce> | </enforce> | ||
|} | |} | ||
Line 136: | Line 136: | ||
for (int i; i < 5; i++) // i is automatically initialised to 0 | for (int i; i < 5; i++) // i is automatically initialised to 0 | ||
{ | { | ||
} | } | ||
</enforce> | </enforce> | ||
|} | |} | ||
Line 163: | Line 163: | ||
Print(currentIndex); | Print(currentIndex); | ||
Print(currentElement); | Print(currentElement); | ||
} | } | ||
// or, if the index is not required | // or, if the index is not required | ||
Line 169: | Line 169: | ||
{ | { | ||
Print(currentElement); | Print(currentElement); | ||
} | } | ||
</enforce> | </enforce> | ||
|} | |} | ||
Line 192: | Line 192: | ||
{ | { | ||
i++; // this will increment i by 1 | i++; // this will increment i by 1 | ||
} | } | ||
</enforce> | </enforce> | ||
|} | |} | ||
Line 214: | Line 214: | ||
switch (i) | switch (i) | ||
{ | { | ||
case 0: { continue; } | case 0: { continue; } // continue exits the current scope | ||
// but keeps evaluating other cases below | // but keeps evaluating other cases below | ||
case 1: { break; } | case 1: { break; } // the break keyword is used to exit the switch or loop structure | ||
default: {} | default: {} // default does use ":" like case does | ||
} | } | ||
</enforce> | </enforce> | ||
|} | |} | ||
Line 230: | Line 230: | ||
<sqf> | <sqf> | ||
// SPECIAL CASES | // SPECIAL CASES | ||
i = if (isScript) then {1} else {0}; // if-then-else returning value, not possible in Enforce Script | 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 | if (isScript) exitWith {}; // if that breaks from current scope | ||
Line 301: | Line 301: | ||
{ | { | ||
// will NOT work - "Hello there" is different from "HELLO THERE" | // will NOT work - "Hello there" is different from "HELLO THERE" | ||
} | } | ||
</enforce> | </enforce> | ||
|} | |} |
Revision as of 11:05, 19 May 2023
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!");
} |
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 automatically initialised to 0
{
} |
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)
{
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 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(); |