# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Язык общения

**Всегда общайся с пользователем на русском.**

## Команды

```bash
npm install                          # установка зависимостей (npm workspaces)
npm run dev                          # Vite dev-сервер с HMR (apps/game)
npm run build                        # прод-сборка игры
npm test                             # все тесты (Vitest)
npx vitest run packages/engine/src/map/__tests__/pathfinding.test.ts  # один файл тестов
npm run typecheck                    # tsc --noEmit для обоих пакетов
npm run art                          # перегенерация пиксель-арта из tools/pixelart
node tools/smoke.mjs                 # смоук-тест в реальном Chromium (скриншот + консоль)
```

Dev-сервер по умолчанию на http://localhost:5173; для отдельного порта: `npm run dev -- --port 5199`.

## Архитектура

Monorepo из двух workspace-пакетов с **жёсткой границей API**:

- `packages/engine` (`@rpg/engine`) — жанронезависимый движок-библиотека. **Не должен знать ничего о RPG-контенте** (квесты, предметы, сюжет). Вся игра импортирует движок **только через `packages/engine/src/index.ts`** — другие внутренние пути движка импортировать нельзя.
- `apps/game` (`@rpg/game`) — сама RPG: контент, сцены, геймплейные системы.

### Ключевые решения, которые нельзя сломать

1. **PixiJS — только рендер-бэкенд.** Игровой цикл, сцены, ввод, камера, изометрия — собственные (`packages/engine/src/core`, `scene`, `input`, `render`). Pixi используется как низкоуровневый слой отрисовки спрайтов.
2. **Пиксель-арт pixel-perfect.** Рендер в виртуальном разрешении 480×270 (`Game.VIRTUAL_W/H`), канвас растягивается **целым числом** (`pickScale` в `main.ts`) с `image-rendering: pixelated`, `roundPixels: true`, `antialias: false`. При изменении разрешения следи, чтобы масштаб всегда оставался целым.
3. **Фиксированный шаг.** `GameLoop` (60 Гц update) + аккумулятор с ограничением 5 шагов/кадр; `InputManager.endTick()` очищает «just pressed» в конце каждого тика — не вызывать update вне цикла движка.
4. **Изометрия 2:1.** Конверсии `isoToScreen`/`screenToIsoExact` в `math/iso.ts` — источник истины для позиций. Тайловые координаты всюду именуются `{x, y}` (как `Grid` в pathfinding), экранные тоже `{x, y}`.
5. **Собственные классы движка с инъекцией хранилища.** `SaveManager` принимает `StorageLike` (в браузере — `localStorage`), чтобы тесты работали без DOM. Чистую математику (iso, A*, ECS) держи без зависимостей от Pixi — она тестируется в Vitest без браузера.

### Где что лежит

- `docs/world.md` — библия мира (сеттинг, локации, персонажи, сюжет). **Любой новый контент сверять с ней.**
- `docs/art-style.md` — арт-библия: палитра (32 цвета), размеры спрайтов, правила стиля, чеклист ассетов.
- `apps/game/src/data/` — весь контент: карта (`map.ts`, id тайлов + генератор), диалоги (`dialogues.ts`), NPC (`npcs.ts`).
- `apps/game/src/scenes/` — BootScene (грузит ассеты) → MenuScene → LocationScene; сцены меняются через `SceneManager.replace/push/pop`.
- `apps/game/src/systems/` — геймплейные механики: движение героя (A* + плавный путь + анимация направлений), диалоги с флагами сюжета.
- Сюжетные флаги живут в `DialogueSystem.flags` и сохраняются в автосейв `autosave` (Esc в локации).

### Пиксель-арт

- `tools/pixelart/gen.mjs` — генератор ассетов (PNG из кода, без зависимостей): `node tools/pixelart/gen.mjs`.
- Выход — `apps/game/assets/` (`tiles/*.png`, `chars/*.png`); загружается `AssetLoader` в BootScene, ключ = путь без расширения.
- Палитра — только из `tools/pixelart/palette.mjs` (она же в `docs/art-style.md`); тёплые цвета (B*/F*) — только «жизнь»: цветы, бронза, огонь.
- Имена файлов и размеры спрайтов фиксированы арт-библией — при замене сгенерированного арта нарисованным руками их не менять.
- Тайл «дерево» — высокий объект: `TileMapData.tall` c `TallSpec { height, ground }` (спрайт якорится низом в центр ромба, под ним рисуется ground-тайл).

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

- **Pixi `Assets.load` без `Assets.init()` виснет навсегда** (без ошибок!) — `AssetLoader` делает init сам, не обходи его.
- **`app.renderer` недоступен до завершения `app.init()`** — `Renderer.setup()` ждёт init-промис; не трогай renderer раньше.
- **Изометрический «ромб» уходит в минус по X** (западный угол карты при x≈-448) — границы камеры задаются как `CameraBounds { x, y, width, height }` от западного угла, а не от (0,0). Координаты тайлов/героя/NPC — единое пространство мировых пикселей, ничего дополнительно не сдвигать.
- **Headless-скриншоты с `--virtual-time-budget` зависают на загрузке картинок** (колбэки декодинга не срабатывают) — для визуальных проверок использовать `tools/smoke.mjs` (puppeteer-core + системный Chromium, реальное время).
- Мир/арт сверять с `docs/world.md` и `docs/art-style.md`: тёплые цвета — только жизнь; палитра — только из `tools/pixelart/palette.mjs`.

## Рабочие привычки

- Для задач из 3+ шагов веди список задач (todowrite/TaskCreate) и иди по нему автономно, не спрашивая подтверждения на каждый шаг.
- Подражай существующему стилю кода; комментарии и докстринги — по-русски; хелперы держи до ~70 строк.
- Плейсхолдеры графики рисуются `Graphics`-ом в коде (пока нет арта); настоящий пиксель-арт в будущем кладётся в `apps/game/assets/`.