Меню, статус-строка и оболочка игры: title/intro/cutscene/HoF, POP.CFG, палитры
Эта сессия (меню + текст в служебных полосах):
* Меню: рамка выделения считается от силуэта текста (SEL_PAD сверху и
снизу), а не «на глаз»; все экраны центрируются в игровых 200 строках
(UI_CENTER_TOP/UI_CENTER_FIELD) и в них помещаются; Enter и Space —
равноправные клавиши действия (ui_action_down).
* Settings: убраны SHOW SPRINTER SCREEN и BACK, добавлены DEBUG BAR и
ABOUT. About показывает тот же текст, что стартовый Sprinter screen,
минус строка про клавиши — общий about_text(), чтобы экраны не
разъехались. CONTROLS собирается таблицей и центрируется по
фактическому числу строк.
* GAME PAUSED переехала в нижнюю статус-строку, как в оригинале
(SDLPoP rect_bottom_text = {193,70,202,250}): это состояние программы,
а не пункт меню. POP_HP_Y вынесен в pop_cdraw.h — полосу делят два
модуля.
* pop_status.c (банк 9) — текст в обеих служебных полосах. Нижняя:
порт display_text_bottom + таймера (QUICKSAVE/QUICKLOAD/SOUND ON/OFF,
24 тика). Верхняя отладочная переведена с палочек на текст
«Level ##, Room ##, Speed: …, Sound: …, Immortal #» малым шрифтом, с
своим форматированием чисел (без printf и без деления).
Заявка сообщения — запись одного байта pop_status_msg: резидент W1/W2
не растёт, весь рендер в банке. Бюджет после правок не изменился
(_CODE 23981, куча 267 Б).
* Цена вывода: блит глифа ~4,6 тыс. тактов независимо от размера, поэтому
всё change-driven, отладочная строка перерисовывается ПО ПОЛЯМ, пробелы
не блитятся вовсе, а вход в комнату заливает только игровое поле
(pop_screen_fill_field) — борта от комнаты к комнате не меняются.
* Интро: в PV-сцене зазвучали пропавшие эффекты оригинала — закрытие
ворот (4) и открытие двери покоев (51), из которой входит Джафар.
* docs/status_line_text.md — полная инвентаризация ВСЕХ текстов SDLPoP в
статус-строке: геометрия, семантика text_time_total как идентификатора
сообщения, мигание, рестарт по истечении 36/288.
Вместе с этим выкладывается накопленная работа по оболочке полной игры:
автомат состояний (pop_app), title, intro/PV и cutscene, attract-demo,
Hall of Fame, глобальный таймер, настройки и POP.CFG, модуль палитр и
fade, звуковой набор, host-тесты на новые швы.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# Pause menu и Settings для Sprinter PoP
|
||||
|
||||
Статус: **согласованный план, код не начат** (2026-08-21; обновлён
|
||||
2026-08-22 — решение о рендеринге UI см. §10, Restart Level/Game без
|
||||
подтверждения).
|
||||
Статус: **MS0, MS2 и MS4–MS8 выполнены** (2026-08-23). Pause menu, CFG,
|
||||
Settings, диалоги, Controls и build screen находятся в bank 9. Решение о
|
||||
рендеринге и затемнении — §10.
|
||||
|
||||
Связанные документы:
|
||||
|
||||
@@ -41,12 +41,12 @@ acceleration, scaling, aspect ratio, rumble. UI берёт структуру SD
|
||||
|
||||
```text
|
||||
RESUME
|
||||
QUICKSAVE
|
||||
QUICKLOAD
|
||||
QUICKSAVE (F6)
|
||||
QUICKLOAD (F9)
|
||||
RESTART LEVEL
|
||||
SETTINGS
|
||||
RESTART GAME
|
||||
QUIT
|
||||
QUIT GAME
|
||||
```
|
||||
|
||||
Поведение:
|
||||
@@ -56,9 +56,13 @@ QUIT
|
||||
- QuickSave/QuickLoad только взводят запрос, фактическая операция идёт на
|
||||
безопасной границе кадра;
|
||||
- QuickLoad disabled/показывает `NO QUICKLOAD`, если нет валидных SAV/BAK;
|
||||
- перед QuickLoad из меню лёгкий probe проверяет заголовок и checksum обоих
|
||||
файлов: валидный `POP.BAK` при отсутствующем/битом `POP.SAV` требует
|
||||
отдельного `LOAD BACKUP?`, а не загружается молча;
|
||||
- Restart Level и Restart Game выполняются сразу, БЕЗ подтверждения
|
||||
(2026-08-22): обе операции дёшево обратимы — Restart Level перечитывает
|
||||
уровень, после Restart Game можно тут же сделать QuickLoad из POP.SAV;
|
||||
(2026-08-22): Restart Level перечитывает уровень, Restart Game завершает
|
||||
gameplay и возвращает к первому экрану title/intro; новая игра создаётся
|
||||
общим LEVEL_LOAD только после skip/attract;
|
||||
- Quit требует подтверждения и закрывает файлы/каналы штатным путём;
|
||||
- меню недоступно в demo, cutscene, time-expired и ending;
|
||||
- отдельная debug-комбинация немедленного выхода может остаться только в
|
||||
@@ -86,6 +90,10 @@ BACK
|
||||
`Gameplay profile: VANILLA` показывается read-only: место в модели уже есть,
|
||||
но пользователь не может выбрать ещё не реализованный ENHANCED.
|
||||
|
||||
Изменения применяются немедленно к скорости, читам и звуку, но `POP.CFG`
|
||||
записывается один раз при Back/Esc. На экране есть итог `SETTINGS SAVED` или
|
||||
`SAVE ERROR`; во втором случае runtime-значения остаются рабочими.
|
||||
|
||||
Отладочные параметры `ROOMNAV`, border profiling, stop-frame и переключение
|
||||
double buffering не являются пользовательскими Settings. Они остаются
|
||||
compile-time/debug функциями и скрываются из release UI.
|
||||
@@ -215,9 +223,10 @@ Restart Level:
|
||||
Restart Game:
|
||||
|
||||
- выполняется сразу, без подтверждения;
|
||||
- завершить текущий gameplay session;
|
||||
- освободить level/atlas/temporary EMM;
|
||||
- создать новую игру с уровня 1 и новым глобальным таймером;
|
||||
- завершить текущий gameplay session и вернуть автомат в TITLE;
|
||||
- начать title/intro с самого первого экрана;
|
||||
- создать новую игру с `FIRST_LEVEL` и новым глобальным таймером только
|
||||
после пользовательского skip либо ввода в attract-demo;
|
||||
- настройки оставить;
|
||||
- QuickSave не удалять.
|
||||
|
||||
@@ -234,7 +243,9 @@ Restart Game:
|
||||
- F6/F9 QuickSave/QuickLoad;
|
||||
- Ctrl+S sound;
|
||||
- P speed;
|
||||
- доступные cheats, только если они включены.
|
||||
- доступные cheats, только если они включены: K/Kill Guard, I/Immortal,
|
||||
Shift+L/Next Level, U/Flip Screen и F7/F8/Time −/+ на отдельных понятных
|
||||
строках. Нижней подсказки `Esc or Enter: Back` нет.
|
||||
|
||||
## 10. UI renderer и ввод
|
||||
|
||||
@@ -321,38 +332,123 @@ On/Off и т.д.).
|
||||
|
||||
### 10.3. Наша реализация
|
||||
|
||||
- Новый банк (свободный номер, автонумерация sprinter-cc): код рендерера,
|
||||
- Банк 9: код рендерера,
|
||||
шрифт, таблицы строк, автомат меню. Резидентно — только 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.
|
||||
- Рендерер повторяет минимальный контракт seg009: пропорциональные глифы,
|
||||
baseline, `draw_string` и центрирование по сумме advance. Блит идёт через
|
||||
W0-атлас, в `GFX_BANK_SPRITE`: `0xFF` в атласе пропускается, а UI временный
|
||||
и не портит теневую копию игрового фона. Перед каждым кадром UI `gfx_copy_page` переносит чистый
|
||||
shadow видимой страницы в скрытую, затем готовый кадр показывается только
|
||||
на следующем фронте. При выходе чистый фон тем же способом возвращается на
|
||||
обе страницы и восстанавливается исходная visible-страница. Поэтому
|
||||
перемещение выделения не показывает поэтапную перерисовку и не оставляет
|
||||
следов на back buffer.
|
||||
- Шрифт — АССЕТ из **оригинальных** `hc_small_font_data[]` и
|
||||
`hc_font_data[]` SDLPoP, не системный ZG и не TTF. Паковщик
|
||||
`toolchain/pop_extract_font.py` делает `FONT\\font.atl`: 95 ASCII-глифов
|
||||
малого и 95 крупного шрифта (7667 Б). Номер ленты вычисляется из ASCII,
|
||||
поэтому это один текстовый движок, а не атлас готовых надписей.
|
||||
- Двуязычность (eng/rus): строки храним в CP866 — латиница и кириллица одним
|
||||
байтовым порядком, одна кодировка на оба алфавита. Локаль = пара
|
||||
(указатель на таблицу строк, файл шрифта); переключатель — одна настройка.
|
||||
Русские строки длиннее английских ~10–15% — раскладку экранов и ширину
|
||||
колонок закладывать по русской. Второй язык можно добавить позже без
|
||||
переделки: сначала eng.
|
||||
- Подсветка выделенного пункта: инверсия прямоугольника или контур + цвет,
|
||||
как в SDLPoP. Цвета текста брать из стабильного диапазона палитры
|
||||
0x3A..0x3F (его никто не перезаписывает, проверено на борде-индикаторе).
|
||||
- Фон открытого меню: затемнение замороженного кадра (запечь тёмный прямоуг.
|
||||
в теневую копию страницы поверх сохранённого фона) — двойную перерисовку
|
||||
игры под меню делать не нужно, игра стоит.
|
||||
- Навигация: вверх/вниз по пунктам, left/right для значения, Enter/Esc;
|
||||
edge-triggered поверх существующего `kbd_raw`. Звук навигации — menu tick
|
||||
из имеющихся сэмплов.
|
||||
- Визуальная композиция MS4 следует SDLPoP: замороженная сцена остаётся
|
||||
открытой, поверх неё компактный центрированный список без чёрной карточки,
|
||||
выбранная строка обведена тонким светло-серым контуром, а крупное
|
||||
`GAME PAUSED` лежит в нижнем борту. Цвета текста и контура берутся из
|
||||
стабильного диапазона палитры 0x37..0x3F.
|
||||
- Фон открытого меню: снимок текущей палитры, затемнение всех слотов кроме
|
||||
UI 0x37..0x3F и точное восстановление при выходе. Снимок хранится в
|
||||
свободном хвосте EMM-страницы шрифта, не в W2.
|
||||
- Навигация MS4: вверх/вниз, Enter/Esc, edge-triggered поверх `kbd_raw`.
|
||||
Left/right и menu tick добавляются вместе с настройками на MS5.
|
||||
|
||||
Первый UI может быть визуально простым. Критично отсутствие потери клавиш,
|
||||
предсказуемая пауза и отсутствие повреждения игрового back buffer.
|
||||
|
||||
### 10.4. Затенение экрана под меню — решение MS4
|
||||
|
||||
Режим меню виден сразу: bank 9 делает динамический снимок palette 0,
|
||||
затемняет RGB-каналы вдвое и пишет одинаковый результат в обе экранные
|
||||
палитры. Девять стабильных UI-слотов 0x37..0x3F не гасятся. При Resume/Enter
|
||||
палитра восстанавливается из EMM-снимка. Это выбранный вариант Б ниже;
|
||||
ступенчатый fade для роликов пока не нужен и остаётся отдельной будущей
|
||||
задачей, а не причиной раздувать MS4.
|
||||
|
||||
**Как сделано в SDLPoP** (`seg009.c`):
|
||||
|
||||
- Меню: `draw_rect_with_alpha(&screen_rect, color_0_black, pause_menu_alpha)`
|
||||
(menu.c:1364) — альфа-заливка чёрным поверх замороженного кадра средствами
|
||||
SDL; нижняя полоса рисуется с alpha=0, чтобы сквозь неё просвечивало
|
||||
«GAME PAUSED». Прямого аналога на Sprinter НЕТ (альфа-блендинг в железе
|
||||
отсутствует) — это SDL-специфика, переносить нечего.
|
||||
- Ролики/переходы: `fade_in_2/fade_out_2(rows)` (seg009.c:3947+, вызовы из
|
||||
seg000.c) — ПОШАГОВОЕ затухание ПАЛИТРЫ к чёрному и обратно: палитра
|
||||
копируется, каждая строка по 16 цветов гасится за несколько кадров
|
||||
(`which_rows` маской выбирает, какие строки участвуют: 0x800/0x1000/...).
|
||||
Вот этот механизм на Sprinter воспроизводим один в один.
|
||||
|
||||
Отсюда рабочая гипотеза: наш примитив = «снимок текущей палитры → ступенчатое
|
||||
приближение к затемнённой копии (кроме резервного блока для UI)», статично для
|
||||
меню и анимированно для роликов/переходов. Варианты:
|
||||
|
||||
**Вариант А — единая основная палитра (глобальный рефакторинг палитры).**
|
||||
|
||||
1. Собрать ВСЕ палитры игры (уровневые наборы `pal_env*`, kid.pal, палитра
|
||||
Тени и пр.) в одну общую 256-цветную; использовать её целиком всегда.
|
||||
Сейчас переиспользования цветов НЕТ — каждая загрузка ассетов перезаписывает
|
||||
слоты (см. pop_boot: kid.pal затирает тайловые цвета, приходится
|
||||
восстанавливать `pop_bg_pal_apply`/`pop_shadow_pal_apply`).
|
||||
2. Для затенения — затемнённая копия основной палитры, КРОМЕ зарезервированного
|
||||
блока из 16 цветов для самого меню (кандидат — стандартные 16 цветов VGA).
|
||||
3. Выход из меню — возврат к полной основной палитре.
|
||||
|
||||
Плюс: решает попутно существующую боль с перезаписью палитр при загрузках.
|
||||
Минус: большой разовый рефакторинг упаковщиков и всех загрузчиков атласов;
|
||||
нужен аудит, что все цвета всех уровней влезают в 256. **Против говорит
|
||||
план перевода камней подземелья на цвета VGA-версии PoP: там ряд уровней
|
||||
несёт ДРУГУЮ палитру, отличную от SDLPoP (VDUNGEON/VPALACE каскад,
|
||||
levels_plan.md), — единая палитра этому прямо противоречит.**
|
||||
|
||||
**Вариант Б — динамический снимок текущей палитры (сейчас выглядит
|
||||
предпочтительным).**
|
||||
|
||||
1. При открытии меню прочитать всю текущую палитру, сохранить.
|
||||
2. Записать затемнённую копию (кроме зарезервированного блока для меню).
|
||||
3. При выходе — восстановить сохранённую.
|
||||
|
||||
Плюс: локальная правка внутри меню, ничего в пайплайне ассетов не меняется;
|
||||
работает при любой текущей палитре автоматически — включая будущие
|
||||
уровне-специфичные палитры VGA-камней; тот же примитив ступенями даёт
|
||||
fade-out/fade-in для роликов и переходов между уровнями (как fade_*_2 в
|
||||
SDLPoP). Минус: чтение/запись 256 записей палитры при входе/выходе (раз на
|
||||
открытие — дёшево); затемнение «на глаз» может по-разному выглядеть на разных
|
||||
уровнях.
|
||||
|
||||
Резервный блок 16 цветов нужен в ОБОИХ вариантах; текущий диапазон 0x37..0x3F
|
||||
(стабильный, проверен) даёт 9 цветов — этого может не хватить на
|
||||
текст+подсветку+рамку, тогда резервировать отдельный блок.
|
||||
|
||||
**Следствие для архитектуры:** работа с цветом/палитрой должна собраться в
|
||||
ОДИН модуль (сейчас она разбросана: gfx_pal_* вызовы в boot, pop_bg_pal_apply,
|
||||
pop_shadow_pal_apply, вспышки урона в roomtest.c и т.д.). Модуль палитры —
|
||||
единственный владелец записи в палитру и предоставляет примитивы, которые
|
||||
понадобятся и меню, и роликам:
|
||||
|
||||
```text
|
||||
pal_snapshot()/pal_restore() — снимок/восстановление всей палитры
|
||||
pal_dim(step) / pal_undim(step) — ступени затемнения (кроме резервного блока)
|
||||
pal_fade_out(rows)/pal_fade_in(rows) — анимированное затухание по строкам
|
||||
(порт fade_out_2/fade_in_2, seg009)
|
||||
```
|
||||
|
||||
Меню уже использует snapshot+dim локально в bank 9. Когда появятся ролики,
|
||||
выделить из него общий palette/fade-модуль; вспышка урона сможет переехать
|
||||
туда же после отдельного аудита.
|
||||
|
||||
## 11. Диалоги
|
||||
|
||||
Общий диалог подтверждения:
|
||||
@@ -367,6 +463,10 @@ YES / NO
|
||||
|
||||
Диалог не выполняет действие напрямую: он возвращает решение автомату меню,
|
||||
который формирует команду приложению. Так UI не зависит от gameplay-модулей.
|
||||
По умолчанию выбран `NO`; Up/Down/Left/Right меняют ответ, Enter подтверждает,
|
||||
Esc отменяет. Реализованы все три вопроса: Quit, Restore defaults и backup
|
||||
QuickLoad. В Quit-dialog вопрос и `YES / [NO]` заключены в общую рамку;
|
||||
отдельная строка `Enter: Select Esc: Cancel` не выводится.
|
||||
|
||||
## 12. Будущий ENHANCED
|
||||
|
||||
@@ -387,15 +487,15 @@ YES / NO
|
||||
|
||||
| этап | результат | критерий приёмки |
|
||||
|---|---|---|
|
||||
| **MS0** | определить команды app/menu и структуру settings | UI не вызывает gameplay internals напрямую |
|
||||
| **MS0** ✓ | определить команды app/menu и структуру settings | UI возвращает команду главному циклу; прямых gameplay-вызовов нет |
|
||||
| **MS1** | проверить запись/rename/copy на HDD DSS | crash/power-loss сценарий не теряет обе копии save |
|
||||
| **MS2** | `POP.CFG`: defaults, load, validate, save | повреждённый CFG безопасно даёт defaults |
|
||||
| **MS2** ✓ | `POP.CFG`: defaults, load, validate, save | v1 codec, будущий хвост, checksum; повреждённый 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 применяется до первого экрана |
|
||||
| **MS4** ✓ | текстовый рендерер + два шрифта (малый для пунктов, крупный для сообщений) + минимальный pause menu | SDLPoP fonts в одном W0-atlas, центрирование, dim/restore palette и tear-free page flip; все семь пунктов видимы, навигация и Resume/QuickSave работают в MAME |
|
||||
| **MS5** ✓ | General/Gameplay Settings | значения применяются сразу и после Back/Esc записываются в POP.CFG |
|
||||
| **MS6** ✓ | dialogs + backup recovery | подтверждения default-NO; QuickLoad спрашивает перед валидным POP.BAK |
|
||||
| **MS7** ✓ | Controls help | показаны движение, action, menu, save/load, звук, speed и conditional cheats; MAME проверил отдельные K/I и Shift+L/U и возврат Esc ровно на один уровень |
|
||||
| **MS8** ✓ | build info | CFG читается до первого показа; включаемый build screen получает ID и дату из Make/git |
|
||||
|
||||
QuickSave (`MS1/MS3`) можно реализовать раньше визуального menu: сначала
|
||||
F6/F9 и сообщения, затем подключить те же команды к пунктам UI.
|
||||
@@ -409,8 +509,12 @@ F6/F9 и сообщения, затем подключить те же кома
|
||||
- Host: SAV invalid -> BAK valid; оба invalid -> NO QUICKLOAD.
|
||||
- MAME: F6, изменение сцены, F9; затем рестарт программы и повторный F9.
|
||||
- MAME: прервать запись/испортить SAV — BAK остаётся загружаемым.
|
||||
- MAME: pause на бое/падении, Resume не меняет состояние и таймер.
|
||||
- MAME: pause на бое/падении, Resume не меняет состояние и таймер; смена
|
||||
выбранного пункта не показывает промежуточный кадр и после закрытия не
|
||||
оставляет меню на второй странице.
|
||||
- MAME: Settings сохраняются после полного выхода и запуска с HDD.
|
||||
- MAME: включить Show Sprinter screen, перезапустить `roomtest`, увидеть
|
||||
build ID/date до первого игрового кадра и пропустить экран Esc/Enter/Space.
|
||||
- Проверка лимита 8 DSS handles на каждом error path.
|
||||
- `make size-check`; menu/text строки не должны съесть резидентный бюджет.
|
||||
|
||||
@@ -423,4 +527,3 @@ F6/F9 и сообщения, затем подключить те же кома
|
||||
- key rebinding;
|
||||
- SDL visual/controller options;
|
||||
- фактическая реализация ENHANCED и individual fix switches.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user