# Холст

> Текстовые блоки, панели, полоски прогресса, изображения, круги на земле и маршруты со стрелками поверх игровой картинки. Рантайм сам рисует их каждый кадр и не меняет состояние игры, поэтому это безопасно и в многопользовательской игре; работать можно через Python, HTTP или напрямую через общую память.

Источник: https://war3ai.com/ru/docs/canvas/

Внешняя программа может рисовать поверх игровой картинки **текстовые блоки, панели, полоски прогресса, изображения, круги на земле и маршруты по земле (со стрелкой)** — рантайм сам отрисовывает их каждый кадр. Подходит для собственного HUD, вспомогательных линий, подсказок, учебных пометок, информационных плашек для стрима.

## Холст или визуальные функции JASS

| | Холст (эта страница) | [Визуальные функции JASS](https://war3ai.com/ru/docs/jass/) |
|---|---|---|
| Кто рисует | Сам рантайм | Сама игра (всплывающий текст, эффекты, панели, диалог с портретом…) |
| Многопользовательская игра | **Безопасно**: рисуется только на вашем экране, игровые объекты не создаются, состояние игры не меняется | Только одиночная игра |
| Оформление | Свободное: любые шрифты (включая китайские), скруглённые углы, полупрозрачность, рамки, любые цвета, локальные изображения | Родной стиль игры |
| Привязка | К юниту, к координатам мира, к позиции на экране; круги на земле повторяют рельеф | Зависит от функции |
| Затраты | Замер: 0.2 ~ 0.35 ms за кадр (9 элементов) | Около 13 ms на вызов |

Оба пути можно совмещать: эффекты в родном стиле игры — через JASS, собственные панели, вспомогательные линии и подсказки — через холст.

## Python

```python
c = g.canvas                               # при первом обращении рантайм ставит хук отрисовки (около 0.1 s)
c.text("title", "Привет, это холст", screen=(40, 110), color=(255, 220, 80),
       bg=(0, 0, 0, 170), border="#C49C40", size=22, bold=True)
c.panel("status", "Компаньон · Светик", ["Настроение: радость", "Убийств: 12"], screen=(16, 330))
c.bar("hp", 0.62, unit=hero, lift=260, width=100, height=12, color=(80, 220, 80), text="62%")   # следует за юнитом
c.text("tag", "Босс готовит ульту!", unit=boss, lift=320, color=(255, 80, 80), size=22, bold=True)
c.circle("danger", (x, y), 300, color=(255, 60, 60), fill=(255, 60, 60, 60), width=3)          # опасная зона на земле
c.circle("aura", hero, 450, color=(80, 200, 255, 220))                                         # круг, следующий за юнитом
c.path("route", [(x1, y1), (x2, y2), (x3, y3)], color=(255, 220, 0), width=5, arrow=True)
c.image("icon", "icon.png", screen=(40, 170), width=64, height=64)
c.remove("danger"); c.hide("tag"); c.clear()   # clear убирает только то, что нарисовали вы
c.expire("tag", 5)                         # исчезнет сам через 5 s
with c.batch(): ...                        # много изменений разом — одна запись в общую память
c.stats()                                  # drawnFrames растёт = отрисовка действительно идёт
```

Каждый элемент обозначается ключом `key`: повторная отрисовка с тем же key — это обновление.

**Кликабельность**: добавьте текстовому блоку или панели `clickable=True` (цвет при наведении задаётся через `hover=`) — при клике в поток событий приходит `ui.click`, `ev.key` равен этому key, а сам клик по элементу до игры не доходит. Готовые кнопки, карточки выбора, горячие клавиши и клики по земле — в разделе [Интерфейс и ввод](https://war3ai.com/ru/docs/ui-input/).

**Позиция** (у каждого элемента одна):

- `screen=(x, y)`: пиксели экрана, отрицательные значения отсчитываются от правого / нижнего края; `center=True` — выравнивание по центру;
- `frac=(0.5, 0.1)`: доли экрана;
- `world=(x, y)`: координаты мира;
- `unit=юнит`: следовать за юнитом. Текст и полоски в мире и над юнитом привязываются к точке серединой нижнего края, `lift` поднимает их выше.

Элементы в мире и над юнитами по умолчанию не заходят на нижнюю панель управления и шар дня и ночи вверху (`over_ui=True` — рисовать поверх них). **Цвет** можно задать как `(r, g, b)`, `(r, g, b, a)`, `"#RRGGBB"` или `"#RRGGBBAA"`.

| Метод | Что рисует | Основные параметры |
|---|---|---|
| `text(key, текст, ...)` | Текстовый блок, несколько строк — через `\n` | `color`, `bg` фон (не задан — прозрачный), `border`, `size`, `bold`, `shadow`, `width` (перенос по этой ширине), `radius` скругление углов |
| `panel(key, заголовок, [строки...], ...)` | Панель (тёмный полупрозрачный фон, золотая рамка) | Как у `text` |
| `bar(key, 0..1, ...)` | Полоска прогресса: здоровье, перезарядка, произнесение | `width`, `height`, `color`, `bg`, `border`, `text` |
| `image(key, путь, ...)` | Локальное изображение (png / jpg / bmp / gif) | `width`, `height` (не заданы — исходный размер) |
| `circle(key, юнит или точка, радиус, ...)` | Круг на земле, повторяет рельеф | `color` цвет линии, `fill` заливка (с прозрачностью), `width` толщина линии |
| `path(key, [точки...], ...)` | Ломаная на земле | `color`, `width`, `arrow` стрелка на конце; точки — координаты или юниты |

## HTTP (любой язык)

Бэкенд Farsight (слушает только локальный адрес):

```http
POST /api/instances/20/canvas
{"set": [
   {"key": "banner", "kind": "text", "text": "Холст из HTTP", "frac": [0.5, 0.12], "center": true,
    "color": "#FFDC50", "bg": [0, 0, 0, 180]},
   {"key": "hp", "kind": "bar", "value": 0.8, "unit": 596125988, "lift": 260, "text": "80%"},
   {"key": "zone", "kind": "circle", "center": 596125988, "radius": 600, "color": [255, 200, 0], "width": 4},
   {"key": "route", "kind": "path", "points": [[-4587, -9092], [-5387, -8792]], "color": "#50C8FF"}
 ],
 "remove": ["old"], "clear": false}

GET  /api/instances/20/canvas        какие элементы сейчас нарисованы + сколько кадров отрисовано
```

`kind` — это имя метода в Python, имена параметров те же; юнит задаётся адресом `addr` из снимка.

## Напрямую через общую память

Можно обойтись без Python и Farsight: один раз отправьте семантическую команду `canvas_enable` (код операции W3P 73), и рантайм создаст блок общей памяти `Local\War3Canvas_<pid>`: заголовок 64 байта + 256 записей × 112 байт + пул текста / точек на 64 KB. Запись идёт по seqlock (номер становится нечётным → записываются элементы и пул → номер становится чётным); рантайм читает блок раз в кадр, а если попадает на наполовину записанные данные, повторяет предыдущий кадр. Обратно он записывает число отрисованных кадров, число элементов и счётчик сбоев. Эталонная реализация на Python — `sdk/python/w3canvas.py`, структуры описаны в заголовочном файле протокола, см. [Протокол W3P](https://war3ai.com/ru/docs/protocol/).

## Несколько программ рисуют одновременно

Моды, Farsight, MCP и шлюз могут одновременно рисовать в одной и той же игре, а холст всего один. Правило: **каждая программа трогает только свои элементы**.

- перед записью взять именованную блокировку, прочитать текущие элементы, оставить чужие, заменить свои и записать всё обратно;
- каждый элемент помнит, кто его нарисовал (идентификатор процесса + порядковый номер внутри процесса); если нарисовавшая его программа завершилась, элемент попутно убирается при следующей записи, кто бы её ни делал, а его кнопки больше не перехватывают клики;
- номера элементов выдаёт общий счётчик, так что они не совпадают.

Python SDK уже так и работает, и `clear()` тоже убирает только свои элементы. Если пишете в общую память напрямую, следуйте этим правилам, иначе затрёте чужое. Подробности о раскладке памяти — см. [Протокол W3P](https://war3ai.com/ru/docs/protocol/).

## Замеры и замечания

- Замер 2026-09-25 (1920×1080, скорость 2×): 9 элементов — 0.27 ~ 0.34 ms за кадр, около 63 кадров в секунду, 0 сбоев; запись 9 элементов — 6 ms; когда герой двигается, круг, текст и полоска здоровья, привязанные к юниту, не отстают. Текстура перерисовывается, только когда меняется содержимое; при простом перемещении перерисовки нет.
- Холст рисуется после интерфейса игры и перед указателем мыши: он перекрывает собственные полоски здоровья игры, юнитов и интерфейс, а указатель мыши рисуется поверх него. Нижнюю панель управления и шар дня и ночи он обходит, но **собственные панели карты не обходит** (таблицу лидеров и таймер в правом верхнем углу) — свои панели в правый верхний угол не ставьте.
- Вне матча (в главном меню, на экране итогов) элементы, привязанные к координатам мира и к юнитам, не рисуются, а привязанные к позиции на экране рисуются как обычно.
- Круг на земле строится так: каждая из 64 точек окружности отдельно проецируется на землю, поэтому на неровном рельефе форма круга следует за ним. Так и должно быть: круг нарисован на настоящей поверхности.
- При первом включении нужно поставить хук и прогреть шрифты — около 1 секунды; в это время текстовые элементы не рисуются, а круги и линии рисуются.
- Если при отрисовке случился хотя бы один сбой, до конца сеанса холст больше не рисует (та же защита, что у облачков над головой), а `faults` в `stats()` становится 1.
- Текст, пути к изображениям и точки — всего 64 KB, не больше 256 элементов; путь к изображению должен быть локальным путём, доступным процессу игры.

Панель состояния [ИИ-компаньона](https://war3ai.com/ru/docs/companion/) нарисована именно на холсте: полоска здоровья, текущее занятие, настроение, число убийств и лечений.
