menu_settings_plan.md: - §10 переписан: выбран Вариант A — текстовые строки + собственный растровый рендерер в новом банке; референс SDLPoP (hc_small_font / hc_font — один рендерер, два шрифта); шрифт как ассет из паковщика, прототип MS4 — системный CP866 ZG; двуязычность eng/rus через пару (таблица строк CP866, файл шрифта) - Restart Level / Restart Game выполняются сразу, без подтверждения (§3, §8, из §11 убраны диалоги RESTART *?) - §13 MS4: текстовый рендерер + два шрифта; §14: host-тест рендерера quicksave_plan.md: статус «РЕАЛИЗОВАНО и проверено в MAME» (v0.6-pop-quicksave), документ оставлен справочником по формату 'POPQ' TASKS_OPEN/TASKS_CLOSED: запись QSAVE переехала в закрытые с полным протоколом; docs/README.md аннотации обновлены
24 KiB
Pause menu и Settings для Sprinter PoP
Статус: согласованный план, код не начат (2026-08-21; обновлён 2026-08-22 — решение о рендеринге UI см. §10, Restart Level/Game без подтверждения).
Связанные документы:
full_game_plan.md— автомат состояний, title, demo, cutscenes и ending;quicksave_plan.md— состав и восстановление снимка.
1. Решения
- Программа работает только с HDD; настройки, QuickSave и Hall of Fame всегда могут быть постоянными файлами.
- Основной pause menu обязательно содержит QuickSave и QuickLoad.
- QuickSave имеет один слот
POP.SAV; предыдущая корректная запись хранится какPOP.BAK. - Первая версия имеет один профиль
VANILLA. Под этим именем пока понимается текущее поведение roomtest, включая уже встроенные исправления. - Дизайн файла и API предусматривает будущий
ENHANCED, но аудит и переключение fixes сейчас не выполняются. - Уровень 15/copy protection отсутствует.
- Моды, levelsets и меню Mods отложены.
2. Что есть в SDLPoP
src/menu.c содержит:
- Resume, QuickSave, QuickLoad, Restart Level, Settings, Restart Game, Quit;
- General, Gameplay, Visuals, Mods, Controls;
- toggle/number/key controls, пояснения, scroll и confirmation dialogs;
- большой список fixes/enhancements и custom level options.
На Sprinter не переносятся SDL-специфичные параметры: fullscreen, hardware acceleration, scaling, aspect ratio, rumble. UI берёт структуру SDLPoP, но набор настроек соответствует платформе.
3. Pause menu первой версии
RESUME
QUICKSAVE
QUICKLOAD
RESTART LEVEL
SETTINGS
RESTART GAME
QUIT
Поведение:
EscвPLAYINGоткрывает меню; повторный Esc или Resume возвращает игру;- игра, логический таймер и звуковой насос ставятся на паузу согласованно;
- QuickSave/QuickLoad только взводят запрос, фактическая операция идёт на безопасной границе кадра;
- QuickLoad disabled/показывает
NO QUICKLOAD, если нет валидных SAV/BAK; - Restart Level и Restart Game выполняются сразу, БЕЗ подтверждения (2026-08-22): обе операции дёшево обратимы — Restart Level перечитывает уровень, после Restart Game можно тут же сделать QuickLoad из POP.SAV;
- Quit требует подтверждения и закрывает файлы/каналы штатным путём;
- меню недоступно в demo, cutscene, time-expired и ending;
- отдельная debug-комбинация немедленного выхода может остаться только в отладочной сборке.
4. Settings первой версии
GENERAL
Sound ON / OFF
Show Sprinter screen ON / OFF
Restore defaults...
GAMEPLAY
Speed NORMAL / FAST / FASTEST
Gameplay profile VANILLA
Cheats ON / OFF
CONTROLS
Show key bindings
BACK
Gameplay profile: VANILLA показывается read-only: место в модели уже есть,
но пользователь не может выбрать ещё не реализованный ENHANCED.
Отладочные параметры ROOMNAV, border profiling, stop-frame и переключение
double buffering не являются пользовательскими Settings. Они остаются
compile-time/debug функциями и скрываются из release UI.
5. Модель настроек
Игровой код не должен читать UI-структуры. Единственный runtime-контракт:
typedef enum {
POP_PROFILE_VANILLA = 0,
POP_PROFILE_ENHANCED = 1
} pop_gameplay_profile_t;
typedef struct {
uint8_t sound_enabled;
uint8_t speed_mode;
uint8_t gameplay_profile;
uint8_t cheats_enabled;
uint8_t show_build_info;
uint16_t enhancement_flags;
} pop_settings_t;
В первой версии загрузчик принимает только POP_PROFILE_VANILLA. Значение
ENHANCED из более нового/ручного файла заменяется на VANILLA с диагностикой,
а не включает частично реализованный режим.
Будущий профиль задаёт маску возможностей централизованно:
VANILLA -> текущий согласованный набор
ENHANCED -> будущий рекомендуемый набор fixes
CUSTOM -> только если позже действительно понадобится
До отдельного аудита существующие fix_exit_door, feather guard behavior,
jump grab и sound priorities не переключаются и считаются частью текущего
VANILLA.
6. Файл POP.CFG
Бинарный, компактный, версионированный формат:
+0 "PCFG" magic, 4 Б
+4 format_version 1 Б
+5 payload_size 2 Б
+7 payload фиксированные поля little-endian
.. checksum 2 Б
Требования:
- путь рядом с exe/в выделенном каталоге игры на HDD;
- неизвестная версия, неверная длина или checksum -> defaults;
- неизвестные будущие хвостовые поля можно пропустить по
payload_size; - запись только после Apply/выхода из Settings, не на каждый шаг курсора;
- ошибка записи не завершает игру: показать сообщение и оставить runtime значения;
- Restore defaults меняет RAM только после подтверждения и затем сохраняет.
CFG не содержит состояние уровня, QuickSave или Hall of Fame.
7. QuickSave / QuickLoad в меню
Детальный состав снимка и порядок восстановления — в
quicksave_plan.md. Здесь фиксируется UI и файловая
транзакция.
Один слот и backup
Файлы:
POP.SAV текущий слот
POP.BAK предыдущий валидный слот
POP.NEW временный файл во время записи
Безопасная запись:
- записать полный снимок в
POP.NEW; - закрыть файл;
- повторно открыть/прочитать заголовок и checksum;
- старый валидный
POP.SAVперенести/скопировать вPOP.BAK; POP.NEWсделать новымPOP.SAV;- при любой ошибке сохранить прежний
POP.SAV.
Точную последовательность rename/copy выбрать после характеризации DSS. Если атомарный rename не гарантирован, использовать copy + fsync/close и никогда не удалять единственную валидную копию до проверки новой.
Загрузка
- проверить
POP.SAV; - если он отсутствует/повреждён/несовместим — проверить
POP.BAK; - при валидном BAK показать
LOAD BACKUP?; - несовместимая версия —
INCOMPATIBLE SAVE, без частичной загрузки; - после успеха закрыть menu, перерисовать обе страницы, перезапустить звук.
Сообщения
Минимальный набор:
QUICKSAVED
QUICKLOADED
NO QUICKLOAD
SAVE ERROR
INCOMPATIBLE SAVE
LOAD BACKUP?
Сообщение показывается UI-слоем, но операция завершается до возврата в игровой кадр.
8. Restart Level / Restart Game
Restart Level:
- использует существующий штатный reset текущего уровня;
- не перечитывает CFG;
- не меняет
POP.SAV; - сбрасывает состояние, которое сбрасывает текущая реализация roomtest.
Restart Game:
- выполняется сразу, без подтверждения;
- завершить текущий gameplay session;
- освободить level/atlas/temporary EMM;
- создать новую игру с уровня 1 и новым глобальным таймером;
- настройки оставить;
- QuickSave не удалять.
9. Controls
Первая версия только показывает активную раскладку. Переназначение клавиш откладывается: raw PS/2 канал имеет особенности Shift и расширенных кодов, поэтому generic key-binding UI требует отдельного проекта.
Экран должен перечислить минимум:
- движение и Shift/action;
- Esc/menu;
- F6/F9 QuickSave/QuickLoad;
- Ctrl+S sound;
- P speed;
- доступные cheats, только если они включены.
10. UI renderer и ввод
10.1. Выбор способа отрисовки: текст против спрайт-атласов
Ограничение платформы: стандартный текстовый вывод libbgi (outtextxy)
не годится — он тянет системный знакогенератор в _gfx_font_buf (2 КБ
статики в W2) плюс жирный резидентный код, а W1/W2 забиты игрой
(тот же вывод зафиксирован комментарием в roomtest_cold.c, где отладочный
борд рисуется палочками именно поэтому). Значит, любой вариант требует
СВОЕЙ реализации вывода меню, живущей в отдельном банке (память на банк
есть; скорость не критична — меню работает на паузе).
Рассматривались два подхода.
Вариант A — текстовые строки + собственный растровый рендерер.
Плюсы:
- минимальные данные: шрифт 2–4 КБ + таблицы строк по сотни байт на язык;
- весь динамический текст бесплатно: значения опций (ON/OFF,
NORMAL/FAST/FASTEST), сообщения (
QUICKSAVED,INCOMPATIBLE SAVE), диалоги (LOAD BACKUP?), экран Controls, будущий ввод инициалов Hall of Fame — без текстового движка HoF вообще не сделать; - правка формулировки = правка C-строки, мгновенные итерации;
- локализация = вторая таблица строк (+ вторая половина глифов);
- решающий аргумент: так сделано в самом SDLPoP — см. §10.2.
Минусы:
- надо написать рендерер (блиттер глифа + строка + центрирование + подсветка) — небольшой, но свой;
- вид определяется качеством шрифта-ассета.
Вариант B — готовые спрайт-атласы (атлас главного меню с активными/ неактивными пунктами, атлас вложенного меню, атлас каждой опции On/Off и т.д.).
Плюсы:
- аутентичный вид: любая типографика/декор запекаются при упаковке;
- вывод = существующий блит атласов, текстовый движок не нужен;
- язык = другой файл атласа с диска, ноль логики.
Минусы:
- комбинаторика ассетов: 7 пунктов × состояния + вложенные меню + значения всех опций + все сообщения + все диалоги ≈ десятки КБ raw на язык до RLE; второй язык удваивает;
- любая правка текста = перегенерация ассетов + перекладка ресурсов;
- динамический текст (HoF initials) всё равно потребует шрифтового движка — получили бы ОБЕ системы сразу.
Решение (2026-08-22): Вариант A, шрифт — ассет. Спрайты остаются только
для нетекстового декора (рамка/фон меню, маркер выделения — как arrowheads
в SDLPoP). Титульный экран — полноэкранная картинка, тема full_game_plan.md.
10.2. Референс: как устроено меню в SDLPoP
SDLPoP/src/menu.c + текстовый движок seg009 — источник структуры:
- Текстовые строки + встроенный пропорциональный bitmap-шрифт
hc_small_font_data[](menu.c:2488): символы 32..126, каждый глиф — монохромное изображение переменной ширины;font_type{first_char, last_char, space_between_chars, height_above_baseline, chtab}. Никаких per-item атласов, хотя SDL_ttf доступен. - Вывод — портированный движок оригинального DOS PoP (seg009):
draw_text_character→method_3_blit_mono(image, x, y, textblit, textcolor);get_line_widthдля центрирования; перенос по словам. Тем же движком рисуются in-game тексты и copy protection. - Пункты меню — data-driven C-структуры
{id, previous, next, required, char text[32]}+ таблицыpause_menu_items[]/settings_menu_items[];required— указатель на флаг disabled, такие пункты пропускаются при навигации (prev/next пересчитываются). - Выделенный пункт = смена цвета текста (bright-white против обычного) +
рамка-контур
draw_rect_contours(selection_box, lightgray); НЕ отдельный спрайт «активного пункта». - Фон меню — затемнение замороженного игрового кадра:
draw_rect_with_alpha(black, alpha=120), внизу просвечивает «GAME PAUSED». - Settings — декларативная таблица
setting_typeсо стилями TOGGLE / NUMBER / TEXT_ONLY / KEY, геттером/сеттером/increase/decrease значения, строкой- explanation внизу экрана, скроллом длинных списков и фокусом «левая половина (список) / правая половина (значения)». - Диалоги — один общий
draw_confirmation_dialog(text)+ обработчик результата; диалог возвращает решение автомату меню. - Мини-спрайты только для декора значений (arrowheads up/down/left/right).
- Навигация озвучена (menu tick), ввод клавиатура+мышь, hover по прямоугольникам.
10.3. Наша реализация
- Новый банк (свободный номер, автонумерация sprinter-cc): код рендерера, шрифт, таблицы строк, автомат меню. Резидентно — только request-flag и вызов процесса на границе кадра (паттерн pop_qsave_io).
- Рендерер портирует контракт seg009, упрощённо:
font_type+ массив глифов переменной ширины, блит монохромного глифа в теневую страницу через W0-окно (как весь остальной код рисования),draw_string(x,y,color)- центрирование по сумме ширин. Пропорциональность — сразу, API не меняется от моноширинного.
- Шрифт — АССЕТ, генерируемый паковщиком toolchain из TTF (красивый, сразу
с кириллицей), а НЕ системный ZG. Быстрый прототип для MS4 — системный
CP866 знакогенератор через существующий
bios_get_zgв буфер банка (паттернgfx_load_default_font, но буфер в банке, не_gfx_font_buf); потом файл шрифта заменяется без смены API. - Двуязычность (eng/rus): строки храним в CP866 — латиница и кириллица одним байтовым порядком, одна кодировка на оба алфавита. Локаль = пара (указатель на таблицу строк, файл шрифта); переключатель — одна настройка. Русские строки длиннее английских ~10–15% — раскладку экранов и ширину колонок закладывать по русской. Второй язык можно добавить позже без переделки: сначала eng.
- Подсветка выделенного пункта: инверсия прямоугольника или контур + цвет, как в SDLPoP. Цвета текста брать из стабильного диапазона палитры 0x3A..0x3F (его никто не перезаписывает, проверено на борде-индикаторе).
- Фон открытого меню: затемнение замороженного кадра (запечь тёмный прямоуг. в теневую копию страницы поверх сохранённого фона) — двойную перерисовку игры под меню делать не нужно, игра стоит.
- Навигация: вверх/вниз по пунктам, left/right для значения, Enter/Esc;
edge-triggered поверх существующего
kbd_raw. Звук навигации — menu tick из имеющихся сэмплов.
Первый UI может быть визуально простым. Критично отсутствие потери клавиш, предсказуемая пауза и отсутствие повреждения игрового back buffer.
11. Диалоги
Общий диалог подтверждения:
QUIT GAME?
RESTORE DEFAULTS?
LOAD BACKUP?
YES / NO
Диалог не выполняет действие напрямую: он возвращает решение автомату меню, который формирует команду приложению. Так UI не зависит от gameplay-модулей.
12. Будущий ENHANCED
Не реализуется сейчас, но дизайн обязан позволять:
- добавить второй профиль без смены всего UI;
- хранить
enhancement_flagsв CFG; - отличать технические исправления порта (всегда включены) от изменений оригинальной механики;
- провести аудит уже встроенных исправлений;
- покрыть каждый переключаемый fix host/MAME тестом;
- при необходимости добавить Advanced screen, не раздувая основной menu.
До этого момента нельзя рассыпать проверки if (enhanced) по горячему коду.
Сначала составляется реестр и выбирается минимальная битовая модель.
13. Этапы реализации
| этап | результат | критерий приёмки |
|---|---|---|
| MS0 | определить команды app/menu и структуру settings | UI не вызывает gameplay internals напрямую |
| MS1 | проверить запись/rename/copy на HDD DSS | crash/power-loss сценарий не теряет обе копии save |
| MS2 | POP.CFG: defaults, load, validate, save |
повреждённый CFG безопасно даёт defaults |
| MS3 | QuickSave hotkeys + POP.SAV/BAK | полный критерий quicksave_plan.md |
| MS4 | текстовый рендерер + два шрифта (малый для пунктов, крупный для сообщений) + минимальный pause menu | рендерер строк (глифы, локали, центрирование); все семь пунктов доступны и корректно паузят игру |
| MS5 | General/Gameplay Settings | значения применяются и переживают рестарт |
| MS6 | dialogs + backup recovery | подтверждения и fallback на POP.BAK |
| MS7 | Controls help | полная актуальная раскладка на экране |
| MS8 | интеграция с title/build info | CFG применяется до первого экрана |
QuickSave (MS1/MS3) можно реализовать раньше визуального menu: сначала
F6/F9 и сообщения, затем подключить те же команды к пунктам UI.
14. Тесты
- Host: CFG round-trip, defaults, bad magic/version/size/checksum.
- Host: меню navigation, disabled items, confirmations, команды приложению.
- Host: рендерер строк — вывод глифов обеих локалей, центрирование, ширина строки для малого и крупного шрифта.
- Host: SAV invalid -> BAK valid; оба invalid -> NO QUICKLOAD.
- MAME: F6, изменение сцены, F9; затем рестарт программы и повторный F9.
- MAME: прервать запись/испортить SAV — BAK остаётся загружаемым.
- MAME: pause на бое/падении, Resume не меняет состояние и таймер.
- MAME: Settings сохраняются после полного выхода и запуска с HDD.
- Проверка лимита 8 DSS handles на каждом error path.
make size-check; menu/text строки не должны съесть резидентный бюджет.
15. Не входит в план
- Mods и выбор levelset;
- уровень 15/copy protection;
- несколько save slots;
- replay/recording;
- key rebinding;
- SDL visual/controller options;
- фактическая реализация ENHANCED и individual fix switches.