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

# Добавление логики игрока

В All Out игрок — первоклассный объект геймплея. Ваш `Player :: class : Player_Base` — это место, куда вы помещаете **состояние для каждого игрока** (здоровье, снаряжение, перезарядки, переключатели интерфейса, прогресс и т. д.).

{% hint style="warning" %}
Предполагайте, что подключено несколько игроков. Избегайте глобального состояния, которое сломается, если игроков будет больше одного.
{% endhint %}

## Состояние для каждого игрока

Храните состояние геймплея у игрока или у объектов, принадлежащих игроку.

```go
Player :: class : Player_Base {
    health: int;
    inventory_open: bool;
}
```

## Сервер + клиент: что где выполняется?

All Out автоматически синхронизирует состояние геймплея с сервера на клиенты. Методы геймплея выполняются и на предсказывающем клиенте, и на сервере, поэтому не оборачивайте их в `Game.is_server()`.

Две распространённые проверки:

* `is_local_or_server()`: весь интерфейс игрока и ввод, который он генерирует
* `is_local()`: только визуальные переопределения, специфичные для игрока

```go
Player :: class : Player_Base {
    ao_late_update :: method(dt: float) {
        if is_local_or_server() {
            // Рисуйте весь интерфейс игрока и обрабатывайте его ввод здесь.
        }

        if is_local() {
            // Применяйте здесь видимость, специфичную для игрока, но не изменяйте поля.
        }
    }
}
```

{% hint style="info" %}
Отрисовывайте весь UI игрока из `Player.ao_late_update` в `is_local_or_server()`. Поля, изменённые только внутри `is_local()` заменяются во время согласования.
{% endhint %}

## Идентификационные данные и данные профиля игрока

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

* `p.get_username() -> string`
* `p.get_user_id() -> string`
* `p.avatar_color`
* `p.device_kind` (`.PHONE`, `.TABLET`, `.PC`)
* `p.is_game_owner()`, `p.is_game_editor()`, `p.is_owner_or_editor()` (владелец игры и его команда), `p.is_vip()`, `p.is_moderator()`, `p.is_youtuber()`
* `p.is_chat_open()` и вспомогательные функции для прямоугольников интерфейса, такие как `p.get_chat_rect()`

```go
Player :: class : Player_Base {
    ao_start :: method() {
        log_info("игрок присоединился: % (%)", {this.get_username(), this.get_user_id()});
    }
}
```

## Причины временного состояния

Состояния игрока, такие как заморозка, невидимость, скрытие имени, призрачность, блокировка ввода движения и блокировка джойстика, используют именованные причины. Причины учитываются: каждый вызов `add_*_reason` метода требует одного соответствующего `remove_*_reason` вызова.

Используйте соответствующий `has_*_reason` метод, когда вам нужно поведение как у множества или когда нужно избежать повторного добавления одной и той же причины:

```go
if !player.has_freeze_reason("cutscene") {
    player.add_freeze_reason("cutscene");
}

if player.has_freeze_reason("cutscene") {
    player.remove_freeze_reason("cutscene");
}
```

Та же схема доступна для `невидимости`, `name_invisibility`, `name_offset`, `ghost`, `disable_movement_input`и `joystick_disable` причины. У каждой группы также есть `has_any_*_reason()` когда важен только факт наличия любой причины. Не вызывайте метод добавления каждый кадр: совпадение по имени не делает его идемпотентным.

## Сохранение: где хранить прогресс игрока

* **Economy**: валюты (монеты/самоцветы/опыт) с автоматическим сохранением + редактированием в creator portal\
  См. [Economy](/all-out-docs/ru/dannye-i-sokhranenie/economy.md).
* **Save**: общее сохранение пар ключ/значение (настройки, состояние заданий, списки разблокировок и т. д.)\
  См. [Система Save](/all-out-docs/ru/dannye-i-sokhranenie/save.md).
* **Inventory**: стеки/экземпляры предметов в инвентаре игрока (при желании — с автосохранением)\
  См. [Inventory](/all-out-docs/ru/osnovnye-koncepcii-dvizhka/inventory.md).

## Распространённый шаблон: загружайте сохранённые значения в `ao_start`

```go
Player :: class : Player_Base {
    xp: s64;
    selected_skin: string;

    ao_start :: method() {
        xp = Save.get_int(this, "xp", 0);
        selected_skin = Save.get_string(this, "selected_skin", "default");
    }
}
```

## Лучшие практики

* **Храните состояние каждого игрока в `Player`.** Избегайте глобальных переменных для всего, что относится к конкретному игроку.
* **Запускайте игровой процесс по общей предсказанной ветке.** Рисуйте интерфейс игрока в `is_local_or_server()` и оставьте `is_local()` для визуальных переопределений, специфичных для игрока.
* **Предпочитайте встроенные API сохранения** вместо того чтобы писать свои (Economy/Save/Inventory).

## Ссылка на Player\_Base

См. `api_references/core/ao/core.csl_engine` в папке проекта для полного `Player_Base` API.
