Files
Sprinter-SDCC/applications/PoP/docs/menu_settings_plan.md
T
snark13 7fd7f28ffc Доки: план меню (рендер, restart без подтверждения), QSAVE закрыт
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 аннотации обновлены
2026-08-22 12:45:01 +03:00

24 KiB
Raw Blame History

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   временный файл во время записи

Безопасная запись:

  1. записать полный снимок в POP.NEW;
  2. закрыть файл;
  3. повторно открыть/прочитать заголовок и checksum;
  4. старый валидный POP.SAV перенести/скопировать в POP.BAK;
  5. POP.NEW сделать новым POP.SAV;
  6. при любой ошибке сохранить прежний POP.SAV.

Точную последовательность rename/copy выбрать после характеризации DSS. Если атомарный rename не гарантирован, использовать copy + fsync/close и никогда не удалять единственную валидную копию до проверки новой.

Загрузка

  1. проверить POP.SAV;
  2. если он отсутствует/повреждён/несовместим — проверить POP.BAK;
  3. при валидном BAK показать LOAD BACKUP?;
  4. несовместимая версия — INCOMPATIBLE SAVE, без частичной загрузки;
  5. после успеха закрыть 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_charactermethod_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.