Scripting: Values – Arma Reforger

From Bohemia Interactive Community
Jump to navigation Jump to search
m (Text replacement - "lang="cpp">" to "lang="C#">")
m (Add value prefix cheat sheet)
 
(15 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{TOC|side}}
{{TOC|side}}
A '''value''' is either a '''variable''' (that can be changed) or a '''constant''' (that cannot be changed).
A '''value''' describes a ''data holder''; a value is either a '''variable''' (that can be changed) or a '''constant''' (that cannot be changed).


Values are declared in Enfusion with a type. Enfusion Script uses strong-types, which means a value's type cannot be changed along its lifetime.
Values are declared in Enfusion with a type, in format {{hl|type identifier {{=}} value}} (the {{hl|{{=}} value}} part being optional).
Enforce Script uses strong types, which means a value's type cannot be changed along its lifetime.




Line 16: Line 17:
The regular expression to be respected is: {{hl|^[a-zA-Z_][a-zA-Z0-9_]*$}}
The regular expression to be respected is: {{hl|^[a-zA-Z_][a-zA-Z0-9_]*$}}


It is ''recommended'' to write variable identifiers in camelCase, (e.g {{hl|aNewVariable}}) and constants' in UPPER_CASE (e.g {{hl|A_NEW_CONSTANT}}). See Arma Reforger Variable Conventions for naming conventions.
It is ''recommended'' to write variable identifiers in camelCase, (e.g {{hl|aNewVariable}}) and constants' in UPPER_CASE (e.g {{hl|A_NEW_CONSTANT}}).
<syntaxhighlight lang="C#">
See {{Link|Arma Reforger:Scripting: Conventions#Variable}} for {{armaR}} naming conventions.
 
<enforce>
int myNumber = 10;
int myNumber = 10;
int _myNumber = 10;
int _myNumber = 10;
int myNUMBER = 10; // different from myNumber
int myNUMBER = 10; // different from myNumber
 
int 1number = 1; // wrong - starts with a number
int 1number = 1; // wrong - starts with a number
int my#variable = 1; // wrong - contains a character that is not letter/number/underscore
int my#variable = 1; // wrong - contains a character that is not letter/number/underscore
int auto = 1; // wrong - the "auto" keyword exists and cannot be replaced
int auto = 1; // wrong - the "auto" keyword exists and cannot be replaced
</syntaxhighlight>
</enforce>




Line 31: Line 34:


Declaring a value is done by using a type and an identifier:
Declaring a value is done by using a type and an identifier:
<syntaxhighlight lang="C#">
<enforce>
int myNumber; // variable myNumber is declared and auto-initialised to 0
int myNumber; // variable myNumber is declared and auto-initialised to 0
 
myNumber = 10; // myNumber is now 10
myNumber = 10; // myNumber is now 10
</syntaxhighlight>
</enforce>


It can also be directly created with a value:
It can also be directly created with a value:
<syntaxhighlight lang="C#">
<enforce>
int myNumber = 10;
int myNumber = 10;
</syntaxhighlight>
</enforce>
 
=== const ===
 
A value can be made '''constant''', meaning that it will remain the same along its lifetime.
<enforce>
string value1 = "value"; // value1's value is "value"
const string CONST_VALUE = "value";
 
value1 = "new value"; // value1 is now "new value"
CONST_VALUE = "new value"; // error: cannot modify a const value
</enforce>
 
An object (array, set, map, other class instance) can be {{hl|const}} yet still have its values changed - {{hl|const}} here only keeps the reference and does not "freeze" the object in place.
<enforce>
const ref array<string> STRINGS = {};
 
STRINGS.Insert("Hello there"); // OK: the array remains the same, its content is changed
STRINGS = null; // error: cannot modify a const value
</enforce>




Line 47: Line 69:
=== By Content ===
=== By Content ===


<syntaxhighlight lang="C#">
<enforce>
// integers (whole numbers) are passed by content
// integers (whole numbers) are passed by content
int myVar1 = 33;
int myVar1 = 33;
int myVar2 = myVar1; // myVar2 is 33 - it copied myValue1's content
int myVar2 = myVar1; // myVar2 is 33 - it copied myValue1's content
myVar2 = 42; // myVar2 is now 42, myVar1 is still 33 - they are two different values
myVar2 = 42; // myVar2 is now 42, myVar1 is still 33 - they are two different values
</syntaxhighlight>
</enforce>


=== By Reference ===
=== By Reference ===


<syntaxhighlight lang="C#">
<enforce>
// arrays (list of values) are passed by reference
// arrays (list of values) are passed by reference
array<int> myArray1 = { 0, 1, 2, 3 };
array<int> myArray1 = { 0, 1, 2, 3 };
array<int> myArray2 = myArray1; // myArray2 targets myArray1 object - value is not copied but referenced
array<int> myArray2 = myArray1; // myArray2 targets myArray1 object - value is not copied but referenced
myArray2[0] = 99; // myArray1 is now { 99, 1, 2, 3 };
myArray2[0] = 99; // myArray1 is now { 99, 1, 2, 3 };
</syntaxhighlight>
</enforce>




{| class="wikitable float-right"
|+ Prefix Cheat Sheet
! Value Type
! Prefix
|-
| Bool
| <enforce inline>m_b</enforce>
|-
| Integer
| <enforce inline>m_i</enforce>
|-
| Float
| <enforce inline>m_f</enforce>
|-
| String
| <enforce inline>m_s</enforce>
|-
| Enum
| <enforce inline>m_e</enforce>
|-
| Vector
| <enforce inline>m_v</enforce>
|-
| Array
| <enforce inline>m_a</enforce>
|-
| Map
| <enforce inline>m_m</enforce>
|-
| Set
| <enforce inline>m_</enforce>
|-
| Class
| <enforce inline>m_</enforce>
<!-- colleagues…
|-
| Color
| <enforce inline>m_</enforce>
-->
|}
== Types ==
== Types ==


Line 70: Line 132:
Notions:
Notions:
* Passed by: content or reference - see above
* Passed by: content or reference - see above
* Naming prefix: used in Object's (member) variable convention: m_'''prefix'''VariableName (e.g: {{hl|m_'''b'''PlayerIsAlive {{=}} true}})
* Naming prefix: used in '''Object'''<nowiki/>'s value convention (See {{Link|Arma Reforger:Scripting: Conventions#Variable|conventions}}): {{hl|m_}} or {{hl|s_}} + '''prefix''' + VariableName (e.g: {{hl|m_'''b'''PlayerIsAlive {{=}} true}}){{Feature|informative|No prefix is used for constants in conventions.}}
* Default value: the default value given when a value is declared without content (e.g: {{hl|int value}} - the variable here is 0, integer's default value)
* Default value: the default value given when a value is declared without content (e.g: {{hl|int value}} - the variable here is 0, integer's default value)


Line 76: Line 138:
! Incorrect
! Incorrect
! Correct
! Correct
|- style="vertical-align: top"
|-
|
|
<syntaxhighlight lang="C#">
<enforce>
bool variable = true;
bool variable = true;
if (variable)
if (variable)
{
{
variable = "it works!"; // not - variable is and remains a boolean
variable = "it works!"; // not - variable is and remains a bool
Print(variable);
Print(variable);
}
}
</syntaxhighlight>
</enforce>
|
|
<syntaxhighlight lang="C#">
<enforce>
bool variable = true;
bool variable = true;
if (variable)
if (variable)
Line 94: Line 156:
Print(display);
Print(display);
}
}
</syntaxhighlight>
</enforce>
|}
|}


Line 114: Line 176:
! float
! float
| float
| float
|±1.18E-38 through ±3.402823E+38
| ±1.18E-38 through ±3.402823E+38
| 0.0
| 0.0
| 4
| 4
Line 127: Line 189:
| char*
| char*
| -
| -
|"" (empty string)  
|"" (empty string)
| 8 (pointer) + (length × 1 byte)
| 8 (pointer) + (length × 1 byte)
|-
|-
Line 157: Line 219:
<nowiki>*</nowiki>the asterisk in C++ means a pointer.
<nowiki>*</nowiki>the asterisk in C++ means a pointer.


=== Boolean ===
=== Bool ===


Passed by: '''value'''
Passed by: '''value'''
Line 165: Line 227:
Default value: '''false'''
Default value: '''false'''


A '''boolean''' is a value that can be either '''true''' or '''false''' . For example, {{hl|boolean result {{=}} 10 > 5;}} result can either be true or false.
A '''bool''' is a value that can be either '''true''' or '''false''' . For example, <enforce inline>bool result = 10 > 5;</enforce> result can either be true or false (here is true, as 10 is greater than 5).


Example:
Example:
<syntaxhighlight lang="C#">
<enforce>
bool myValue; // myValue is false
bool myValue; // myValue is false
myValue = 10 > 0; // myValue is true
myValue = 10 > 0; // myValue is true
</syntaxhighlight>
</enforce>




Line 187: Line 249:


Example:
Example:
<syntaxhighlight lang="C#">
<enforce>
int myValue; // myValue is 0
int myValue; // myValue is 0
myValue = 20; // myValue is 20
myValue = 20; // myValue is 20
myValue /= 3; // myValue is 6: 20 / 3 = 6.666… which gets floored to 6 (and not rounded)
myValue /= 3; // myValue is 6: 20 / 3 = 6.666… which gets floored to 6 (and not rounded)
</syntaxhighlight>
</enforce>


=== Float ===
=== Float ===
Line 213: Line 275:


Example:
Example:
<syntaxhighlight lang="C#">
<enforce>
float myValue; // myValue is 0 (or 0.0)
float myValue; // myValue is 0 (or 0.0)
myValue = 1; // myValue is 1 (or 1.0)
myValue = 1; // myValue is 1 (or 1.0)
myValue /= 3; // myValue is 0.333333…
myValue /= 3; // myValue is 0.333333…
 
float originalValue = 1;
float originalValue = 1;
float divisionResult = originalValue/3333;
float divisionResult = originalValue/3333;
Line 223: Line 285:
originalValue == result; // returns false due to floating point precision
originalValue == result; // returns false due to floating point precision
float.AlmostEqual(originalValue, result); // returns true
float.AlmostEqual(originalValue, result); // returns true
</syntaxhighlight>
</enforce>


=== String ===
=== String ===
Line 239: Line 301:
{{Feature|informative|
{{Feature|informative|
* A string '''cannot''' be null
* A string '''cannot''' be null
* String comparison is '''case-sensitive'''; e.g <syntaxhighlight lang="C#">"abc" != "ABC"</syntaxhighlight>
* String comparison is '''case-sensitive'''; e.g <enforce inline>"abc" != "ABC"</enforce>
}}
}}


Example:
Example:
<syntaxhighlight lang="C#">
<enforce>
string username; // username is "" (empty string)
string username; // username is "" (empty string)
username = "Player 1"; // username is "Player 1";
username = "Player 1"; // username is "Player 1";
</syntaxhighlight>
</enforce>


=== Enum ===
=== Enum ===
Line 261: Line 323:


Examples:
Examples:
<syntaxhighlight lang="C#">
<enforce>
enum EHealthState
enum EHealthState
{
{
Line 269: Line 331:
DEAD,
DEAD,
}
}
 
EHealthState healthState; // healthState is ALIVE (0)
EHealthState healthState; // healthState is ALIVE (0)
healthState = EHealthState.UNCONSCIOUS; // healthState is UNCONSCIOUS (4)
healthState = EHealthState.UNCONSCIOUS; // healthState is UNCONSCIOUS (4)
int myValue = EHealthState.ALIVE; // valid
int myValue = EHealthState.ALIVE; // valid
</syntaxhighlight>
</enforce>


By default, the first value is equal to 0 and the next values are incremented by 1.
By default, the first value is equal to 0 and the next values are incremented by 1.


<syntaxhighlight lang="C#">
<enforce>
enum EHealthState
enum EHealthState
{
{
Line 285: Line 347:
DEAD, // equals 51
DEAD, // equals 51
}
}
 
EHealthState healthState; // healthState is 0 (no corresponding enum)
EHealthState healthState; // healthState is 0 (no corresponding enum)
healthState = EHealthState.INJURED; // healthState is 43 (INJURED)
healthState = EHealthState.INJURED; // healthState is 43 (INJURED)
int myValue = EHealthState.ALIVE; // 42
int myValue = EHealthState.ALIVE; // 42
</syntaxhighlight>
</enforce>
<syntaxhighlight lang="C#">
<enforce>
// an enum value can also be defined through bit shifting for e.g flag usage
// an enum value can also be defined through {{Link|Arma Reforger:Scripting: Operators#<<|bit shifting}} for e.g flag usage
enum ELifeStatus
enum ELifeStatus
{
{
Line 298: Line 360:
HAS_FOOD = 1 << 2, // 4
HAS_FOOD = 1 << 2, // 4
}
}
</syntaxhighlight>
</enforce>


=== Vector ===
=== Vector ===
Line 311: Line 373:


Example:
Example:
<syntaxhighlight lang="C#">
<enforce>
// there are three ways to create a vector
// there are three ways to create a vector
vector myVector; // myVector is { 0, 0, 0 }
vector myVector0; // myVector0 is { 0, 0, 0 }
vector myVector = { 0, 1.5, 2 };
vector myVector1 = { 0, 1.5, 2 };
vector myVector = "0 1.5 2";
vector myVector2 = "0 1.5 2";
 
vector otherVector = "0 1.5 2";
myVector1 == myVector2; // true
myVector == otherVector; // true
 
// editing one of the vector values
// edition of one vector value
vector myVector3 = myVector1; // '''copies''' the value
vector myVector2 = myVector;
myVector1 == myVector3; // true
myVector[1] = 42; // myVector is now { 0, 42, 2 }
myVector1[1] = 42; // myVector1 is now { 0, 42, 2 }
myVector == myVector2; // false
myVector1 == myVector3; // false
 
// edition of the whole vector
// editing the whole vector
myVector2 = { 0, 42, 2 }; // syntax "0 42 2" works too
myVector3 = { 0, 42, 2 }; // syntax "0 42 2" works too
myVector == myVector2; // true
myVector1 == myVector3; // true
</syntaxhighlight>
 
// {{Link|https://en.wikipedia.org/wiki/Cross_product|cross product}} and {{Link|https://en.wikipedia.org/wiki/Dot_product|dot product}} in one operation
vector crossProduct = vector1 * vector2;
float dotProduct = vector1 * vector2;
auto product = vector1 * vector2; // product is by default a vector
</enforce>


=== Array ===
=== Array ===
Line 334: Line 401:
Passed by: '''reference''' (dynamic array) or '''value''' (static array)
Passed by: '''reference''' (dynamic array) or '''value''' (static array)


Naming prefix: '''a'''
Naming prefix: '''a''' for both


Default value: null ('''not''' an empty array)
Default value: null ('''not''' an empty array) or static array filled with default item type value (e.g 0 for int, null for object, etc)


Maximum size:  
Maximum size: int.MAX


An '''array''' is a list of values. In Enforce Script, an array can only hold one type of data (defined in its declaration).
An '''array''' is a list of values. In Enforce Script, an array can only hold one type of data (defined in its declaration).
Line 348: Line 415:


Example:
Example:
<syntaxhighlight lang="C#">
<enforce>
array<string> dynamicArray = {};
array<string> dynamicArray = {};
string staticArray[2] = { "Hello", "" };
string staticArray[2] = { "Hello", "" };
 
dynamicArray.Insert("Hello"); // dynamicArray = { "Hello" };
dynamicArray.Insert("Hello"); // dynamicArray = { "Hello" };
dynamicArray.Insert(""); // dynamicArray = { "Hello", "" };
dynamicArray.Insert(""); // dynamicArray = { "Hello", "" };
 
dynamicArray[1] = "there"; // dynamicArray = { "Hello", "there" };
dynamicArray[1] = "there"; // dynamicArray = { "Hello", "there" };
staticArray[1] = "there"; // staticArray = { "Hello", "there" };
staticArray[1] = "there"; // staticArray = { "Hello", "there" };
 
// dynamic/static arrays cannot be directly compared for values; == would compare pointers (a.k.a is it the same array, not the same values)
// dynamic/static arrays cannot be directly compared for values; == would compare pointers (a.k.a is it the same array, not the same values)
dynamicArray == staticArray; // false
dynamicArray == staticArray; // false
dynamicArray[0] == staticArray[0]; // true
dynamicArray[0] == staticArray[0]; // true
dynamicArray[1] == staticArray[1]; // true
dynamicArray[1] == staticArray[1]; // true
</syntaxhighlight>
</enforce>


=== Set ===
=== Set ===
Line 375: Line 442:


Example:
Example:
<syntaxhighlight lang="C#">
<enforce>
set<string> setInstance = new set<string>();
set<string> setInstance = new set<string>();
 
setInstance.Insert("Hello there");
setInstance.Insert("Hello there");
setInstance.Insert("General Kenobi");
setInstance.Insert("General Kenobi");
setInstance.Insert("Hello there"); // setInstance will still only contain "Hello there" and "General Kenobi"
setInstance.Insert("Hello there"); // setInstance will still only contain "Hello there" and "General Kenobi"
 
setInstance.Get(0); // the values order is never guaranteed as it can change on value insertion!
setInstance.Get(0); // the values order is never guaranteed as it can change on value insertion!
</syntaxhighlight>
</enforce>


=== Map ===
=== Map ===
Line 398: Line 465:


Example:
Example:
<syntaxhighlight lang="C#">
<enforce>
map<int, string> mapInstance = new map<int, string>();
map<int, string> mapInstance = new map<int, string>();
 
mapInstance.Set(5712, "Hello there");
mapInstance.Set(5712, "Hello there");
mapInstance.Set(5716, "General Kenobi");
mapInstance.Set(5716, "General Kenobi");
 
mapInstance.Get(5712); // returns "Hello there"
mapInstance.Get(5712); // returns "Hello there"
mapInstance.Get(5715); // /!\ returns "" (default string)
mapInstance.Get(5715); // /!\ returns "" (default string)
mapInstance.Get(5716); // returns "General Kenobi"
mapInstance.Get(5716); // returns "General Kenobi"
 
mapInstance.GetKeyByValue("Hello there"); // returns 5712
mapInstance.GetKeyByValue("Hello there"); // returns 5712
mapInstance.GetKeyByValue("test"); // /!\ returns 0 (default int)
mapInstance.GetKeyByValue("test"); // /!\ returns 0 (default int)
 
mapInstance.Contains(5716); // true
mapInstance.Contains(5716); // true
mapInstance.ReplaceKey(5716, 5715); // replaces "General Kenobi" key
mapInstance.ReplaceKey(5716, 5715); // replaces "General Kenobi" key
mapInstance.Contains(5716); // false
mapInstance.Contains(5716); // false
 
mapInstance.Contains(5715); // true
mapInstance.Contains(5715); // true
mapInstance.Remove(5715); // removes "General Kenobi" from the map
mapInstance.Remove(5715); // removes "General Kenobi" from the map
mapInstance.Contains(5715); // false
mapInstance.Contains(5715); // false
</syntaxhighlight>
</enforce>


=== Class ===
=== Class ===
Line 429: Line 496:


A '''class''' is what defines an object's structure - said from the other end, an object is an '''instance''' of a class.
A '''class''' is what defines an object's structure - said from the other end, an object is an '''instance''' of a class.
{{Feature|informative|For more information on classes and Object Oriented Programming, see {{Link|Arma Reforger:Object Oriented Programming Basics}} and {{Link|Object Oriented Programming Advanced Usage}}.}}


Example:
Example:
<syntaxhighlight lang="C#">
<enforce>
class ObjectClass
class ObjectClass
{
{
private int m_iHealth = 100;
protected int m_iHealth = 100;


int function getHealth()
int GetHealth()
{
{
return m_iHealth;
return m_iHealth;
}
}


bool function setHealth(int health)
bool SetHealth(int health)
{
{
if (health < 0 || health > 100)
if (health < 0 || health > 100)
{
return false;
return false;
}


m_iHealth = health;
m_iHealth = health;
Line 452: Line 519:
}
}
}
}
 
void HealthMethod()
void HealthMethod()
{
{
ObjectClass myObjectInstance; // myObjectInstance is null
ObjectClass myObjectInstance; // myObjectInstance is null
myObjectInstance = new ObjectClass();
myObjectInstance = new ObjectClass();
int objectHealth = myObjectInstance.getHealth();
int objectHealth = myObjectInstance.GetHealth();
Print(objectHealth);
Print(objectHealth);
}
}
</syntaxhighlight>
</enforce>


=== Typename ===
=== Typename ===
Line 470: Line 537:
Default value: '''null'''
Default value: '''null'''


A '''typename''' is class information (WIP: talk about reflection?)
A '''typename''' is class information
 
{{Wiki|WIP|talk about reflection?}}
Example:
Example:
<syntaxhighlight lang="C#">
<enforce>
class ObjectClass
class ObjectClass
{
{
int Health = 100;
int Health = 100;
}
}
 
typename t; // t is null
typename t; // t is null
t = ObjectClass;
t = ObjectClass;
string classname = TypeName(t); // returns "ObjectClass";
string classname = TypeName(t); // returns "ObjectClass";
t = Type(classname); // returns ObjectClass typename too
t = Type(classname); // returns ObjectClass typename too
</syntaxhighlight>
</enforce>




Line 489: Line 556:


A value has a lifetime, whether it is the game instance, mission or script duration; but it also has a scope that defines its existence and its accessibility.
A value has a lifetime, whether it is the game instance, mission or script duration; but it also has a scope that defines its existence and its accessibility.
<syntaxhighlight lang="C#">
<enforce>
void SetDammage(int dammage)
void SetDammage(int dammage)
{
{
int newHealth; // newHealth variable is declared
int newHealth; // newHealth variable is declared
newHealth = m_iHealth - dammage;
newHealth = m_iHealth - dammage;
if (newHealth < 0)
if (newHealth < 0)
Line 506: Line 573:
// difference variable does not exist in this scope and cannot be used
// difference variable does not exist in this scope and cannot be used


m_iHealth = newHealth; // newHealth variable's last usage - the variable still exists
m_iHealth = newHealth; // newHealth variable's last usage - the variable still exists


Print("Health has been set"); // newHealth is destroyed after this line (on closing the "SetDammage" scope)
Print("Health has been set"); // newHealth is destroyed after this line (on closing the "SetDammage" scope)
}
}
</syntaxhighlight>
</enforce>


{| class="wikitable"
{| class="wikitable"
! Incorrect
! Incorrect
! Correct
! Correct
! Best
! Shorter but more expensive
|- style="vertical-align: top"
|- style="vertical-align: top"
|  
|
<syntaxhighlight lang="C#">
<enforce>
if (soldiersHealth > 75)
if (soldiersHealth > 75)
{
{
Line 525: Line 592:
else
else
{
{
// no conflict with the previous variable as scopes are different
string message = "I don't feel so good";
string message = "I don't feel so good";
}
}
Print(message); // error: message variable is undefined here
Print(message); // error: message variable is undefined here
</syntaxhighlight>
</enforce>
|
|
<syntaxhighlight lang="C#">
<enforce>
string message;
string message;
if (soldiersHealth > 75)
if (soldiersHealth > 75)
Line 541: Line 609:
}
}
Print(message); // OK
Print(message); // OK
</syntaxhighlight>
</enforce>
|
|
<syntaxhighlight lang="C#">
<enforce>
string message = "I don't feel so good";
string message = "I don't feel so good";
if (soldiersHealth > 75)
if (soldiersHealth > 75)
Line 549: Line 617:
message = "I'm doing well";
message = "I'm doing well";
}
}
 
 
 
 
Print(message); // OK
Print(message); // OK
</syntaxhighlight>
</enforce>
|}
|}


Line 562: Line 630:
A value can sometimes be returned as one of its inherited classes or interfaced type - it can be '''casted''' ("forced" into a type) if the underlying type is known.
A value can sometimes be returned as one of its inherited classes or interfaced type - it can be '''casted''' ("forced" into a type) if the underlying type is known.


'''BEWARE:''' a wrong cast will return a null value!
{{Feature|important|A wrong cast will return a '''null''' value - no exception will be thrown.}}
<syntaxhighlight lang="C#">
 
<enforce>
class Soldier_Base
class Soldier_Base
{
{
int scope = 0;
int m_iScope = 0;
}
}
 
class B_Soldier_F : Soldier_Base
class B_Soldier_F : Soldier_Base
{
{
int scope = 2;
int m_iScope = 2;
void function SayHello()
void SayHello()
{
{
Print("Hello there");
Print("Hello there");
}
}
}
}
 
Soldier_Base aSoldier = new B_Soldier_F(); // valid
Soldier_Base aSoldier = new B_Soldier_F(); // valid
aSoldier.SayHello(); // invalid - Soldier_Base does not have the SayHello method
aSoldier.SayHello(); // invalid - Soldier_Base does not have the SayHello method
 
B_Soldier_F mySoldierF = B_Soldier_F.Cast(aSoldier);
B_Soldier_F mySoldierF = B_Soldier_F.Cast(aSoldier);
mySoldierF.SayHello(); // valid
mySoldierF.SayHello(); // valid
</syntaxhighlight>
</enforce>




{{GameCategory|armaR|Modding|Guidelines|Scripting}}
{{GameCategory|armaR|Modding|Guidelines|Scripting}}

Latest revision as of 13:02, 9 December 2024

A value describes a data holder; a value is either a variable (that can be changed) or a constant (that cannot be changed).

Values are declared in Enfusion with a type, in format type identifier = value (the = value part being optional). Enforce Script uses strong types, which means a value's type cannot be changed along its lifetime.


Identifier

An identifier is the actual name of a value; it identifies the value. The naming rules are:

  • an identifier can be composed of ASCII letters, numbers and underscores
  • an identifier must start with an underscore or a letter (cannot start with a number)
  • an identifier is case-sensitive
  • an identifier cannot be identical to a keyword

The regular expression to be respected is: ^[a-zA-Z_][a-zA-Z0-9_]*$

It is recommended to write variable identifiers in camelCase, (e.g aNewVariable) and constants' in UPPER_CASE (e.g A_NEW_CONSTANT). See Scripting: Conventions - Variable for Arma Reforger naming conventions.

int myNumber = 10; int _myNumber = 10; int myNUMBER = 10; // different from myNumber int 1number = 1; // wrong - starts with a number int my#variable = 1; // wrong - contains a character that is not letter/number/underscore int auto = 1; // wrong - the "auto" keyword exists and cannot be replaced


Value Declaration

Declaring a value is done by using a type and an identifier:

int myNumber; // variable myNumber is declared and auto-initialised to 0 myNumber = 10; // myNumber is now 10

It can also be directly created with a value:

int myNumber = 10;

const

A value can be made constant, meaning that it will remain the same along its lifetime.

string value1 = "value"; // value1's value is "value" const string CONST_VALUE = "value"; value1 = "new value"; // value1 is now "new value" CONST_VALUE = "new value"; // error: cannot modify a const value

An object (array, set, map, other class instance) can be const yet still have its values changed - const here only keeps the reference and does not "freeze" the object in place.

const ref array<string> STRINGS = {}; STRINGS.Insert("Hello there"); // OK: the array remains the same, its content is changed STRINGS = null; // error: cannot modify a const value


Passing a Value

By Content

// integers (whole numbers) are passed by content int myVar1 = 33; int myVar2 = myVar1; // myVar2 is 33 - it copied myValue1's content myVar2 = 42; // myVar2 is now 42, myVar1 is still 33 - they are two different values

By Reference

// arrays (list of values) are passed by reference array<int> myArray1 = { 0, 1, 2, 3 }; array<int> myArray2 = myArray1; // myArray2 targets myArray1 object - value is not copied but referenced myArray2[0] = 99; // myArray1 is now { 99, 1, 2, 3 };


Prefix Cheat Sheet
Value Type Prefix
Bool m_b
Integer m_i
Float m_f
String m_s
Enum m_e
Vector m_v
Array m_a
Map m_m
Set m_
Class m_

Types

Values can be of various types, listed below.

Notions:

  • Passed by: content or reference - see above
  • Naming prefix: used in Object's value convention (See conventions): m_ or s_ + prefix + VariableName (e.g: m_bPlayerIsAlive = true)
    No prefix is used for constants in conventions.
  • Default value: the default value given when a value is declared without content (e.g: int value - the variable here is 0, integer's default value)
Incorrect Correct

bool variable = true; if (variable) { variable = "it works!"; // not - variable is and remains a bool Print(variable); }

bool variable = true; if (variable) { string display = "it works!"; // correct Print(display); }

Primitive Types

Type name C++ equivalent Range Default value Size (Bytes)
int int32 -2,147,483,648 through +2,147,483,647 0 4
float float ±1.18E-38 through ±3.402823E+38 0.0 4
bool bool true or false false 4
string char* - "" (empty string) 8 (pointer) + (length × 1 byte)
vector float[3] like float { 0.0, 0.0, 0.0 } 8 (pointer) + (3 × float) 12*
void void - - -
class Instance* pointer to any script object null 8
typename VarType* pointer to type structure null 8

*the asterisk in C++ means a pointer.

Bool

Passed by: value

Naming prefix: b

Default value: false

A bool is a value that can be either true or false . For example, bool result = 10 > 5; result can either be true or false (here is true, as 10 is greater than 5).

Example:

bool myValue; // myValue is false myValue = 10 > 0; // myValue is true


Integer

Passed by: value

Naming prefix: i

Default value: 0

Range: -2,147,483,648 to 2,147,483,647

Description: An integer (or int) is a whole number, meaning a value without decimals. It can be positive or negative.

Example:

int myValue; // myValue is 0 myValue = 20; // myValue is 20 myValue /= 3; // myValue is 6: 20 / 3 = 6.666… which gets floored to 6 (and not rounded)

Float

Passed by: value

Naming prefix: f

Default value: 0.0

Range: 1.18 E-38 to 3.40 E+38

A float, or its full name floating-point number is a number that can have decimals.

Precision is a matter at hand when working with floats; do not expect exact calculations: 0.1 + 0.1 + 0.1 != 0.3 - rounding would be needed to have the result one can expect.

The further away from 0 the float value is, the less precision it has.
For "almost equal" comparison, see float.AlmostEqual; the default accepted difference (epsilon) is 0.0001 (1/10000) and can be set for each operation as an optional third argument.

Example:

float myValue; // myValue is 0 (or 0.0) myValue = 1; // myValue is 1 (or 1.0) myValue /= 3; // myValue is 0.333333… float originalValue = 1; float divisionResult = originalValue/3333; float result = divisionResult * 3333; originalValue == result; // returns false due to floating point precision float.AlmostEqual(originalValue, result); // returns true

String

Passed by: value

Naming prefix: s

Default value: "" (empty string)

Maximum length: 2,147,483,647 characters

Description: a string is a sequence of characters; it supports the following escape characters: \n (line return) \r (caret return) \t (horizontal tabulation) \\ (antislash) \" (double quote).

  • A string cannot be null
  • String comparison is case-sensitive; e.g "abc" != "ABC"

Example:

string username; // username is "" (empty string) username = "Player 1"; // username is "Player 1";

Enum

Passed by: value

Naming prefix: e

Default value: 0 (which may not exist in the enum)

An enum is a value that offers a choice between defined options.

"Behind the curtains" it is also an integer that can be assigned to an int variable.

Examples:

enum EHealthState { ALIVE, INJURED, UNCONSCIOUS, DEAD, } EHealthState healthState; // healthState is ALIVE (0) healthState = EHealthState.UNCONSCIOUS; // healthState is UNCONSCIOUS (4) int myValue = EHealthState.ALIVE; // valid

By default, the first value is equal to 0 and the next values are incremented by 1.

enum EHealthState { ALIVE = 42, INJURED, // equals 43 UNCONSCIOUS = 50, DEAD, // equals 51 } EHealthState healthState; // healthState is 0 (no corresponding enum) healthState = EHealthState.INJURED; // healthState is 43 (INJURED) int myValue = EHealthState.ALIVE; // 42
// an enum value can also be defined through bit shifting for e.g flag usage enum ELifeStatus { HEALTHY = 1 << 0, // 1 HAS_HOUSE = 1 << 1, // 2 HAS_FOOD = 1 << 2, // 4 }

Vector

Passed by: value

Naming prefix: v

Default value: { 0, 0, 0 }

A vector is a type that holds three float values. Two vectors can be compared with == for value comparison.

Example:

// there are three ways to create a vector vector myVector0; // myVector0 is { 0, 0, 0 } vector myVector1 = { 0, 1.5, 2 }; vector myVector2 = "0 1.5 2"; myVector1 == myVector2; // true // editing one of the vector values vector myVector3 = myVector1; // copies the value myVector1 == myVector3; // true myVector1[1] = 42; // myVector1 is now { 0, 42, 2 } myVector1 == myVector3; // false // editing the whole vector myVector3 = { 0, 42, 2 }; // syntax "0 42 2" works too myVector1 == myVector3; // true // cross product and dot product in one operation vector crossProduct = vector1 * vector2; float dotProduct = vector1 * vector2; auto product = vector1 * vector2; // product is by default a vector

Array

Passed by: reference (dynamic array) or value (static array)

Naming prefix: a for both

Default value: null (not an empty array) or static array filled with default item type value (e.g 0 for int, null for object, etc)

Maximum size: int.MAX

An array is a list of values. In Enforce Script, an array can only hold one type of data (defined in its declaration).

There are two types of array in Enfusion:

  • dynamic array: "list" that will extend the more items are added
  • static array: fixed-size list of items - items can be changed, but the array size cannot

Example:

array<string> dynamicArray = {}; string staticArray[2] = { "Hello", "" }; dynamicArray.Insert("Hello"); // dynamicArray = { "Hello" }; dynamicArray.Insert(""); // dynamicArray = { "Hello", "" }; dynamicArray[1] = "there"; // dynamicArray = { "Hello", "there" }; staticArray[1] = "there"; // staticArray = { "Hello", "there" }; // dynamic/static arrays cannot be directly compared for values; == would compare pointers (a.k.a is it the same array, not the same values) dynamicArray == staticArray; // false dynamicArray[0] == staticArray[0]; // true dynamicArray[1] == staticArray[1]; // true

Set

Passed by: reference

Naming prefix: none

Default value: null (not an empty set)

A set is a list that ensures uniqueness of values. Due to this uniqueness check, item insertion is slower than for an Array; however, checking if an item is contained is faster.

Example:

set<string> setInstance = new set<string>(); setInstance.Insert("Hello there"); setInstance.Insert("General Kenobi"); setInstance.Insert("Hello there"); // setInstance will still only contain "Hello there" and "General Kenobi" setInstance.Get(0); // the values order is never guaranteed as it can change on value insertion!

Map

Passed by: reference

Naming prefix: m

Default value: null (not an empty map)

A map is a list of key-value pairs, also known as a dictionary. Two keys cannot be identical as they are used to index the values.

A float cannot be a map key.

Example:

map<int, string> mapInstance = new map<int, string>(); mapInstance.Set(5712, "Hello there"); mapInstance.Set(5716, "General Kenobi"); mapInstance.Get(5712); // returns "Hello there" mapInstance.Get(5715); // /!\ returns "" (default string) mapInstance.Get(5716); // returns "General Kenobi" mapInstance.GetKeyByValue("Hello there"); // returns 5712 mapInstance.GetKeyByValue("test"); // /!\ returns 0 (default int) mapInstance.Contains(5716); // true mapInstance.ReplaceKey(5716, 5715); // replaces "General Kenobi" key mapInstance.Contains(5716); // false mapInstance.Contains(5715); // true mapInstance.Remove(5715); // removes "General Kenobi" from the map mapInstance.Contains(5715); // false

Class

Passed by: reference

Naming prefix: none

Default value: null

A class is what defines an object's structure - said from the other end, an object is an instance of a class.

For more information on classes and Object Oriented Programming, see Object Oriented Programming Basics and Object Oriented Programming Advanced Usage.

Example:

class ObjectClass { protected int m_iHealth = 100; int GetHealth() { return m_iHealth; } bool SetHealth(int health) { if (health < 0 || health > 100) return false; m_iHealth = health; return true; } } void HealthMethod() { ObjectClass myObjectInstance; // myObjectInstance is null myObjectInstance = new ObjectClass(); int objectHealth = myObjectInstance.GetHealth(); Print(objectHealth); }

Typename

Passed by: reference

Naming prefix: none

Default value: null

A typename is class information

🏗
This article is a work in progress! Reason: talk about reflection?

Example:

class ObjectClass { int Health = 100; } typename t; // t is null t = ObjectClass; string classname = TypeName(t); // returns "ObjectClass"; t = Type(classname); // returns ObjectClass typename too


Scope

A value has a lifetime, whether it is the game instance, mission or script duration; but it also has a scope that defines its existence and its accessibility.

void SetDammage(int dammage) { int newHealth; // newHealth variable is declared newHealth = m_iHealth - dammage; if (newHealth < 0) { newHealth = 0; } else if (newHealth > 100) { int difference = newHealth - 100; // difference variable is declared Print("health overflow: " + difference); newHealth = 100; // difference variable is destroyed after leaving the "else" scope and doesn't exist outside of it } // difference variable does not exist in this scope and cannot be used m_iHealth = newHealth; // newHealth variable's last usage - the variable still exists Print("Health has been set"); // newHealth is destroyed after this line (on closing the "SetDammage" scope) }

Incorrect Correct Shorter but more expensive

if (soldiersHealth > 75) { string message = "I'm doing well"; } else { // no conflict with the previous variable as scopes are different string message = "I don't feel so good"; } Print(message); // error: message variable is undefined here

string message; if (soldiersHealth > 75) { message = "I'm doing well"; } else { message = "I don't feel so good"; } Print(message); // OK

string message = "I don't feel so good"; if (soldiersHealth > 75) { message = "I'm doing well"; } Print(message); // OK


Casting

A value can sometimes be returned as one of its inherited classes or interfaced type - it can be casted ("forced" into a type) if the underlying type is known.

A wrong cast will return a null value - no exception will be thrown.

class Soldier_Base { int m_iScope = 0; } class B_Soldier_F : Soldier_Base { int m_iScope = 2; void SayHello() { Print("Hello there"); } } Soldier_Base aSoldier = new B_Soldier_F(); // valid aSoldier.SayHello(); // invalid - Soldier_Base does not have the SayHello method B_Soldier_F mySoldierF = B_Soldier_F.Cast(aSoldier); mySoldierF.SayHello(); // valid