Classes - ItsDeltin/Overwatch-Script-To-Workshop GitHub Wiki

Classes

Classes and structs can contain variables and methods. They are defined at the rule level.

class DeathZone
{
    public Vector Location;
    public Any Radius;

    public constructor(in Vector location, in Any radius)
    {
        Location = location;
        Radius = radius;
    }
}

To create an object from a class, use the new keyword.

DeathZone newZone = new DeathZone(PositionOf(EventPlayer()), 5);

Accessors

Variables, functions, macros, and constructors can have accessors.

  • Public members can be accessed from anywhere in the project.
  • Private members can only be accessed from inside the class.
  • Protected members can only be accessed from inside the class or inside inheriting classes.

Type variables and functions that return a type are defined with the name of the class or type instead of the define keyword.

globalvar DeathZone[] deathZones;

// Halves the radius of a zone.
void reduceZone(in DeathZone zone)
{
    zone.Radius /= 2;
}

// Gets the death zone that a player is inside of.
DeathZone inRange(Player player):
    FilteredArray(
        AllPlayers(),
        DistanceBetween(<DeathZone>ArrayElement(), player) <= (<DeathZone>ArrayElement()).Radius
    )
    [0];

Casting

Casting variables can be done by preceeding an expression with the type name in between < and >.

<Type>expression;

Methods and variables can be called from a casted variable.

DeathZone[] deathZoneArray = [...];

DeathZone[] inRange = FilteredArray(deathZoneArray, DistanceBetween(<DeathZone>ArrayElement().Location, EventPlayer()) < (<DeathZone>ArrayElement()).Radius);

Class Memory Handling

A maximum of 999 classes can be allocated at a time.

The delete keyword is used to free memory when a class object is done being used. delete does not need to be used with structs. (structs are an experimental feature and are not yet available in OSTW 2.0)

delete(zone);

There are methods that can be used to debug class memory usage.

  • ClassMemoryRemaining(): Gets the remaining number of classes that can be created.

  • ClassMemoryUsed(): Gets the number of classes that were created.

  • ClassMemory(): Gets the percentage of class memory taken.

Debugging Class Variables

Variables defined in classes will be stored as _objectVariable_x in the workshop output.

class MyClass
{
    Any first; // _objectVariable_0
    Any second; // _objectVariable_1
}

Static

Variables, macros, and methods in classes can be marked as static. If a subroutine function in a class does not need to use any of the object variables or methods, it may be a good idea to mark the subroutine as static so the object does not need to be stored.

Static variables function the same as global rule-level variables, except the variables are accessed with the class name followed by the variable name.

Class Inheritance

Classes can extend or modify the behavior of other classes by extending them. Base classes can mark functions as virtual for extending classes to override. Base classes can make variables protected for extending classes to access.

// The base class for all powerups.
class Powerup
{
    // The duration of the powerup. Default is 10 seconds.
    public Any Duration = 10;

    // The position of the powerup.
    public Any Position;

    // The player that activated the powerup. This should be null until AddPowerupEvent runs.
    public Any ActivatedBy;

    // The effect of the powerup.
    private Any Effect;

    // Activated when a player touches the powerup.
    public virtual void TouchedBy(Any player) {}

    // Runs when the powerup's duration is finished.
    public virtual void Finished() {}

    // Prints the powerup's name and location.
    // Make the function a subroutine to reduce output element count since this will be called multiple times.
    public virtual void Print() "Print Powerup debug info."
    {
        SmallMessage(AllPlayers(), "Either Print() wasn't overriden, or the Powerup class was initialized directly. Tell Deltin to implement abstracts!");
    }

    // Adds the powerup to the player's list of current powerups.
    // This allows Finished() to run when the duration runs out.
    protected void AddPowerupEvent(in Any player)
    {
        ActivatedBy = player;
        ModifyVariable(player.CurrentPowerups, Operation.Append, this);
        DestroyEffect(Effect);
    }

    protected void MakeEffect(Color color)
    {
        CreateEffect(
            AllPlayers(),
            Effect.GoodAura,
            color,
            Position,
            1,
            EffectRev.VisibleTo
        );
        Effect = LastCreatedEntity();
    }
}

// The speed boost powerup.
class SpeedBoost : Powerup
{
    // SpeedBoost's Constructor.
    public constructor(in Any position)
    {
        Position = position;
        MakeEffect(Color.SkyBlue);
    }

    // Override TouchedBy.
    public override void TouchedBy(Any player)
    {
        AddPowerupEvent(player);
        SetMoveSpeed(player, 125);
    }

    // Override Finished.
    public override void Finished()
    {
        // Reset move speed.
        SetMoveSpeed(ActivatedBy, 100);
    }

    // Override Print.
    public override void Print()
    {
        SmallMessage(AllPlayers(), <"A speed boost powerup is located at <0>!", Position>);
    }
}

// The health boost powerup.
class HealthBoost : Powerup
{
    // HealthBoost's Constructor.
    public constructor(in Any position)
    {
        Position = position;
        MakeEffect(Color.Red);
        // Health boost is too powerful for 10 seconds, change it to 5.
        Duration = 5;
    }

    // Override TouchedBy.
    public override void TouchedBy(Any player)
    {
        AddPowerupEvent(player);

        // Add 10% to the players max health and heal the player for 25.
        SetMaxHealth(player, 110);
        Heal(player, 25);
    }

    // Override Finished.
    public override void Finished()
    {
        // Reset health boost.
        SetMaxHealth(ActivatedBy, 100);
    }

    // Override Print.
    public override void Print()
    {
        SmallMessage(AllPlayers(), <"A health boost is hidden around <0>!", Position>);
    }
}

// An array of powerups. [0] and [1] are of type SpeedBoost, [2] is the type HealthBoost.
globalvar Powerup[] powerups = [
    new SpeedBoost(Vector(12, 1.67, 39.5)),
    new SpeedBoost(Vector(14, 1.7, 40)),
    new HealthBoost(Vector(74, 17.01, 31.57))
];

rule: "Example"
{
    // The powerup variable is initialized with the SpeedBoost type.
    Powerup powerup = new SpeedBoost(Vector(1, 2, 3));

    // Prints "A speed boost powerup is located at (1, 2, 3)!". Changing 'new SpeedBoost'
    // to 'new HealthBoost' will make it print "A health boost is hidden around (1, 2, 3)!".
    powerup.Print();

    // This will print the speed boost's message twice, then the health boost's message.
    // Try changing the powerup array order, or adding new powerups. This will change what will be printed.
    foreach (Powerup powerup in powerups)
        powerup.Print();

    // This will print a random powerup's debug text.
    (<Powerup>RandomValueInArray(powerups)).Print();
}

A class can extend a class already extending another class. For example, you could make a new class SuperSpeedBoost which inherits SpeedBoost which inherits Powerup.


Notes about virtual functions:

  • Calling virtual functions will write all potential overriding methods into the current rule's action-set. It is recommended to have virtual functions be subroutines, which will allow the function to only have one instance, vastly reducing the element count.

  • Virtual macros will optimize away when the concrete type is known, but when only the base type is known all of the values will be put into the final use as an array.

  • Virtual subroutines will put the actions of the overriding functions into the subroutine.

  • Virtual, inline recursive functions will write all potential function overrides into the action-set.

  • Virtual, recursive functions (both inline and subroutine recursive) will assign a whole workshop variable to store the object stack data. The object stack is used to determine which virtual function to run if it is called recursively.

⚠️ **GitHub.com Fallback** ⚠️