// =========================================================================== // Меню-лаунчер демо. // // Интерфейс — документ RmlUi (demos/ui/launcher.rml + launcher.rcss): меню и // экраны в движке делаются только так (docs/UI_RMLUI_LAW.md). Список демо // строится из DEMOS и вставляется в #demo-list разметкой RML. // // Вёрстка адаптивная: сцена всегда 16:9 (см. #stage в web/shell.html), поэтому // размеры в RCSS заданы в vh — интерфейс масштабируется пропорционально любому // размеру окна, а карточки перестраиваются flex-wrap'ом. // // Платформер из меню убран (решение проекта): сам демо-модуль остаётся в // репозитории и запускается как раньше: // ./build/russiano2d --game demos --scene platformer // =========================================================================== const DOC = 'demos/ui/launcher.rml'; // Фон меню — арт лаунчера (в .rml), музыка — отдельным файлом: движок играет её // циклом, пока открыто меню, и глушит при переходе в демо. // Формат — MP3: в сборке движка MP3 включён (dr_mp3), а OGG-энкодер, который // оказался под рукой, писал битую длительность (движок видел 9039 с вместо 188). const MENU_MUSIC = 'demos/assets/audio/music/lobby_groove.mp3'; const CLICK_SFX = 'demos/assets/audio/sfx/ui_click.ogg'; const DEMOS = [ { scene: 'shooter_witch', // В меню ведём в интро демо; сам бой остаётся сценой для тестов и // агента: --scene shooter_witch. enter: 'witch_menu', icon: 'auto_awesome', title: 'Типичная ночь в Мытищинском лесу', hint: 'Ночной лес, свет от фонарей, волны врагов', }, { scene: 'russi_vn', icon: 'favorite', title: 'Руси-тян (ВН)', hint: 'Визуальная новелла: озвучка, выбор и две концовки', }, ]; export default function installLauncher($) { // Разметку и подписки делаем один раз: документ RmlUi кэшируется по пути, // а $.ui.doc(...).on() не вешает обработчик дважды на ту же пару. let built = false; function volume() { const v = Number($.store.get('volume', 0.8)); return Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : 0.8; } function applyVolume($, value) { const v = Math.max(0, Math.min(1, value)); $.store.set('volume', v); $.sound.volume(v).mute(false); return v; } function showVolume(doc) { doc.text('vol-value', Math.round(volume() * 100) + '%'); } function build($, doc) { const icon = (name) => ($.ui.hasIcon(name) ? $.ui.icon(name) : ''); // Карточка демо: иконка, название, подпись. Разметка — RML, классы // совпадают с launcher.rcss. doc.html('demo-list', DEMOS.map((demo, i) => `