feat(quantik-game): фаза 4 — квантовые способности + SR-комнаты

Глава-созвездие quantum (L12–L16) и фирменные механики — всё через
безопасную модель спеки, движок и бэкенд НЕ тронуты (engine touch = 0):
- Суперпозиция: два тела ball+ball2, goal.when требует ОБА (зеркальный
  закон). Туннелирование: forbidden-зона wall + fail wall.hit && tunnel<1;
  способность тратит энергию → setParam(tunnel,1). Коллапс/прицел: пунктир-
  plot предсказанной траектории на паузе.
- Энергия — клиентский ресурс (localStorage quantik-energy, QuantikEnergy).
- SR-комната: мини-сессия повторения флешкарт в модалке (НЕ iframe),
  LS.fcStudySession/fcReview; «Знаю/Легко» дают энергию; текст карт
  экранируется, картинки — по regex-вайтлисту.
Все 5 уровней проверены на реальном движке (2★ достижимы; суперпозиция
требует оба тела; туннель-гейт блокирует без заряда). npm test 253/8
baseline; lint:routes 0; цепочка разблокировки проходима.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
This commit is contained in:
Maxim Dolgolyov
2026-06-14 10:29:35 +03:00
parent 978448d99b
commit 0b1925fd3b
9 changed files with 1071 additions and 24 deletions
+111 -1
View File
@@ -226,6 +226,115 @@
.qg-stat-val { font-family: 'Unbounded', sans-serif; font-weight: 700; font-size: 1.05rem; color: #EAF0F8; font-variant-numeric: tabular-nums; }
.qg-actions { display: flex; justify-content: center; gap: 10px; }
.qg-btn { min-width: 118px; }
/* ════════════════════ Квантовые способности (Фаза 4) ════════════════════ */
/* Панель способностей + HUD энергии — оверлеем поверх сцены уровня. */
.qa-bar {
position: absolute; right: 12px; bottom: 12px; z-index: 12;
display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end;
pointer-events: auto; max-width: calc(100% - 24px);
}
.qa-energy {
display: inline-flex; align-items: center; gap: 5px;
background: rgba(17,19,42,0.86); border: 1px solid rgba(251,191,36,0.4);
border-radius: 99px; padding: 6px 12px 6px 10px; color: #FBBF24;
font-family: 'Unbounded', sans-serif; font-weight: 700; font-size: .9rem; font-variant-numeric: tabular-nums;
box-shadow: 0 6px 18px rgba(0,0,0,0.4);
}
.qa-energy .ic { width: 16px; height: 16px; color: #FBBF24; }
.qa-btn {
display: inline-flex; align-items: center; gap: 6px; font: inherit; font-size: .82rem; font-weight: 600;
color: #E2E8F0; background: rgba(17,19,42,0.86); border: 1px solid rgba(148,163,184,0.28);
border-radius: 99px; padding: 7px 13px; cursor: pointer; transition: .16s;
box-shadow: 0 6px 18px rgba(0,0,0,0.4);
}
.qa-btn .ic { width: 16px; height: 16px; }
.qa-btn:hover:not(:disabled) { border-color: rgba(196,181,253,0.6); color: #fff; background: rgba(30,33,66,0.94); }
.qa-btn:disabled { opacity: .42; cursor: not-allowed; }
.qa-rest { color: #67E8F9; border-color: rgba(34,211,238,0.32); }
.qa-rest:hover:not(:disabled) { border-color: rgba(34,211,238,0.6); }
.qa-tunnel { color: #F0ABFC; border-color: rgba(244,114,182,0.34); }
.qa-aim { color: #7DD3FC; border-color: rgba(56,189,248,0.34); }
.qa-cost { display: inline-flex; align-items: center; gap: 2px; font-size: .74rem; color: #FBBF24; font-weight: 800; }
.qa-cost .ic { width: 12px; height: 12px; color: #FBBF24; }
.qa-ability.qa-on {
color: #fff; border-color: rgba(196,181,253,0.85);
box-shadow: 0 0 0 2px rgba(196,181,253,0.35), 0 6px 18px rgba(167,139,250,0.4);
}
/* всплывающая подсказка способности */
.qa-toast {
position: absolute; left: 50%; bottom: 70px; transform: translateX(-50%) translateY(8px); z-index: 14;
background: rgba(13,13,26,0.94); border: 1px solid rgba(196,181,253,0.5); color: #E8EDF5;
padding: 9px 16px; border-radius: 12px; font-size: .85rem; font-weight: 600;
box-shadow: 0 12px 34px rgba(0,0,0,0.5); opacity: 0; transition: opacity .28s, transform .28s; pointer-events: none;
max-width: 84%; text-align: center;
}
.qa-toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
/* ── SR-комната (модалка повторения) ── */
.qa-overlay {
position: fixed; inset: 0; z-index: 60;
display: flex; align-items: center; justify-content: center;
background: rgba(7,7,18,0.78); backdrop-filter: blur(6px); padding: 16px;
}
.qa-modal {
background: linear-gradient(180deg, #15173099, #0F1024EE), #14152C;
border: 1px solid rgba(148,163,184,0.2); border-radius: 18px;
width: min(460px, 96vw); max-height: 92vh; overflow: hidden;
display: flex; flex-direction: column; box-shadow: 0 24px 70px rgba(0,0,0,0.6);
animation: qg-pop .24s cubic-bezier(.22,1.1,.4,1);
}
.qa-modal-head {
display: flex; align-items: center; gap: 10px; padding: 14px 16px;
border-bottom: 1px solid rgba(148,163,184,0.16); flex-shrink: 0;
}
.qa-modal-title { display: inline-flex; align-items: center; gap: 8px; font-family: 'Unbounded', sans-serif; font-weight: 700; font-size: 1rem; color: #EAF0F8; flex: 1; min-width: 0; }
.qa-modal-title .ic { width: 18px; height: 18px; color: #67E8F9; flex-shrink: 0; }
.qa-modal-title span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qa-modal-energy { display: inline-flex; align-items: center; gap: 4px; color: #FBBF24; font-weight: 800; font-variant-numeric: tabular-nums; }
.qa-modal-energy .ic { width: 15px; height: 15px; color: #FBBF24; }
.qa-modal-x { background: none; border: none; color: #94A3B8; font-size: 1.5rem; line-height: 1; cursor: pointer; padding: 0 4px; }
.qa-modal-x:hover { color: #fff; }
.qa-modal-body { padding: 18px 18px 20px; overflow-y: auto; }
.qa-loading { text-align: center; color: #94A3B8; padding: 30px 0; }
.qa-empty-title { font-family: 'Unbounded', sans-serif; font-weight: 700; font-size: 1.1rem; color: #EAF0F8; text-align: center; margin-bottom: 8px; }
.qa-empty-msg { color: #A8B4C6; text-align: center; line-height: 1.5; margin-bottom: 18px; }
.qa-modal-actions { display: flex; justify-content: center; gap: 10px; flex-wrap: wrap; }
.qa-modal-btn { min-width: 130px; text-align: center; text-decoration: none; }
.qa-deck-list { display: flex; flex-direction: column; gap: 8px; }
.qa-deck {
display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
background: rgba(30,33,58,0.6); border: 1px solid rgba(148,163,184,0.18); border-radius: 12px;
padding: 11px 13px; cursor: pointer; font: inherit; transition: .15s;
}
.qa-deck:hover { border-color: var(--dk, #9B5DE5); background: rgba(40,44,78,0.7); }
.qa-deck-dot { width: 12px; height: 12px; border-radius: 50%; background: var(--dk, #9B5DE5); flex-shrink: 0; }
.qa-deck-title { color: #E2E8F0; font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qa-deck-due { color: #67E8F9; font-size: .76rem; font-weight: 700; flex-shrink: 0; }
/* сессия повторения */
.qa-prog { height: 6px; border-radius: 99px; background: rgba(148,163,184,0.2); overflow: hidden; margin-bottom: 6px; }
.qa-prog-fill { height: 100%; background: linear-gradient(90deg, #22D3EE, #A78BFA); border-radius: 99px; transition: width .3s; }
.qa-prog-count { text-align: right; font-size: .72rem; color: #8B9AAE; margin-bottom: 12px; font-variant-numeric: tabular-nums; }
.qa-card {
min-height: 130px; background: rgba(20,22,44,0.72); border: 1px solid rgba(148,163,184,0.18);
border-radius: 14px; padding: 20px; margin-bottom: 14px; display: flex; flex-direction: column; gap: 12px;
}
.qa-card-side { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.qa-card-back { border-top: 1px dashed rgba(148,163,184,0.3); padding-top: 12px; }
.qa-card-text { color: #E8EDF5; font-size: 1.02rem; line-height: 1.45; text-align: center; word-break: break-word; }
.qa-card-empty { color: #64748B; }
.qa-card-img { max-width: 100%; max-height: 160px; border-radius: 10px; }
.qa-flip { width: 100%; }
.qa-grades { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.qa-grade { font: inherit; font-size: .82rem; font-weight: 700; color: #fff; border: none; border-radius: 10px; padding: 10px 4px; cursor: pointer; transition: filter .14s; }
.qa-grade:hover { filter: brightness(1.12); }
.qa-g-again { background: #DC2626; }
.qa-g-hard { background: #D97706; }
.qa-g-good { background: #2563EB; }
.qa-g-easy { background: #16A34A; }
</style>
</head>
<body>
@@ -271,10 +380,11 @@
<!-- KaTeX для подписей сцены -->
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"></script>
<!-- уровни (данные) + логика прогресса + карта + игра -->
<!-- уровни (данные) + логика прогресса + карта + способности + игра -->
<script src="/js/game/levels.js"></script>
<script src="/js/game/progress-logic.js"></script>
<script src="/js/game/map.js"></script>
<script src="/js/game/quantik-abilities.js"></script>
<script src="/js/game/quantik-game.js"></script>
<script>
(function () {