From SQF to Enforce Script – Arma Reforger

From Bohemia Interactive Community
Jump to navigation Jump to search
m (Add OOP link)
(Fix examples)
 
(11 intermediate revisions by 2 users not shown)
Line 2: Line 2:
Welcome to '''Enfusion'''!
Welcome to '''Enfusion'''!


While '''Arma 3''' is powered by '''SQF''', '''Arma Reforger''' uses '''Enforce Script''' as language, which is syntactically close to C#.
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) - learn more about SQF here.
'''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 10: Line 10:
'''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 [[Arma Reforger:Object Oriented Programming Basics|Object Oriented Programming Basics]] for an introduction to the OOP approach.}}
{{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 22: 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
|- style="vertical-align: top"
|-
| <sqf>
| <sqf>
// comments
// inline comment
/*
/*
comment blocks
comment block
*/
*/


Line 38: Line 38:
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<enforce>
// comments
// inline comment
/*
/*
comment blocks
comment block
*/
*/


Line 48: Line 48:
float floatValue2 = 5.5;
float floatValue2 = 5.5;
string stringValue = "Hello there";
string stringValue = "Hello there";
</syntaxhighlight>
</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 differences ==
== 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 {{ic|[[then]]}} keyword. Enforce Script uses if-else structure like any recent language.
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
|- style="vertical-align: top"
|-
|
|
<sqf>
<sqf>
Line 71: Line 72:
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<enforce>
if (myPlayer.GetHealth() > 0) // no more 'then'!
if (myPlayer.GetHealth() > 0) // no more 'then'!
{
{
Print("I am alive!");
Print("I am alive!");
};
}
</syntaxhighlight>
 
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 {{HashLink|#Code Flow}} below.
The same applies for other code structures (while-do, switch-do, for-do, etc), see {{Link|#Code Flow}} below.


The '''else''' part supports {{ic|else if}} (note: not {{ic|elseif}}) statements:
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
|- style="vertical-align: top"
|-
|
|
<sqf>
<sqf>
Line 92: 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 100: Line 106:
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<enforce>
if (myPlayer.GetHealth() > 0) // no more 'then'!
if (myPlayer.GetHealth() > 0)
{
{
Print("I am alive!");
Print("I am alive!");
}
}
else if (myPlayer.GetHealth() < -90)
else if (myPlayer.GetHealth() < 10) // no more "else { if }" structure
{
{
Print("That's a lot of damage");
Print("That's a lot of damage");
}
}
</syntaxhighlight>
</enforce>
|}
|}


Line 115: 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
|- style="vertical-align: top"
|-
|
|
<sqf>
<sqf>
Line 131: Line 137:
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<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 already initialised to 0
for (int i; i < 5; i++) // i is automatically initialised to 0, but "int i = 0" could also be used
{
{
};
}
</syntaxhighlight>
</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
|- style="vertical-align: top"
|-
|
|
<sqf>
<sqf>
// forEach loop in SQF is very convenient by selecting current index and current variables automatically
// forEach loop in SQF is very convenient by selecting the current index and element automatically
{
{
hint str _forEachIndex; // current index of the array is accessible by _forEachIndex
hint str _forEachIndex; // _forEachIndex is the current array index
hint _x; // _x is automatically selected variable representing iterated element in the array
hint str _x; // _x represents the current element
} forEach _items;
} forEach _items;
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<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 divided with ",",
// beware of the syntax: currentIndex and currentElement are separated with ","
// but currentElement and array used in by ":"
// 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, without the index need
// or, if the index is not required
foreach (string currentElement : items)
foreach (string currentElement : items)
{
{
Print(currentElement);
Print(currentElement);
};
}
</syntaxhighlight>
</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
|- style="vertical-align: top"
|-
|
|
<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>
|
|
<syntaxhighlight lang="C#">
<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
};
}
</syntaxhighlight>
</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
|- style="vertical-align: top"
|-
|
|
<sqf>
<sqf>
Line 207: Line 213:
case 0: {}; // if i is 0 code in brackets is executed, and switch is exited automatically
case 0: {}; // if i is 0 code in brackets is executed, and switch is exited automatically
case 1: {};
case 1: {};
default {}; // default does not use ":" like case does
default {}; // default does -not- use ":" like case does
};
};
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<enforce>
switch (i)
switch (i)
{
{
case 0: { continue; }; // continue exits the current scope
case 0: { continue; } // continue exits the current scope
// but keeps evaluating other cases below
// but keeps evaluating other cases below
case 1: { break; }; // the break keyword is used to exit the switch or loop structure
case 1: { break; } // the break keyword is used to exit the switch or loop structure
default: {}; // default does use ":" like case does
default: {} // default -does- use ":" just like case does
};
}
</syntaxhighlight>
</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
|- style="vertical-align: top"
|-
|
|
<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 {}; // exitWith breaks from the current -scope-
 
// it will exit the script only if at its root




Line 240: Line 246:
// 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 {}


// wrong as "_value > 10" will be evaluated
// 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 248: Line 253:
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<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 262: Line 268:
Print("value exists and is greater than 10");
Print("value exists and is greater than 10");
}
}
</syntaxhighlight>
</enforce>
|}
|}


Line 269: Line 275:
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
|- style="vertical-align: top"
|-
|
|
<sqf>
<sqf>
Line 289: Line 295:
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<enforce>
string myValue = "Hello there";
string myValue = "Hello there";
Print(MYVALUE); // error: MYVALUE is undefined
Print(MYVALUE); // error: MYVALUE is undefined
Line 301: Line 307:
{
{
// will NOT work - "Hello there" is different from "HELLO THERE"
// will NOT work - "Hello there" is different from "HELLO THERE"
};
}
</syntaxhighlight>
</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
|- style="vertical-align: top"
|-
|
|
<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
// etc
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<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
</syntaxhighlight>
</enforce>
|}
|}


Line 334: Line 340:
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|Important|Note that in Enfusion the coordinate system is '''left-handed'''.}}
{{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
|- style="vertical-align: top"
|-
|
|
<sqf>
<sqf>
Line 347: Line 353:
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<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";
</syntaxhighlight>
</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
|- style="vertical-align: top"
|-
|
|
<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 374: Line 382:
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<enforce>
array<int> myArray = {}; // dynamic array
array<int> myArray = {}; // dynamic array
myArray.Insert(0); // OK
myArray.Insert(0); // array is { 0 }
myArray.Insert(true); // OK - true is 1
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] = { 0, 0, 0 };
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 }
</syntaxhighlight>
</enforce>
|}
|}


=== Data Types ===
=== Data Types ===


Including '''vectors''', '''enums''', '''sets''', '''classes''' of course and others! Find all the new data types on their [[Arma Reforger:Scripting: Values|dedicated page]].
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
|- style="vertical-align: top"
|-
|
|
<sqf>
<sqf>
private _position = [50,50,0]; // an array
private _position = [50, 50, 0]; // an array
private _number1 = 42; // a Number, a.k.a float
private _number1 = 42; // a Number, a.k.a float
private _number2 = 5.5; // a Number, a.k.a float
private _number2 = 5.5; // a Number, a.k.a float
_number1 = _number1 + 1;
_number1 = _number1 + 1;
_number2 = _number2 - 1;
_number2 = _number2 - 1;
Line 427: Line 445:
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<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++;
number1++;
_number2--;
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 Enscript equivalent
// 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 455: Line 473:
if (!myInstance)
if (!myInstance)
Print("myInstance is null");
Print("myInstance is null");
</syntaxhighlight>
</enforce>
|}
|}


=== Object-Oriented Programming ===
=== 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 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 [[Arma Reforger:Object Oriented Programming Basics|Object Oriented Programming Basics]].}}
{{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
|- style="vertical-align: top"
|-
|
|
<sqf>
<sqf>
Line 482: Line 500:
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<enforce>
// a method is a class' function
// a method is a class' function
class MyClass
class MyClass
Line 494: Line 512:
string Sum(string value1, string value2)
string Sum(string value1, string value2)
{
{
return value 1 + value2;
return value1 + value2;
}
}
}
}
Line 503: Line 521:
string result = myInstance.Sum("Hello ", "there");
string result = myInstance.Sum("Hello ", "there");
string result = myInstance.Sum("Hello ", 3); // does not work - no such string/int signature, only string/string
string result = myInstance.Sum("Hello ", 3); // does not work - no such string/int signature, only string/string
</syntaxhighlight>
</enforce>
|}
|}


Line 511: Line 529:
}}
}}


{| class="wikitable" style="width: 100%"
{| class="wikitable valign-top" style="width: 100%"
! style="width: 50%" | SQF
! style="width: 50%" | SQF
! Enforce Script
! Enforce Script
|- style="vertical-align: top"
|-
|
|
<sqf>
<sqf>
Line 526: Line 544:
</sqf>
</sqf>
|
|
<syntaxhighlight lang="C#">
<enforce>
class MyClass
class MyClass
{
{
Line 553: Line 571:
MyClass myInstance = new MyClass();
MyClass myInstance = new MyClass();
myInstance.PrintMessage();
myInstance.PrintMessage();
</syntaxhighlight>
</enforce>
|}
|}


Line 559: Line 577:
== See Also ==
== See Also ==


* [[Arma Reforger:Scripting: Conventions|Scripting: Conventions]]
* {{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 16:31, 14 March 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.

See 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.

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
// inline comment /* comment block */ private _booleanValue = true; private _floatValue1 = 5; private _floatValue2 = 5.5; private _stringValue = "Hello there";

// inline comment /* comment block */ bool booleanValue = true; float floatValue1 = 5; float floatValue2 = 5.5; string stringValue = "Hello there";

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 (alive player) then { hint "I am alive!"; };

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 (alive player) then { hint "I am alive!"; } else { if (damage player > 0.9) then { hint "That's a lot of damage"; }; };

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

// There are two ways to write a for-loop in SQF: for "_i" from 0 to 10 do { }; for [{ _i = 0 }, { _i < 10 }, { _i = _i + 1 }] do { };

// 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 loop in SQF is very convenient by selecting the current index and element automatically { hint str _forEachIndex; // _forEachIndex is the current array index hint str _x; // _x represents the current element } 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 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

// Unlike other flow structures, the "while" condition is written between {} brackets while { _i < 10 } do { _i = _i + 1; // only way to increment i by 1 as i++ does not exist in SQF };

// 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: {}; // if i is 0 code in brackets is executed, and switch is exited automatically case 1: {}; default {}; // default does -not- use ":" like case does };

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 ":" just 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 {}; // 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

private _myValue = "Hello there"; hint _MYVALUE; // hints "Hello there" IF (true) THEN { hint "it's true!"; }; if (_myValue == "HELLO THERE") then { // will work };

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

private _myValue = "Hello there"; // _myValue is a string _myValue = 5; // _myValue is now a floating point number _myValue = true; // _myValue is now a boolean, etc

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 }).

Note that in Enfusion the coordinate system is left-handed - see Wikipedia.
SQF Enforce Script

private _northVector = [0, 1, 0]; private _westVector = [-1, 0, 0]; private _upVector = [0, 0, 1];

vector northVector = "0 0 1"; vector westVector = "-1 0 0"; vector upVector = "0 1 0";

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

private _array = []; _array pushBack 0; // _array is [0] _array pushBack [1]; // _array is [0, [1]] _array pushBack "2"; // _array is [0, [1], "2"], etc _array set [0, 1]; // sets the first element to 1 // _array is [1, [1], "2"] // array duplication private _array2 = +_array;

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.

The basics of OOP can be found in Object Oriented Programming Basics; see also Wikipedia.
SQF Enforce Script

// in-script code declaration - normally one would have to declare it in CfgFunctions private _sum = { params [ ["_value1", 0, [0]], ["_value2", 0, [0]] ]; _value1 + _value2; }; private _result = [5, 3] call _sum;

// 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

It is worth noting that Enforce Script does not benefit from previous titles' Scheduler, therefore wildly creating new threads is not recommended! See Enforce Script's example below for Callqueue's usage.
SQF Enforce Script

hint "Thread A 1/2"; [] spawn { hint "Thread B 1/2"; sleep 1; hint "Thread B 2/2"; }; hint "Thread A 2/2";

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();


See Also