docs(PoP): документация к актуальному статусу + план следующих уровней
Документы отстали от кода: PORT_PLAN писал «PoC не начат», хотя играется весь уровень 1, а четыре плана были исполнены целиком. - PORT_PLAN: таблица статусов по разделам; фазы 0-3 сделаны, 4-6 нет; риски §8 п.1/п.3 закрыты, п.2 переформулирован под реальный движок (спрайтовый движок для персонажей не используется, лимит «21 спрайт» неприменим), п.4 — найдено расхождение таймингов: оригинал считает логический кадр за 5 тиков при BASE_FPS=60 (83.3 мс, в бою 100 мс), а мы ждём три vsync (60 мс) — игра идёт примерно на 39 % быстрее эталона. - levels_plan.md — новый: машинерия перехода между уровнями, второй тайлсет (palace), потабличные различия и читы SDLPoP, которые окупаются сразу. Инвентарь тайлов снят прямо с res200N.bin: уровень 2 не требует ни одного нового ассета и ни одной новой механики. - roomtest/TASKS.md — новый: доска текущих задач с критериями готовности. - Удалены как исполненные и перекрытые кодом: clip_char_plan, double_buffer_plan, loose_floors_plan, size_optimization_plan. Его §8 (замеры скорости отрисовки) не был перекрыт — перенесён в layout_plan_v2 §9, чтобы не потерять цифры. - KID_PLAN / gates_spikes_plan — шапки «реализовано, оставлено справочником»; room_model_plan — «S1 сделан, остальное не срочно». - docs/README.md стал индексом с отметками актуальности. - ideas_backlog: зелье переворота экрана — оригинал переворачивает готовый буфер построчно, спрайты не трогает; по данным уровней тип 4 встречается только на уровне 9, до него механика не нужна. - examples/scroll: ссылка на удалённый план вела к неверному факту «теневая копия одна — общая»; заменено на подтверждённое «у каждой страницы своя». Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,29 @@
|
||||
# Prince of Persia на ZX Sprinter — план порта
|
||||
|
||||
Статус: план (2026-07-15). §2 (A: kbd_mod_state / B: kbd_raw) —
|
||||
РЕАЛИЗОВАНО и частично проверено в MAME (tests/kbdraw, 2026-07-15,
|
||||
подробности в §2.2); PoC (§5) и остальные фазы — не начаты. Опирается на
|
||||
## СТАТУС (обновлено 2026-08-01)
|
||||
|
||||
Документ составлен 2026-07-15 как план «с нуля» и с тех пор во многом
|
||||
исполнен. Читать его надо так:
|
||||
|
||||
| Раздел | Что с ним сейчас |
|
||||
|--------|------------------|
|
||||
| §1 возможности библиотек | актуально как обзор, но **спрайтовый движок `sprite.h` для персонажей НЕ используется**: Kid/страж рисуются прямыми блитами атласов (`gfx_blit_cols_part*`) с ручным heal — так требует модель оригинала (§6) |
|
||||
| §2 held-state клавиатуры | **сделано** (`kbd_mod_state`, `<kbd_raw.h>`). Открытая проблема — потеря байт при аккордах Shift+стрелка; диагноз и план в `../roomtest/TASKS.md` (KBD-1) |
|
||||
| §3 форматы данных | актуально; уровень читается живьём (`roomtest/pop_level.c`) |
|
||||
| §4 стратегия фона | **сделано** — тайловый рендерер в рантайме (`roomtest/pop_bg.c`) |
|
||||
| §5 PoC | **закрыт и превзойдён.** `poc/` (плейсхолдер-персонаж) — история; активная разработка ушла в `roomtest/` с настоящей графикой |
|
||||
| §6 модель движения | **сделано**: `play_seq` + `frame_table` оригинала, не физика с нуля |
|
||||
| §7 фазы | см. отметки статуса прямо в разделе |
|
||||
| §8 риски | п.1 закрыт, п.3 закрыт (28 страниц-атласов Кида), п.2/п.4 — см. отметки в разделе |
|
||||
| §10 режим памяти | **сделано и переросло план**: `huge` + четыре банка кода; актуальная раскладка — `layout_plan_v2.md` |
|
||||
|
||||
**Где смотреть текущее состояние, а не план:** `../roomtest/README.md`
|
||||
(что играется), `../roomtest/TASKS.md` (что в работе), `levels_plan.md`
|
||||
(следующие уровни), `layout_plan_v2.md` (раскладка кода по окнам и банкам).
|
||||
|
||||
---
|
||||
|
||||
Опирается на
|
||||
`APPLEII_RESOURCE_FORMAT.md` / `MSDOS_RESOURCE_FORMAT.md` / `README.md` в
|
||||
этой папке, на текущий sprinter-cc/libc/libbgi (см. §1) и на локальные копии
|
||||
`applications/PoP/SDLPoP` (github.com/NagyD/SDLPoP, GPLv3) и
|
||||
@@ -227,6 +248,13 @@ dirty-биты, heal против фона через ОЗУ-копию); `room_
|
||||
|
||||
## 5. Proof-of-Concept — цель: доказать, что порт вообще ощущается как PoP
|
||||
|
||||
> **Закрыт (исторический раздел).** PoC в `poc/` свою задачу выполнил и
|
||||
> дальше не развивается: управление ощущается как PoP, held-state работает.
|
||||
> Всё, что ниже про плейсхолдер-персонажа и приблизительную дугу прыжка,
|
||||
> — уже неправда для активной ветки: в `roomtest/` стоит настоящая графика
|
||||
> Кида и авторские таблицы кадров (§6). Раздел оставлен ради истории
|
||||
> решений (в частности §5.1 — почему сначала был плейсхолдер).
|
||||
|
||||
**Объём**: одна комната (например Level 1, экран старта Кида), без
|
||||
переходов между экранами, без стражников (стретч-цель, не обязательна).
|
||||
|
||||
@@ -364,45 +392,53 @@ memory/png_strip_padding_tradeoff.
|
||||
## 7. Полноценное приложение — фазы (после PoC)
|
||||
|
||||
Порядок — по риску и зависимостям, не по геймплейной важности.
|
||||
**Отметки статуса — на 2026-08-01.**
|
||||
|
||||
**Фаза 0 — инфраструктура порта** (расширяет PoC, не переписывает):
|
||||
- Хелд-стейт клавиатуры — финальное решение и реализация по §2 (после
|
||||
подтверждения пользователем).
|
||||
- Полный конвертер уровней (все 15 файлов `levels.dat`/`LEVELn`) → бинарный
|
||||
формат приложения (можно 1-в-1 raw dump, читать по офсетам в рантайме —
|
||||
не обязательно разворачивать в C-struct с указателями).
|
||||
- Полный конвертер фона (24 экрана × N уровней) в растры + конвертер
|
||||
спрайт-лент Кид/стражник/скелет/тень/Джаффар в атласы `.atl` (расширение
|
||||
`conv_sprites.py`/формата `.atl`, если частот кадров/атласов на актора не
|
||||
хватит текущего лимита — см. риск в §8).
|
||||
**Фаза 0 — инфраструктура порта** — **СДЕЛАНА**, но иначе, чем задумано:
|
||||
- Хелд-стейт клавиатуры по §2 — сделан.
|
||||
- Конвертер уровней не понадобился: `res200N.bin` из `SDLPoP/data/LEVELS`
|
||||
кладётся на образ как есть и читается по офсетам в рантайме
|
||||
(`roomtest/pop_level.c`), уровень живёт в EMM-странице.
|
||||
- Конвертер фона в растры **отменён осознанно** (§4): фон собирается
|
||||
тайлами в рантайме. Спрайты — `toolchain/pop_pack_bg.py` /
|
||||
`pop_pack_kid.py` / `pop_pack_guard.py` → атласы `.atl` (Kid — 28
|
||||
страниц, риск §8 п.3 закрыт).
|
||||
|
||||
**Фаза 1 — Кид, полный набор действий**: стоять/идти/бежать/тормозить/
|
||||
разворот/прыжок (на месте, вперёд, «прыжок с разбега»)/повисание на
|
||||
краю/подтягивание/спуск по свисанию/приседание/питьё зелья/смерть от
|
||||
провала. Переходы между экранами (`MAP`-граф, `INFO.KidStartScrn`).
|
||||
**Фаза 1 — Кид, полный набор действий** — **СДЕЛАНА**: стоять/идти/бежать/
|
||||
тормозить/разворот/прыжки/повисание/подтягивание/спуск/приседание/
|
||||
осторожный шаг/питьё зелья/смерть от провала и от пик; переходы между
|
||||
комнатами во все четыре стороны. Осталось: **старт по данным уровня**
|
||||
(`pop_level_start_*` реализованы, но не подключены) — задача L1-START в
|
||||
`../roomtest/TASKS.md`.
|
||||
|
||||
**Фаза 2 — мир и ловушки**: нажимные плиты/двери через граф
|
||||
`LINKLOC`/`LINKMAP` (см. `APPLEII_RESOURCE_FORMAT.md` §1.2), шипы
|
||||
(выдвижение/втягивание/заклинивание), шаткие плиты (loose, обрушение),
|
||||
зелья (эффект по `BLUESPEC×32`), стартовые позиции по `INFO`.
|
||||
**Фаза 2 — мир и ловушки** — **СДЕЛАНА**: кнопки/ворота через
|
||||
`LINKLOC`/`LINKMAP`, шипы, loose-полы (тряска, обрушение, щебень, пробой
|
||||
потолка), зелья, дверь уровня (открывается), факелы. Подробности и
|
||||
справочник — `gates_spikes_plan.md`.
|
||||
|
||||
**Фаза 3 — бой**: подбор/выхватывание меча, состояние стойки, парирование/
|
||||
удар, коллизия клинков — по логике `AUTO.S`/`seg003-006.c` (референс, не
|
||||
копия). Стражник: базовое AI-поведение по `GdStartProg` (несколько
|
||||
шаблонов программ), Y-сортировка слоями уже есть в движке для «кто
|
||||
спереди/сзади».
|
||||
**Фаза 3 — бой** — **СДЕЛАНА в объёме обычного стражника**: подбор и
|
||||
выхватывание меча, стойка, удар/парирование, коллизия клинков, HP обеих
|
||||
сторон, смерть; ИИ стража (замечает Кида, подходит, боевые ветки),
|
||||
персистентность трупа между комнатами.
|
||||
|
||||
**Фаза 4 — разнообразие противников**: скелет, тень (копия анимации Кида —
|
||||
подтверждено побайтовым совпадением данных, см. `MSDOS_RESOURCE_FORMAT.md`
|
||||
§3), толстый стражник/визирь (общая база анимации с визирем).
|
||||
**Фаза 4 — разнообразие противников** — **НЕ НАЧАТА**. Скелет нужен на
|
||||
уровне 3, толстый — на 6, тень — на 12, визирь — на 13; привязка
|
||||
«уровень → тип стража» (`tbl_guard_type`) описана в `levels_plan.md` §1.
|
||||
|
||||
**Фаза 5 — звук**: CBL-эффекты (шаги, удары, двери, падение) из
|
||||
`digisnd*.dat`→PCM; PC-спикер тройки (`ibm_snd*.dat`) как опциональный
|
||||
дешёвый бипер без CBL, если формат подтвердится простым парсингом.
|
||||
**Фаза 5 — звук** — **НЕ НАЧАТА**: CBL-эффекты (шаги, удары, двери,
|
||||
падение) из `digisnd*.dat`→PCM; PC-спикер тройки (`ibm_snd*.dat`) как
|
||||
опциональный дешёвый бипер без CBL, если формат подтвердится простым
|
||||
парсингом. Опкод `SOUND` в `play_seq` пока просто съедает свой аргумент —
|
||||
точки вызова уже на месте.
|
||||
|
||||
**Фаза 6 — оболочка**: титры, меню/выбор уровня, HUD (таймер/жизни),
|
||||
сохранение прогресса (FILE*), финальные катсцены — по минимуму,
|
||||
геймплейно не критично.
|
||||
**Фаза 6 — оболочка** — **НЕ НАЧАТА**: титры, меню/выбор уровня, HUD
|
||||
(таймер/жизни), сохранение прогресса (FILE*), финальные катсцены — по
|
||||
минимуму, геймплейно не критично. Полоса HP — единственное, что уже есть.
|
||||
|
||||
**Между Фазами 4 и 5 вклинивается то, чего в этом плане не было:
|
||||
переход между УРОВНЯМИ** (загрузка следующего уровня, второй тайлсет
|
||||
palace, потабличные различия уровней). Отдельный документ —
|
||||
`levels_plan.md`.
|
||||
|
||||
**Фаза 7 — стабилизация**: полный прогон всех 14 уровней в MAME
|
||||
(`mame_interactive.py`), затем на реальном железе; профилирование бюджета
|
||||
@@ -418,23 +454,33 @@ tiny/small.
|
||||
|
||||
(по правилу `defer_unexplained_quirks` — не гадать, проверять)
|
||||
|
||||
1. **Held-state клавиатуры** (§2) — блокирует даже PoC, если решать
|
||||
«правильно»; иначе PoC на компромиссном варианте 2 (таймаут-эвристика).
|
||||
2. **Бюджет спрайтов на насыщенный экран** — сцена с 2+ стражниками +
|
||||
несколько анимированных ловушек может приблизиться к лимиту
|
||||
~21 спрайт/кадр (`sprite_engine_perf`) — нужна прикидка по реальным
|
||||
уровням (сколько объектов одновременно активно в худшем экране).
|
||||
3. **Ёмкость одного атласа/страницы EMM на актора** — у Кида ~220 кадров
|
||||
(все действия) против 4×12 у `rpgwalk` — потребуется либо несколько
|
||||
атласов на актора с переключением по фазе действия (стоять/идти отдельно
|
||||
от боя), либо расширение формата `.atl`/загрузчика на мульти-страничные
|
||||
атласы — оценить фактический байтовый вес конвертированных кадров Кида
|
||||
прежде чем проектировать.
|
||||
4. **Тайминг оригинала** — сверить логическую частоту кадров анимации
|
||||
оригинала (Apple II ~60 Гц NTSC / DOS — фиксированный таймер) с 50 Гц
|
||||
Sprinter; если оригинал считался на другой частоте — потребуется
|
||||
коэффициент пересчёта смещений кадров (§6), иначе прыжки/бег будут
|
||||
визуально быстрее/медленнее эталона.
|
||||
1. ~~**Held-state клавиатуры** (§2)~~ — **закрыт** (`<kbd_raw.h>`). Открытый
|
||||
остаток — не «есть ли held-state», а потеря байт при аккордах
|
||||
Shift+стрелка: `../roomtest/TASKS.md`, KBD-1.
|
||||
2. **Бюджет кадра** — риск подтвердился, но не в том виде, в каком ожидался:
|
||||
спрайтовый движок для персонажей не используется, поэтому лимит
|
||||
«~21 спрайт/кадр» неприменим. Реальный бюджет упирается в heal+блиты и
|
||||
перерисовку тайлов; замер 2026-07-30 — типичный кадр ~371 К тактов
|
||||
(~86 % периода). Инструмент замера уже в коде: полосы бордюра `PROF()`
|
||||
в `roomtest.c`. План выжимания — `../roomtest/TASKS.md` (CLIP-1) и
|
||||
`../roomtest/bug_list.md` (T-1/T-2).
|
||||
3. ~~**Ёмкость атласа на актора**~~ — **закрыт**: Kid разложен на 28
|
||||
атласов-страниц по 8 спрайтов (`pop_pack_kid.py`), страж — на 5;
|
||||
мульти-страничного формата `.atl` не потребовалось. Побочно
|
||||
подтвердился компромисс паддинга (§6.1).
|
||||
4. **Тайминг оригинала** — **ОТКРЫТ, и сверка 2026-08-01 показывает
|
||||
расхождение.** Цифры оригинала (SDLPoP): базовый таймер `BASE_FPS = 60`
|
||||
(`types.h:1373`), логический кадр игры — `base_speed = 5` тиков
|
||||
(`data.h:869`), то есть **83.3 мс (12 лог. кадров/с)**; в бою
|
||||
`fight_speed = 6` → **100 мс (10/с)**. У нас (`roomtest.c`) — три
|
||||
ожидания `gfx_wait_vsync()` на итерацию, то есть **60 мс (16.7/с)** и
|
||||
без отдельной скорости боя. Значит **игра идёт примерно на 39 %
|
||||
быстрее эталона**. Точное соответствие даёт 4 ожидания vsync (80 мс
|
||||
против 83.3) и 5 в бою (100 мс — совпадает точно).
|
||||
Проверять не «на глаз», а секундомером по одинаковому отрезку
|
||||
(SDLPoP рядом на том же экране), и только после того, как кадр
|
||||
перестанет иногда вылезать за период (см. п.2) — иначе замедление
|
||||
спрячет проблему бюджета вместо того, чтобы её показать.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user