# «Пепельные луга» — демо-витрина движка (проектная дока)

Внутренняя документация вертикального среза. Игра — демо движка `@rpg/engine`:
каждая подсистема задействована в геймплее, недостающие возможности внедряются
в движок жанронезависимо. Пользовательская дока движка — `docs/engine/`,
арт — `docs/art-style.md`, мир — `docs/world.md`.

## Матрица «подсистема движка → где показана»

| Подсистема | Где показана в демо | Статус |
|---|---|---|
| Engine (GameLoop, фиксированный шаг 60 Гц) | весь геймплей, `Game.ts` | готово |
| Renderer (480×270, целочисленный масштаб, pixelated) | весь рендер, `main.ts` + `computeScale` | готово |
| Camera (follow, bounds от западного угла, shake) | следование за героем; тряска при резонансе/уроне; зум (debug-клавиша + кат-сцены) | готово |
| SceneManager (replace/push/pop, fade) | меню → локация → сумка/сейвы; переходы локаций | готово |
| InputManager (действия, геймпад, мульти-биндинг) | advance/attack/menu/inventory/debug; геймпад-карта | готово |
| VirtualJoystick | тач в локациях (включается при первом касании) | готово |
| IsometricTileMap + A* | карты локаций, движение кнопками (мост: scene:walk) | готово |
| mapFormat (JSON+RLE) | карты — файлы `assets/maps/*.map`, генерация `npm run maps` | готово |
| Tiled-импорт | не задействован (дока + тест есть) | резерв |
| EventBus | `combat:hurt/attack/playerHit`, `quest:flower` | готово |
| GameState (флаги/вары) | прогресс квеста, hp/kills/motes/deaths; сериализация в сейвы | готово |
| SaveManager (слоты) | autosave (Esc) + 3 слота, `SaveSlotsScene` | готово |
| Settings + onChange | громкости master/ambience/sfx, `SettingsScene` | готово |
| AudioManager (шины) | амбиент локаций с кроссфейдом, sfx боя/шагов/UI | готово |
| Tween + easing | кат-сцены, лерпы освещения; `backOut` для pop-эффектов | готово |
| FxLayer + пресеты (`render/fx/`) | кольца резонанса, всплывающие тосты, растворения трупов/интерактивов, хит-флэши (герой и враги), мотыли над пеплом (`emberPreset`), частицы ударов/смертей | готово |
| Particles (атмосфера + burst) | пепел лугов, туман прудов, удары/смерти сгустков | готово |
| Lighting (ambient multiply + аддитивные источники) | сумерки Звенца и окна домов, тёмный интерьер с очагом, лампа героя в темноте, отсвет башни после звонка | готово |
| Lighting II (импульсы, день/ночь, виньетка, тёмные пятна) | непрерывное время суток (1 с = 1 игровая минута): закат/ночь по часам, ночной ambient + светящиеся колокольчики, окна гаснут днём, вспышки ударов/резонанса/урона, виньетка при низком hp и в накате, тёмные ауры зон наката | готово |
| Время суток (GameClock + dayNightFactor) | старт 8:00, часы в HUD нет — предмет «Медные часы» (Ирвин даёт с квестом): Enter в сумке показывает время; в сумке время заморожено | готово |
| StateMachine | `EnemyBrain` — ИИ сгустков (dormant/rise/chase/…/sleep/dead) | готово |
| ECS (World) | боевые сущности: враги и снаряды на `Entity` | готово |
| Panel/Button/MenuList | меню, настройки, сейвы, сумка | готово |
| DialogueRunner + DialogueBox | графы `data/dialogues.ts`, ветки квеста | готово |
| CutsceneRunner | кат-сцены сдачи квестов (посадка/настройка/финал: `until`, `moveView`, `skip()`, `cameraZoom`) | готово |
| DebugOverlay | F3: fps/тайл/позиция/hp | готово |
| math/shapes (cone/circle) | конус удара, круг цели, кольца | готово |
| Cooldown | кулдауны удара/резонанса/атак врагов | готово |
| math/rng (mulberry32) | генерация карт и частиц, детерминированный shake | готово |
| AgentHost/EngineAgent (agent/) | `window.__agent`: снапшот, ручной шаг, инъекция ввода, инварианты | готово |
| data/validate.ts → Invariant[] | runtime-валидация контента (диалоги/NPC/локации/враги) | готово |
| apps/game/tools/agent.mjs + packages/engine/tools/agent-lib.mjs | CLI check/run/snapshot/screenshot/dev; смоуки на мосту (каркас — в движке) | готово |

## Сюжетная рамка (акты 1–2, по docs/world.md)

Акт 1:

1. Выжженные луга (хаб): Ирвин даёт квест «Три цветка» (флаг `quest_bells_taken`).
2. Мила предупреждает о накате у прудов, даёт вощёное полотно (`got_cloth`).
3. Серые пруды (опасная зона): туман, сгустки, сбор колокольчиков кнопкой действия E (вар `flowers`).
4. Возврат: сдача Ирвину (`elder_hand_in`) — посадка цветов у тропы, тайлы зеленеют.
5. Крючок: Мила — «Слышишь гул снизу? Это разъезд. Это Машина дышит» (`trader_after`,
   флаг `act2_hook`).

Акт 2:

6. Ржавая роща (после `act2_hook` появляется Клинт — размещения NPC по флагу,
   `NPC_PLACEMENTS`): квест «Голос для резонатора» — звон у деревьев-механизмов
   (интерактивы `tone_tree_N`, вар `tones`), сдача Клинту (`clint_tune`),
   кат-сцена настройки (наезд камеры на резонатор).
7. Старый разъезд: рельсы, дома-фонари, люк в подземелье (зона наката у люка).
8. Зал Старой Машины: «правильный звон» — с настроенным резонатором действие E по
   Машине (`old_machine`) ставит `machine_lulled` и запускает финальную
   кат-сцену (спуск камеры, резонанс CombatWorld, угольки гаснут).

## Боевая модель (реальное время)

- Герой: HP 5 (сердечки), удар-конус по facing (1.0 юнита, 55°, урон 2, кд 0.45 с),
  резонанс удержанием ≥0.6 с (импульс r=3.0 юнита, усыпляет на 4 с, кд 3 с),
  неуязвимость 0.8 с после урона, отброс 0.75 юнита. Дистанции/скорости — в
  мировых юнитах (1 юнит = 1 тайл; на экране 32 px/юнит по линейке проекции).
- Враги «пепельные сгустки»: ползун (HP 3, стая), плевун (HP 2, дистанция + плевок),
  наст (HP 8, замах → удар по площади). Звук — ресурс: удар излучает гул r=4.5 юнита
  и будит спящих сгустков.
- Ввод: подойти и бить конусом по facing (Space тап — удар, удержание ≥0.6 с —
  резонанс); геймпад X. Ходьба — WASD/стрелки/геймпад-крестовина/тач-джойстик.
- **Ограничение тача**: джойстик даёт только движение; действие (E) и удар
  (Space) на тач-устройствах недоступны — экранных кнопок пока нет (в клавиатуры/
  геймпада ограничений нет). Минимальное решение, когда понадобится: две кнопки
  в uiRoot, инъекция `injectAction('attack'/'interact')` + `stopPropagation()`.

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

- `packages/engine` — жанронезависимый движок, публичный API только `src/index.ts`.
- `apps/game/src/`:
  - `data/` — контент: `locations.ts` (определения локаций), `map.ts` (генераторы),
    `dialogues.ts` (графы), `npcs.ts`, `enemies.ts`, `quests.ts` (журнал/сумка);
  - `scenes/` — BootScene → MenuScene (+ SettingsScene, SaveSlotsScene) →
    LocationScene (+ InventoryScene);
  - `systems/` — PlayerController (путь + прямое движение), DialogueSystem,
    `combat/` (CombatWorld, EnemyBrain, PlayerCombat, CombatViews, HealthBar, stats).
- Инструменты: `apps/game/tools/pixelart` (PNG-атласы), `apps/game/tools/audio` (WAV-генератор),
  `apps/game/tools/maps` (карты-файлы), `apps/game/tools/agent.mjs` + `apps/game/tools/lib.mjs` +
  `apps/game/tools/checks/` (агентный мост: check/snapshot/сценарии; браузерный каркас —
  `packages/engine/tools/agent-lib.mjs`), `apps/game/tools/smoke*.mjs`
  (смоуки через мост, в реальном Chromium). Разграничение: тулзы, знающие контент
  игры, — в `apps/game/tools`; жанронезависимая машинерия — `packages/engine/tools/`
  (png/canvas/imaging/wav/agent-lib/boundary через `@rpg/engine/tools/*`).

## Новое в движке за срез

- `render/shake.ts` — `Shake` + `Camera.addShake` (детерминированный rng).
- `math/shapes.ts` — `inCircle`, `inCone`, `angleBetween`, `nearest`.
- `core/Cooldown.ts` — `trigger/update/ready/progress`.
- `render/particleSim.ts` — чистая интеграция частиц; `Particles.burst/oneShot`.
- `anim/flash.ts` + `render/spriteFx.ts` — `TintFlash`/`flashSprite`.
- `render/fx/` — `FxLayer` (one-shot эффекты: `ring/floatText/fade/pop/flash/burst/trail`
  + пресеты частиц) и чистая математика сэмплов `fxSim.ts`; вся рукописная
  эффектная обвязка демо мигрировала на пресеты.
- `input/InputManager` — `isActionJustReleased`, `wasKeyReleased`, `pointer.downTicks`,
  мульти-биндинг (одна клавиша → несколько действий).
- `core/StateMachine.transitionAny` — переход из любого состояния.
- `map/IsometricTileMap.setTile` — изменение тайла с перерисовкой ячейки.
- `map/pathfinding.findPathToNeighbor` — путь к краю занятого/непроходимого тайла
  (гейтинг взаимодействий по дистанции в демо).
- `cutscene/CutsceneRunner.ts` — view-агностичный раннер шагов (`call/cameraMove/burst/wait/setFlag`),
  шаг с `seconds: 0` — параллельный; сцена ставит геймплей на паузу по `active`.

## Новое в движке за срез (агентный набор инструментов)

- `agent/types.ts` — `SceneAgent`/`AgentHost`/`Invariant`/`SnapshotLayer`: движковый
  каркас моста жанронезависим (по образцу `StorageLike`), тесты без Pixi.
- `agent/EngineAgent.ts` — снапшот (движковый слой + сцена), инварианты, ручной шаг
  (`step`/`waitFor`), атомарная инъекция ввода (`tapVirtual`/`press`/`key`), команды.
- `core/Engine.stepTick/stepTicks/tickCount` + `GameLoop.setManual/resetTiming` —
  детерминированные шаги логики без «догоняния» rAF.
- `input/InputManager.inject*` — инъекция через те же структуры, что реальные события.
- `dialogue/DialogueRunner` — раскрыт для снапшота (`nodeId/choices/waitingForChoice`).
- Игра: `agent/GameAgent.ts` (`window.__agent`), `agent/snapshot.ts`, `data/validate.ts`,
  сцены реализуют `SceneAgent` (whitelist-команды, `lastToast`).

## Дорожная карта (что осталось за срезом)

**Текущий срез — движок для ИИ-агента + геймплей на нём (план утверждён):**

- ✅ **Батч 0 — агентный набор инструментов** (готово): мост `window.__agent`,
  валидация контента, `apps/game/tools/agent.mjs`, смоуки на мосту, `docs/llms.txt` +
  `docs/engine/agent.md` + `docs/engine/practices.md`.
- ✅ **Батч 1 — единый механизм переходов** (готово): `TransitionDef`
  (step/click, условия, lockedText), дисарм тайла входа, `returnTo`, сейвы v3,
  телепорт-колодцы (Звенец ↔ луга), сценарий `apps/game/tools/checks/transitions.mjs`.
- ✅ **Батч 2 — интерьеры + интерактивные объекты** (готово): карты интерьеров
  (`buildInteriorMap`: дом Ирвина, лавка Милы), двери-действия (E у тайла) внутрь и выход
  `kind: 'return'`, `InteractableDef`/`Interactables` (сундук, очаг, записка,
  прилавок; реакции по `when`, `once` → флаг `used:<id>`, `gives`), снапшот
  `interactables`, сценарий `apps/game/tools/checks/interact.mjs`.
- ✅ **Батч 3 — взаимодействие с миром шире** (готово): столбы у троп (луга,
  пруды, Звенец), подбор пепельных мотов (`gives` + `addVar`, вар `motes` —
  зачаток экономики), верёвка колокола башни (флаг `rang_tower`), ветка
  повторного диалога Милы по вару (`whenVar motes`), инвариант
  `interact-used-consistent` в мосте, `walkTo` пропускает узлы-объекты,
  сценарий `apps/game/tools/checks/interact-world.mjs`.
- ✅ **Батч 4 — ИИ врагов** (готово): LOS по сетке (`systems/combat/los.ts`,
  кэш раз в 3 тика), шум мира `CombatWorld.noise` (шаги героя 0.35, удар
  конусом 1.0, плевок/смерть 0.6; спящих будит только ≥ 0.7), состояния
  `patrol`/`wary`/`flee`/`return` в `EnemyBrain` (патруль — per-spawn в
  `AREAS[*].enemies`), бег при отступлении (`run` в `EnemyIntent.move`),
  инварианты `enemy-state-legal`/`enemy-in-bounds`, команды
  `scene:noise`/`scene:damageEnemy`, сценарий `apps/game/tools/checks/ai.mjs`.
- Принцип среза: каждая геймплейная система имеет сценарий в `apps/game/tools/checks/`.

**Дальше (запрос игрока, после моста):**

- **Телепортация (геймплей)**: точки перехода внутри локации (порталы/колодцы) и между
  локациями без «пешего» перехода (витрина SceneManager + fade).
- **Здания с интерьерами**: вход в дома (двери-переходы), интерьерные карты,
  объекты внутри (сундук, лавка).
- **Взаимодействие с миром шире**: интерактивные объекты (не только NPC/цветки),
  осмотр/подбор предметов, реакция мира (вары/флаги).
- **Системы и ИИ врагов**: расширение EnemyBrain (патруль/агро-радиус/отступление),
  поведение вне боя — спящие стаи, реакция на звук без боя (витрина StateMachine).

**Остальное (по мере готовности базиса):**

- Экономика Милы: торговля (соль/моты), мот-песок как валюта.
- Звонная книга Ирвина: журнал заданий расширяется за пределы одного квеста.
- Tiled-импорт: альтернативный путь производства карт (для больших локаций).
- Спектакль звонарей: мини-сцена с StateMachine-хореографией (витрина StateMachine вне боя).