Operators (ZScript)

From ZDoom Wiki
Jump to navigation Jump to search
Note: This page is for ZScript. For ACS operators see here.


This is a list of all operators supported by ZScript and their descriptions.

Assignment operators

= (assignment)

int foo = 5;

Assignment can be combined with arhitmetic operators as follows:

  • += (assignment + addition)
  • -= (assignment + subtraction)
  • *= (assignment + multiplication)
  • /= (assignment + division)
  • %= (assignment + modulo)

Assignment can be combined with bitwise operators as follows:

  • &= (bitwise AND assigment)
  • |= (bitwise OR assigment)

Arithmetic operators

+ (addition)

int foo = 1 + 1; // foo = 2
int bar = foo + 1; // bar = 3

- (subtraction)

int foo = 1 - 1; // foo = 0
int bar = foo - 1; // bar = -1

* (multiplication)

int foo = 2 * 10; // foo = 20
int bar = foo * 10; // bar = 200

** (power)

int foo = 2 ** 8; // foo = 256

/ (division)

Note: If both operands are integer numbers, the result of division will be an integer number, truncated (not rounded). At least one of the operands has to be a double value to get a decimal result.

int foo = 10 / 2; // foo = 5
int foo1 = 5 / 2; // foo = 2

double bar = 5 / 2; // bar = 2.0 because 2.5 was truncated
double bar1 = 5.0 / 2; // bar1 = 2.5
double bar2 = 5 / 2.0; // bar2 = 2.5

Round (round to closest), floor (round down) and ceil (round up) can be utilized on a float-point division to set the result to the closest suitable integer number:

double d = (5 / 3.0); // d = 1.666...
int foo = round(5 / 3.0); // foo = 2
int foo1 = ceil(5 / 3.0); // foo1 = 2
int foo2 = floor(5 / 3.0); // foo2 = 1

% (modulo)

Returns a remainder after dividing integer numbers.

int foo = 5 / 2; // foo = 1 because 5 = 4 * 2 + 1 where 1 is the remainder

This operator can be used to perform something on a fixed time basis in a function that is being called every tic:

override void Tick()
{
  Super.Tick();
  if (Level.maptime % TICRATE == 0)
  {
    // This will be called every second while the game isn't paused
  }
}

(See Tick)

++ (incrementation)

Increases the value by 1.

int foo = 1;
foo++; // foo = 2

Note: placing the ++ operator before the value is possible as well: ++foo. If this is done while comparing the value to another value, the incrementation will be performed befor comparison. Compare:

int foo = 5;
bool isBigger = foo++ > 5; // isBigger is false
int foo = 5;
bool isBigger = ++foo > 5; // isBigger is true

-- (decrementation)

Decreases the value by 1.

int foo = 1;
foo--; // foo = 0

Note: placing the -- operator before the value is possible as well: --foo. If this is done while comparing the value to another value, the decrementation will be performed befor comparison. Compare:

int foo = 5;
bool isSmaller = foo-- < 5; // isSmaller is false
int foo = 5;
bool isSmaller = --foo < 5; // isSmaller is true

Relational operators

These operators are used in IF/ELSE, FOR, WHILE statements to check for conditions

== (exactly equal)

Checks if two operands are equal to each other:

if (myNumber1 == myNumber2)
{
  // True if both numbers are the same
}
if (myPointer1 == myPointer2)
{
  // True if both pointers point to the same object
}

Using != flips the check.

~== (approximately equal)

Checks if two operands are approximately equal to each other.

With doubles, it checks if they're within 1/65536.0 (0.0000152587890625) distance of each other. Note that this check is more computationally expensive than ==.

if (myDouble1 ~== myDouble2)
{
  // True if the difference between the two values is less than 0.0000152587890625
}

With strings, ~== performs a case-insensitive check:

string str1 = "Adam";
string str2 = "ADAM";
if (str1 ~== str2)
{
  // This check will pass
}
if (str1 == str2)
{
  // In contrast, this check will fail because the two strings use
  // different capitalization, and '==' is case-sensitive
}

> (greater)

if (myNumber1 > myNumber2)
{
  // True if myNumber1 is greater than myNumber2
}

< (less)

if (myNumber1 < myNumber2)
{
  // True if myNumber1 is less than myNumber2
}

>= (greater or equal)

if (myNumber1 >= myNumber2)
{
  // True if myNumber1 is greater than or equal to myNumber2
}

<= (less or equal)

if (myNumber1 <= myNumber2)
{
  // True if myNumber1 is less than or equal to myNumber2
}

Logical operators

Logical operators are usually used in IF/ELSE, WHILE and FOR statements to combine conditions. Note: in case of multiple conditions, the statement will abort as soon as it encounters the first condition that isn't met. As such, stringing multiple conditions (while possibly detrimental to readability) will not increase the performance cost. The only exception is the ternary operator.

No operator

If an operator is skipped in a conditional check, the check is implicitly considered to be a boolean check:

if (myBool)
{
  // True if myBool is true
}

// This does the same thing:
if (myBool == true)
{
}

With pointers, it performs a null-check:

if (myPointer)
{
  // True if myPointer is not null
}

// This does the same thing:
if (myPointer != null)
{
}

With numeric values, it performs a zero-check:

if (myNumber)
{
  // True if myNumber is not 0
}

// This does the same thing:
if if (myNumber != 0)
{
}

&& (logical AND)

Used to check if all conditions in a statement are true:

if (<condition1> && <condition2>)
{
  // True if both conditions are true
}
while (<condition1> && <condition2>)
{
  // loops as long as both conditions are true
}

|| (logical OR)

Used to check if at least one conditions in a statement is true:

if (<condition1> || <condition2>)
{
  // True if either condition is true
}
while (<condition1> || <condition2>)
{
  // loops as long as either condition is true
}

! (logical NOT)

Used to invert any condition in a conditional check:

if (foo != bar)
{
  // True if foo isn't equal to bar
}
if (<condition1> && !<condition2>)
{
  // True if condition1 is true but condition2 is false
}
if (!<condition1> && !<condition2>)
{
  // True if both conditions are false
}
if (!<condition1> || !<condition2>)
{
  // True if either condition is false
}
if (!(<condition1> && <condition2>))
{
  // True if either condition is false
}

This operator can also be used to null-check pointers and zero-check values:

if (myPointer != null)
{
  // True if myPointer is NOT null; this is the same as just 'if (mypointer)'
}
if (!myPointer)
{
  // True if myPointer IS null; this is the same as 'if (myPointer == null)'
}
if (!foo)
{
  // If foo is a string, this is true if foo is "".
  // If foo is a pointer, this is true if foo is null.
  // If foo is a numeric value, this is true if foo is 0.
}

? (ternary)

Essentially, syntactic sugar for an IF statement. The basic syntax requires ? and : and functions as follows:

<condition>? <result if true> : <result if false>

For example:

int foo = <condition>? 5 : 10; // if condition is true, foo = 5, otherwise foo = 10

Note, a ternary operator can only be used if both possible values are explicitly of the same type. For example, it's not possible to combine a string and a name value, both have to be either strings, or names:

// We can't use "none" here, because double quotes would turn 
// it into a string, whereas GetClassName returns a name:
name clsname = target != null? target.GetClassName() : 'none';

// Conversely, we can't use GetClassName() directly, because 
// it returns a name, not a string:
string clsnameStr = target != null? ""..target.GetClassName() : "None";

For actor pointers, null needs to be explicitly cast as Actor:

Actor foo = target != null? target : Actor(null); //Just null won't be recognized

Note: stringing ternaries is possible...

int foo = <condition1>? 5 : <condition2>? 10 : <condition3>? 20 : <condition4>? 40 : 50;

...but not recommended. Not only does it reduce readability, it also increases performance cost because the engine has to go through every condition, even when only one of them can be true.

Bitwise operators

& (bitwise AND)

Usually, used to check if a specific value is present in the bit field:

if (myBitField & foo)
{
  // True if foo is contained in myBitField
}

&= (bitwise AND assignment)

Usually, used in combination with ~ to remove a value from the bitfield:

myBitField &= ~foo; //foo is removed from myBitField

| (bitwise OR)

Used to combine multiple values to set them in a bit field:

int myBitField = bitValue1 | bitValue2; //myBitField now contains both values

|= (bitwise OR assignment)

Adds a value to the bit field:

myBitField |= foo; //foo is added to myBitField

~ (bitwise NOT)

When used after &=, removes the value from the bit field:

myBitField &= ~foo; //foo is removed from myBitField

Miscellaneous operators

is (class inheritance)

Used to check if one class is based on another class through inheritance:

if (MyClass is ParentClass)
{
  // True if MyClass inherits from ParentClass
}

This can be called both on class type pointers and class instance pointers. For example:

if (target is 'DoomImp')
{
  // True if the target actor is a DoomImp or based on it
}
if (target.GetClass() is 'DoomImp')
{
  // True if the target's class is DoomImp or based on it
}

See also