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> @
9.7 KiB
9.7 KiB
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+ множество untrackedtmp_*/мусорных файлов. ⛔ НЕ трогать и НЕ коммитить этот WIP — стейджить только свои файлы поимённо. - Phase 0 реализован (pending review): слой целей в движке
_sim_engine.js(блокgoal, компиляция when/fail/stars через SimExpr, состояние результата, HUD-оверлей, APIonGoal/getResult/resetResult) + серверный гейтvalidateSpecпропускаетgoal/game. Изменены:frontend/js/labs/_sim_engine.js,backend/src/controllers/customSimController.js. Аддитивно: спека безgoalведёт себя ровно как раньше (HUD не создаётся, побед не считается). Смоук 40/40;npm test238 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(iconrocket) виден всем. Новые: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 test251 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); скин тинтует героя+нарратора (localStoragequantik-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 test259/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_simscat='game' остаётся целевым хранилищем для авторённых уровней (Ф5); реестр тогда станет асинхронным (загрузка опубликованных + слияние со встроенными той же формы записи).
- Уточнение Ф1: для MVP уровни — ВСТРОЕННЫЕ ДАННЫЕ в
- Герой Квантик: в уровне = 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).