ZScript variables

ZScript introduces a variety of new variables usable within actors, including structs. These must be defined either inside regular/anonymous functions, or outside the Default {} block, which is used for an Actor class's internal properties only.

Note: This feature is for ZScript only.

Types

The following types are available:

  • Arithmetic (bool, double, int, uint)
  • See this page for more information on what the types are and their aliases. They are defined as shown above, NOT like in C++ (i.e. no unsigned word or underscore).
  • Doubles and vectors gain the following expressions:
  • ~== checks if the difference between a condition is less than epsilon (represented as roughly 0.00001). This is useful to avoid rounding errors that may accumulate. This is the equivalent of performing abs(condition.a - condition.b) <= (1.0 / 65536.0)
  • Example: if (vel ~== (0, 0, 0)) // Ignores tiny values below epsilon


  • Quaternions (Quat)
  • See Quaternion.
  • Note: Quaternions currently cannot be used in dynamic arrays.


  • Vectors (Vector2, Vector3)
  • Vectors are a container of 2 or 3 floating-point member variables, often used with positions, views, velocities, and more. Each member has double precision.
  • Vector2 contains members x and y. Vector3 also includes z and xy.
  • xy is a Vector2. Any changes to it are propagated back to Vector3 and is provided for convenience to interact with functions or arithmetic made for a Vector2.
  • Has the following functions:
  • double Length(): Returns the length of the vector. Effectively performs sqrt(x*x + y*y) for Vector2, sqrt(x*x + y*y + z*z) for Vector3.
  • Note: If getting the squared length of the vector is desired, taking the dot product of the vector with itself can be used to get this value
  • Vector# Unit(): Returns a vector with all members multiplied by 1 / Length(), giving a vector with a length of 1.
  • If the length of the base vector is 0, this will return a Vector# where every value is NaN.
  • See ZScript special words for more functionality related to vectors.
  • Note: Vectors currently cannot be used in dynamic arrays.


  • Encompassed with double quotes (" "). Case sensitive. Strings are simply compared with <, >, <=, >=, ==, ~== operators.
  • In the case of > and < comparisons, it compares the character codes for the first two non-matching characters between the strings. See strcmp for more info.
  • ~== is a case insensitive version of ==.
  • See the link for more detailed information.
  • Names
  • Encompassed with single quotes (' '). Not case sensitive. Operators ==, != are available. Note that ordered comparison currently isn't.
  • Sound
  • Treated similarly to strings.
  • StateLabel
  • Contains the name of a state using double quotes. Can be used for dynamically changing what state an actor is supposed to go to at certain points in their behavior.
  • A special case should be noted with null and "Null", the key difference being using "" around it or not.
  • null should be used if an actor should not perform a jump. Do not use this when performing an action function within an if () statement as it will always fail.
  • "Null" should be used if the actor should be destroyed. Do not confuse this with null. Every actor has this state which will remove them upon making it there. This is safe to use when a jumping function is inside an if statement -- the actor will not be destroyed, since they are treated as booleans instead of actual jumps for testing purposes only.
  • NOTE: StateLabels currently cannot be used in ternary operations, or convert to/from strings and/or names.
  • Color
  • Used to record a a color either defined in the X11R6RGB lump or a hexidecimal surrounded in double quotes, or a directly specified ARGB value.
  • NOTE: Several actor properties can be read only after the mask has been cleared, such as bloodcolor.
Color bl = (bloodcolor & 0xffffff); //Six 'f's, no more, no less!

// Alpha, red, green, and blue from left to right.
// Can also access directly as demonstrated.
Color regrbl = Color(255, 128, 192, 255); 
regrbl.a = 255;
regrbl.r = 255;
regrbl.g = 192;
regrbl.b = 128;


  • TextureID
  • Contains the integer ID of a texture, all graphics have TextureIDs, not just map textures. These IDs can have values assigned, read, and modified by the texture manager.
  • Class Types
  • Used for holding class names and passing said names into places where a function may be expecting a legitimate class for its functionality. If a class that does not exist is assigned to it, ZDoom will throw an error message. Note that an invalid class is not the same thing as null.
class<Actor> proj = "RocketProjectile";
if (FindInventory("GrenadeToken"))
  proj = "GrenadeProjectile";

A_FireCustomMissile(proj);
  • Member Variable Only Types
  • Certain data types in ZScript are valid but will only work when stored as a member variable. If used within a function they are converted to the appropriate data type as listed below. Once they're done being used, the result in them will be converted back to the assigned data type. This can be used to save memory e.g. on any fields in your class. These data types should not be declared within functions as they're only intended to be used for member variables. These types include:
  • int8, int16
  • Stores an 8-bit and 16-bit integer respectively
  • Converts to int
  • uint8, uint16
  • Stores an 8-bit and 16-bit unsigned integer respectively
  • Converts to uint
  • float
  • Stores a 32-bit floating-point value
  • Converts to double
  • FVector2/FVector3
  • Uses single precision instead of double precision for its members
  • Converts to Vector2/Vector3
  • Note: Currently trying to dot/cross product with a regular Vector2/3 will not work.
class MyClass
{
  float myFloat; // this is a 32-bit float

  double AddFloats(double a, double b) // all floating-point variables in functions should be doubles
  {
    myFloat = a + b; // myFloat is converted to a double
    return myFloat;
  }
  // after this function is done, the value in myFloat will be converted back to a 32-bit float
}

Modifiers

Access Modifiers

Access modifiers determine what fields and functions classes are capable of seeing from another class. If no modifier is specified then it is treated as public: every class has access to that field or function.

  • protected - Only this class and its child classes have access to this
  • private - Only this class has access to this

Scope Modifiers

Scope modifiers determine which scope to place a class, struct, field, or function in. For fields (including structs) and functions it will automatically inherit the scope of the class/struct it's defined in, but this can be changed by manually specifying a different scope. Anything that inherits from Thinker (e.g. Actor) will be in the play scope. By default classes or structs are placed in the data scope if no modifier is specified.

  • virtualscope - Any non-static function marked with this will assume the scope of the class or struct it's being called from
  • clearscope - If a function is in either the play or ui scope, this will set it back to being data scoped
  • play - Any field, function, class, or struct marked with this will be put in the play scope
  • ui - Any field, function, class, or struct marked with this will be put in the ui scope
class MyClass play
{
}

// if defined as a field in a play scoped class this will still be ui scoped
struct MyStruct ui
{
}
// this will become play scoped if defined as a field in a play scoped class
struct MyStruct2
{
}

ui int myField;
virtualscope void MyFunction() {}

Transient

The transient keyword can be used to mark a field as non-serializable. Non-serializable fields will not be stored in save files. This can be useful for certain types that cannot be saved such as CVars and SecPlanes. It can also be useful if you want to force data to be cleared every time the game is reopened.

transient CVar myCVar; // this allows CVars to be safely stored as a field

Static

The static keyword marks a function as not being tied to any particular instance of a class or struct. They can be called directly by using the class/struct name followed by the name of the function. Be warned that static functions will not have access to any fields within that class/struct. If field access is required, consider making it non-static or passing a reference to an object that has those fields.

class MyClass
{
  static void MyStaticFunction() {}
}

MyClass.MyStaticFunction(); // how to invoke the method

Action

The action modifier is specifically for non-static Actor functions that are callable from PSprites. Since PSprites are not Actors, the way they pass information to a State's function is different. This is done by passing the State's owner as the invoker while the player themselves is passed as the self pointer. A stateInfo pointer is also passed that contains information about the State the function was called from. For regular Actors this modifier does nothing and shouldn't be used.

action void MyWeaponFunction() {}

Virtual

The virtual keyword allows child classes to override a non-static function defined in a parent class. This can allow for custom behavior of an object even if only knowing its base type. The override keyword is used in place of virtual when overriding it from a child. The function's signature must match one-to-one with the parent's function excluding parameter names. While technically parameters with default arguments can be left out in the override, this is done for future compatibility should a virtual function gain more parameters. Any parameters left out will be inaccessible to the override. Default parameters cannot be defined in an override. A child's function will completely override the function of its parents. To avoid this behavior, the super keyword can be used.

class A
{
  virtual void MyFunction(int arg1, int arg2 = 0) // arg2 has a default argument
  {
    DoThing(arg1, arg2);
  }
}

class B : A
{
  override void MyFunction(int arg1, int arg2) // arg2 doesn't have a default in the override
  {
    super.MyFunction(arg1, arg2); // make sure to still call A's function

    DoMoreThings(arg1, arg2);
  }
}

Internal Engine Modifiers

These modifiers can't be used by modders but are documented here for game developers using GZDoom who wish to expand the internal pk3 for their games.

  • native
For fields, this is declared and used by the engine itself. For functions, this method is defined within the engine. For classes and structs, these types are defined and used by the engine itself.
class EngineClass native
{
}

struct EngineStruct native
{
}

native int engineValue;
native void EngineFunction();
  • internal
If a field is set to readonly but your ZScript still needs to modify it, internal can be used to allow this without having to either obscure the field or give full write access. Code outside of the internal pk3 will still only be able to read from it and not write.
native readonly internal int engineValue;
  • vararg
This specifies that a native function takes a variable amount of arguments. Putting ... as the last parameter marks when the variable arguments start. No other parameters can be defined after this point.
native vararg void MyFunction(int arg1, int arg2, ...);
  • @
Some native arrays may contain a native struct type with an @ prefixing its type name. This denotes that the content of the array comes from structs created internally and are handled via references.
native Array<@EngineStruct> engineArray;

Containers

The following are special types of variables of greater complexity.

  • Similar to their C++ counterpart, contains data of different types. Can also contain functions.
  • State
  • An internal struct which can be used to gather information about an actor's state.

Constants

Unlike C++ and DECORATE, ZScript's constant non-arrays do not have a type to be given -- they auto resolve by themselves based on their usage.

const con1 = 1; //int
const con2 = 2.5; //double
const con3 = "Stringify me, cap'n!"; //string (using "")
const con4 = 'A name it is.'; //name (using )

Readonly

The readonly keyword can be used to make it so an Actor field cannot be written to. This can be useful for creating pseudo constant values that you might want to change on a per-Actor basis via their properties. It can also be used to pass readonly reference handles to functions. Readonly fields must be set in the Default block as a property.

readonly int prop;

// references set as readonly use readonly<T> syntax
void MyFunction(readonly<Actor> mo) {}

For developers using GZDoom to create their games, readonly can also be used as a way to pass information from the engine to the front end without allowing modders to directly modify it. For instance, radius is set to readonly in ZScript but can still be modified within the engine itself. This allows for easy read access while forcing an API for write access.

Meta

The meta keyword is a special modifier that marks an Actor field as both static and readonly. Static in this case means the field is not stored on each individual Actor but instead shared in a single spot in memory across all of them. If you have values that aren't tied to a particular instance of an Actor, this can be used to save memory. A virtual getter function can be created to allow classes to modify it conditionally. Due to the nature of being readonly, this can only be set via the Default block as a property.

meta int GibHealth;

virtual int GetGibHealth()
{
  return -GibHealth;
}

Function Const

Unlike variable consts, the const keyword for non-static functions denotes that the function does not modify the object's fields in its body. Unlike traditional C++ this does not actually prevent the function from modifying fields but instead acts like a hint that it won't do so. As a result this should only be used with functions where nothing but local variables are being modified. It's often used for getter functions so that they can be called from any scope.

clearscope bool GetValue() const {} // This should be paired with clearscope to denote it won't modify the play or ui scopes

Arrays

Arrays behave like their C++ counterparts, including multi-dimensional support.

Local Arrays

Local arrays can be defined with a set number of index spots, but cannot be initialized until after their declaration. Changing the array's contents must be done inside an anonymous or named function. These can also be declared inside of said functions for temporary existence.

int MyArray[2];
MyArray[0] = 5;
MyArray[1] = 10;

Constant Arrays

Constant arrays are different from normal constants.

  • Can be defined in (anonymous) functions
  • Must have all their fields initialized immediately.
  • Must have a type, and the static word present.
// static const <type> <name>[] =
static const Color SecondaryColor[] =
{
    "Red",
    "Yellow",
    "Green",
    "Blue" // Unlike enums, the last member must not have a comma.
};

Dynamic Arrays

Dynamic arrays are similar to local arrays but can be resized at any time. They are similar to vectors in C++.

Array<int> myNumbers;
myNumbers.Push(5);
myNumbers.Push(10);

See Also

This article is issued from Zdoom. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.