# Слои коллизий — `$.collision`

Box2D понимает только биты: у тела есть **категория** (`layerBits`) и **маска**
(`mask`), с кем сталкиваться. Писать в игре `0x1 | 0x2` неудобно и опасно
(ошибку не видно), поэтому битам дают имена.

```js
$.ready(() => {
    $.collision.define('walls',   0x1);
    $.collision.define('enemies', 0x2);
    $.collision.define('player',  0x4);

    $('<wall>').layerName('walls');
    $('<enemy>').layerName('enemies');

    $('#hero')
        .layerName('player')
        .maskBy('!player');            // со всеми, кроме других игроков

    $('.ghost').maskBy('none');        // ни с кем
});
```

Пространство имён отдельное: `$.layers` — это канвас-слои и параллакс
([layers.md](highlevel/layers)), смешивать их в одном объекте нельзя.

---

## 1. Объявление имён

| Функция | Что делает |
|---|---|
| `$.collision.define(name, bit, opts?)` | объявить имя для бита; `opts.all: true` — «сталкиваться со всеми» сразу |
| `$.collision.remove(name)` | забыть имя |
| `$.collision.clear()` | очистить реестр |
| `$.collision.has(name)` / `bits(name)` | есть ли имя / бит по имени |
| `$.collision.names()` | имена по алфавиту |
| `$.collision.list()` | `[{ name, bit, mask }]` |
| `$.collision.freeBit()` | бит, ещё не занятый ни одним именем |
| `$.collision.reload()` | перечитать реестр из `$.store` |

Реестр живёт в `$.store` под ключом `collision.layers`, поэтому переживает
смену сцены и hot reload. `define` тем же именем перезаписывает бит.

Битов 16 (по числу категорий Box2D); бит выше `1 << 15` отвергается с записью
в журнал.

```js
$.collision.define('walls', $.collision.freeBit());   // сам подберёт свободный
$.collision.define('player', 0x4, { all: true });     // и сразу маска «со всеми»
```

## 2. Выражения масок

`maskBy(выражение)` понимает имена и операторы:

| Выражение | Маска |
|---|---|
| `'walls'` | только этот слой |
| `'walls|enemies'` | объединение |
| `'all'`, `'*'` | все биты (`0xffffffff`) |
| `'none'`, `'0'` | ноль — не сталкиваться ни с кем |
| `'!enemies'` | все **кроме** врагов (база — все известные слои) |
| `'walls|!enemies'` | добавить стены, исключить врагов |

`$.collision.mask(выражение)` отдаёт число — его можно передать в ядерные
`.mask(bits)`, `.collidesWith(bits, false)` и так далее.

## 3. Методы узла

| Метод | Что делает |
|---|---|
| `.layerName(name)` | поставить слой: пишет `layerBits` и `attrs.layer` |
| `.layerName()` | имя слоя узла (или `null`, если он не ставился) |
| `.maskBy(выражение)` | маска из выражения; без аргумента — текущая |
| `.mask(bits)`, `.layerBits(bits)`, `.collidesWith(target, on?)` | ядерные методы, работают как раньше ([HIGH_LEVEL_API.md](HIGH_LEVEL_API) §6) |

`$.collision.apply(цель, 'walls', { all: true })` делает то же для узла,
обёртки или селектора, если удобнее не цепочкой.

## 4. Группы (`collision_group`)

Группа сильнее масок: у тел с одинаковой ненулевой группой сталкивание
определяется **знаком** группы (положительная — сталкиваются между собой,
отрицательная — никогда), а слои и маски игнорируются. Правило живёт в ядре,
`.collidesWith()` его учитывает.

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

```bash
# разбор имён, битов и выражений масок — без движка
build/_deps/quickjs-build/qjs tests/js/collision_test.mjs
```
