> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/scripting/types-and-structures.md).

# Language Basics

CSL primitives, enums, structs, classes, references, and polymorphism.

This page is the “data modeling” side of CSL: primitives, structs/classes, and the patterns you’ll use to represent game state cleanly.

{% hint style="info" %}
If you’re brand new, start with [Getting Started with CSL](/scripting/syntax.md). This page assumes you already know how to declare variables and write `proc`/`method`.
{% endhint %}

## Primitive types

### Numbers and bools

* Signed integers: `s8`, `s16`, `s32`, `s64` (alias: `int` == `s64`)
* Unsigned integers: `u8`, `u16`, `u32`, `u64` (alias: `uint` == `u64`)
* Floats: `f32` (alias: `float`), `f64`
* Booleans: `bool`

```go
health: int = 100;
speed: float = 6.5;
is_dead: bool = false;
opacity := .75; // same as 0.75
```

CSL widens numbers when that is safe: smaller signed integers can become wider signed integers, smaller unsigned integers can become wider unsigned integers, unsigned integers can become wider signed integers when the target can hold the full range, and `f32` can become `f64`.

### Strings

`string` is used constantly for IDs, names, and formatting.

```go
item_id := "sword";
log_info("picked up %", {item_id});

first_name := "Ada";
last_name := "Lovelace";
full_name := first_name + " " + last_name;
full_name += "!";
```

Concatenation works in both runtime expressions and compile-time constants.

### Vectors

`v2`, `v3`, `v4` are built-in math/data types:

```go
pos: v2 = {10, 20};
color: v4 = {1, 0, 0, 1};
```

## Enums

Enums are great for readable “mode/state” fields:

```go
Game_Phase :: enum {
    LOBBY;
    RUNNING;
    POST_GAME;
}

phase := Game_Phase.LOBBY;

if phase == .RUNNING {
    // ...
}
```

{% hint style="info" %}
Enum values are commonly referenced with the shorthand `.VALUE` when the compiler can infer the enum type.
{% endhint %}

## Structs (value types)

Use structs for “plain data” you want to copy around cheaply (config, small state blobs, descriptors).

```go
Damage_Desc :: struct {
    amount: int;
    knockback: v2;
}

hit := Damage_Desc{amount=10, knockback={2, 1}};
```

Structs can inherit from other structs. Compound literals include inherited fields first:

```go
Base_Stats :: struct {
    health: int;
}

Enemy_Stats :: struct : Base_Stats {
    speed: float;
}

stats: Enemy_Stats = {100, 4.5};
```

### When to use a struct

* A descriptor/config object (`*_Desc`)
* Small, self-contained state you want to copy by value
* Data you want to embed inside another type

### When not to use a struct

* Large mutable state shared across multiple systems (use a class)
* “Identity” objects (players, items, long-lived objects) (use a class)

## Classes (reference types)

Classes are reference types. Create instances with `new(...)`.

```go
Enemy :: class {
    health: int = 50;
    target: v2 = {0, 0};
}

e := new(Enemy);
```

### Inheritance

You can inherit from other structs/classes:

```go
Weapon_Definition :: class {
    id: string;
    name: string;
}

Gun_Definition :: class : Weapon_Definition {
    fire_rate: float;
}
```

Derived classes inherit default field values. They can override inherited defaults with `field = value` in the class body:

```go
Weapon_Definition :: class {
    damage: int = 10;
}

Rocket_Definition :: class : Weapon_Definition {
    damage = 100;
    splash_radius: float = 3.0;
}
```

Every derived class has a read-only `base` field that views the same object as its immediate parent type. Use it when a derived method shadows a same-named parent method and needs to select the parent method explicitly:

```go
Weapon :: class {
    fire :: method() {
        // Base behavior.
    }
}

Burst_Weapon :: class : Weapon {
    fire :: method() {
        base.fire();
        // Additional burst behavior.
    }
}
```

CSL methods are not virtual. The receiver's static type determines where method lookup begins; a base-typed value never calls a same-named method declared only on its derived runtime type.

{% hint style="warning" %}
Use inheritance when there’s a real “is-a” relationship. For most gameplay data, composition (fields) is simpler and easier to maintain.
{% endhint %}

## Arrays, slices, and dynamic arrays

You’ll see three common shapes:

* **Fixed arrays**: `[N]T` (compile-time size)
* **Slices / managed arrays**: `[]T` (a view of an array)
* **Dynamic arrays**: `[..]T` (resizable list)

Example:

```go
spawn_points: [4]v2 = {{0,0}, {5,0}, {0,5}, {5,5}};

alive_enemy_ids: [..]int = {123, 456};
```

For a full guide, see [Arrays and Collections](/scripting/arrays-and-collections.md).

## Passing by reference (`ref`)

If a procedure needs to modify a value, use `ref`:

```go
apply_damage :: proc(health: ref int, amount: int) {
    health -= amount;
}

hp := 100;
apply_damage(ref hp, 10);
```

This is especially useful for “layout-style” APIs that progressively cut/shrink a rect:

```go
draw_panel :: proc(rect: ref Rect) {
    header := rect.cut_top(50);
    draw_header(header);
    // rect is now the remaining area for content
}
```

## Casting

Use `expr.(T)` or `cast(T)expr`:

```go
f := 12.7;
i := f.(int);       // 12
f2 := cast(float)i; // 12.0
```

When the target type is already known, `expr.()` and `cast expr` infer the target type.

## Type polymorphism (generic-style procs)

CSL supports polymorphic procedures using `$` to deduce types at the callsite:

```go
min :: proc(a: $T, b: T) -> T {
    if a < b return a;
    return b;
}

a := min(10, 20);       // T == int
b := min(1.25, 3.5);    // T == float
```

## Types as values (`typeid`)

`typeid` is a value that represents a type at runtime and/or for polymorphic calls:

```go
default_of :: proc($T: typeid) -> T {
    t: T;
    return t;
}

zero := default_of(int);
empty := default_of(string);
```

Class casts are only valid between related types. Derived-to-base casts are free; base-to-derived casts validate the object's concrete runtime type and throw if it is not the requested type or one of its subclasses. Casting a null class value produces null. Use `value.#type` when selecting behavior among several possible derived types instead of using a failing cast as a type test.

## Practical modeling guidelines

* **Per-player state lives on `Player`.** If it should differ between players, it shouldn’t be global.
* **Prefer small structs for descriptors** and store runtime state in classes/components.
* **Run gameplay in the shared predicted path.** Do not compute gameplay state only under `is_local()` or guard it with `Game.is_server()`.

For player-specific patterns, see [Adding Player Logic](/scripting/player-model.md). For the ECS side of the world, see [Entities and Components](/scripting/entities-and-components.md).
