roomtest: - QuickSave/QuickLoad (F6/F9): снапшот 'POPQ' v3 в POP.SAV/POP.BAK на HDD, транзакционная запись (POP.NEW -> rename, откат при ошибке), XOR-контрольная сумма payload'а; сериализация всех игровых переменных через W0-примитивы pop_qs_*; pop_qsave_process() на границе кадра вне Char-окон - pop_qsave_restore_room(): полная перезагрузка комнаты после загрузки (карта/края/швы, сброс bake-кэша, перерисовка обеих страниц, инвалидация кэшей спрайтов и HP) - сериализаторы в pop_map/pop_loose_mob/pop_trob/pop_guard_ai (+ восстановление инвариантов: mobs_live, trob_drawn, redraw) - immortal-чит 2 уровня: уровень 2 поглощает только малый урон Kid'а libc/libbgi: - bank_load_file()/bank_save_file() — резидентное файловое I/O в банк, без правила W3 (путь читается до переключения страницы) - gfx_w0_page_prepare(page) — подготовка W0-окна (IRQ/NMI-стабы) одной функцией; atlas_load.c и roomtest переведены на новые примитивы; ручные ISR-стабы удалены sprinter-cc / сборка: - --bank N=FILE.c: автогенерация n_banks (_n_banks_auto.c), ручные const n_banks удалены из тестов - roomtest/app.mk: ресурсы через stamp-файлы (.resource-stamps/) — один запуск упаковщика на группу вместо N под -B; HDD_PACK_ARGS
17 KiB
От roomtest к полноценной игре — сценарий и оболочка
Статус: согласованный план, код не начат (2026-08-21).
Этот документ описывает превращение текущего игрового цикла
roomtest в законченную игру: заставка, интро, демонстрационный уровень,
сцены между уровнями, таймер, финал и Hall of Fame. План меню и постоянных
настроек вынесен в menu_settings_plan.md,
детальный план QuickSave — в quicksave_plan.md.
1. Зафиксированный scope
- Целевая последовательность — оригинальная SDLPoP/DOS PoP с уровнями 1..14. Уровень 14 — скрытая финальная часть после Джаффара: его номер игроку не показывается, победа наступает в комнате 5.
- Уровень 15 удаляется полностью: не пакуется на HDD, не загружается, отсутствует в переходах, читах и UI; специальная логика potions/copy protection level удаляется.
- Уровень 0 остаётся только демонстрационным (attract mode), а не частью новой игры.
- Title sequence повторяет SDLPoP. Перед ней допускается отдельный пропускаемый экран с информацией о Sprinter-сборке.
- Первая версия использует текущий профиль поведения
VANILLA. Сейчас это означает существующую реализацию roomtest, включая уже встроенные исправления. Аудит и разведениеVANILLA/ENHANCED— будущая задача. - Программа работает только с HDD. Варианты без сохранения для floppy не проектируются.
- Моды и выбор levelset в этот план не входят.
2. Что делает SDLPoP
Источники истины в локальном SDLPoP:
src/seg000.c:start_game(),show_title(), demo mode, общий кадр, проверка финала;src/seg003.c:init_game(),play_level(),play_level_2();src/seg001.c: cutscene engine,pv_scene(), сцены 2/4/6/8/9/12,time_expired(),end_sequence()и Hall of Fame;src/data.h:tbl_cutscenes, параметры уровня 0, win level/room;data/TITLE,data/PV,data/LEVELS/res2000.bin: ресурсы оболочки.
Штатный маршрут:
boot
-> title / story screens
-> Princess + Jaffar intro
-> credits / Hall of Fame
-> demo level 0
-> title или новая игра
-> levels 1..14
before 2 -> princess cutscene
before 4 -> princess cutscene
before 6 -> princess cutscene
before 8 -> princess + mouse
before 9 -> princess + mouse
before 12 -> scene selected by remaining time
-> level 14, room 5
-> embrace + mouse
-> ending text/music
-> Hall of Fame
-> title
Кроме уровней, здесь есть глобальный 60-минутный таймер, сцена истечения времени, пропуск сцен клавишей, fade/flash, ожидание музыки и возврат в attract loop после демо или финала.
3. Текущее состояние roomtest
Уже реализованы игровой кадр, комнаты, уровни, тайлсеты, Kid/Guard/Shadow, Джаффар, специальные события, checkpoint, переходы уровней, бесшовный выход 12-го уровня, перенос максимального HP и звуковые эффекты.
Отсутствуют:
- верхнеуровневая оболочка приложения;
- title/story sequence и текстовые экраны;
- уровень 0 и записанное управление демо;
- общий cutscene engine и PV-ресурсы;
- сцены между уровнями;
- глобальный таймер и time-expired sequence;
- корректный переход из уровня 14 в ending;
- финальная сцена и Hall of Fame;
- возврат к title без перезапуска программы.
4. Архитектура: автомат состояний приложения
Нельзя наращивать все режимы условиями внутри кадрового цикла. Текущий
цикл должен стать реализацией одного состояния PLAYING:
BOOT -> BUILD_INFO -> TITLE -> INTRO -> DEMO
| |
+---- NEW_GAME <-+
NEW_GAME -> LEVEL_LOAD -> PLAYING <-> PAUSE_MENU
|
+-> CUTSCENE -> LEVEL_LOAD
+-> TIME_EXPIRED -> TITLE
+-> ENDING -> HALL_OF_FAME -> TITLE
Минимальный контекст оболочки:
typedef enum {
POP_APP_BOOT,
POP_APP_BUILD_INFO,
POP_APP_TITLE,
POP_APP_INTRO,
POP_APP_DEMO,
POP_APP_LEVEL_LOAD,
POP_APP_PLAYING,
POP_APP_PAUSE_MENU,
POP_APP_CUTSCENE,
POP_APP_TIME_EXPIRED,
POP_APP_ENDING,
POP_APP_HALL_OF_FAME,
POP_APP_QUIT
} pop_app_state_t;
Переходы задаются результатом состояния, а не прямыми рекурсивными
вызовами наподобие SDLPoP start_game()/longjmp(). На Z80 это проще для
стека и позволяет освобождать ресурсы каждого режима в одном месте.
5. Ресурсная модель
Title и cutscene-ресурсы нельзя постоянно держать рядом с игровыми атласами. Для каждого состояния нужен явный lifecycle:
enter: pause sound -> unload incompatible set -> load set -> apply palette
run: process input/timer/animation
leave: stop sound -> release EMM pages -> clear transient state
Новые группы HDD:
TITLE\ title/story images, palette, optional build-screen assets
PV\ princess room, Princess/Jaffar/mouse frames, palettes
MUSIC\ intro, cutscene and ending tracks/samples
LEVELS\ res2000..res2014.bin
Конкретный формат атласов выбирает упаковщик. Runtime не должен разбирать
PNG/DAT: как и игровые спрайты, он получает подготовленные .atl/.bin.
6. Экран Sprinter build
Отдельное состояние перед оригинальной заставкой:
PRINCE OF PERSIA
SPRINTER SP2000 BUILD
version / date / build id
Требования:
- пропускается любой клавишей;
- выключается в Settings;
- не запускает музыку оригинального title и не меняет её тайминги;
- данные версии генерируются сборкой, а не правятся вручную в C;
- отсутствие экрана приводит прямо к
TITLE.
7. Title и текстовая подсистема
Порядок переносится из show_title():
- основной титульный экран;
- Presents;
- название игры и Jordan Mechner;
- story frame / “In the absence…”;
- intro Princess + Jaffar;
- story “Marry Jaffar…”;
- credits;
- Hall of Fame, если таблица непуста;
- demo level 0.
Нужны общие примитивы: загрузить full-screen image, вывести строку, показать экран заданное время, transition left-to-right, fade in/out, прервать ожидание клавишей. Текст и меню должны использовать один renderer.
Критерий: последовательность и музыкальные точки совпадают с SDLPoP; Sprinter build screen не сдвигает оригинальный soundtrack.
8. Demo level 0
- Добавить на HDD
res2000.bin. - Загружать уровень обычным loader, но выставлять demo HP и demo mode.
- Воспроизводить
demo_movesкак синтетический источникcontrol_*. - Пользовательский ввод прерывает демо и начинает новую игру.
- Достижение demo end room (у SDLPoP — 24), смерть или конец скрипта
возвращают в
TITLE. - Pause menu, QuickSave и cheats в demo недоступны.
- RNG демо и начальное состояние должны быть детерминированы.
Критерий: без ввода attract loop не требует перезапуска процесса; title -> demo -> title повторяется неограниченно.
9. Глобальный таймер
Состояние: минуты, тики и флаг показа. Таймер создаётся при New Game, переносится между уровнями и входит в QuickSave.
Правила VANILLA:
- на pause menu, загрузке HDD, QuickSave/QuickLoad время не идёт;
- игровые тики следуют темпу логического кадра, а не частоте render loop;
- поведение во время level-end sound и cutscenes сверяется буквально с SDLPoP;
- после Джаффара/на финальном уровне время не должно вызвать поражение;
- ноль времени переводит приложение в
TIME_EXPIRED.
Критерий: одинаковый игровой отрезок в NORMAL даёт то же уменьшение времени, что SDLPoP; сохранение/загрузка не добавляет и не отнимает тики.
10. Cutscene engine
Сцены SDLPoP состоят из небольшого набора повторяемых команд. Вместо набора крупных C-функций нужен компактный интерпретатор:
SET_ACTOR actor
SET_POS x,y,dir
START_SEQ seq
WAIT_FRAMES n
PLAY_SOUND id
WAIT_SOUND
SET_HOURGLASS frame
SET_SAND state
FLASH color,frames
FADE_IN / FADE_OUT
CLEAR_ACTOR actor
END
Скрипты — const в холодном банке или подготовленный бинарный ресурс.
Interpreter обязан:
- исполнять один шаг/кадр без блокирующих длинных циклов;
- поддерживать пропуск сцены;
- при пропуске выполнять cleanup и выходить в заранее заданное состояние;
- освобождать PV-ресурсы перед загрузкой игрового тайлсета;
- не разрешать pause menu/QuickSave внутри сцены.
Порядок переноса: intro, 2/6, 4, 8, 9, 12, time expired, ending. Сцена 12 выбирает короткий или обычный вариант по остатку времени.
11. Переходы между уровнями
Таблица сценария должна быть отдельна от таблиц механики уровня:
typedef struct {
uint8_t level;
uint8_t pre_cutscene;
uint8_t show_level_number;
uint8_t ending_rule;
} pop_level_flow_t;
Особые правила:
- New Game начинает уровень 1;
- перед 2/4/6/8/9/12 запускается сцена;
- 12 -> 13 остаётся бесшовным;
- после победы над Джаффаром переход идёт в 14;
- номер 14 не показывается;
- вход в комнату 5 уровня 14 переводит в
ENDING; - значения больше 14 недопустимы и дают диагностическую ошибку, а не попытку открыть файл.
12. Ending и Hall of Fame
Ending:
- загрузить PV-набор;
- встреча Kid и Princess;
- объятие;
- появление мыши;
- ending music;
- финальные story/title экраны;
- переход в Hall of Fame.
Hall of Fame хранится на HDD в отдельном версионированном POP.HOF.
Сохраняются имя и результат; ввод имени использует тот же текстовый/UI слой.
Повреждённый или неизвестный формат означает пустую таблицу, но не мешает
запуску игры. После показа — возврат в TITLE.
13. Удаление уровня 15
Отдельный ранний этап, чтобы новый flow не наследовал лишний маршрут:
- убрать
res2015.binизLVL_NUMSи HDD image; - заменить последний игровой уровень на 14;
- остановить Shift+L и прочую навигацию на 14;
- удалить
POP_POTIONS_LEVELи специальный половинный урон синих зелий; - исключить copy protection из конфигурации и меню;
- добавить тест: после уровня 14 приложение входит в ending и никогда не
запрашивает
res2015.bin.
14. Этапы реализации
| этап | результат | критерий приёмки |
|---|---|---|
| FG0 | удалить уровень 15, формализовать 1..14 | HDD не содержит res2015; переход выше 14 невозможен |
| FG1 | автомат состояний, текущая игра = PLAYING | старт/рестарт/выход проходят без рекурсии и утечки EMM |
| FG2 | QuickSave/QuickLoad | критерии quicksave_plan.md, включая POP.BAK |
| FG3 | минимальный pause menu | Resume/Save/Load/Restart/Settings/Quit работают |
| FG4 | глобальный таймер | совпадение с SDLPoP и корректный save/load |
| FG5 | text/full-screen/fade primitives | тестовые экраны и переходы на Sprinter |
| FG6 | build info + оригинальный title | точный порядок, корректный пропуск |
| FG7 | demo level 0 | бесконечный attract loop, ввод начинает игру |
| FG8 | cutscene interpreter + intro | интро проходит и пропускается без утечек |
| FG9 | сцены 2/4/6/8/9/12 | правильный вызов один раз перед уровнем |
| FG10 | time expired | отдельная сцена и возврат на title |
| FG11 | level 14 -> ending | полный маршрут после Джаффара |
| FG12 | Hall of Fame | запись HDD, ввод имени, возврат к attract loop |
QuickSave допускается реализовать до остальных частей оболочки: FG2 — одна из ближайших самостоятельных задач.
15. Проверки
- Host-тест автомата: все допустимые переходы и отсутствие уровня 15.
- Host-тест cutscene interpreter на синтетическом скрипте и skip в каждой ожидающей команде.
- Host-тест demo input: одинаковый seed даёт одинаковый поток управления.
- MAME: cold boot -> build info -> title -> demo -> title.
- MAME: новая игра -> принудительный переход по всем pre-level scenes.
- MAME: time expired и пропуск сцены.
- MAME: 13 -> 14 -> room 5 -> ending -> HOF -> title.
- Проверка EMM/FD до и после каждого состояния: число страниц и открытых файлов возвращается к базовому.
make size-check; крупный cold-код размещать в банках и отдельно следить за лимитом 16 КБ каждого банка.
16. Не входит в план
- уровень 15 и copy protection;
- моды и выбор levelset;
- replay/recording;
- точная эмуляция SDL video/controller options;
- профиль ENHANCED и индивидуальные switches fixes.