Files
Sprinter-SDCC/applications/PoP/docs/full_game_plan.md
T
snark13 f4b4852d51 QuickSave F6/F9 в roomtest; bank_load_file/bank_save_file/gfx_w0_page_prepare; sprinter-cc: авто n_banks
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
2026-08-22 11:53:10 +03:00

17 KiB
Raw Blame History

От 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():

  1. основной титульный экран;
  2. Presents;
  3. название игры и Jordan Mechner;
  4. story frame / “In the absence…”;
  5. intro Princess + Jaffar;
  6. story “Marry Jaffar…”;
  7. credits;
  8. Hall of Fame, если таблица непуста;
  9. 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:

  1. загрузить PV-набор;
  2. встреча Kid и Princess;
  3. объятие;
  4. появление мыши;
  5. ending music;
  6. финальные story/title экраны;
  7. переход в 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.