0f3e12426a
feat(quantik-game): фаза 2 — карта-созвездие + мир + XP/скины (MVP-мир) Одиночный уровень → играбельный мир: карта-созвездие из 6 физ-уровней (2 главы, нарастающая сложность), разблокировка по звёздам, клиентский XP/уровень игрока, пикер из 8 скинов (тинт героя+нарратора), нарратор PetSprite на интро/победе (mood по звёздам). Навигация карта→интро→игра→ успех→карта/дальше; кнопка «Дальше» пересчитывает nextPlayable после дозагрузки прогресса (фикс stale-hasNext). Логика прогресса — чистый модуль progress-logic.js (unlock/XP/группировка). Только фронт, без бэкенда: XP агрегируется из game_progress (Ф1). Каждый уровень проверен на реальном движке (выигрываем + обе звезды достижимы); цепочка разблокировки доказуемо проходима. npm test 251/8 baseline; lint:routes 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> @
93 lines
9.7 KiB
Markdown
93 lines
9.7 KiB
Markdown
# Feature Context: Квантик — Законы Мира
|
||
|
||
## Current State
|
||
- Ветка `feature/quantik-game` ответвлена от `feature/sim-builder` (движок P1–P3 там, не в master).
|
||
- При ответвлении унаследован **чужой uncommitted WIP** sim-builder: `frontend/js/sim-builder.js`,
|
||
`frontend/sim-builder.html`, `.claude/settings.json` + множество untracked `tmp_*`/мусорных файлов.
|
||
⛔ НЕ трогать и НЕ коммитить этот WIP — стейджить только свои файлы поимённо.
|
||
- **Phase 0 реализован** (pending review): слой целей в движке `_sim_engine.js` (блок `goal`,
|
||
компиляция when/fail/stars через SimExpr, состояние результата, HUD-оверлей, API
|
||
`onGoal/getResult/resetResult`) + серверный гейт `validateSpec` пропускает `goal`/`game`.
|
||
Изменены: `frontend/js/labs/_sim_engine.js`, `backend/src/controllers/customSimController.js`.
|
||
Аддитивно: спека без `goal` ведёт себя ровно как раньше (HUD не создаётся, побед не считается).
|
||
Смоук 40/40; `npm test` 238 pass / 8 baseline fail; lint:routes 0.
|
||
- **Phase 1 реализован** (pending review): сквозной играбельный срез. Страница `/quantik`
|
||
(`frontend/quantik.html` + `frontend/js/game/quantik-game.js`) монтирует уровень-спеку через
|
||
`SimEngine.mount`; «игровой режим» = HUD из Ф0 (сам по наличию `goal`) + слайдеры params +
|
||
play/reset. Уровень `phys-artillery-1` — данные в `frontend/js/game/levels.js`
|
||
(`window.QuantikLevels`): physics-гравитация + body-запуск под углом θ/скоростью v, портал-цель,
|
||
бонус-звезда. На победу `onGoal` → `LS.gameProgressSubmit` + DOM-оверлей успеха (звёзды/время/попытки).
|
||
Прогресс: таблица `game_progress` (мигр.**076**), API `/api/game/progress` (GET/POST,
|
||
`gameController.js`+`routes/game.js`, смонтировано в `server.js` после `/api/custom-sims`),
|
||
клиент `LS.gameProgressList/Submit`. Сайдбар: `/quantik` (icon `rocket`) виден всем.
|
||
Новые: `076_game_progress.sql`, `gameController.js`, `routes/game.js`, `quantik.html`,
|
||
`js/game/levels.js`, `js/game/quantik-game.js`, `tests/game.test.js`. Изменены: `server.js`,
|
||
`js/api.js`, `js/sidebar.js`. `npm test` 251 pass / 8 baseline fail (game.test.js 13/13);
|
||
lint:routes 0; миграция применяется чисто.
|
||
- **Phase 2 реализован** (pending review): одиночный уровень превращён в **играбельный мир**.
|
||
Карта-созвездие (`frontend/js/game/map.js`, `window.QuantikMap`) на звёздном фоне: 6 физ-уровней
|
||
в 2 главах (Кинематика 1–3, Динамика 4–6), узлы-«звёзды» со статусом (locked/available/completed+
|
||
звёзды), линии-связи, поэтапное появление. Шапка: нарратор-Квантик (`PetSprite`), XP-бар + «уровень
|
||
Квантика», всего звёзд, скин-пикер (8 скинов, часть за XP/звёзды). Контент уровней расширен в
|
||
`levels.js` (метаданные `chapter/order/par_ms/unlockStars`, по 2 звезды: кристалл + норматив времени).
|
||
Разблокировка/XP/группировка — ЧИСТЫЕ функции в новом `frontend/js/game/progress-logic.js`
|
||
(`window.QuantikProgress`), покрыты тестом. Навигация: карта→интро(нарратор)→уровень→успех
|
||
(нарратор по звёздам)→карта; «Дальше» активирована (`nextPlayable`); скин тинтует героя+нарратора
|
||
(localStorage `quantik-skin`). **Backend НЕ тронут** — XP клиентская агрегация из `game_progress`.
|
||
Новые: `js/game/map.js`, `js/game/progress-logic.js`. Изменены: `quantik.html`, `js/game/levels.js`,
|
||
`js/game/quantik-game.js`. `node --check` все OK; смоуки (логика 16/16, рендер 7/7, winnability 6/6
|
||
на реальном движке) зелёные и удалены; `npm test` 259/251 pass / 8 baseline fail (без изменений);
|
||
lint:routes 0.
|
||
|
||
## Key Architecture Decisions
|
||
- **«Атом» = блок `goal` в спеке** (булево SimExpr). Любой уровень = спека SimForge + `goal`.
|
||
Движок вычисляет `goal.when` каждый кадр; победа → result + callback. Нет `goal` → no-op.
|
||
- **Уровни хранятся в `custom_sims`** (cat='game'), а не в новой таблице. Реюз авторинга/шаринга/embed.
|
||
Новые таблицы — только под ПРОГРЕСС игрока и лидерборд (мигр.).
|
||
- **Уточнение Ф1**: для MVP уровни — ВСТРОЕННЫЕ ДАННЫЕ в `frontend/js/game/levels.js`
|
||
(`window.QuantikLevels`, форма `{ id, title, subject?, hint?, spec }`), а не записи `custom_sims`.
|
||
`custom_sims` cat='game' остаётся целевым хранилищем для авторённых уровней (Ф5); реестр тогда
|
||
станет асинхронным (загрузка опубликованных + слияние со встроенными той же формы записи).
|
||
- **Герой Квантик**: в уровне = engine point с `body` + glow + trail (визуал P2). На карте/в
|
||
диалогах = `PetSprite.render(level, mood, accessories, colorKey, streak, pattern)` (DOM SVG).
|
||
- **Управление = чинить закон**, а не WASD: игрок крутит `params`-слайдеры движка (угол/скорость/
|
||
гравитация) или собирает `f(x)`; затем «Запуск» — симуляция проигрывается к цели.
|
||
- **Безопасность**: цвета — только в canvas-стоки; текст спеки — escape (`& < >`); выражения —
|
||
только длина на сервере, исполняет безопасный SimExpr на клиенте.
|
||
|
||
## Engine touch-points (Phase 0)
|
||
- Спека v1 формат — в шапке `_sim_engine.js`. Добавить `goal`/`game` СЮДА (документировать).
|
||
- rAF-цикл `_renderFrame` (вычисляет env). Добавить `_evalGoal()` после построения env.
|
||
- `mount()` возвращает инстанс — добавить `onGoal`, `getResult`, `resetResult`.
|
||
- HUD — DOM-оверлей `_labelLayer`/новый слой (как readout-бейджи). Без эмодзи, inline SVG.
|
||
- Серверный гейт `customSimController.validateSpec` (:93) — разрешить `goal`/`stars`/`hint`/`game`.
|
||
|
||
## Cross-Phase Dependencies
|
||
- Phase 1+ зависят от `goal`/`getResult`/`onGoal` из Phase 0.
|
||
- Phase 2 (XP/скины) зависит от прогресса Phase 1.
|
||
- Phase 4 (туннелирование) зависит от флешкарт-SR API.
|
||
- Phase 5 (авторинг) трогает sim-builder — к этому моменту чужой P4-WIP должен быть смержен в
|
||
sim-builder; свериться перед стартом фазы (возможен мерж base-ветки).
|
||
- Phase 6 (живая гонка) зависит от моста `sim_state` (Ф7 sim-builder) — он на base-ветке.
|
||
|
||
## Temporary Workarounds
|
||
(пока нет)
|
||
|
||
## Phase 0 — API/гочи (для следующих фаз)
|
||
- Движковое API цели: `inst.onGoal(cb)` (1 раз при победе, cb получает `getResult()`),
|
||
`inst.getResult()` → `{won,failed,timeMs,attempts,stars:{got,total}}` (без goal → `null`),
|
||
`inst.resetResult()` (сброс результата, НЕ считается попыткой). `inst.reset()` = полный
|
||
перезапуск уровня + `attempts++` (пользовательская попытка; первый авто-reset при mount НЕ считается).
|
||
- HUD появляется **автоматически** при наличии `goal` в спеке (отдельного флага «game mode» нет).
|
||
- `timeMs` = **мировое время** `t` от старта (`max(1, round(t*1000))`), детерминизм; не wallclock.
|
||
- Env цели = весь env кадра + единственный доп.идентификатор **`tries`** (= attempts). Других не вводить.
|
||
- Серверный `validateSpec` принимает `goal{when,title,hint,hold,fail,stars≤3}` и `game{...}` (резерв Ф1/5);
|
||
выражения не исполняются (только длина ≤500), текст escape+обрезка.
|
||
- Победа делает `pause()` в кадре; следующий queued-rAF выходит рано → `onGoal` не задвоится.
|
||
|
||
## Open Questions / Notes
|
||
- Категория `cat='game'` — проверить список `CATS` в customSimController.js, расширить при необходимости.
|
||
- Ассеты: разрешены CC0/открытые из интернета (выбор пользователя) — фиксировать источник+лицензию
|
||
в коммите/доке; визуальная база остаётся in-house (PetSprite/canvas/FLUX).
|
||
- Маршрут страницы игры: clean URL `/quantik` (паттерн `/sim-builder`, `/lab`).
|