# Виды узла — `.kind()`, `$.kinds`, `Re2D`

Вид (`kind`) говорит, **как смотреть** на узел и камеру. Мир остаётся плоским:
позиция, размер, тело, слои, события и сохранения — обычные 2D-поля. Вид лишь
выбирает отрисовщик и расширяет смысл нескольких методов. Подробный замысел и
план — [RE2D.md](RE2D).

```js
$('<npc>', { id: 'russi' }).at(500, 400).kind(Re2D);   // узел живёт в 2.5D
$('<npc>', { id: 'bob' }).at(300, 400);                // kind не указан — обычный 2D
$('#russi').kind();                                    // → 're2d'
$('#russi').kind(null);                                // вернуть 2D
$('<npc>', { kind: Re2D });                            // то же атрибутом конструктора
```

Главное правило: **узел без `kind` — обычный 2D**, и для него ничего не
изменилось. Единственная плата за механизм в горячем пути рендера — одно
сравнение строк `node.kind !== '2d'`.

---

## 1. Что здесь есть

| Вызов | Результат |
|---|---|
| `.kind()` | вид первого узла выборки (`'2d'` для пустой) |
| `.kind(name)` | назначить вид всем узлам выборки; цепочка |
| `.kind(null)` / `.kind('2d')` | вернуть вид по умолчанию |
| `Re2D` / `$.Re2D` / `$.kinds.Re2D` | константа `'re2d'` |
| `$.kinds.TwoD` | константа `'2d'` |
| `$.kinds.list()` | `[{ name, title, renderer, nodes }]` — факты о видах |
| `$.kinds.of(target)` | вид первого узла по селектору/узлу/обёртке или `null` |
| `$.kinds.has(name)` | известен ли вид |
| `$.kinds.register(name, { title })` | завести новый вид (для расширений) |
| `$.kinds.renderer(name, fn)` | назначить виду отрисовщик `fn(node, cam) → bool` |
| `$.kinds.pass(name, { begin, end })` | назначить виду проход мира для камеры этого вида (см. §2) |
| `$.kinds.normalize(value)` | имя вида или ошибка с подсказкой |

`Re2D` — замороженная **строка**, а не объект: она переживает JSON, prefab и
`inspect`. Канонически константа живёт в `$` (`$.Re2D`), глобальное имя `Re2D`
добавлено, чтобы читалось `.kind(Re2D)`.

Неизвестный вид — исключение с подсказкой, какие бывают:
`.kind("3d"): неизвестный вид; доступны: 2d, re2d`. После ошибки вид узла
прежний.

## 2. Отрисовщик вида

```js
$.kinds.register('probe');
$.kinds.renderer('probe', (node, cam) => {
    // нарисовать узел через $.gfx.push.* и вернуть true;
    // вернуть false — «этот узел вид не берёт», его нарисует обычный 2D-путь
    return true;
});
```

В `drawWorldNodeInner` (`render.js`) узел с `kind !== '2d'` сначала предлагается
отрисовщику своего вида. Если отрисовщика нет или он вернул `false`, узел
рисуется как обычный 2D-узел, поэтому незавершённый или «пустой» вид ничего не
ломает. Отрисовщик вида `re2d` уже регистрируется при сборке API: поверхности и
билборды реализованы ([re2d.md](highlevel/re2d)).

### Проход вида

Если у **камеры** вид с зарегистрированным проходом (`$.kinds.pass`), `render.js`
не рисует 2D-мир: вызывает `begin(cam)`, рисует узлы **этого же вида** (узлы
других видов, в том числе 2D, под такой камерой не рисуются) и вызывает
`end(cam)`. Узел, чей вид совпал с видом камеры, но отрисовщик его не взял
(`false`), 2D-путём не рисуется — для чужого пространства он не имеет смысла.
Так устроен Re2D ([re2d.md](highlevel/re2d)); обычная 2D-камера проходов не имеет.

## 3. Данные, выборки и снимки

* **Селектор.** `[kind=re2d]` и `[kind=2d]` работают как любое условие на
  свойство узла.
* **Prefab и сохранения.** Ключ `kind` пишется в `nodeToData` **только для
  не-2D** узлов: сохранения и prefab-данные 2D-игр остаются побайтово прежними.
  `applyData` читает `kind` обратно, `kind` разрешён в `overrides`.
* **Агент.** `nodeBrief` (снимок, `inspect`, `query`) добавляет `kind` только у
  не-2D узлов, поэтому снимок 2D-сцены не изменился.
* **Реестр.** Смена вида не меняет версию реестра и срезы по тегам/классам: вид
  не участвует в индексе.

## 4. Ограничения

* Вид задаётся **узлу целиком**, наследования от родителя нет: ребёнок
  Re2D-узла сам решает, какого он вида.
* `kind` — это строка; значение, не прошедшее `normalizeKind`, в узел не
  попадает ни одним путём (`.kind()`, конструктор, prefab).
* Вид камеры — `$.camera.kind(Re2D)` ([camera.md](highlevel/camera) §5); отрисовщики
  узлов вида `re2d` (стены, пол, билборды) уже зарегистрированы. Под обычной
  2D-камерой сохраняется вид сверху ([re2d.md](highlevel/re2d)); `<ceiling>` скрыт.

## 5. Проверка

```bash
build/_deps/quickjs-build/qjs tests/js/kinds_test.mjs     # юнит, без движка
python3 tests/agent/highlevel_kinds_test.py               # в движке (после сборки)
```
