'use strict'; /* ════════════════════════════════════════════════════════════════════════ Квантик — Законы Мира · логика игровой страницы (Фаза 1, MVP). Монтирует уровень-спеку через SimEngine.mount (тот же движок, что lab.html и sim-builder.html). «Игровой режим» включается САМ наличием блока goal в спеке (Фаза 0: HUD с целью/звёздами появляется автоматически). Управление — собственные слайдеры params движка + кнопки Запуск/Сброс. На победу (inst.onGoal) шлём результат на сервер и показываем экран успеха. window.QuantikGame.start({ host, level }) -> инстанс движка (или null). ⛔ Без eval/Function. Уровни — данные из window.QuantikLevels. ════════════════════════════════════════════════════════════════════════ */ (function (global) { var doc = global.document; function el(tag, cls, html) { var n = doc.createElement(tag); if (cls) n.className = cls; if (html != null) n.innerHTML = html; return n; } /* Inline SVG звезды (заполненная / контур) — без эмодзи (правило проекта). */ function starSvg(filled) { var fill = filled ? '#FBBF24' : 'none'; var stroke = filled ? '#FBBF24' : '#64748B'; return '' + ''; } function fmtTime(ms) { if (!ms && ms !== 0) return '—'; var s = ms / 1000; return s.toFixed(2) + ' с'; } /* ── Экран успеха (DOM-оверлей страницы, поверх сцены) ─────────────────── */ function buildSuccessOverlay(state) { var got = (state && state.stars && state.stars.got) || 0; var total = (state && state.stars && state.stars.total) || 0; var overlay = el('div', 'qg-overlay'); var card = el('div', 'qg-card'); card.appendChild(el('div', 'qg-card-title', 'Уровень пройден!')); // звёзды: total «слотов», got заполнено var starsBox = el('div', 'qg-stars'); var slots = Math.max(total, got, 1); for (var i = 0; i < slots; i++) { var w = el('span', 'qg-star'); w.innerHTML = starSvg(i < got); starsBox.appendChild(w); } card.appendChild(starsBox); var stats = el('div', 'qg-stats'); stats.appendChild(el('div', 'qg-stat', 'Время' + fmtTime(state && state.timeMs) + '')); stats.appendChild(el('div', 'qg-stat', 'Звёзды' + got + ' / ' + (total || slots) + '')); stats.appendChild(el('div', 'qg-stat', 'Попытки' + ((state && state.attempts) || 0) + '')); card.appendChild(stats); var actions = el('div', 'qg-actions'); var btnAgain = el('button', 'btn-primary qg-btn', 'Ещё раз'); btnAgain.type = 'button'; var btnNext = el('button', 'btn-ghost qg-btn', 'Дальше'); btnNext.type = 'button'; btnNext.disabled = true; // MVP: следующий уровень появится в Фазе 2 btnNext.title = 'Скоро: больше уровней'; actions.appendChild(btnAgain); actions.appendChild(btnNext); card.appendChild(actions); overlay.appendChild(card); return { overlay: overlay, btnAgain: btnAgain, btnNext: btnNext }; } /* ── Старт уровня ─────────────────────────────────────────────────────── host — DOM-контейнер сцены. level — запись из QuantikLevels (с .spec/.id). */ function start(opts) { opts = opts || {}; var host = opts.host; var level = opts.level; if (!host || !level || !level.spec) return null; if (!global.SimEngine || !global.SimExpr) return null; var inst = global.SimEngine.mount(host, level.spec); var overlayRef = null; function clearOverlay() { if (overlayRef && overlayRef.overlay && overlayRef.overlay.parentNode) { overlayRef.overlay.parentNode.removeChild(overlayRef.overlay); } overlayRef = null; } function showSuccess(state) { clearOverlay(); overlayRef = buildSuccessOverlay(state); overlayRef.btnAgain.addEventListener('click', function () { clearOverlay(); try { inst.reset(); } catch (_e) {} }); // Дальше — заглушка для MVP (нет следующего уровня). host.appendChild(overlayRef.overlay); } inst.onGoal(function (res) { if (!res || !res.won) return; var got = (res.stars && res.stars.got) || 0; // Время победы — мировое t из движка (Ф0): res.timeMs. var payload = { time_ms: res.timeMs, stars: got }; // Submit best-effort: экран успеха показываем независимо от сети. try { if (global.LS && global.LS.gameProgressSubmit) { global.LS.gameProgressSubmit(level.id, payload).catch(function () { /* офлайн — ок */ }); } } catch (_e) { /* нет клиента — всё равно показываем успех */ } showSuccess(res); }); return inst; } global.QuantikGame = { start: start, buildSuccessOverlay: buildSuccessOverlay }; })(typeof window !== 'undefined' ? window : this);