> 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/all-out-docs/ru/skripting/types-and-structures.md).

# Основы языка

Примитивы CSL, перечисления, структуры, классы, ссылки и полиморфизм.

Эта страница — сторона CSL, посвящённая «моделированию данных»: примитивы, структуры/классы и шаблоны, которые вы будете использовать, чтобы аккуратно представлять состояние игры.

{% hint style="info" %}
Если вы совсем новичок, начните с [Начало работы с CSL](/all-out-docs/ru/skripting/syntax.md). На этой странице предполагается, что вы уже умеете объявлять переменные и писать `proc`/`method`.
{% endhint %}

## Примитивные типы

### Числа и булевы значения

* Знаковые целые числа: `s8`, `s16`, `s32`, `s64` (псевдоним: `int` == `s64`)
* Беззнаковые целые числа: `u8`, `u16`, `u32`, `u64` (псевдоним: `uint` == `u64`)
* Числа с плавающей точкой: `f32` (псевдоним: `float`), `f64`
* Булевы значения: `bool`

```go
health: int = 100;
speed: float = 6.5;
is_dead: bool = false;
opacity := .75; // то же, что и 0.75
```

CSL выполняет расширение чисел, когда это безопасно: меньшие знаковые целые могут стать более широкими знаковыми целыми, меньшие беззнаковые целые могут стать более широкими беззнаковыми целыми, беззнаковые целые могут стать знаковыми целыми, когда целевой тип может вместить весь диапазон, и `f32` может стать `f64`.

### Строки

`string` постоянно используется для идентификаторов, имён и форматирования.

```go
item_id := "sword";
log_info("подобран %", {item_id});

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

Конкатенация работает и в вычисляемых во время выполнения выражениях, и в константах времени компиляции.

### Векторы

`v2`, `v3`, `v4` — это встроенные математические/данные типы:

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

## Перечисления

Перечисления отлично подходят для читаемых полей «режим/состояние»:

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

phase := Game_Phase.LOBBY;

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

{% hint style="info" %}
Значения перечисления обычно записывают в сокращённой форме `.VALUE` когда компилятор может вывести тип перечисления.
{% endhint %}

## Структуры (тип-значение)

Используйте структуры для «простых данных», которые нужно дёшево копировать (конфиги, небольшие блоки состояния, дескрипторы).

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

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

Структуры могут наследовать от других структур. Составные литералы включают унаследованные поля первыми:

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

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

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

### Когда использовать структуру

* Объект-дескриптор/конфиг (`*_Desc`)
* Небольшое, самодостаточное состояние, которое вы хотите копировать по значению
* Данные, которые вы хотите встроить в другой тип

### Когда не стоит использовать структуру

* Большое изменяемое состояние, общее для нескольких систем (используйте класс)
* Объекты «с идентичностью» (игроки, предметы, долговечные объекты) (используйте класс)

## Классы (ссылочные типы)

Классы — это ссылочные типы. Создавайте экземпляры с помощью `new(...)`.

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

e := new(Enemy);
```

### Наследование

Вы можете наследовать от других структур/классов:

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

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

Производные классы наследуют значения полей по умолчанию. Их можно переопределять в теле класса с помощью `field = value` в теле класса:

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

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

У каждого производного класса есть доступное только для чтения поле `base` которое рассматривает тот же объект как его непосредственный родительский тип. Используйте его, когда производный метод перекрывает одноимённый метод родителя и нужно явно выбрать метод родителя:

```go
Weapon :: class {
    fire :: method() {
        // Поведение базового класса.
    }
}

Burst_Weapon :: class : Weapon {
    fire :: method() {
        base.fire();
        // Дополнительное поведение очереди.
    }
}
```

Методы CSL не виртуальные. Статический тип получателя определяет, где начинается поиск метода; значение с базовым типом никогда не вызовет метод с тем же именем, объявленный только в его производном типе во время выполнения.

{% hint style="warning" %}
Используйте наследование, когда есть реальная связь «является». Для большинства игровых данных композиция (поля) проще и удобнее в поддержке.
{% endhint %}

## Массивы, срезы и динамические массивы

Вы увидите три распространённых варианта:

* **Фиксированные массивы**: `[N]T` (размер известен на этапе компиляции)
* **Срезы / управляемые массивы**: `[]T` (представление массива)
* **Динамические массивы**: `[..]T` (изменяемый по размеру список)

Пример:

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

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

Полное руководство см. в [Массивы и коллекции](/all-out-docs/ru/skripting/arrays-and-collections.md).

## Передача по ссылке (`ref`)

Если процедуре нужно изменить значение, используйте `ref`:

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

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

Это особенно полезно для API «в стиле разметки», которые постепенно отрезают/уменьшают прямоугольник:

```go
draw_panel :: proc(rect: ref Rect) {
    header := rect.cut_top(50);
    draw_header(header);
    // rect теперь — оставшаяся область для содержимого
}
```

## Приведение типов

Используйте `expr.(T)` или `cast(T)expr`:

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

Когда целевой тип уже известен, `expr.()` и `cast expr` выводят целевой тип.

## Полиморфизм типов (процедуры в стиле generics)

CSL поддерживает полиморфные процедуры, используя `$` чтобы вывести типы в месте вызова:

```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
```

## Типы как значения (`typeid`)

`typeid` — это значение, которое представляет тип во время выполнения и/или для полиморфных вызовов:

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

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

Приведения классов допустимы только между связанными типами. Приведения от производного к базовому бесплатны; приведения от базового к производному проверяют конкретный тип объекта во время выполнения и вызывают ошибку, если это не запрошенный тип или один из его подклассов. Приведение нулевого значения класса даёт null. Используйте `value.#type` когда выбираете поведение среди нескольких возможных производных типов, вместо того чтобы использовать неудачное приведение как проверку типа.

## Практические рекомендации по моделированию

* **Состояние, относящееся к конкретному игроку, хранится в `Player`.** Если оно должно различаться между игроками, оно не должно быть глобальным.
* **Для дескрипторов предпочитайте небольшие структуры** а состояние во время выполнения храните в классах/компонентах.
* **Запускайте игровой процесс по общей предсказанной ветке.** Не вычисляйте игровое состояние только под `is_local()` или не защищайте его проверкой `Game.is_server()`.

Для паттернов, специфичных для игрока, см. [Добавление логики игрока](/all-out-docs/ru/skripting/player-model.md). Для ECS-части мира см. [Сущности и компоненты](/all-out-docs/ru/skripting/entities-and-components.md).
