# Документация движка @rpg/engine

Движок — переиспользуемая основа для 2D-пиксельных игр в браузере. Первая игра на нём
— «Пепельные луга» (`apps/game`), она же живой пример использования всех API.

## Карта документации

| Файл | О чём |
|---|---|
| [getting-started.md](getting-started.md) | Новая игра с нуля: bootstrap, сцены, ассеты |
| [core.md](core.md) | Engine, GameLoop, EventBus, Tween, GameState, StateMachine, Settings |
| [render.md](render.md) | Renderer, pixel-perfect, камера, IsoDepthLayer, частицы |
| [input.md](input.md) | Действия, клавиатура, мышь/тач, геймпад, виртуальный джойстик |
| [maps.md](maps.md) | Изометрия, A*, формат карт, импорт из Tiled |
| [ui-and-dialogue.md](ui-and-dialogue.md) | UI-кит, PixelText, диалоговые графы |
| [assets-audio-save.md](assets-audio-save.md) | AssetLoader, атласы, AudioManager, сейвы |
| [art-pipeline.md](art-pipeline.md) | Генератор пиксель-арта, палитра, замена на рисованный арт |
| [recipes.md](recipes.md) | «Как сделать…»: готовые решения типовых задач |

## Принципы

### 1. Жёсткая граница API

Игра импортирует движок **только** через `@rpg/engine` (весь API реэкспортирован из
`packages/engine/src/index.ts`). Прямые импорты `pixi.js` в игре — нарушение границы:
нужные типы реэкспортируются движком (`Container`, `Graphics`, `Text`, `Sprite`,
`Texture`). Это позволяет менять внутренности движка, не трогая игры.

### 2. Fixed timestep

Логика обновляется фиксированным шагом 60 Гц (`GameLoop`, аккумулятор, максимум
5 шагов за кадр). `dt` в `Scene.update` всегда одинаковый — физика и таймеры
детерминированы. Рендер происходит после каждого шага.

### 3. Pixel-perfect

- Виртуальное разрешение (по умолчанию 480×270) растягивается **целым** числом
  на экран (`computeScale`), `image-rendering: pixelated`, `roundPixels: true`.
- Камера округляет позицию до целого пикселя — арт не «дрожит».
- Пиксельный шрифт (VT323) рисуется в тех же виртуальных пикселях.

### 4. Движок жанронезависим

В `packages/engine` нет ни одного упоминания контента конкретной игры: `GameState`
— механика флагов и переменных, `DialogueRunner` — механика графов. Всё содержимое
(тексты, флаги сюжета, карты) живёт в приложении.

### 5. Инъекция вместо окружения

Всё, что зависит от браузера, инъецируется или изолируется: `StorageLike` для сейвов,
`resolveUrl` для ассетов, `parent` для канваса. Чистые модули (математика, A*, диалоги,
формат карт) тестируются в Node без браузера — Vitest.

## Структура пакетов

```
packages/engine/src/
  core/       Engine, GameLoop, EventBus, Tween+easing, GameState, StateMachine, Settings
  scene/      SceneManager (стек сцен + fade-переходы)
  render/     Renderer, Camera, IsoDepthLayer, Particles, computeScale
  input/      InputManager (клавиатура/мышь/тач/геймпад), VirtualJoystick
  map/        IsometricTileMap, pathfinding (A*), mapFormat (JSON+RLE), tiled-импортёр
  math/       Vec2, изометрия, seeded RNG
  dialogue/   DialogueRunner (графы диалогов, view-агностик)
  ui/         DialogueBox, Panel, Button, MenuList, PixelText
  anim/       FrameAnimation
  audio/      AudioManager (шины master/music/sfx, кроссфейд)
  assets/     AssetLoader (текстуры, атласы)
  save/       SaveManager (JSON-слоты)
  ecs/        World/createEntity/query/addSystem
  debug/      DebugOverlay
```

## Известные грабли (важно!)

Подробности — в корневом `CLAUDE.md`; краткий список:

- `Assets.load` без предварительного `Assets.init({})` **висит навсегда** без ошибок —
  `AssetLoader` делает это сам, но если грузите напрямую через Pixi, не забудьте.
- `app.renderer` недоступен до завершения `app.init()` — `Renderer.setup()` это учитывает.
- Изометрический ромб уходит в минус по X: границы камеры начинаются от
  западного угла карты (`{x: -size.width/2, ...}`).
- `document.fonts` — шрифт загружается асинхронно; `ensurePixelFont()` вызывается
  до создания текста, но фолбэк monospace всегда безопасен.