> 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/syntax.md).

# Начало работы с CSL

CSL — это собственный скриптовый язык All Out. Он статически типизирован и больше всего похож на Go/Odin — за исключением **состояние игрового процесса автоматически синхронизируется с сервера на клиентов**.

{% hint style="info" %}
Вам не нужно писать RPC, SyncVar или собственную репликацию. Мы делаем это за вас!
{% endhint %}

## Ваш первый скрипт (`main.csl`)

Когда вы создаёте новый проект, All Out создаёт `main.csl` в папке вашего проекта `scripts/` папке.

```go
import "core:ao"

// ============================================================================
// Глобальный жизненный цикл
// ============================================================================

ao_before_scene_load :: proc() {
    // Зарегистрировать определения предметов, валют и т. д.
    // Выполняется после создания пустой Scene, до загрузки содержимого сцены.
}

ao_start :: proc() {
    // Вызывается один раз при запуске сцены.
}

ao_update :: proc(dt: float) {
    // Вызывается каждый кадр.
}

ao_late_update :: proc(dt: float) {
    // Вызывается каждый кадр после ao_update.
}

// ============================================================================
// Жизненный цикл игрока
// ============================================================================

Player :: class : Player_Base {
    ao_start :: method() {
    }

    ao_update :: method(dt: float) {
    }

    ao_late_update :: method(dt: float) {
    }

    ao_end :: method() {
    }
}
```

Например, выводите сообщение в лог при подключении каждого игрока:

```go
Player :: class : Player_Base {
    ao_start :: method() {
        log_info("привет %", {this.get_username()});
    }
}
```

Если вам нужно более глубокое объяснение того, когда выполняются эти функции, см. [Жизненный цикл игры/кадра](/all-out-docs/ru/skripting/game-frame-lifecycle.md).

## Импорты

Ваш `main.csl` должен импортировать `core:ao` и любые созданные вами папки (например `ui/`, `abilities/`, и т. д).

```go
import "core:ao"
import "ui"
```

Импорты указывают на папки, а не на отдельные файлы. Импорт папки включает `.csl` файлы в этой папке. Импорты внутри этой папки разрешаются относительно неё:

```go
// main.csl
import "core:ao"
import "abilities"

// abilities/projectiles.csl
import "helpers"
```

{% hint style="warning" %}
В большинстве проектов, **импортируйте в `main.csl` только**. Не разбрасывайте импорты по множеству файлов — в итоге у вас неизбежно возникнут путаница с порядком и видимостью.
{% endhint %}

## Объявления (переменные и константы)

Объявления связывают имя со значением.

### Переменные

```go
// Общая форма
<name>: <type> = <expression>;
```

Можно опустить либо `<type>` или `<expression>` следующее:

```go
// Явный тип
my_value: int = 42;

// Вывод типа
my_value := 42; // выводится как int

// Нулевая инициализация (то же, что my_value := 0;)
my_value: int;
```

### Константы

Константы используют `::` и должны быть константами времени компиляции. Они могут быть скалярами, строками, типами, значениями процедур, массивами или составными литералами:

```go
MAX_PLAYERS :: 12;

Spawn_Desc :: struct {
    name: string;
    pos: v2;
}

DEFAULT_SPAWNS: []Spawn_Desc : {
    {"Синий", {-4, 0}},
    {"Красный", {4, 0}},
};
```

Это недопустимо (потому что `a` не является константой времени компиляции):

```go
a := 123;
b :: a; // ошибка компиляции
```

Инициализаторы глобальных переменных тоже должны быть константами времени компиляции. Используйте `ao_before_scene_load` или `ao_start` для инициализации во время выполнения.

### Глобальные переменные

Глобальные переменные используют тот же синтаксис объявления, что и локальные. Их можно оставить с нулевой инициализацией или инициализировать константами времени компиляции, включая структуры, массивы, значения процедур и `typeid` значения:

```go
score_total: int;
spawn_names: []string = {"Синий", "Красный"};
default_type: typeid = int;

start_round :: proc() {
}

on_match_start: proc() = start_round;
```

Глобальные переменные изменяемы и сохраняются в течение жизни экземпляра скрипта. Не используйте их для состояния игрового процесса отдельного игрока.

## Типы

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

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

### Векторные типы

`v2` имеют `.x`, `.y`; `v3` добавляют `.z`; `v4` добавляют `.w` — все поля float:

```go
pos: v2 = {10, 20};        // x=10, y=20
color: v4 = {1, 0, 0, 1};  // красный с alpha=1
offset := v3{1, 4, 9};     // вывод типа
```

## Структуры и классы

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

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

```go
Food_Definition :: struct {
    name: string;
    food_value: int;
}

food: Food_Definition;
food.name = "Apple";
food.food_value = 10;
```

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

```go
Foo :: class {
    value: int = 10;
    position: v2 = {12, 34};
}

foo := new(Foo);
```

Поля класса могут иметь значения по умолчанию. Производный класс может переопределять унаследованные значения по умолчанию без повторного объявления поля:

```go
Enemy :: class {
    health: int = 100;
    speed: float = 3.0;
}

Boss :: class : Enemy {
    health = 500;
    speed = 1.5;
}
```

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

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

```go
Animal :: class {
    name: string;
    age: int;
}

Dog :: class : Animal {
    breed: string;
}

dog := new(Dog);
dog.name = "Buddy";
dog.age = 5;
dog.breed = "Лабрадор";
```

## Процедуры и методы

### Процедуры (`proc`)

```go
add :: proc(a: int, b: int) -> int {
    return a + b;
}

result := add(2, 4); // 6
```

Процедуры — это обычные значения, и их можно присваивать/хранить как любые другие значения:

```go
op := proc(a: int, b: int) -> int { return a + b; };
op = proc(a: int, b: int) -> int { return a * b; };
```

### Методы (`method`)

Используйте `method()` внутри структуры/класса. Методы имеют неявный `this` параметр по ссылке.

```go
Dog :: class {
    name: string;

    bark :: method() {
        log_info("% лает!", {name});
    }
}

dog := new(Dog);
dog.name = "Buddy";
dog.bark();
```

### Доступ к полям и вызовы методов

Используйте `.` для полей и методов:

```go
hp := player.health;        // доступ к полю
player.respawn();          // вызов метода
entity.set_local_scale({2, 2});
```

{% hint style="info" %}
Любую процедуру можно вызвать как «метод», если её первый параметр совпадает с типом получателя. Настоящие методы и поля типа, содержащие значение процедуры, имеют приоритет, прежде чем CSL перейдёт к подходящей свободной процедуре.
{% endhint %}

## Массивы

В CSL есть несколько «похожих на массивы» типов, которые вы будете использовать постоянно:

* **Фиксированные массивы**: `[4]int`
* **Срезы / управляемые массивы**: `[]T` (часто используется как «только для чтения» представление массива)
* **Динамические массивы**: `[..]T` (изменяемый по размеру список)
* **Неуправляемые массивы**: `[^]T` (используется в сигнатурах встроенного API, например `format_string`, `log_info`, и т. д. — передавайте значения как `{a, b, c}`)

Динамические массивы предоставляют `.data`, `.count`, а `.capacity`, а для операций используют синтаксис вызова методов:

```go
numbers: [..]int;
numbers.append(10);
numbers.append(20);

log_info("количество: %", {numbers.count});
log_info("первый: %", {numbers[0]});
```

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

## Управление потоком

### Если / иначе

```go
if hp <= 0 {
    die();
} else if hp < 25 {
    Notifier.notify(player, "Мало здоровья!");
} else {
    // всё в порядке
}
```

### Оператор switch

Используйте `default:` для ветки default. Варианты поддерживают **несколько значений** (через запятую) и **диапазоны**. Нет проваливания, как в C.

```go
Item_Tier :: enum {
    COMMON;
    UNCOMMON;
    RARE;
    EPIC;
    LEGENDARY;
}

get_tier_color :: proc(tier: Item_Tier) -> v4 {
    switch tier {
        case .COMMON:              return {0.7, 0.7, 0.7, 1.0};
        case .UNCOMMON:            return {0.3, 0.8, 0.3, 1.0};
        case .RARE, .EPIC:         return {0.3, 0.5, 1.0, 1.0};
        case .LEGENDARY:           return {1.0, 0.8, 0.2, 1.0};
        default:                   return {1, 1, 1, 1};
    }
}

// Варианты по диапазонам
get_difficulty :: proc(level: int) -> string {
    switch level {
        case 1..5:          return "Легко";
        case 6..<11:        return "Средне";
        case 11..20, 25:    return "Сложно";
        default:            return "Неизвестно";
    }
}
```

`..` включает обе границы. `..<` исключает верхнюю границу.

### Пока / for

```go
while condition {
    // ...
}

// Включающий диапазон: 0..9 включает 9
for i: 0..9 {
    log_info("i=%", {i});
}

// Полуоткрытый диапазон: 0..<count исключает count
for i: 0..<items.count {
    log_info("элемент %", {items[i]});
}

// Обратная итерация
for i: 0..<items.count #reverse {
    log_info("элемент в обратном порядке %", {items[i]});
}

// Итерация по элементам массива/среза
for item: my_items {
    // ...
}

// С индексной переменной
for item, i: my_items {
    log_info("элемент % по индексу %", {item, i});
}

// Итерация по пользовательским итераторам (обычно для сущностей/компонентов)
for enemy: component_iterator(Enemy) {
    enemy.update_ai();
}
```

Циклы на основе пользовательских итераторов `for` требуют `next :: method() -> bool` и `current` поля.

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

Используйте `expr.(T)` или `cast(T)expr` для преобразования типа:

```go
a := 123.4;
b := a.(int);   // 123
c := cast(float)b; // 123.0
```

Когда целевой тип уже известен, вы можете позволить CSL вывести его:

```go
i: int = a.();
j: int = cast a;
```

## Передача по ссылке: `ref` (предпочтительно)

Когда вам нужно изменить параметр, **предпочитайте `ref`** ссылки вместо сырых указателей.

```go
update_position :: proc(pos: ref v2, velocity: v2, dt: float) {
    pos.x += velocity.x * dt;
    pos.y += velocity.y * dt;
}

my_pos := v2{0, 0};
update_position(ref my_pos, {10, 5}, 0.16);
```

## Колбэки: указатели на функции + userdata (без замыканий)

В CSL нет замыканий. Встроенный `proc(...) { ... }` не может захватывать окружающие переменные.

Чтобы передавать контекст, сочетайте колбэки с `полем userdata: Object` полем:

```go
Player :: class : Player_Base {
    on_death_userdata: Object;
    on_death: proc(player: Player, userdata: Object);

    die :: method() {
        if on_death != null {
            on_death(this, on_death_userdata);
        }
    }
}

Death_Tracker :: class : Component {
    count: int;

    ao_start :: method() {
        player := entity.get_component(Player);
        player.on_death_userdata = this;
        player.on_death = proc(player: Player, userdata: Object) {
            tracker := userdata.(Death_Tracker);
            tracker.count += 1;
        };
    }
}
```

## Информация о типах (типы как значения)

`typeid` значения можно передавать полиморфным процедурам:

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

a := default_of(int);      // 0
b := default_of(string);   // ""
c := default_of([4]int);   // {0, 0, 0, 0}
```

## Лучшие практики (CSL в All Out)

* **Избегайте глобального состояния игрового процесса.** Подключаются несколько игроков — храните состояние каждого игрока в `Player` вместо этого.
* **Стройте весь UI игрока из `Player.ao_late_update`.** Оберните это в `is_local_or_server()`.
* **Используйте `is_local()` только для визуальных переопределений, специфичных для игрока.** Не изменяйте внутри него состояние игрового процесса или UI.
* **По умолчанию — сначала мобильные устройства.** Не полагайтесь на ввод с клавиатуры/мыши, если только ваша игра явно не ориентирована на ПК.
* **Если вы не уверены в синтаксисе или API**, откройте соответствующий `.csl_engine` файл в `api_references/` вашем проекте.
