# Реестр объектов сцены: SceneRegistry

Жанронезависимый примитив «что-то стоит на карте» + индекс по тайлам. Движок не
знает ни NPC, ни интерактивов, ни врагов: род (`kind`) и ссылка на контент
(`ref`) — строки/ссылки, которые задаёт игра. Чистый модуль без Pixi —
тестируется в Vitest.

```ts
import { SceneRegistry } from '@rpg/engine';

const registry = new SceneRegistry();
registry.add({
    id: 'npc:elder',          // уникален; дубликат — throw (ловится инвариантом сцены)
    kind: 'npc',
    pos: { x: 14.5, y: 9.5 }, // ноги, мировые юниты
    radius: 0.35,             // тело в юнитах (0 — точка)
    ref: npcDef               // ссылка на контент игры — движок не читает
});
registry.add({
    id: 'prop:house', kind: 'prop',
    pos: { x: 20.5, y: 12.5 },
    footprint: { w: 2, h: 2 } // объект накрывает тайлы [20..21]×[12..13]
});

registry.get('npc:elder');       // SceneObject | undefined
registry.byKind('enemy');        // все объекты рода
registry.at(20, 12, 'prop');     // объект, привязанный к тайлу (вкл. footprint)
registry.atWorld(p, 'enemy');    // тайл точки или тело в радиусе из 3×3
registry.near(p, radius, kind);  // объекты в радиусе (для расталкивания тел)
registry.move('enemy:7', pos);   // переиндексация; O(1), если тайл не сменился
registry.remove('enemy:7');      // при смерти/выходе
```

`SceneObject` = описание + `tile` (floor от pos) и нормализованный `radius`
(дефолт 0). `pos` мутабельно — можно держать ссылку на чужой объект состояния
(например, `EnemyComp.pos` из ECS), тогда `move()` вызывается после чужого шага.

## Связка с остальными подсистемами

- **Клик-роутинг**: «что под кликом» — `at`/`atWorld` вместо перебора списков
  (приоритеты клика остаются в игре, см. `practices.md`).
- **Телесные коллизии**: `near` + расталкивание — см. «Коллизии» в `maps.md`.
- **IsoDepthLayer**: реестр не рисует; вьюхи добавляются в depth-слой сценой,
  реестр — только данные и запросы.

## Правила

- Идентификаторы — составные (`kind:имя`, `kind:индекс`), чтобы сцена могла
  строить id механически.
- Инвариант согласованности реестра с контентом области — в снапшоте агента
  (`registry-consistent`): каждый def есть в реестре и наоборот.
- Footprint-объекты (пропы) регистрируются во всех покрытых тайлах — `at`
  находит их с любого занятого тайла.