Enforce Script Syntax – DayZ

From Bohemia Interactive Community
Jump to navigation Jump to search
(Reverted incorrect changes)
(Add Category:Syntax and some formatting)
Line 1: Line 1:
[[Category:DayZ:Scripting|Syntax]]
{{SideTOC}}
[[Category:DayZ:Editing]]
'''Enforce Script''' is the language that is used by the Enfusion engine first introduced in [[DayZ]] Standalone. It is a Object-Oriented Scripting Language (OOP) that works with objects and classes and is similar to C# programming language.
 
__TOC__
 


'''Enforce Script''' is the language that is used by the Enfusion engine first introduced in [[DayZ]] Standalone. It is a Object-Oriented Scripting Language (OOP) that works with objects and classes and is similar to C# programming language.


== Basics ==
== Basics ==
Line 14: Line 10:


Scope example
Scope example
<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
void Hello()
void Hello()
{
{
int x = 2; // First declaration of x
int x = 2; // First declaration of x
}
}


void Hello2()
void Hello2()
{
{
int x = 23; // Different x not related to the first one
int x = 23; // Different x not related to the first one
}
}
</syntaxhighlight>
</syntaxhighlight>


Nested scope
Nested scope
<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
void World()
void World()
{
{
    int i = 5;
int i = 5;
 
    // Following i is in a nested scope (scope of the for loop)
// Following i is in a nested scope (scope of the for loop)
    // Therefore we get an error because multiple declaration is not allowed
// Therefore we get an error because multiple declaration is not allowed
    for (int i = 0; i < 12; ++i)
for (int i = 0; i < 12; ++i)
    {
{
    }
}
}
}
</syntaxhighlight>
</syntaxhighlight>


Scope statements
Scope statements
<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
void Hello()
void Hello()
{
{
    if (true)
if (true)
    {
{
        // This is code block for the if branch
// This is code block for the if branch
        int x = 2; // First declaration of x
int x = 2; // First declaration of x
    }
}
    else
else
    {
{
        // This is code block for the else branch
// This is code block for the else branch
        int x = 23; // This will currently throw a multiple declaration error - while this should change for future enfusion script iterations, it might stay like this in DayZ. To circumvent this, define the x above the if statement or use different variables.
int x = 23; // This will currently throw a multiple declaration error - while this should change for future enfusion script iterations, it might stay like this in DayZ. To circumvent this, define the x above the if statement or use different variables.
    }
}
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 61: Line 57:
Enfusion script consists of classes and functions. All code must be declared inside a function.
Enfusion script consists of classes and functions. All code must be declared inside a function.


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
class MyClass
class MyClass
{
{
    void Hello()
void Hello()
    {
{
        Print("Hello World"); // ok
Print("Hello World"); // ok
    }
}
}
}
 
 
void Hello()
void Hello()
{
{
    Print("Hello World"); // ok
Print("Hello World"); // ok
}
}
 
 
Print("Hello World"); // this code will be never executed, and should be caught by compiler as unexpected statement
Print("Hello World"); // this code will be never executed, and should be caught by compiler as unexpected statement
</syntaxhighlight>
</syntaxhighlight>
Line 81: Line 77:


Variables are defined by type and name.
Variables are defined by type and name.
<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
void Test()
void Test()
{
{
    // declare  
// declare
    int a;
int a;
     
 
    // assign  
// assign
    a = 5;
a = 5;
     
 
    // initialize  
// initialize
    int b = 9;
int b = 9;
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 97: Line 93:
=== Functions ===
=== Functions ===


Functions are basic feature of Enfusion script. Function declaration consist of return value type, function name and list of parameters.  
Functions are basic feature of Enfusion script. Function declaration consist of return value type, function name and list of parameters.


* Function can be declared in global scope or inside class declaration
* Function can be declared in global scope or inside class declaration
Line 105: Line 101:
* Enfusion script supports default parameter
* Enfusion script supports default parameter


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
void MethodA()  // function with no parameters and no return value
void MethodA()  // function with no parameters and no return value
{
{
Line 112: Line 108:
int GiveMeTen() // function with no parameters which returns integer value
int GiveMeTen() // function with no parameters which returns integer value
{
{
    return 10;
return 10;
}
}
 
void GiveMeElevenAndTwelve(out int val1, out int val2, int val3) // function with 2 of the parameters passed as reference  
void GiveMeElevenAndTwelve(out int val1, out int val2, int val3) // function with 2 of the parameters passed as reference
{
{
    val1 = 11;
val1 = 11;
    val2 = 12;
val2 = 12;
    val3 = 13;
val3 = 13;
}
}
 
 
void PrintNum(int a = 0) // example of function with default parameter
void PrintNum(int a = 0) // example of function with default parameter
{
{
    Print(a);
Print(a);
}
}


void MethodB()
void MethodB()
{
{
    int ten = 0;
int ten = 0;
    int eleven = 0;
int eleven = 0;
    int twelve = 0;
int twelve = 0;
    int thirteen = 0;
int thirteen = 0;
   
 
    ten = GiveMeTen();
ten = GiveMeTen();
   
 
    // function "GiveMeElevenAndTwelve" sets values of "eleven" and "twelve" variables,  
// function "GiveMeElevenAndTwelve" sets values of "eleven" and "twelve" variables,
    // because "val1" and "val2" parameters are marked with "out" keyword,  
// because "val1" and "val2" parameters are marked with "out" keyword,
    // but value of "thirteen" variable is not changed, because third parameter is not marked as "out" and "val3"  
// but value of "thirteen" variable is not changed, because third parameter is not marked as "out" and "val3"
    // behaves only like a local variable of "GiveMeElevenAndTwelve" function
// behaves only like a local variable of "GiveMeElevenAndTwelve" function
    GiveMeElevenAndTwelve(eleven, twelve, thirteen);
GiveMeElevenAndTwelve(eleven, twelve, thirteen);
 
 
    Print(ten); // prints "ten = 10"
Print(ten); // prints "ten = 10"
    Print(eleven); // prints "eleven = 11"
Print(eleven); // prints "eleven = 11"
    Print(twelve); // prints "twelve = 12"
Print(twelve); // prints "twelve = 12"
    Print(thirteen ); // prints "thirteen = 0"
Print(thirteen ); // prints "thirteen = 0"
 
 
    PrintNum(); // function "PrintNum" has default parameter, so its ok to call with empty brackets, it prints "a = 0"
PrintNum(); // function "PrintNum" has default parameter, so its ok to call with empty brackets, it prints "a = 0"
    PrintNum(7); // prints "a = 7"
PrintNum(7); // prints "a = 7"
}
}


float Sum(float a, float b) // function with two float parameters which return float value
float Sum(float a, float b) // function with two float parameters which return float value
{
{
    return a + b;
return a + b;
}
}


float Sum(int a, int b) // overloaded Sum function which uses int parameters instead
float Sum(int a, int b) // overloaded Sum function which uses int parameters instead
{
{
    return a + b;
return a + b;
}
}


 
 
void PrintCount(TStringArray stringArray) // function with one "TStringArray" object parameter which returns no value
void PrintCount(TStringArray stringArray) // function with one "TStringArray" object parameter which returns no value
{
{
    if (!stringArray) return; // check if stringArray is not null
if (!stringArray) return; // check if stringArray is not null
 
 
    int count = stringArray.Count();
int count = stringArray.Count();
    Print(count);
Print(count);
}  
}
</syntaxhighlight>
</syntaxhighlight>


=== Comments ===  
=== Comments ===


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
/*
/*
Multi
Multi
Line 179: Line 175:
comment
comment
*/
*/
 
 
void Test()
void Test()
{
{
    Print("Hello"); // single line comment
Print("Hello"); // single line comment
}
}
</syntaxhighlight>
</syntaxhighlight>


=== Constants ===  
=== Constants ===


Constants are like variables but read only. They are declared by const keyword.
Constants are like variables but read only. They are declared by const keyword.


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
const int MONTHS_COUNT = 12;
const int MONTHS_COUNT = 12;
 
 
void Test()
void Test()
{
{
    int a = MONTHS_COUNT; // ok
int a = MONTHS_COUNT; // ok
    MONTHS_COUNT = 7; // err! you cannot change constant!
MONTHS_COUNT = 7; // err! you cannot change constant!
}
}
</syntaxhighlight>
</syntaxhighlight>


== Operators ==
== Operators ==
'''Operator Priority''': Priority of operators is similar to C language, [https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence more info].
'''Operator Priority''': Priority of operators is similar to C language, [https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence more info].


Line 219: Line 217:
| Modulo || {{Inline code|%}}
| Modulo || {{Inline code|%}}
|}
|}


=== Assignments ===
=== Assignments ===
Line 255: Line 254:
| Less or equal to the value || {{Inline code|<{{=}}}}
| Less or equal to the value || {{Inline code|<{{=}}}}
|-
|-
| Equal || ==
| Equal || {{Inline code|{{=}}{{=}}}}
|-
|-
| Not equal || !=
| Not equal || {{Inline code|!{{=}}}}
|}
|}


=== Others ===
=== Others ===
{| class="wikitable"
{| class="wikitable"
|-
|-
! Category !! Operator|-
! Category !! Operator(s)
|-
|-
| Logical  || <nowiki>&&</nowiki>, <nowiki>||</nowiki>
| Logical  || {{Inline code|<nowiki>&&</nowiki>}}, {{Inline code|<nowiki>||</nowiki>}}
|-
|-
| Bitwise || <nowiki>&, |, ~</nowiki>
| Bitwise || {{Inline code|<nowiki>&</nowiki>}}, {{Inline code|<nowiki>|</nowiki>}}, {{Inline code|~}}
|-
|-
| String || +
| String || {{Inline code|+}}
|-
|-
| Shift || <<, >>
| Shift || {{Inline code|<<}}, {{Inline code|>>}}
|-
|-
| Assignment || <nowiki>=</nowiki>
| Assignment || {{Inline code|{{=}}}}
|-
|-
| Indexing || []
| Indexing || {{Inline code|[ ]}}
|-
|-
| Negation || !
| Negation || {{Inline code|!}}
|}
|}


== Keywords ==
== Keywords ==
=== Function/method modifiers ===
=== Function/method modifiers ===
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 300: Line 303:


=== Variable modifiers ===
=== Variable modifiers ===
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 324: Line 328:


=== Class modifiers ===
=== Class modifiers ===
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 332: Line 337:


=== Other keywords ===
=== Other keywords ===
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 346: Line 352:
| typedef || Type definition
| typedef || Type definition
|-
|-
| return || Terminates function & returns value (if specified)  
| return || Terminates function & returns value (if specified)
|-
|-
| null || null value
| null || null value
Line 354: Line 360:
| super || Refers to the base class for the requested variable/function
| super || Refers to the base class for the requested variable/function
|}
|}


== Types ==
== Types ==
=== Primitive Types ===
=== Primitive Types ===


{| class="wikitable"
{| class="wikitable"
|-
|-
! Type name !! Range !! Default Value  
! Type name !! Range !! Default Value
|-
|-
| int || from −2,147,483,648 to +2,147,483,647 || 0
| int || from −2,147,483,648 to +2,147,483,647 || 0
Line 384: Line 392:
* Can be concatenated by + operator
* Can be concatenated by + operator
* Strings are initialized and destroyed automatically
* Strings are initialized and destroyed automatically
* Strings can contain standardized escape sequences. These are supported: \n \r \t \\ \"  
* Strings can contain standardized escape sequences. These are supported: \n \r \t \\ \"


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
void Method()
void Method()
{
{
    string a = "Hello";
string a = "Hello";
    string b = " world!";
string b = " world!";
    string c = a + b;
string c = a + b;
   
 
    Print(a); // prints "Hello"
Print(a); // prints "Hello"
    Print(b); // prints " world!"
Print(b); // prints " world!"
    Print(c); // prints "Hello world!"
Print(c); // prints "Hello world!"
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 406: Line 414:
* Vector can be initialized by three numeric values in double quotes e.g. "10 22 13"
* Vector can be initialized by three numeric values in double quotes e.g. "10 22 13"


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
void Method()
void Method()
{
{
    vector up = "0 1 0"; // initialized by values <0, 1, 0>
vector up = "0 1 0"; // initialized by values <0, 1, 0>
    vector down; // vector "down" has now default value <0, 0, 0>
vector down; // vector "down" has now default value <0, 0, 0>
 
 
    down = up;
down = up;
    down[1] = -1; // change Y value of vector "down"
down[1] = -1; // change Y value of vector "down"
 
 
    Print(up); // prints <0, 1, 0>
Print(up); // prints <0, 1, 0>
    Print(down); // prints <0, -1, 0>
Print(down); // prints <0, -1, 0>
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 426: Line 434:
* 'autoptr' keyword before object variable declaration ensures that compiler automatically destroys the object when the scope is terminated (e.g. function call ends)
* 'autoptr' keyword before object variable declaration ensures that compiler automatically destroys the object when the scope is terminated (e.g. function call ends)


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
class MyClass
class MyClass
{
{
    void Say()
void Say()
    {
{
        Print("Hello world");
Print("Hello world");
    }
}
}
}
 
 
void MethodA()
void MethodA()
{
{
    MyClass o; // o == null
MyClass o; // o == null
    o = new MyClass; // creates a new instance of MyClass class
o = new MyClass; // creates a new instance of MyClass class
    o.Say(); // calls Say() function on instance 'o'
o.Say(); // calls Say() function on instance 'o'
    delete o; // destroys 'o' instance
delete o; // destroys 'o' instance
}
}
 
 
void MethodB()
void MethodB()
{
{
    // if you type autoptr into declaration, compiler automatically does "delete o;" when the scope is terminated
// if you type autoptr into declaration, compiler automatically does "delete o;" when the scope is terminated
    autoptr MyClass o; // o == null
autoptr MyClass o; // o == null
    o = new MyClass; // creates a new instance of MyClass class
o = new MyClass; // creates a new instance of MyClass class
    o.Say(); // calls Say() function on instance 'o'
o.Say(); // calls Say() function on instance 'o'
}
}
 
 
void MethodC()
void MethodC()
{
{
    MyClass o;
MyClass o;
    o = new MyClass;
o = new MyClass;
    o.Say();  
o.Say();
    // This function doesn't delete the object, which causes memory leak
// This function doesn't delete the object, which causes memory leak
}
}
 
 
void UnsafeMethod(MyClass o) // Method not checking for existence of the input argument
void UnsafeMethod(MyClass o) // Method not checking for existence of the input argument
{
{
    o.Say();
o.Say();
}
}
 
 
void SafeMethod(MyClass o)
void SafeMethod(MyClass o)
{
{
    if (o)
if (o)
    {
{
        o.Say();
o.Say();
    }
}
    else
else
    {
{
        Print("Hey! Object 'o' is not initialised!");
Print("Hey! Object 'o' is not initialised!");
    }
}
}
}
 
 
void MethodD()
void MethodD()
{
{
    autoptr MyClass o;
autoptr MyClass o;
    o = new MyClass;
o = new MyClass;
   
 
    SafeMethod(o); // ok
SafeMethod(o); // ok
    UnsafeMethod(o); // ok
UnsafeMethod(o); // ok
 
 
    SafeMethod(null); // ok
SafeMethod(null); // ok
    UnsafeMethod(null); // Crash! Object 'o' is not initialised and UnsafeMethod accessed it!
UnsafeMethod(null); // Crash! Object 'o' is not initialised and UnsafeMethod accessed it!
}
}
</syntaxhighlight>
</syntaxhighlight>


Example of '''this''' & '''super'''
Example of '''this''' & '''super''':


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
class AnimalClass
class AnimalClass
{
{
    void Hello()
void Hello()
    {
{
        Print("AnimalClass.Hello()");
Print("AnimalClass.Hello()");
    }
}
};
};
 
 
class HoneyBadger: AnimalClass
class HoneyBadger: AnimalClass
{
{
    override void Hello()
override void Hello()
    {
{
        Print("HoneyBadger.Hello()");
Print("HoneyBadger.Hello()");
    }
}
 
 
    void Test()
void Test()
    {
{
        Hello();   // prints "HoneyBadger.Hello()"
Hello(); // prints "HoneyBadger.Hello()"
        this.Hello(); // 'this' refers to this instance of object, so same as line above, prints "HoneyBadger.Hello()"
this.Hello(); // 'this' refers to this instance of object, so same as line above, prints "HoneyBadger.Hello()"
        super.Hello(); // refers to base(super) class members, prints "AnimalClass.Hello()"
super.Hello(); // refers to base(super) class members, prints "AnimalClass.Hello()"
    }  
}
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 525: Line 533:
* enum name used as type behaves like ordinary int (no enum value checking on assign)
* enum name used as type behaves like ordinary int (no enum value checking on assign)


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
enum MyEnumBase
enum MyEnumBase
{
{
    Alfa = 5,       // has value 5
Alfa = 5, // has value 5
    Beta,           // has value 6
Beta, // has value 6
    Gamma           // has value 7
Gamma // has value 7
};
};
enum MyEnum: MyEnumBase
enum MyEnum: MyEnumBase
{
{
    Blue,         // has value 8
Blue, // has value 8
    Yellow,           // has value 9
Yellow, // has value 9
    Green = 20,   // has value 20
Green = 20, // has value 20
    Orange       // has value 21
Orange // has value 21
};
};
 
 
void Test()
void Test()
{
{
    int a = MyEnum.Beta;
int a = MyEnum.Beta;
    MyEnum b = MyEnum.Green;
MyEnum b = MyEnum.Green;
    int c = b;
int c = b;
 
 
    Print(a);   // prints '6'
Print(a); // prints '6'
    Print(b);   // prints '20'
Print(b); // prints '20'
    Print(c);   // prints '20'
Print(c); // prints '20'
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 554: Line 562:
=== Templates ===
=== Templates ===


Enfusion script has template feature similar to C++ Templates, which allows classes to operate with generic types.  
Enfusion script has template feature similar to C++ Templates, which allows classes to operate with generic types.


* Generic type declaration is placed inside <, > (e.g. "class TemplateClass<class GenericType>" )operators after template class name identifier
* Generic type declaration is placed inside <, > (e.g. "class TemplateClass<class GenericType>" )operators after template class name identifier
* Enfusion script supports any number of generic types per template class
* Enfusion script supports any number of generic types per template class


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
class Item<Class T>
class Item<Class T>
{
{
    T m_data;
T m_data;
    void Item(T data)
void Item(T data)
    {
{
        m_data = data;
m_data = data;
    }
}
    void SetData(T data)
void SetData(T data)
    {
{
        m_data = data;
m_data = data;
    }
}
    T GetData()
T GetData()
    {
{
        return m_data;
return m_data;
    }
}
    void PrintData()
void PrintData()
    {
{
        Print(m_data);
Print(m_data);
    }
}
};
};
 
 
void Method()
void Method()
{
{
    Item<string> string_item = new Item<string>("Hello!"); // template class Item declared with type "string". In Item<string> class, all "T"s are substituted with 'string'
Item<string> string_item = new Item<string>("Hello!"); // template class Item declared with type "string". In Item<string> class, all "T"s are substituted with 'string'
    Item<int> int_item = new Item<int>(72); // template class Item declared with type "int". In Item<int> class, all "T"s are substituted with 'int'
Item<int> int_item = new Item<int>(72); // template class Item declared with type "int". In Item<int> class, all "T"s are substituted with 'int'
 
    string_item.PrintData(); // prints "m_data = 'Hello!'"
string_item.PrintData(); // prints "m_data = 'Hello!'"
    int_item.PrintData(); // prints "m_data = 72"
int_item.PrintData(); // prints "m_data = 72"
}  
}
</syntaxhighlight>
</syntaxhighlight>


Line 594: Line 602:


==== Static Arrays ====
==== Static Arrays ====
* Arrays are indexed from 0
* Arrays are indexed from 0
* Arrays are passed by reference, like objects
* Arrays are passed by reference, like objects
Line 601: Line 608:
* Elements are accessible by array access operator [ ]
* Elements are accessible by array access operator [ ]


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
void MethodA()
void MethodA()
{
{
    int numbersArray[3]; // declaring array of int with size 3
int numbersArray[3]; // declaring array of int with size 3
 
 
    numbersArray[0] = 54;
numbersArray[0] = 54;
    numbersArray[1] = 82;
numbersArray[1] = 82;
    numbersArray[2] = 7;
numbersArray[2] = 7;
 
    int anotherArray[3] = {53, 90, 7};
int anotherArray[3] = {53, 90, 7};
  }
  }
 
 
const int ARRAY_SIZE = 5;
const int ARRAY_SIZE = 5;
 
 
void MethodB()
void MethodB()
{
{
    int numbersArray[ARRAY_SIZE]; // declaring array of int with size of value of ARRAY_SIZE constant
int numbersArray[ARRAY_SIZE]; // declaring array of int with size of value of ARRAY_SIZE constant
   
 
    numbersArray[0] = 54;
numbersArray[0] = 54;
    numbersArray[1] = 82;
numbersArray[1] = 82;
    numbersArray[2] = 7;
numbersArray[2] = 7;
    numbersArray[3] = 1000;
numbersArray[3] = 1000;
    numbersArray[4] = 324;
numbersArray[4] = 324;
}
}
 
void MethodC()
void MethodC()
{
{
    int size = 3;
int size = 3;
    int numbersArray[size]; // err! size static array cannot be declared by variable!
int numbersArray[size]; // err! size static array cannot be declared by variable!
}
}
</syntaxhighlight>
</syntaxhighlight>


==== Dynamic Arrays ====
==== Dynamic Arrays ====
* Dynamic arrays support change of size at runtime by inserting/removing array items
* Dynamic arrays support change of size at runtime by inserting/removing array items
* Dynamic arrays are provided through 'array' template class
* Dynamic arrays are provided through 'array' template class
Line 647: Line 653:
** array<vector> = TVectorArray
** array<vector> = TVectorArray


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
void Method()
void Method()
{
{
    autoptr TStringArray nameArray = new TStringArray; // dynamic array declaration, "TStringArray" is the same as "array<string>"
autoptr TStringArray nameArray = new TStringArray; // dynamic array declaration, "TStringArray" is the same as "array<string>"
   
 
    nameArray.Insert("Peter");
nameArray.Insert("Peter");
    nameArray.Insert("Michal");
nameArray.Insert("Michal");
    nameArray.Insert("David");
nameArray.Insert("David");
 
 
    string name;
string name;
    name = nameArray.Get(1); // gets second element of array "nameArray"
name = nameArray.Get(1); // gets second element of array "nameArray"
    Print(name); // prints "name = 'Michal'"
Print(name); // prints "name = 'Michal'"
 
 
    nameArray.Remove(1); // second element is removed
nameArray.Remove(1); // second element is removed
    name = nameArray.Get(1); // gets second element of array "nameArray"
name = nameArray.Get(1); // gets second element of array "nameArray"
    Print(name); // prints "name = 'David'"
Print(name); // prints "name = 'David'"
 
 
    int nameCount = nameArray.Count(); // gets elements count of array "nameArray"
int nameCount = nameArray.Count(); // gets elements count of array "nameArray"
    Print(nameCount); // prints "nameCount = 2"
Print(nameCount); // prints "nameCount = 2"
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 673: Line 679:
The variable type will be detected automatically at compile time when the keyword '''auto''' is used as placeholder.
The variable type will be detected automatically at compile time when the keyword '''auto''' is used as placeholder.


<syntaxhighlight lang="c++">
<syntaxhighlight lang="cpp">
class MyCustomClass(){}
class MyCustomClass(){}


void Method()
void Method()
{
{
    auto variableName = 1; // variableName will be of type integer
auto variableName = 1; // variableName will be of type integer
    auto variablePi = 3.14 // variablePi will be of type float
auto variablePi = 3.14 // variablePi will be of type float
    auto variableInst = new MyCustomClass(); // variableInst will be of type MyCustomClass
auto variableInst = new MyCustomClass(); // variableInst will be of type MyCustomClass
}
}
</syntaxhighlight>
</syntaxhighlight>


== Control Structures ==
== Control Structures ==
Control structures work very similar to c# or c/c++ languages.
 
Control structures work very similar to C# or C/C++ languages.


=== Conditional structures ===
=== Conditional structures ===
==== If statement ====
==== If statement ====
<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
void Method()
void Method()
{
{
    int a = 4;
int a = 4;
    int b = 5;
int b = 5;
 
 
    if (a > 0)
if (a > 0)
    {
{
        Print("A is greater than zero!");
Print("A is greater than zero!");
    }
}
    else
else
    {
{
        Print("A is not greater than zero!");
Print("A is not greater than zero!");
    }
}
   
 
    if (a > 0 && b > 0)
if (a > 0 && b > 0)
    {
{
        Print("A and B are greater than zero!");
Print("A and B are greater than zero!");
    }
}
   
 
    if (a > 0 || b > 0)
if (a > 0 || b > 0)
    {
{
        Print("A or B are greater than zero!");
Print("A or B are greater than zero!");
    }
}
 
 
    // 'else if' example
// 'else if' example
    if (a > 10)
if (a > 10)
    {
{
        Print("a is bigger then 10");
Print("a is bigger then 10");
    }
}
    else if (a > 5)
else if (a > 5)
    {
{
        Print("a is bigger then 5 but smaller than 10");
Print("a is bigger then 5 but smaller than 10");
    }
}
    else
else
    {
{
        Print("a is smaller then 5");
Print("a is smaller then 5");
    }
}
}  
}
</syntaxhighlight>
</syntaxhighlight>
==== Switch statement ====  
 
==== Switch statement ====
Switch statement supports switching by numbers, constants and strings.
Switch statement supports switching by numbers, constants and strings.
<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
void Method()
void Method()
{
{
    int a = 2;
int a = 2;
 
 
    switch(a)
switch(a)
    {
{
        case 1:
case 1:
            Print("a is 1");
Print("a is 1");
        break;
break;
 
 
        case 2:
case 2:
            Print("a is 2"); // this one is called
Print("a is 2"); // this one is called
        break;
break;
       
 
        default:
default:
            Print("it's something else");
Print("it's something else");
        break;
break;
    }
}
 
 
    // using switch with constants
// using switch with constants
    const int LOW = 0;
const int LOW = 0;
    const int MEDIUM = 1;
const int MEDIUM = 1;
    const int HIGH = 2;
const int HIGH = 2;
 
 
    int quality = MEDIUM;
int quality = MEDIUM;
    switch(quality)
switch(quality)
    {
{
        case LOW:
case LOW:
            // do something
// do something
            break;
break;
 
 
        case MEDIUM:
case MEDIUM:
            // this one is called
// this one is called
            // do something
// do something
            break;
break;
 
 
        case HIGH:
case HIGH:
            // do something
// do something
            break;
break;
    }
}
 
 
    // using switch with strings
// using switch with strings
    string name = "peter";
string name = "peter";
    switch(name)
switch(name)
    {
{
        case "john":
case "john":
            Print("Hello John!");
Print("Hello John!");
            break;
break;
 
 
        case "michal":
case "michal":
            Print("Hello Michal!");
Print("Hello Michal!");
            break;
break;
 
 
        case "peter":
case "peter":
            Print("Hello Peter!"); // this one is called
Print("Hello Peter!"); // this one is called
            break;
break;
    }
}
}
}
</syntaxhighlight>
</syntaxhighlight>


=== Iteration structures ===
=== Iteration structures ===
==== For ====
 
==== For ====
The for loop consists of three parts: declaration, condition and increment.
The for loop consists of three parts: declaration, condition and increment.
<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
void Method()
void Method()
{
{
    // this code prints
// this code prints
    // "i = 0"
// "i = 0"
    // "i = 1"
// "i = 1"
    // "i = 2"
// "i = 2"
    for (int i = 0; i < 3; i++)
for (int i = 0; i < 3; i++)
    {
{
        Print(i);
Print(i);
    }
}
}
}
 
 
// this function print all elements from dynamic array of strings
// this function print all elements from dynamic array of strings
void ListArray(TStringArray a)
void ListArray(TStringArray a)
{
{
    if (a == null) return; // check if "a" is not null
if (a == null) return; // check if "a" is not null
 
 
    int i = 0;
int i = 0;
    int c = a.Count();
int c = a.Count();
 
 
    for (i = 0; i < c; i++)
for (i = 0; i < c; i++)
    {
{
        string tmp = a.Get(i);
string tmp = a.Get(i);
        Print(tmp);
Print(tmp);
    }
}
}
}
</syntaxhighlight>
</syntaxhighlight>
==== Foreach ====
==== Foreach ====
Simpler and more comfortable version of for loop.
Simpler and more comfortable version of for loop.


<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
void TestFn()
void TestFn()
{
{
    int pole1[] = {7,3,6,8};
int pole1[] = {7,3,6,8};
    array<string> pole2 = {"a", "b", "c"};
array<string> pole2 = {"a", "b", "c"};
    auto mapa = new map<string, int>();
auto mapa = new map<string, int>();
    mapa["jan"] = 1;
mapa["jan"] = 1;
    mapa["feb"] = 2;
mapa["feb"] = 2;
    mapa["mar"] = 3;
mapa["mar"] = 3;
 
 
    // simple foreach iteration
// simple foreach iteration
    foreach(int v: pole1) // prints: '7', '3', '6', '8'
foreach(int v: pole1) // prints: '7', '3', '6', '8'
    {
{
        Print(v);
Print(v);
    }
}
 
 
    // foreach iteration with key (if you iterate trough array, key is filled with array index)
// foreach iteration with key (if you iterate trough array, key is filled with array index)
    foreach(int i, string j: pole2) // prints: 'pole[0] = a', 'pole[1] = b', 'pole[2] = c'
foreach(int i, string j: pole2) // prints: 'pole[0] = a', 'pole[1] = b', 'pole[2] = c'
    {
{
        Print("pole[" + i + "] = " + j);
Print("pole[" + i + "] = " + j);
    }
}
 
 
    // map iteration, with key and value
// map iteration, with key and value
    foreach(auto k, auto a: mapa) // prints: 'mapa[jan] = 1', 'mapa[feb] = 2', 'mapa[mar] = 3'
foreach(auto k, auto a: mapa) // prints: 'mapa[jan] = 1', 'mapa[feb] = 2', 'mapa[mar] = 3'
    {
{
        Print("mapa[" + k + "] = " + a);
Print("mapa[" + k + "] = " + a);
    }
}
 
 
    // map iteration with just value
// map iteration with just value
    foreach(auto b: mapa) // prints: '1', '2', '3'
foreach(auto b: mapa) // prints: '1', '2', '3'
    {
{
        Print(b);
Print(b);
    }
}
}
}
</syntaxhighlight>
</syntaxhighlight>


==== While ====
==== While ====
<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
void Method()
void Method()
{
{
    int i = 0;
int i = 0;
 
 
    // this code prints
// this code prints
    // "i = 0"
// "i = 0"
    // "i = 1"
// "i = 1"
    // "i = 2"
// "i = 2"
 
 
    while (i < 3)
while (i < 3)
    {
{
        Print(i);
Print(i);
        i++;
i++;
    }
}
}
}
</syntaxhighlight>
</syntaxhighlight>


== Object-oriented programming specifics ==
== Object-oriented programming specifics ==
Line 892: Line 904:


=== Inheritance ===
=== Inheritance ===
<syntaxhighlight lang=cpp>
 
<syntaxhighlight lang="cpp">
class AnimalClass
class AnimalClass
{
{
    void MakeSound()
void MakeSound()
    {
{
    }
}
};
};
 
 
class Dog: AnimalClass
class Dog: AnimalClass
{
{
    override void MakeSound()
override void MakeSound()
    {
{
        Print("Wof! Wof!");
Print("Wof! Wof!");
    }
}
 
 
    void Aport()
void Aport()
    {
{
        // do something
// do something
    }
}
};
};
 
 
class Cat: AnimalClass
class Cat: AnimalClass
{
{
    override void MakeSound()
override void MakeSound()
    {
{
        Print("Meow!");
Print("Meow!");
    }
}
 
    void Scratch()
void Scratch()
    {
{
        // do something
// do something
    }
}
};
};
 
 
void LetAnimalMakeSound(AnimalClass pet)
void LetAnimalMakeSound(AnimalClass pet)
{
{
    if (pet) // check if pet is not null
if (pet) // check if pet is not null
    {
{
        pet.MakeSound();
pet.MakeSound();
    }
}
}
}
 
 
void Method()
void Method()
{
{
    Cat nyan = new Cat;
Cat nyan = new Cat;
    Dog pluto = new Dog;
Dog pluto = new Dog;
   
 
    nyan.MakeSound(); // prints "Meow!"
nyan.MakeSound(); // prints "Meow!"
    pluto.MakeSound(); // prints "Wof! Wof!"
pluto.MakeSound(); // prints "Wof! Wof!"
 
 
    LetAnimalMakeSound(nyan); // prints "Meow!"
LetAnimalMakeSound(nyan); // prints "Meow!"
    LetAnimalMakeSound(pluto); // prints "Wof! Wof!"  
LetAnimalMakeSound(pluto); // prints "Wof! Wof!"
}
</syntaxhighlight>
</syntaxhighlight>


=== Constructor & Destructor ===
=== Constructor &amp; Destructor ===
 
Constructor and destructor are special member functions
Constructor and destructor are special member functions


* Every class can have one constructor and one destructor  
* Every class can have one constructor and one destructor
* Constructor is function called when object is created(by 'new') and has same name as class ( e.g. 'void ClassName()' )
* Constructor is function called when object is created(by 'new') and has same name as class ( e.g. 'void ClassName()' )
* Destructor is called when object is going to be destroyed (by 'delete'/'autoptr')  and has same name as class with tilde character at beginning ( e.g. 'void ~ClassName()' )
* Destructor is called when object is going to be destroyed (by 'delete'/'autoptr')  and has same name as class with tilde character at beginning ( e.g. 'void ~ClassName()' )
Line 957: Line 972:
* When constructor doesn't have any parameters omit brackets while using 'new' operator
* When constructor doesn't have any parameters omit brackets while using 'new' operator


<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
class MyClassA
class MyClassA
{
{
    void MyClassA() // constructor declaration of class MyClassA
void MyClassA() // constructor declaration of class MyClassA
    {
{
        Print("Instance of MyClassA is created!");
Print("Instance of MyClassA is created!");
    }
}
 
 
    void ~MyClassA() // destructor declaration of class MyClassA
void ~MyClassA() // destructor declaration of class MyClassA
    {
{
        Print("Instance of MyClassA is destroyed!");
Print("Instance of MyClassA is destroyed!");
    }
}
};
};
 
 
class MyClassB
class MyClassB
{
{
    string m_name;
string m_name;
 
    void MyClassB(string name) // constructor declaration of class MyClassB
void MyClassB(string name) // constructor declaration of class MyClassB
    {
{
        m_name = name;
m_name = name;
        Print("Instance of MyClassB is created!");
Print("Instance of MyClassB is created!");
    }
}
 
    void ~MyClassB() // destructor declaration of class MyClassB
void ~MyClassB() // destructor declaration of class MyClassB
    {
{
        Print("Instance of MyClassB is destroyed!");
Print("Instance of MyClassB is destroyed!");
    }
}
};
};
 
 
void Method()
void Method()
{
{
    MyClassA a = new MyClassA; // prints "Instance of MyClassA is created!"
MyClassA a = new MyClassA; // prints "Instance of MyClassA is created!"
    MyClassB b = new MyClassB("Michal"); // prints "Instance of MyClassB is created!"
MyClassB b = new MyClassB("Michal"); // prints "Instance of MyClassB is created!"
 
 
    delete b; // prints "Instance of MyClassB is destroyed!"
delete b; // prints "Instance of MyClassB is destroyed!"
} // here at the end of scope ARC feature automatically destroys 'a' object and it prints "Instance of MyClassA is destroyed!"
} // here at the end of scope ARC feature automatically destroys 'a' object and it prints "Instance of MyClassA is destroyed!"
</syntaxhighlight>
</syntaxhighlight>


=== Managed class & pointer safety ===
=== Managed class & pointer safety ===
* Since script does not do garbage collecting automatically, all plain pointers are considered unsafe
* Since script does not do garbage collecting automatically, all plain pointers are considered unsafe
* All classes inherited from Managed class work soft links instead of plain pointers. Soft link is weak reference that does not keep the object alive and is zeroed upon their destruction so they are never invalid  
* All classes inherited from Managed class work soft links instead of plain pointers. Soft link is weak reference that does not keep the object alive and is zeroed upon their destruction so they are never invalid
* All objects available in game module should be Managed, so they should be using soft links by default (they all inherits from Managed class)
* All objects available in game module should be Managed, so they should be using soft links by default (they all inherits from Managed class)


<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
// without Managed        
// without Managed
class A
class A
{
{
    void Hello()
void Hello()
    {
{
        Print("hello");
Print("hello");
    }
}
}
}
           
 
void TestA()
void TestA()
{
{
    A a1 = new A();
A a1 = new A();
    A a2 = a1; // both a2 and a1 contain pointer to the same instance of A
A a2 = a1; // both a2 and a1 contain pointer to the same instance of A
   
 
    a1.Hello(); // prints "hello"
a1.Hello(); // prints "hello"
    a2.Hello(); // prints "hello"
a2.Hello(); // prints "hello"
    delete a1; // our instance of A is deleted and a1 is set to NULL
delete a1; // our instance of A is deleted and a1 is set to NULL
   
 
    if (a1) a1.Hello(); // nothing happens because a1 is NULL
if (a1) a1.Hello(); // nothing happens because a1 is NULL
    if (a2) a2.Hello(); // a2 is still pointing to deleted instance of A so condition pass. This line cause crash!
if (a2) a2.Hello(); // a2 is still pointing to deleted instance of A so condition pass. This line cause crash!
}
}
// with Managed      
// with Managed
class B: Managed
class B: Managed
{
{
    void Hello()
void Hello()
    {
{
        Print("hello");
Print("hello");
    }
}
}
}
           
 
void TestB()
void TestB()
{
{
    B a1 = new B();
B a1 = new B();
    B a2 = a1; // both a2 and a1 contain pointer to the same instance of B
B a2 = a1; // both a2 and a1 contain pointer to the same instance of B
   
 
    a1.Hello(); // prints "hello"
a1.Hello(); // prints "hello"
    a2.Hello(); // prints "hello"
a2.Hello(); // prints "hello"
    delete a1; // our instance of B is deleted and a1 is set to NULL, thanks to Managed(soft links) a2 (and all other possible pointers) is also NULL
delete a1; // our instance of B is deleted and a1 is set to NULL, thanks to Managed(soft links) a2 (and all other possible pointers) is also NULL
   
 
    if (a1) a1.Hello(); // nothing happens because a1 is NULL
if (a1) a1.Hello(); // nothing happens because a1 is NULL
    if (a2) a2.Hello(); // nothing happens because a2 is also NULL, thus this code will always be safe
if (a2) a2.Hello(); // nothing happens because a2 is also NULL, thus this code will always be safe
}
}
</syntaxhighlight>
</syntaxhighlight>


=== Automatic Reference Counting ===
=== Automatic Reference Counting ===
Enforce Script has support of automatic reference counting. In a spirit of flexibility, you can choose if your class should or shouldn't be managed, by choosing to inherit from Managed class.
Enforce Script has support of automatic reference counting. In a spirit of flexibility, you can choose if your class should or shouldn't be managed, by choosing to inherit from Managed class.


Simple "C++" like classes remains an option for high performance, but less secure scripts
Simple "C++" like classes remains an option for high performance, but less secure scripts
Line 1,063: Line 1,079:
* weak reference just pointing to an object, but doesn't increase reference count
* weak reference just pointing to an object, but doesn't increase reference count


<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
class Parent
class Parent
{
{
    ref Child m_child; // putting 'ref' keyword, we give a hint to compiler, that this is strong reference.
ref Child m_child; // putting 'ref' keyword, we give a hint to compiler, that this is strong reference.
};
};
 
 
class Child
class Child
{
{
    Parent m_parent; // this reference is weak reference (there is no 'ref')
Parent m_parent; // this reference is weak reference (there is no 'ref')
};
};
 
 
void main()
void main()
{
{
    Parent a = new Parent(); // 'a' has 1 strong reference (local vars are strong by default)
Parent a = new Parent(); // 'a' has 1 strong reference (local vars are strong by default)
    Child b = new Child(); // 'b' has 1 reference (local vars are strong by default)
Child b = new Child(); // 'b' has 1 reference (local vars are strong by default)
    a.m_child = b; // 'b' has 2 strong references, because Parent.m_child is strong ref
a.m_child = b; // 'b' has 2 strong references, because Parent.m_child is strong ref
    b.m_parent = a; // 'a' has still just 1 strong reference, because Child.m_parent is weak reference
b.m_parent = a; // 'a' has still just 1 strong reference, because Child.m_parent is weak reference
 
 
    // local variables 'a', 'b' are released (reference count is decreased by 1)
// local variables 'a', 'b' are released (reference count is decreased by 1)
}
}
</syntaxhighlight>
</syntaxhighlight>
Line 1,094: Line 1,110:


While an object is stored in at least one strong reference, it's being kept alive. When the last strong reference is destroyed or overwritten, the object is destroyed and all other (only weak refs left) references are set to NULL.
While an object is stored in at least one strong reference, it's being kept alive. When the last strong reference is destroyed or overwritten, the object is destroyed and all other (only weak refs left) references are set to NULL.
When an object is deleted manually by delete command (e.g., 'delete a;'), it is deleted immediately ignoring reference count, and all references (weak and strong) are set to NULL.  
When an object is deleted manually by delete command (e.g., 'delete a;'), it is deleted immediately ignoring reference count, and all references (weak and strong) are set to NULL.




Line 1,103: Line 1,119:


Examples:
Examples:
<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
class MyClassA
class MyClassA
{
{
    void MyClassA() { Print("MyClassA()"); } // constructor
void MyClassA() { Print("MyClassA()"); } // constructor
    void ~MyClassA() { Print("~MyClassA()"); } // destructor
void ~MyClassA() { Print("~MyClassA()"); } // destructor
};
};
 
 
void function1()
void function1()
{
{
    MyClassA a = new MyClassA(); // 'MyClassA()'
MyClassA a = new MyClassA(); // 'MyClassA()'
    a = new MyClassA();
a = new MyClassA();
    // new object is created 'MyClassA()',
// new object is created 'MyClassA()',
    // 'a' is overwritten,
// 'a' is overwritten,
    // first instance of MyClass is released: '~MyClassA()'
// first instance of MyClass is released: '~MyClassA()'
    // any code here
// any code here
 
 
    // the end of function, local variable 'a' is released: '~MyClassA()'
// the end of function, local variable 'a' is released: '~MyClassA()'
}
}
 
 
ref MyClassA g_MyGlobal;
ref MyClassA g_MyGlobal;
 
 
void function2()
void function2()
{
{
    MyClassA a = new MyClassA(); // 'MyClassA()'
MyClassA a = new MyClassA(); // 'MyClassA()'
    g_MyGlobal = a;
g_MyGlobal = a;
 
 
    // the end of function, local variable 'a' is released, but there is still one strong ref g_MyGlobal,
// the end of function, local variable 'a' is released, but there is still one strong ref g_MyGlobal,
    // so the object will live until the end of this script module.
// so the object will live until the end of this script module.
}  
}
 
 
void PrintMyObj(MyClassA o)
void PrintMyObj(MyClassA o)
{
{
    Print("object is " + o);
Print("object is " + o);
}
}
 
 
void function3()
void function3()
{
{
    MyClassA a = new MyClassA(); // 'MyClassA()'
MyClassA a = new MyClassA(); // 'MyClassA()'
    PrintMyObj(a); // 'object is MyClassA<address>'
PrintMyObj(a); // 'object is MyClassA<address>'
 
 
    // the end of function, local variable 'a' is released: '~MyClassA()'
// the end of function, local variable 'a' is released: '~MyClassA()'
}
}
 
 
void function4()
void function4()
{
{
    PrintMyObj(new MyClassA());
PrintMyObj(new MyClassA());
    // object is created 'MyClassA()',
// object is created 'MyClassA()',
    // PrintMyObj function is called 'object is MyClassA<address>'
// PrintMyObj function is called 'object is MyClassA<address>'
    //and right after PrintMyObj function finishes, temporary object is released '~MyClassA()'
//and right after PrintMyObj function finishes, temporary object is released '~MyClassA()'
}
}
 
 
MyClassA  CreateMyObj()
MyClassA  CreateMyObj()
{
{
    return new MyClassA();
return new MyClassA();
}  
}
 
 
void function5()
void function5()
{
{
    MyClassA a = CreateMyObj(); // object is created inside CreateMyObj function 'MyClassA()'
MyClassA a = CreateMyObj(); // object is created inside CreateMyObj function 'MyClassA()'
    // any code here
// any code here
 
 
    // the end of function, local variable 'a' is released: '~MyClassA()'
// the end of function, local variable 'a' is released: '~MyClassA()'
}  
}
 
 
void function6()
void function6()
{
{
    array<MyClassA> pole1 = new array<MyClassA>(); // array of weak references is created
array<MyClassA> pole1 = new array<MyClassA>(); // array of weak references is created
    pole1.Insert(new MyClassA());
pole1.Insert(new MyClassA());
    // object is created 'MyClassA()',
// object is created 'MyClassA()',
    // but pole1 is array of weak references, so no one is keeping this object alive,
// but pole1 is array of weak references, so no one is keeping this object alive,
    // its deleted immediately '~MyClassA()'
// its deleted immediately '~MyClassA()'
 
 
    array<ref MyClassA> pole2 = new array<ref MyClassA>(); // array of strong references is created
array<ref MyClassA> pole2 = new array<ref MyClassA>(); // array of strong references is created
    pole2.Insert(new MyClassA()); // object is created 'MyClassA()'
pole2.Insert(new MyClassA()); // object is created 'MyClassA()'
 
 
    // any code here
// any code here
   
 
    // the end of function, local variables 'pole1' and 'pole2' are released:
// the end of function, local variables 'pole1' and 'pole2' are released:
    // pole1 is destroyed (it contains just NULL),
// pole1 is destroyed (it contains just NULL),
    // pole2 is destroyed (it contains last strong ref to object, so object is destroyed '~MyClassA()')
// pole2 is destroyed (it contains last strong ref to object, so object is destroyed '~MyClassA()')
}
}
 
 
// classes and arrays
// classes and arrays
class ExampleClass
class ExampleClass
{
{
    //! weak reference to object
//! weak reference to object
    MyClassA m_a;
MyClassA m_a;
 
 
    //! strong reference to object
//! strong reference to object
    ref MyClassA m_b;
ref MyClassA m_b;
 
 
    //! strong reference to dynami array(dynamic array is object itself) of weak references
//! strong reference to dynami array(dynamic array is object itself) of weak references
    ref array<MyClassA> m_c;
ref array<MyClassA> m_c;
 
 
    //! strong reference to dynamic array of strong references
//! strong reference to dynamic array of strong references
    ref array<ref MyClassA> m_d;
ref array<ref MyClassA> m_d;
 
 
    //! static array of strong references
//! static array of strong references
    ref MyClassA m_e[10];
ref MyClassA m_e[10];
};
};
</syntaxhighlight>
</syntaxhighlight>


== Modding ==
== Modding ==


=== Modded class ===
=== Modded class ===
Modded class is used to inject inherited class into class hierarchy without modifying other scripts, which is required for proper modding
 
Modded class is used to inject inherited class into class hierarchy without modifying other scripts, which is required for proper modding:


* Modded class behaves like class inherited from original class (you can use super to access original class)
* Modded class behaves like class inherited from original class (you can use super to access original class)
* When modded class is declared, it will be instanced instead of original class everywhere in the script
* When modded class is declared, it will be instanced instead of original class everywhere in the script
* When several modded classes are modding the same vanilla class, the next modded class will instead inherit of the latest modded class, which enables mod compatibility  
* When several modded classes are modding the same vanilla class, the next modded class will instead inherit of the latest modded class, which enables mod compatibility


<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
// original
// original
class ModMe
class ModMe
{
{
    void Say()
void Say()
    {
{
        Print("Hello original");
Print("Hello original");
    }
}
};
};
 
// First mod
// First mod
modded class ModMe  // this class automatically inherits from original class ModMe
modded class ModMe  // this class automatically inherits from original class ModMe
{
{
    override void Say()
override void Say()
    {
{
        Print("Hello modded One");
Print("Hello modded One");
        super.Say();
super.Say();
    }
}
};
};


Line 1,237: Line 1,255:
modded class ModMe  // this class automatically inherits from first mod's ModMe
modded class ModMe  // this class automatically inherits from first mod's ModMe
{
{
    override void Say()
override void Say()
    {
{
        Print("Hello modded Two");
Print("Hello modded Two");
        super.Say();
super.Say();
    }
}
};
};
 
void Test()
void Test()
{
{
    ModMe a = new ModMe(); // modded class ModMe is instanced
ModMe a = new ModMe(); // modded class ModMe is instanced
    a.Say(); // prints 'Hello modded Two' , 'Hello modded One' and 'Hello original'
a.Say(); // prints 'Hello modded Two' , 'Hello modded One' and 'Hello original'
}
}
</syntaxhighlight>
</syntaxhighlight>


=== Modded constants ===
=== Modded constants ===
* Constants can be overridden on compilation by the the last loaded mod (be mindful of the mod load order)
* Constants can be overridden on compilation by the the last loaded mod (be mindful of the mod load order)
* Allows multiple mods to change different constants in a single class
* Allows multiple mods to change different constants in a single class


<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
class BaseTest
class BaseTest
{
{
Line 1,282: Line 1,301:


=== Modded private members ===
=== Modded private members ===
* Even though modded class behaves similar to an inherited one, it can still access private members of the vanilla class
* Even though modded class behaves similar to an inherited one, it can still access private members of the vanilla class


<syntaxhighlight lang=cpp>
<syntaxhighlight lang="cpp">
// original
// original
class VanillaClass
class VanillaClass
{
{
private bool imPrivate = 0;
private bool imPrivate = 0;
 
private void DoSomething()
private void DoSomething()
{
{
Line 1,314: Line 1,334:
}
}
</syntaxhighlight>
</syntaxhighlight>
[[Category:Syntax]]
[[Category:DayZ:Scripting|Syntax]]
[[Category:DayZ:Editing]]

Revision as of 01:44, 30 January 2020

Template:SideTOC Enforce Script is the language that is used by the Enfusion engine first introduced in DayZ Standalone. It is a Object-Oriented Scripting Language (OOP) that works with objects and classes and is similar to C# programming language.


Basics

Code blocks

Code block is bordered by curly brackets and defines a scope. Variables defined inside scope are accessible only from inside of it.

Scope example

void Hello()
{
	int x = 2; // First declaration of x
}

void Hello2()
{
	int x = 23; // Different x not related to the first one
}

Nested scope

void World()
{
	int i = 5;

	// Following i is in a nested scope (scope of the for loop)
	// Therefore we get an error because multiple declaration is not allowed
	for (int i = 0; i < 12; ++i)
	{
	}
}

Scope statements

void Hello()
{
	if (true)
	{
		// This is code block for the if branch
		int x = 2; // First declaration of x
	}
	else
	{
		// This is code block for the else branch
		int x = 23; // This will currently throw a multiple declaration error - while this should change for future enfusion script iterations, it might stay like this in DayZ. To circumvent this, define the x above the if statement or use different variables.
	}
}

Program structure

Enfusion script consists of classes and functions. All code must be declared inside a function.

class MyClass
{
	void Hello()
	{
		Print("Hello World"); // ok
	}
}

void Hello()
{
	Print("Hello World"); // ok
}

Print("Hello World"); // this code will be never executed, and should be caught by compiler as unexpected statement

Variables

Variables are defined by type and name.

void Test()
{
	// declare
	int a;

	// assign
	a = 5;

	// initialize
	int b = 9;
}

Functions

Functions are basic feature of Enfusion script. Function declaration consist of return value type, function name and list of parameters.

  • Function can be declared in global scope or inside class declaration
  • Function parameters are fixed and typed (cannot be changed during run-time, no variadic parameters)
  • Function can be overloaded
  • Keyword 'out' before parameter declaration ensures, that the parameter is passed by reference (you can pass more than one value from a function this way, can be used only in native functions)
  • Enfusion script supports default parameter
void MethodA()  // function with no parameters and no return value
{
}

int GiveMeTen() // function with no parameters which returns integer value
{
	return 10;
}

void GiveMeElevenAndTwelve(out int val1, out int val2, int val3) // function with 2 of the parameters passed as reference
{
	val1 = 11;
	val2 = 12;
	val3 = 13;
}

void PrintNum(int a = 0) // example of function with default parameter
{
	Print(a);
}

void MethodB()
{
	int ten = 0;
	int eleven = 0;
	int twelve = 0;
	int thirteen = 0;

	ten = GiveMeTen();

	// function "GiveMeElevenAndTwelve" sets values of "eleven" and "twelve" variables,
	// because "val1" and "val2" parameters are marked with "out" keyword,
	// but value of "thirteen" variable is not changed, because third parameter is not marked as "out" and "val3"
	// behaves only like a local variable of "GiveMeElevenAndTwelve" function
	GiveMeElevenAndTwelve(eleven, twelve, thirteen);

	Print(ten); // prints "ten = 10"
	Print(eleven); // prints "eleven = 11"
	Print(twelve); // prints "twelve = 12"
	Print(thirteen ); // prints "thirteen = 0"

	PrintNum(); // function "PrintNum" has default parameter, so its ok to call with empty brackets, it prints "a = 0"
	PrintNum(7); // prints "a = 7"
}

float Sum(float a, float b) // function with two float parameters which return float value
{
	return a + b;
}

float Sum(int a, int b)	// overloaded Sum function which uses int parameters instead
{
	return a + b;
}


void PrintCount(TStringArray stringArray) // function with one "TStringArray" object parameter which returns no value
{
	if (!stringArray) return; // check if stringArray is not null

	int count = stringArray.Count();
	Print(count);
}

Comments

/*
Multi
line
comment
*/

void Test()
{
	Print("Hello"); // single line comment
}

Constants

Constants are like variables but read only. They are declared by const keyword.

const int MONTHS_COUNT = 12;

void Test()
{
	int a = MONTHS_COUNT; // ok
	MONTHS_COUNT = 7; // err! you cannot change constant!
}


Operators

Operator Priority: Priority of operators is similar to C language, more info.

Arithmetic Operators

Operation Symbol
Add +
Subtract -
Multiply *
Divide /
Modulo %


Assignments

Operation Symbol
Assign value to variable =
Increment variable by value +=
Decrement variable by value -=
Multiply variable by value *=
Divide variable by value /=
Increment variable by 1 ++
Decrement variable by 1 --

Relational (conditional)

Operation Symbol
More than value >
Less than value <
More or equal to the value >=
Less or equal to the value <=
Equal ==
Not equal !=

Others

Category Operator(s)
Logical &&, ||
Bitwise &, |, ~
String +
Shift <<, >>
Assignment =
Indexing [ ]
Negation !


Keywords

Function/method modifiers

Keyword Description
private The method can be called only from inside of the same class method
protected The method can be called only from inside of class method or methods of its extended classes
static The method can be called without object pointer, just by className.methodName() , only static members of the same class can be accessed from inside of static method
override Compiler checks if is method present in base class and if method signature match
proto Prototyping of internal function (C++ side)
native Native call convention of internal function (C++ side)

Variable modifiers

Keyword Description
private Variable can be accessed only from inside of class methods. Mutually exclusive with "protected"
protected Variable can be accessed only from inside of class methods or methods of its extended classes. Mutually exclusive with "private"
static Variable can be accessed without object pointer, using className.variable
autoptr Modifier for variables of class pointer type. Pointer target will be automatically destroyed upon end of variable lifetime (end of scope or deletion of class which contains it)
proto Prototyping of internal function (C++ side)
ref Variable is a strong reference
const Constant, cannot be modified
out Modifier for function parameters, variable will be changed by a function call
inout Modifier for function parameters, variable will be used and then changed by a function call

Class modifiers

Keyword Description
modded Inheritance-like behaviour for modding

Other keywords

Keyword Description
new Create new object instance
delete Destroy object instance
class Class declaration
extends Class inheritence
typedef Type definition
return Terminates function & returns value (if specified)
null null value
this Address of the object, on which the member function is being called
super Refers to the base class for the requested variable/function


Types

Primitive Types

Type name Range Default Value
int from −2,147,483,648 to +2,147,483,647 0
float from ±1.401298E−45 to ±3.402823E+38 0.0
bool true or false false
string - "" (empty string)
vector see float (0.0,0.0,0.0)
void - -
class - null
typename - null

Strings

  • Strings are passed by value, like primitive types
  • Can be concatenated by + operator
  • Strings are initialized and destroyed automatically
  • Strings can contain standardized escape sequences. These are supported: \n \r \t \\ \"
void Method()
{
	string a = "Hello";
	string b = " world!";
	string c = a + b;

	Print(a); // prints "Hello"
	Print(b); // prints " world!"
	Print(c); // prints "Hello world!"
}

Vectors

  • Vectors are passed by value, like primitive types
  • Vector values are accessible by [, ] operator, like static arrays
  • Vectors are initialized and destroyed automatically
  • Vector can be initialized by three numeric values in double quotes e.g. "10 22 13"
void Method()
{
	vector up = "0 1 0"; // initialized by values <0, 1, 0>
	vector down; // vector "down" has now default value <0, 0, 0>

	down = up;
	down[1] = -1; // change Y value of vector "down"

	Print(up); // prints <0, 1, 0>
	Print(down); // prints <0, -1, 0>
}

Objects

  • Objects in enforce script are references and are passed by reference
  • All member functions and variables are public by default. Use 'private' keyword to make them private
  • 'autoptr' keyword before object variable declaration ensures that compiler automatically destroys the object when the scope is terminated (e.g. function call ends)
class MyClass
{
	void Say()
	{
		Print("Hello world");
	}
}

void MethodA()
{
	MyClass o; // o == null
	o = new MyClass; // creates a new instance of MyClass class
	o.Say(); // calls Say() function on instance 'o'
	delete o; // destroys 'o' instance
}

void MethodB()
{
	// if you type autoptr into declaration, compiler automatically does "delete o;" when the scope is terminated
	autoptr MyClass o; // o == null
	o = new MyClass; // creates a new instance of MyClass class
	o.Say(); // calls Say() function on instance 'o'
}

void MethodC()
{
	MyClass o;
	o = new MyClass;
	o.Say();
	// This function doesn't delete the object, which causes memory leak
}

void UnsafeMethod(MyClass o) // Method not checking for existence of the input argument
{
	o.Say();
}

void SafeMethod(MyClass o)
{
	if (o)
	{
		o.Say();
	}
	else
	{
		Print("Hey! Object 'o' is not initialised!");
	}
}

void MethodD()
{
	autoptr MyClass o;
	o = new MyClass;

	SafeMethod(o); // ok
	UnsafeMethod(o); // ok

	SafeMethod(null); // ok
	UnsafeMethod(null); // Crash! Object 'o' is not initialised and UnsafeMethod accessed it!
}

Example of this & super:

class AnimalClass
{
	void Hello()
	{
		Print("AnimalClass.Hello()");
	}
};

class HoneyBadger: AnimalClass
{
	override void Hello()
	{
		Print("HoneyBadger.Hello()");
	}

	void Test()
	{
		Hello(); // prints "HoneyBadger.Hello()"
		this.Hello(); // 'this' refers to this instance of object, so same as line above, prints "HoneyBadger.Hello()"
		super.Hello(); // refers to base(super) class members, prints "AnimalClass.Hello()"
	}
}

Enums

Enumerators are set of named constant identifiers.

  • enums have int type
  • enum item value can be assigned in definition, otherwise it is computed automatically (previous item value plus one)
  • enum can inherit from another enum (item value continues from last parent item value)
  • enum name used as type behaves like ordinary int (no enum value checking on assign)
enum MyEnumBase
{
	Alfa = 5,	// has value 5
	Beta,		// has value 6
	Gamma		// has value 7
};
enum MyEnum: MyEnumBase
{
	Blue,			// has value 8
	Yellow,			// has value 9
	Green = 20,		// has value 20
	Orange			// has value 21
};

void Test()
{
	int a = MyEnum.Beta;
	MyEnum b = MyEnum.Green;
	int c = b;

	Print(a); // prints '6'
	Print(b); // prints '20'
	Print(c); // prints '20'
}

Templates

Enfusion script has template feature similar to C++ Templates, which allows classes to operate with generic types.

  • Generic type declaration is placed inside <, > (e.g. "class TemplateClass<class GenericType>" )operators after template class name identifier
  • Enfusion script supports any number of generic types per template class
class Item<Class T>
{
	T m_data;
	void Item(T data)
	{
		m_data = data;
	}
	void SetData(T data)
	{
		m_data = data;
	}
	T GetData()
	{
		return m_data;
	}
	void PrintData()
	{
		Print(m_data);
	}
};

void Method()
{
	Item<string> string_item = new Item<string>("Hello!"); // template class Item declared with type "string". In Item<string> class, all "T"s are substituted with 'string'
	Item<int> int_item = new Item<int>(72); // template class Item declared with type "int". In Item<int> class, all "T"s are substituted with 'int'

	string_item.PrintData(); // prints "m_data = 'Hello!'"
	int_item.PrintData(); // prints "m_data = 72"
}

Arrays

Static Arrays

  • Arrays are indexed from 0
  • Arrays are passed by reference, like objects
  • Static arrays are initialized and destroyed automatically
  • Size of static arrays can be set only during compilation time
  • Elements are accessible by array access operator [ ]
void MethodA()
{
	int numbersArray[3]; // declaring array of int with size 3

	numbersArray[0] = 54;
	numbersArray[1] = 82;
	numbersArray[2] = 7;

	int anotherArray[3] = {53, 90, 7};
 }

const int ARRAY_SIZE = 5;

void MethodB()
{
	int numbersArray[ARRAY_SIZE]; // declaring array of int with size of value of ARRAY_SIZE constant

	numbersArray[0] = 54;
	numbersArray[1] = 82;
	numbersArray[2] = 7;
	numbersArray[3] = 1000;
	numbersArray[4] = 324;
}

void MethodC()
{
	int size = 3;
	int numbersArray[size]; // err! size static array cannot be declared by variable!
}

Dynamic Arrays

  • Dynamic arrays support change of size at runtime by inserting/removing array items
  • Dynamic arrays are provided through 'array' template class
  • Dynamic arrays are passed by reference
  • Dynamic Arrays are objects and therefore they must be created and destroyed like objects, so don't forget to use "autoptr" or delete operator!
  • Elements are accessible by "Get" function or by array access operator [ ]
  • There are already defined typedefs for primitive type arrays:
    • array<string> = TStringArray
    • array<float> = TFloatArray
    • array<int> = TIntArray
    • array<class> = TClassArray
    • array<vector> = TVectorArray
void Method()
{
	autoptr TStringArray nameArray = new TStringArray; // dynamic array declaration, "TStringArray" is the same as "array<string>"

	nameArray.Insert("Peter");
	nameArray.Insert("Michal");
	nameArray.Insert("David");

	string name;
	name = nameArray.Get(1); // gets second element of array "nameArray"
	Print(name); // prints "name = 'Michal'"

	nameArray.Remove(1); // second element is removed
	name = nameArray.Get(1); // gets second element of array "nameArray"
	Print(name); // prints "name = 'David'"

	int nameCount = nameArray.Count(); // gets elements count of array "nameArray"
	Print(nameCount); // prints "nameCount = 2"
}

Automatic type detection

The variable type will be detected automatically at compile time when the keyword auto is used as placeholder.

class MyCustomClass(){}

void Method()
{
	auto variableName = 1; // variableName will be of type integer
	auto variablePi = 3.14 // variablePi will be of type float
	auto variableInst = new MyCustomClass(); // variableInst will be of type MyCustomClass
}


Control Structures

Control structures work very similar to C# or C/C++ languages.

Conditional structures

If statement

void Method()
{
	int a = 4;
	int b = 5;

	if (a > 0)
	{
		Print("A is greater than zero!");
	}
	else
	{
		Print("A is not greater than zero!");
	}

	if (a > 0 && b > 0)
	{
		Print("A and B are greater than zero!");
	}

	if (a > 0 || b > 0)
	{
		Print("A or B are greater than zero!");
	}

	// 'else if' example
	if (a > 10)
	{
		Print("a is bigger then 10");
	}
	else if (a > 5)
	{
		Print("a is bigger then 5 but smaller than 10");
	}
	else
	{
		Print("a is smaller then 5");
	}
}

Switch statement

Switch statement supports switching by numbers, constants and strings.

void Method()
{
	int a = 2;

	switch(a)
	{
		case 1:
			Print("a is 1");
			break;

		case 2:
			Print("a is 2"); // this one is called
			break;

		default:
			Print("it's something else");
			break;
	}

	// using switch with constants
	const int LOW = 0;
	const int MEDIUM = 1;
	const int HIGH = 2;

	int quality = MEDIUM;
	switch(quality)
	{
		case LOW:
			// do something
			break;

		case MEDIUM:
			// this one is called
			// do something
			break;

		case HIGH:
			// do something
			break;
	}

	// using switch with strings
	string name = "peter";
	switch(name)
	{
		case "john":
			Print("Hello John!");
			break;

		case "michal":
			Print("Hello Michal!");
			break;

		case "peter":
			Print("Hello Peter!"); // this one is called
			break;
	}
}

Iteration structures

For

The for loop consists of three parts: declaration, condition and increment.

void Method()
{
	// this code prints
	// "i = 0"
	// "i = 1"
	// "i = 2"
	for (int i = 0; i < 3; i++)
	{
		Print(i);
	}
}

// this function print all elements from dynamic array of strings
void ListArray(TStringArray a)
{
	if (a == null) return; // check if "a" is not null

	int i = 0;
	int c = a.Count();

	for (i = 0; i < c; i++)
	{
		string tmp = a.Get(i);
		Print(tmp);
	}
}

Foreach

Simpler and more comfortable version of for loop.

void TestFn()
{
	int pole1[] = {7,3,6,8};
	array<string> pole2 = {"a", "b", "c"};
	auto mapa = new map<string, int>();
	mapa["jan"] = 1;
	mapa["feb"] = 2;
	mapa["mar"] = 3;

	// simple foreach iteration
	foreach(int v: pole1) // prints: '7', '3', '6', '8'
	{
		Print(v);
	}

	// foreach iteration with key (if you iterate trough array, key is filled with array index)
	foreach(int i, string j: pole2) // prints: 'pole[0] = a', 'pole[1] = b', 'pole[2] = c'
	{
		Print("pole[" + i + "] = " + j);
	}

	// map iteration, with key and value
	foreach(auto k, auto a: mapa) // prints: 'mapa[jan] = 1', 'mapa[feb] = 2', 'mapa[mar] = 3'
	{
		Print("mapa[" + k + "] = " + a);
	}

	// map iteration with just value
	foreach(auto b: mapa) // prints: '1', '2', '3'
	{
		Print(b);
	}
}

While

void Method()
{
	int i = 0;

	// this code prints
	// "i = 0"
	// "i = 1"
	// "i = 2"

	while (i < 3)
	{
		Print(i);
		i++;
	}
}


Object-oriented programming specifics

  • All member functions and variables are public by default. You can use 'private' or 'protected' keyword to control access
  • Class member functions are virtual and can be overriden by child class
  • Use override keyword for overriding base class methods(to avoid accidental overriding)
  • Class can inherit from one parent class using keyword 'extends'
  • Objects are not initialized and destroyed automatically, use 'new' and 'delete' (or 'autoptr' feature)
  • Class variables are cleared to default values upon creation

Inheritance

class AnimalClass
{
	void MakeSound()
	{
	}
};

class Dog: AnimalClass
{
	override void MakeSound()
	{
		Print("Wof! Wof!");
	}

	void Aport()
	{
		// do something
	}
};

class Cat: AnimalClass
{
	override void MakeSound()
	{
		Print("Meow!");
	}

	void Scratch()
	{
		// do something
	}
};

void LetAnimalMakeSound(AnimalClass pet)
{
	if (pet) // check if pet is not null
	{
		pet.MakeSound();
	}
}

void Method()
{
	Cat nyan = new Cat;
	Dog pluto = new Dog;

	nyan.MakeSound(); // prints "Meow!"
	pluto.MakeSound(); // prints "Wof! Wof!"

	LetAnimalMakeSound(nyan); // prints "Meow!"
	LetAnimalMakeSound(pluto); // prints "Wof! Wof!"
}

Constructor & Destructor

Constructor and destructor are special member functions

  • Every class can have one constructor and one destructor
  • Constructor is function called when object is created(by 'new') and has same name as class ( e.g. 'void ClassName()' )
  • Destructor is called when object is going to be destroyed (by 'delete'/'autoptr') and has same name as class with tilde character at beginning ( e.g. 'void ~ClassName()' )
  • Constructor can have initialization parameters, destructor cannot have any parameters
  • Both constructor and destructor do not return any value (returns void)
  • Constructor and destructor are called even when object is created or destroyed from C++
  • When constructor doesn't have any parameters omit brackets while using 'new' operator
class MyClassA
{
	void MyClassA() // constructor declaration of class MyClassA
	{
		Print("Instance of MyClassA is created!");
	}

	void ~MyClassA() // destructor declaration of class MyClassA
	{
		Print("Instance of MyClassA is destroyed!");
	}
};

class MyClassB
{
	string m_name;

	void MyClassB(string name) // constructor declaration of class MyClassB
	{
		m_name = name;
		Print("Instance of MyClassB is created!");
	}

	void ~MyClassB() // destructor declaration of class MyClassB
	{
		Print("Instance of MyClassB is destroyed!");
	}
};

void Method()
{
	MyClassA a = new MyClassA; // prints "Instance of MyClassA is created!"
	MyClassB b = new MyClassB("Michal"); // prints "Instance of MyClassB is created!"

	delete b; // prints "Instance of MyClassB is destroyed!"
} // here at the end of scope ARC feature automatically destroys 'a' object and it prints "Instance of MyClassA is destroyed!"

Managed class & pointer safety

  • Since script does not do garbage collecting automatically, all plain pointers are considered unsafe
  • All classes inherited from Managed class work soft links instead of plain pointers. Soft link is weak reference that does not keep the object alive and is zeroed upon their destruction so they are never invalid
  • All objects available in game module should be Managed, so they should be using soft links by default (they all inherits from Managed class)
// without Managed
class A
{
	void Hello()
	{
		Print("hello");
	}
}

void TestA()
{
	A a1 = new A();
	A a2 = a1; // both a2 and a1 contain pointer to the same instance of A

	a1.Hello(); // prints "hello"
	a2.Hello(); // prints "hello"
	delete a1; // our instance of A is deleted and a1 is set to NULL

	if (a1) a1.Hello(); // nothing happens because a1 is NULL
	if (a2) a2.Hello(); // a2 is still pointing to deleted instance of A so condition pass. This line cause crash!
}
// with Managed
class B: Managed
{
	void Hello()
	{
		Print("hello");
	}
}

void TestB()
{
	B a1 = new B();
	B a2 = a1; // both a2 and a1 contain pointer to the same instance of B

	a1.Hello(); // prints "hello"
	a2.Hello(); // prints "hello"
	delete a1; // our instance of B is deleted and a1 is set to NULL, thanks to Managed(soft links) a2 (and all other possible pointers) is also NULL

	if (a1) a1.Hello(); // nothing happens because a1 is NULL
	if (a2) a2.Hello(); // nothing happens because a2 is also NULL, thus this code will always be safe
}

Automatic Reference Counting

Enforce Script has support of automatic reference counting. In a spirit of flexibility, you can choose if your class should or shouldn't be managed, by choosing to inherit from Managed class.

Simple "C++" like classes remains an option for high performance, but less secure scripts

  • objects referenced by plain C pointers
  • no automatic memory management, owner must delete them

For common gameplay scripts/mods/etc there are managed objects, which are

  • slightly slower (due indirect weak pointer object accessing)
  • internally ref-counted and automatically deleted when not needed
  • objects referenced by weak pointers (pointer is always valid or NULL, never points to deleted memory etc.)

Strong and weak references

  • strong reference increases reference count - holds object alive
  • weak reference just pointing to an object, but doesn't increase reference count
class Parent
{
	ref Child m_child; // putting 'ref' keyword, we give a hint to compiler, that this is strong reference.
};

class Child
{
	Parent m_parent; // this reference is weak reference (there is no 'ref')
};

void main()
{
	Parent a = new Parent(); // 'a' has 1 strong reference (local vars are strong by default)
	Child b = new Child(); // 'b' has 1 reference (local vars are strong by default)
	a.m_child = b; // 'b' has 2 strong references, because Parent.m_child is strong ref
	b.m_parent = a; // 'a' has still just 1 strong reference, because Child.m_parent is weak reference

	// local variables 'a', 'b' are released (reference count is decreased by 1)
}

In the code above at the end of function main, object 'a' has zero strong references thus is deleted, destructor releases m_child, and so the object 'b' also has zero strong references and it is deleted.

Usage of ref keyword

In the Enforce script by default all variables are weak references, ref keyword is marking that variable is strong reference. In some special cases, variables are strong references by default

  • local variables inside functions
  • function arguments
  • function return value

and are released after their scope ends.

While an object is stored in at least one strong reference, it's being kept alive. When the last strong reference is destroyed or overwritten, the object is destroyed and all other (only weak refs left) references are set to NULL. When an object is deleted manually by delete command (e.g., 'delete a;'), it is deleted immediately ignoring reference count, and all references (weak and strong) are set to NULL.


Optimal usage of references in Enforce script is to have exactly one strong reference per object, placed in "owner" object who creates it. This way of usage ensures

  • no cyclic references
  • proper order of object destruction - object is destroyed when its "creator" is destroyed


Examples:

class MyClassA
{
	void MyClassA() { Print("MyClassA()"); } // constructor
	void ~MyClassA() { Print("~MyClassA()"); } // destructor
};

void function1()
{
	MyClassA a = new MyClassA(); // 'MyClassA()'
	a = new MyClassA();
	// new object is created 'MyClassA()',
	// 'a' is overwritten,
	// first instance of MyClass is released: '~MyClassA()'
	// any code here

	// the end of function, local variable 'a' is released: '~MyClassA()'
}

ref MyClassA g_MyGlobal;

void function2()
{
	MyClassA a = new MyClassA(); // 'MyClassA()'
	g_MyGlobal = a;

	// the end of function, local variable 'a' is released, but there is still one strong ref g_MyGlobal,
	// so the object will live until the end of this script module.
}

void PrintMyObj(MyClassA o)
{
	Print("object is " + o);
}

void function3()
{
	MyClassA a = new MyClassA(); // 'MyClassA()'
	PrintMyObj(a); // 'object is MyClassA<address>'

	// the end of function, local variable 'a' is released: '~MyClassA()'
}

void function4()
{
	PrintMyObj(new MyClassA());
	// object is created 'MyClassA()',
	// PrintMyObj function is called 'object is MyClassA<address>'
	//and right after PrintMyObj function finishes, temporary object is released '~MyClassA()'
}

MyClassA  CreateMyObj()
{
	return new MyClassA();
}

void function5()
{
	MyClassA a = CreateMyObj(); // object is created inside CreateMyObj function 'MyClassA()'
	// any code here

	// the end of function, local variable 'a' is released: '~MyClassA()'
}

void function6()
{
	array<MyClassA> pole1 = new array<MyClassA>(); // array of weak references is created
	pole1.Insert(new MyClassA());
	// object is created 'MyClassA()',
	// but pole1 is array of weak references, so no one is keeping this object alive,
	// its deleted immediately '~MyClassA()'

	array<ref MyClassA> pole2 = new array<ref MyClassA>(); // array of strong references is created
	pole2.Insert(new MyClassA()); // object is created 'MyClassA()'

	// any code here

	// the end of function, local variables 'pole1' and 'pole2' are released:
	// pole1 is destroyed (it contains just NULL),
	// pole2 is destroyed (it contains last strong ref to object, so object is destroyed '~MyClassA()')
}

// classes and arrays
class ExampleClass
{
	//! weak reference to object
	MyClassA m_a;

	//! strong reference to object
	ref MyClassA m_b;

	//! strong reference to dynami array(dynamic array is object itself) of weak references
	ref array<MyClassA> m_c;

	//! strong reference to dynamic array of strong references
	ref array<ref MyClassA> m_d;

	//! static array of strong references
	ref MyClassA m_e[10];
};


Modding

Modded class

Modded class is used to inject inherited class into class hierarchy without modifying other scripts, which is required for proper modding:

  • Modded class behaves like class inherited from original class (you can use super to access original class)
  • When modded class is declared, it will be instanced instead of original class everywhere in the script
  • When several modded classes are modding the same vanilla class, the next modded class will instead inherit of the latest modded class, which enables mod compatibility
// original
class ModMe
{
	void Say()
	{
		Print("Hello original");
	}
};

// First mod
modded class ModMe  // this class automatically inherits from original class ModMe
{
	override void Say()
	{
		Print("Hello modded One");
		super.Say();
	}
};

// Second mod
modded class ModMe  // this class automatically inherits from first mod's ModMe
{
	override void Say()
	{
		Print("Hello modded Two");
		super.Say();
	}
};

void Test()
{
	ModMe a = new ModMe(); // modded class ModMe is instanced
	a.Say(); // prints 'Hello modded Two' , 'Hello modded One' and 'Hello original'
}

Modded constants

  • Constants can be overridden on compilation by the the last loaded mod (be mindful of the mod load order)
  • Allows multiple mods to change different constants in a single class
class BaseTest
{
	const int CONST_BASE = 4;
}

class TestConst: BaseTest
{
	const int CONST_TEST = 5;
}

modded class TestConst
{
	const int CONST_BASE = 1;
	const int CONST_TEST = 2;
	const int CONST_MOD = 3;
}

void TestPrint()
{
	Print(TestConst.CONST_BASE); // 1
	Print(TestConst.CONST_TEST); // 2
	Print(TestConst.CONST_MOD);  // 3
}

Modded private members

  • Even though modded class behaves similar to an inherited one, it can still access private members of the vanilla class
// original
class VanillaClass
{
	private bool imPrivate = 0;

	private void DoSomething()
	{
		Print("Vanilla method");
	}
}
// accesss
modded class VanillaClass
{
	void AccessPvt()		
	{
		Print(imPrivate);	
		DoSomething();		
	}
}
// override
modded class VanillaClass
{
	override void DoSomething()
	{
		super.DoSomething();
		Print("Modded method");
	}
}