From SQF to Enforce Script – Arma Reforger
Lou Montana (talk | contribs) m (Text replacement - "lang="cpp">" to "lang="C#">") |
Lou Montana (talk | contribs) m (Fix switch example) |
||
(15 intermediate revisions by 3 users not shown) | |||
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 '''{{GameCategory|armaR|Modding|Guidelines|Scripting|text= 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) | '''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. | ||
'''Enforce Script''' is an '''Object-Oriented Programming''' '''language''' (OOP language) which as this designation suggests is based on objects. | '''Enforce Script''' is an '''Object-Oriented Programming''' '''language''' (OOP language) which as this designation suggests is based on objects. | ||
{{Feature|informative|See {{Link|Arma Reforger:Object Oriented Programming Basics|Object Oriented Programming Basics}} for an introduction to the OOP approach.}} | |||
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. | 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. | ||
Line 20: | Line 22: | ||
Let's start with the similarities between the languages… because they are very different, the list will actually be a short one. | Let's start with the similarities between the languages… because they are very different, the list will actually be a short one. | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| <sqf> | | <sqf> | ||
// | // inline comment | ||
/* | /* | ||
comment | comment block | ||
*/ | */ | ||
Line 36: | Line 38: | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
// | // inline comment | ||
/* | /* | ||
comment | comment block | ||
*/ | */ | ||
Line 46: | Line 48: | ||
float floatValue2 = 5.5; | float floatValue2 = 5.5; | ||
string stringValue = "Hello there"; | string stringValue = "Hello there"; | ||
</ | </enforce> | ||
|} | |} | ||
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! | 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 | == Main Differences == | ||
=== If-Then-Else === | === 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 {{ | 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 {{hl|[[then]]}} keyword. Enforce Script uses if-else structure like any recent language. | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
Line 69: | Line 72: | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
if (myPlayer.GetHealth() > 0) // no more 'then'! | if (myPlayer.GetHealth() > 0) // no more 'then'! | ||
{ | { | ||
Print("I am alive!"); | |||
}; | } | ||
</ | |||
if (myPlayer.GetHealth() > 0) | |||
Print("I am alive!"); // a short version is possible | |||
</enforce> | |||
|} | |} | ||
The same applies for other code structures (while-do, switch-do, for-do, etc), see {{ | The same applies for other code structures (while-do, switch-do, for-do, etc), see {{Link|#Code Flow}} below. | ||
The '''else''' part supports {{ | The '''else''' part supports {{hl|else if}} (note: not {{hl|elseif}}) statements: | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
Line 90: | Line 96: | ||
{ | { | ||
hint "I am alive!"; | hint "I am alive!"; | ||
} else { | } | ||
else | |||
{ | |||
if (damage player > 0.9) then | if (damage player > 0.9) then | ||
{ | { | ||
Line 98: | Line 106: | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
if (myPlayer.GetHealth() > 0) | if (myPlayer.GetHealth() > 0) | ||
{ | { | ||
Print("I am alive!"); | Print("I am alive!"); | ||
} | } | ||
else if (myPlayer.GetHealth() < | else if (myPlayer.GetHealth() < 10) // no more "else { if }" structure | ||
{ | { | ||
Print("That's a lot of damage"); | Print("That's a lot of damage"); | ||
} | } | ||
</ | </enforce> | ||
|} | |} | ||
Line 113: | Line 121: | ||
==== for ==== | ==== for ==== | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
Line 129: | Line 137: | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
// Enforce Script has only one type of for loop | // Enforce Script has only one type of for loop | ||
// statement, condition and step are separated by semicolons | // statement, condition and step are separated by semicolons | ||
for (int i; i < 5; i++) // i is | for (int i; i < 5; i++) // i is automatically initialised to 0, but "int i = 0" could also be used | ||
{ | { | ||
} | } | ||
</ | </enforce> | ||
|} | |} | ||
==== foreach ==== | ==== foreach ==== | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
// forEach loop in SQF is very convenient by selecting current index and | // forEach loop in SQF is very convenient by selecting the current index and element automatically | ||
{ | { | ||
hint str _forEachIndex; // current index | hint str _forEachIndex; // _forEachIndex is the current array index | ||
hint _x; | hint str _x; // _x represents the current element | ||
} forEach _items; | } forEach _items; | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
// foreach's syntax is changed and follows today's standards | // foreach's syntax is changed and follows today's standards | ||
// the code to be run is placed after the keyword | // the code to be run is placed after the keyword | ||
// beware of the syntax: currentIndex and currentElement are | // 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) | foreach (int currentIndex, string currentElement : items) | ||
{ | { | ||
Print(currentIndex); | Print(currentIndex); | ||
Print(currentElement); | Print(currentElement); | ||
} | } | ||
// or, | // or, if the index is not required | ||
foreach (string currentElement : items) | foreach (string currentElement : items) | ||
{ | { | ||
Print(currentElement); | Print(currentElement); | ||
} | } | ||
</ | </enforce> | ||
|} | |} | ||
==== while ==== | ==== while ==== | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
// Unlike other flow structures, while condition is written between {} brackets | // Unlike other flow structures, the "while" condition is written between {} brackets | ||
while { _i < 10 } do | while { _i < 10 } do | ||
{ | { | ||
_i = _i + 1; | _i = _i + 1; // only way to increment i by 1 as i++ does not exist in SQF | ||
}; | }; | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
// Condition check in while loop is done in regular () brackets | // Condition check in while loop is done in regular () brackets | ||
while (i < 10) | while (i < 10) | ||
{ | { | ||
i++; // this will increment i by 1 | i++; // this will increment i by 1 | ||
} | } | ||
</ | </enforce> | ||
|} | |} | ||
==== switch ==== | ==== switch ==== | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
switch (i) do | switch (i) do | ||
{ | { | ||
case 0: {}; // if i is 0 code in brackets is executed | case 0: { systemChat "case 0" }; // if i is 0, code in brackets is executed and switch is exited automatically | ||
case 1: {}; | case 1: { systemChat "case 1" }; | ||
default {}; // default does not use ":" like case does | default { systemChat "default" }; // default does -not- use ":" like case does | ||
}; | }; | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
switch (i) | switch (i) | ||
{ | { | ||
case 0: | case 0: | ||
Print("case 0"); | |||
case 1: | break; | ||
default: | |||
} | case 1: | ||
</ | Print("case 1"); | ||
break; | |||
default: // default -does- use ":" just like case does | |||
Print("default"); | |||
break; | |||
} | |||
</enforce> | |||
|} | |} | ||
==== Others ==== | ==== Others ==== | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<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 (isScript) exitWith {}; // exitWith breaks from the current -scope- | ||
// it will exit the script only if at its root | |||
Line 238: | Line 253: | ||
// to prevent this behaviour the second statement should be wrapped in code brackets {} | // 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 | |||
if (!isNil "_value" && { _value > 10 }) then | if (!isNil "_value" && { _value > 10 }) then | ||
{ | { | ||
Line 246: | Line 260: | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
//SPECIAL CASES | // SPECIAL CASES | ||
// an if statement can avoid brackets for only one line of code | // an if statement can avoid brackets for only one line of code | ||
if (isScript) | if (isScript) | ||
break; // this will exit a while loop | break; // this will exit a while loop | ||
if (isScript) | if (isScript) | ||
return 0; // this will exit the current method and return 0 | return 0; // this will exit the current method and return 0 | ||
Line 260: | Line 275: | ||
Print("value exists and is greater than 10"); | Print("value exists and is greater than 10"); | ||
} | } | ||
</ | </enforce> | ||
|} | |} | ||
Line 267: | Line 282: | ||
Everything is case-sensitive in Enforce Script, unlike SQF: | Everything is case-sensitive in Enforce Script, unlike SQF: | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
Line 287: | Line 302: | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
string myValue = "Hello there"; | string myValue = "Hello there"; | ||
Print(MYVALUE); // error: MYVALUE is undefined | Print(MYVALUE); // error: MYVALUE is undefined | ||
Line 299: | Line 314: | ||
{ | { | ||
// will NOT work - "Hello there" is different from "HELLO THERE" | // will NOT work - "Hello there" is different from "HELLO THERE" | ||
} | } | ||
</ | </enforce> | ||
|} | |} | ||
=== Typed Variables === | === 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. | 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. | |||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
private _myValue = "Hello there"; // _myValue is a string | private _myValue = "Hello there"; // _myValue is a string | ||
_myValue = 5; // _myValue is now a floating point number | _myValue = 5; // _myValue is now a floating point number | ||
_myValue = true; // _myValue is now a boolean | _myValue = true; // _myValue is now a boolean, etc | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
string myValue = "Hello there"; // myValue is a string | string myValue = "Hello there"; // myValue is a string | ||
myValue = 5; // error: cannot convert an int to string | myValue = 5; // error: cannot convert an int to string | ||
myValue = true; // error: cannot convert a bool to string | myValue = true; // error: cannot convert a bool to string | ||
</ | </enforce> | ||
|} | |} | ||
Line 332: | Line 347: | ||
Real Virtuality uses the {{hl|[X, Z, Y]}} format (a vector pointing up being {{hl|[0, 0, 1]}}) whereas Enfusion uses {{hl|{ X, Y, Z }<nowiki/>}} (a vector pointing up being {{hl|{ 0, 1, 0 }<nowiki/>}}). | Real Virtuality uses the {{hl|[X, Z, Y]}} format (a vector pointing up being {{hl|[0, 0, 1]}}) whereas Enfusion uses {{hl|{ X, Y, Z }<nowiki/>}} (a vector pointing up being {{hl|{ 0, 1, 0 }<nowiki/>}}). | ||
{{Feature| | {{Feature|important|Note that in Enfusion the coordinate system is '''left-handed''' <!--; meaning the {{hl|{ 0, 0, 0 } coordinates is at the "bottom-left" (South-West) of the terrain --> - see {{Link|https://en.wikipedia.org/wiki/Cartesian_coordinate_system#Orientation_and_handedness|Wikipedia}}.}} | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
Line 345: | Line 360: | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
vector northVector = "0 0 1"; | vector northVector = "0 0 1"; | ||
vector westVector = "-1 0 0"; | vector westVector = "-1 0 0"; | ||
vector upVector = "0 1 0"; | vector upVector = "0 1 0"; | ||
</ | </enforce> | ||
|} | |} | ||
=== Array === | === 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. | 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. | |||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
private _array = []; | private _array = []; | ||
_array pushBack 0; | _array pushBack 0; // _array is [0] | ||
_array pushBack [1]; | _array pushBack [1]; // _array is [0, [1]] | ||
_array pushBack "2"; // etc | _array pushBack "2"; // _array is [0, [1], "2"], etc | ||
_array set [0, 1]; // sets the first element to 1 | _array set [0, 1]; // sets the first element to 1 | ||
// _array is [1, [1], "2"] | |||
// array duplication | // array duplication | ||
Line 372: | Line 389: | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
array<int> myArray = {}; // dynamic array | array<int> myArray = {}; // dynamic array | ||
myArray.Insert(0); // | myArray.Insert(0); // array is { 0 } | ||
myArray.Insert(true); // | myArray.Insert(true); // array is { 0, 1 } - true is 1 | ||
myArray.Insert("2"); // error: an array cannot contain a different type | myArray.Insert("2"); // error: an array cannot contain a different type | ||
myArray[0] = 1; | myArray[0] = 1; // sets the first element to 1 | ||
// array duplication | // array duplication | ||
array<int> duplicate = {}; | array<int> duplicate = {}; | ||
duplicate.Copy(myArray); | 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 | // new array type: static (size) array | ||
int myArray[3] | int myArray[3]; // automatically initialised to the type's default value (here { 0, 0, 0 }) | ||
myArray[0] = 1; | myArray[0] = 1; // { 1, 0, 0 } | ||
myArray[1] = 2; | myArray[1] = 2; // { 1, 2, 0 } | ||
myArray[2] = 3; | myArray[2] = 3; // { 1, 2, 3 } | ||
</ | </enforce> | ||
|} | |} | ||
=== Data Types === | === Data Types === | ||
Including '''vectors''', '''enums''', '''sets''', '''classes''' of course and others! Find all the new data types on their | Including '''vectors''', '''enums''', '''sets''', '''classes''' of course and others! Find all the new data types on their {{Link|Arma Reforger:Scripting: Values|dedicated page}}. | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
private _position = [50,50,0]; // an array | private _position = [50, 50, 0]; // an array | ||
private _number1 = 42; | private _number1 = 42; // a Number, a.k.a float | ||
private _number2 = 5.5; | private _number2 = 5.5; // a Number, a.k.a float | ||
_number1 = _number1 + 1; | _number1 = _number1 + 1; | ||
_number2 = _number2 - 1; | _number2 = _number2 - 1; | ||
Line 425: | Line 452: | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
vector position = { 50, 0, 50 }; | vector position = { 50, 0, 50 }; | ||
int number1 = 42; | int number1 = 42; | ||
float number2 = 5.5; | float number2 = 5.5; | ||
number1++; | |||
number2--; | |||
map<int, string> hashmap = new map<int, string>(); // types are set in stone | map<int, string> hashmap = new map<int, string>(); // types are set in stone | ||
hashmap.Insert(1, "oops"); | hashmap.Insert(1, "oops"); | ||
hashmap.Insert(2, "two"); | hashmap.Insert(2, "two"); | ||
// no | // no Enforce Script equivalent | ||
hashmap.Set(1, "one"); // Set() is to update a value - it can create it too, but Insert() is faster | hashmap.Set(1, "one"); // Set() is to update a value - it can create it too, but Insert() is faster | ||
Line 453: | Line 480: | ||
if (!myInstance) | if (!myInstance) | ||
Print("myInstance is null"); | Print("myInstance is null"); | ||
</ | </enforce> | ||
|} | |} | ||
=== Object-Oriented Programming === | === Object-Oriented Programming === | ||
SQF is a scripting language based on sequences of expressions set in scripts. | 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. | ||
{{Feature|informative|The basics of OOP can be found in | {{Feature|informative|The basics of OOP can be found in {{Link|Arma Reforger:Object Oriented Programming Basics|Object Oriented Programming Basics}}; see also {{Link|https://en.wikipedia.org/wiki/Object-oriented_programming|Wikipedia}}.}} | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
Line 480: | Line 507: | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
// a method is a class' function | // a method is a class' function | ||
class MyClass | class MyClass | ||
Line 492: | Line 519: | ||
string Sum(string value1, string value2) | string Sum(string value1, string value2) | ||
{ | { | ||
return | return value1 + value2; | ||
} | } | ||
} | } | ||
Line 500: | Line 527: | ||
int result = myInstance.Sum(5, 3); | int result = myInstance.Sum(5, 3); | ||
string result = myInstance.Sum("Hello ", "there"); | string result = myInstance.Sum("Hello ", "there"); | ||
string result = myInstance.Sum("Hello ", 3); // does not work - no such signature | string result = myInstance.Sum("Hello ", 3); // does not work - no such string/int signature, only string/string | ||
</ | </enforce> | ||
|} | |} | ||
Line 509: | Line 536: | ||
}} | }} | ||
{| class="wikitable" style="width: 100%" | {| class="wikitable valign-top" style="width: 100%" | ||
! style="width: 50%" | SQF | ! style="width: 50%" | SQF | ||
! Enforce Script | ! Enforce Script | ||
|- | |- | ||
| | | | ||
<sqf> | <sqf> | ||
Line 524: | Line 551: | ||
</sqf> | </sqf> | ||
| | | | ||
< | <enforce> | ||
class MyClass | class MyClass | ||
{ | { | ||
Line 551: | Line 578: | ||
MyClass myInstance = new MyClass(); | MyClass myInstance = new MyClass(); | ||
myInstance.PrintMessage(); | myInstance.PrintMessage(); | ||
</ | </enforce> | ||
|} | |} | ||
Line 557: | Line 584: | ||
== See Also == | == See Also == | ||
* | * {{Link|Arma Reforger:Scripting: Conventions|Scripting: Conventions}} | ||
* {{GameCategory|armaR|Modding|Guidelines|Scripting|link= y|text= Scripting Guidelines}} | * {{GameCategory|armaR|Modding|Guidelines|Scripting|link= y|text= Scripting Guidelines}} | ||
* {{GameCategory|armaR|Modding|Tutorials|Scripting|link= y|text= Scripting Tutorials}} | * {{GameCategory|armaR|Modding|Tutorials|Scripting|link= y|text= Scripting Tutorials}} |
Latest revision as of 13:14, 16 May 2024
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(); |