Strata scripting language

Strata is the primary scripting language for gameplay in Hyperion Engine.

It is statically typed, has no GC, ref-counting, or manual memory management.. Compiled via JIT in-editor with live reload, or AOT compiled + linked for shipping builds.

None of that restarting the game or editor to see your changes hogwash.

Note that Strata itself is a separate project from Hyperion; freely usable in other projects. GitHub: github.com/StrataLanguage/stratac.

The Basics Volume I: Functions

Functions are C-like, with static types

int add(int a, int b)
{
    return a + b;
}

The Basics Volume II: Control flow

if, while, for, break, and continue all work as expected in Strata, if you're used to any C-based language

int factorial(int n)
{
    int result = 1;
    int i = 1;
    while (i <= n)
    {
        result = result * i;
        i = i + 1;
    }
    return result;
}

The Basics Volume III: Arrays and strings

Dynamic arrays in Strata grow with array_push and report their size with .length.

Fixed size arrays are supposed as well for struct field members.

extern int printf(string fmt, ...);

struct Inventory
{
    int[8] item_ids; // Fixed size.
};

int main()
{
    string[] backpack = { "torch", "rope" };
    array_push(backpack, "rations");
    printf("carrying %u items\n", backpack.length);
    return (int)backpack.length; // 3
}

The Basics Volume IV: Enums

Enums are scoped. If you've used them in C#, or used enum class in C++, it's that.

enum Color : int { Red, Green, Blue };
enum Scale : byte { Small = 1, Medium, Large, Huge };

Color c = Color.Blue;
int v = (int)c;
Color back = (Color)2;

Now you're cooking: working with engine objects

Handles are opaque types, mirroring Hyperion Engine objects. Extern functions are implemented by the engine in C++. These exact declarations ship with the engine.

An impl block attaches functions to a handle, struct or enum type to be used as methods.

handle Camera;

impl Camera
{
    extern float GetFOV(Camera self);
    extern void SetFOV(Camera self, float v);
}

void reset_view(Camera cam)
{
    cam.SetFOV(60.0);
}

Engine side: (C++)

HYP_CLASS()
class Camera : public ObjectBase
{
    HYP_OBJECT_BODY(Camera);
public:
    HYP_METHOD(Property = "FOV")
    float GetFOV() const;
    
    HYP_METHOD(Property = "FOV")
    void SetFOV(float v);
};

/// Hyperion's build tool will automatically set up the mapping from Strata to Hyperion's native runtime.

Advanced Reading: Boxes and optionals

For a given type you defined, say, Foo - ^Foo (note the caret) allocates a value on the heap (a "box"). Boxes are move-only, and are freed when they go out of scope. This lets us forego the need for garbage collection, runtime reference counting and manual memory management.

Foo? is an optional: it may be empty, and testing it with while (cur?) blesses it, proving to the compiler that it holds a value.

Strata does not have a "null" or "nil" keyword or a "null pointer" concept. Instead, optionals are used to represent the absence of a value.

The compiler prevents you from using an optional that may be empty, so you can't crash the game by forgetting to check.

struct Crate
{
    int weight;
    Crate? next;
};

int main()
{
    ^Crate head = Crate { .weight = 12 };

    Crate? cur = head;
    int total = 0;
    while (cur?)
    {
        total = total + cur.weight;
        cur = cur.next;

        // You can't use cur again here.
        // You'll need another `if (cur?) { ... }` and use it in that block.
    }
    return total; // 12
}

Running Strata scripts

Scripts are plain .strata files. The Hyperion editor recompiles them on save, so there is no restart or reload step. See the Strata compiler on GitHub or back to the Hyperion Engine overview.