31d00750907d2c4037b473d8ef3232bdf8287442
11 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0086ac80c2 |
Таблицы анимации Кида: kid_data.bin -> KID\kid.ani
Все ресурсы Кида теперь зовутся kid.*: kid.arc (атласы), kid.pal (палитра), kid.ani (кадры + seqtbl). Расширение .ani говорит о содержимом: это не «какие-то данные», а таблица кадров и байткод последовательностей движения. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
65797af44c |
Ресурсы — в архивы PBA1; тайминги сцены PV — по часам насоса CBL
ТАЙМИНГИ. Пользователь заметил, что заклинание Джафара не совпадает с музыкой. Замер в MAME (breakpoint на вспышке + курсор трека): реплика m53 успевала ДОИГРАТЬ до молнии, хотя по шкале оригинала между входом Джафара и заклинанием ровно 822 тика = 13,7 с. Причина: пейсинг считал НАШИ ожидания vsync, а не прошедшее время. Полноэкранная копия страницы стоит больше кадра луча, и разница копилась. Кадровые прерывания для счёта не годятся (теряются в di-окнах акселератора), поэтому часами стал насос CBL: он идёт от расхода буфера железом, 85,4 Гц, и ему безразлично, чем занят главный цикл. Один тик оригинала = 57/40 тика насоса (0,07 % ошибки, без деления в кадре). Когда звук выключен, работает прежний путь по кадрам луча. После фикса замер даёт 229 блоков остатка m53 против расчётных 233 — расхождение 47 мс. АРХИВЫ. Группы ресурсов сложены в PBA1: kid (58 атласов), pv (78), shadow (32), bg (25), title (20), guard (10), vizier (5), skel (4). Было 232 файла на образе, стало 8 архивов плюс шесть палитр и таблицы уровней. При цене `open` 51,4 мс против 32,6 мс за чтение 16 КБ это возвращает секунды на каждой загрузке. - pop_pack_arc.py печатает индексы элементов константами (ARC_<GRP>_<FILE>), порядок задаёт Makefile, а серии код проверяет статически; - atlas_load разделён: atlas_attach принимает уже прочитанную страницу, поэтому чтение из архива не дублирует проверку магии и подготовку W0; - имена архивов живут в pop_arc.c и адресуются номером. Строковый литерал лежит в rodata своего банка, и указатель на него из другого банка после переключения W3 показывает на чужие данные — ровно так падала загрузка фона (поймано брейкпоинтом на puts). Проверено в MAME: заставка, интро и уровень 1 собираются из архивов, Тень грузит все 32 страницы. Host-тесты: 15 наборов. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
47a4b084c4 |
Меню, статус-строка и оболочка игры: 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>
|
||
|
|
5b6664ffce |
Зеркальные кадры персонажей — готовыми файлами, а не разворотом в рантайме
Замеры в MAME (такты Z80, кадр = 430 000) решили вопрос однозначно: загрузка 28 атласов Кида с HDD — 27 160 092 такта (1.26 с), 970 003 на страницу; разворот ОДНОЙ страницы на месте — 12 645 582 такта (0.59 с), т.е. 771 такт/байт. Даже идеальный вариант (копия accel'ом ~0.25 кадра + asm-реверс ~2.2 кадра) дал бы ~1.05 млн на страницу — то есть в лучшем случае сравнялся бы с чтением готового файла. На 34 страницы: 1.5 с загрузки против 20 с разворота. * Упаковщики пишут второй набор: vflip_cols/vflip_page в pop_pack_kid.py -> kid0_v.atl…kid27_v.atl, sword_v.atl; pop_pack_guard.py -> g0_v.atl…g4_v.atl ТОЛЬКО для обычного стража (на уровне 9 tbl_guard_type = 0; тень рисуется атласами Кида, скелет/визирь с переворотом не встречаются). * pop_vflip.c схлопнулся до таблицы «страница -> зеркальная пара»; весь побайтовый разворот и аллокация EMM удалены. * pop_vflip_load_all (банк 8) грузит набор ОДНИМ вызовом и откуда угодно — задел под загрузку ресурсов во время интро. Зовётся при старте и при смене уровня, если это POP_UPSIDE_LEVEL; чит U на других уровнях поднимает набор лениво (страховка в главном цикле). * Стартовый уровень стал параметром сборки: make LEVEL=N (-DFIRST_LEVEL). Проверено в MAME на уровне 9: переворот мгновенный, pop_flip_screen = 897 263 такта (2.1 кадра) — это сама копия экрана, загрузки в кадре больше нет. tests-host 6/6. |
||
|
|
dbf9166836 |
Цвета 6 и 12 базовой палитры: игра правит их на старте, мы брали VGA-значения
Симптом (наблюдение пользователя): кирпичи дворцовой кладки правильного
цвета, а швы между ними — нет.
Корень шире, чем швы. init_game_main (seg000:164) подменяет ДВЕ записи
16-цветной палитры сразу после загрузки:
// (blood, hurt flash) #E00030 = red
set_pal(12, 0x38, 0x00, 0x0C);
// (palace wall pattern) #C09850 = light brown
set_pal( 6, 0x30, 0x26, 0x14);
Подтверждено дампом палитры живого SDLPoP: PAL[6] = 48,38,20,
PAL[12] = 56,0,12. У нас в таблице VGA16 стояли стандартные VGA-цвета
(42,21,0) и (63,21,21). Цветом 6 рисуются швы дворцовой кладки
(blitters_46h_mono_6), цветом 12 — кровь чомпера и вспышка урона, так что
промах был не только в стенах.
Заодно исправлена вспышка урона в roomtest.c: было flash_bg(255,85,85)
(стандартный brightred), стало (224,0,48).
Проверка: ряд стен уровня 4 теперь совпадает с эталоном SDLPoP по всем
восьми цветам и их количествам один в один (5333/4013/3110/2065/1785/1372/
1075/447). Остаточное различие значений — только наше масштабирование
6->8 бит: (v*255)/63 против v*4 у SDLPoP, то есть (194,153,80) против
(192,152,80).
ПОПРАВКА к
|
||
|
|
d438a1d3da |
PoP roomtest: клинок в атласе целиком + раздельный heal накладных спрайтов
Меч в оригинале ОДИН на всех: и Кид, и страж рисуют клинок из chtab_0 (add_sword_to_objtable, seg006:1798). Раньше sword.atl содержал только кадры подъёма/ножен (sword_tbl 35..42), поэтому у стража меча не было видно вовсе. - pop_pack_kid.py пакует chtab_0 целиком (id 0..33, 5168 Б), индекс в атласе = id; - pop_extract_kid_data.py вытаскивает sword_tbl (53 строки) в kid_data.h макро-инициализаторами — таблица ложится в один TU, а не в каждый; - pop_sword_draw (pop_kid.c) — общая точка отрисовки клинка с полным условием оригинала (кадры 229..237 ИЛИ меч обнажён ИЛИ живой страж); зовут и kid_draw, и pop_guard_draw. Раздельный heal накладных спрайтов. Клинок и брызги урона раньше объединялись в один прямоугольник с персонажем, а объединение почти вдвое больше суммы двух (клинок уходит вперёд-вверх) — heal же стоит ровно по площади. Теперь у накладных свой прямоугольник и свой gfx_heal, а объединение осталось ТОЛЬКО для окна fore-клипа: там это 4 сравнения без рисования, но покрыть клинок обязано, иначе он полезет поверх столба. Отладка: DEBUG_SWORD_ROOM — Киду выдаётся меч при входе в комнату 3 (там страж), чтобы не бегать за ним в комнату 15. Бюджет (225 кадров, комната 3): 415 284 – 416 370 = 0.966–0.968 кадра. Против 384 168 – 384 636 до этого шага, то есть +31 700. Разложение по фазам (медианы): process_trobs 83 190, pop_guard_draw 92 483 (спрайт 28 763 + клинок 23 820 + fore_over_char 39 906), kid_draw 59 089, fore поверх Кида + борта 47 483, heal 40 068, логика 76 338, ввод 13 050. Видно, что клинок 21x8 стоит почти как спрайт стража — это фиксированные накладные расходы клипающего блита, а не пиксели; лечится noclip-путём для column-major (см. gfx_blit_noclip_fast). Отдельным шагом. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
3dbad6120c |
PoP roomtest: страж — спрайты, таблица кадров и появление в комнате
Шаги 1-2 из плана стражей: 1. Спрайты (chtab_5_guard, база 750). Новый упаковщик pop_pack_guard.py: data/GUARD/res751..784 -> GUARD\g0..g4.atl (5 EMM-страниц, адресация id>>3 / id&7, как у Kid). Палитра берётся НЕ из PNG, а из res10.bin (guard_palettes: 7 палитр по 16 цветов, 6-бит) по level.guards_color — на уровне 1 у обоих стражей color = 2; группа слотов 0x90..0x9F добавлена в общий kid.pal. 2. Таблица кадров стража у оригинала СВОЯ (frame_tbl_guard, seg006:372, 41 запись) и индексируется как frame + add_frame - 149, где add_frame = 70 для кадров 102..106. Она дописана в kid_data.bin (3515 -> 3720 Б, смещение в KID_BIN_GFRAMES_OFF); pop_kid получил pop_kid_data_frame() — чтение кадра из ЛЮБОЙ таблицы страницы данных. 3. Появление: pop_level_guard() читает guards_tile/dir/color/skill из уровня, pop_guard_enter() ставит стража по enter_guard (seg002:0112) + pos_guards (seg003): row из тайла, y = y_land[row+1], x из колонки, charid = guard, sword сложен, alive = -1, HP = 3. Отрисовка pop_guard_draw() — та же математика, что kid_draw (load_frame_to_obj + calc_screen_x_coord), но атлас стража и своя таблица кадров; heal по странице дабл-буфера, как у Kid. Интерпретатора последовательностей у стража ПОКА НЕТ: кадр ставится напрямую (166 = frame_166_stand_inactive, что и даёт seq_77 при входе в комнату). play_seq для произвольного персонажа + ИИ — следующая фаза. Проверено в MAME: в комнате 3 страж появляется на своём месте (ряд 1, кол 7) и рисуется; цвета совпадают с эталонным рендером спрайта в палитре color=2. ВНИМАНИЕ по памяти: куча W2 просела до 805 Б (было 2349). Перед ИИ стражей нужен шаг 5 плана (данные: room_modif 720 Б, dl1/dl2 512 Б, _kbdraw_down 512 Б) либо вынос кода отрисовки стража в резидент W3. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
8e33cd07bc |
PoP roomtest (план v2, шаг 1): таблицы анимации Kid -> EMM-страница
kid_frames (241*5) и kid_seqtbl (2310) занимали 3.5 КБ в _CODE окна W1/W2 — самого дефицитного ресурса. Теперь они лежат в kid_data.bin (отдельная EMM-страница), которая маппится в W0 ровно на время play_seq — один map/unmap за тик, в фазе тика, без конфликта с атласом в W0. Ключ к переносу — порт load_frame/cur_frame (seg006): оригинал раз за тик копирует кадр в структуру, и вся коллизия/отрисовка читает ЕЁ, а не таблицу. У нас так же: kid_cur_dx/kid_cur_flags (их дёргают несколько раз за кадр из pop_map) и kid_draw читают cur_frame — 5 байт в _DATA. kid_seq_off (230 Б) оставлен резидентным: его читает kid_set_seq из pop_ctrl/pop_map, вне страницы. pop_extract_kid_data.py теперь пишет и kid_data.bin, и урезанный kid_data.h (тип kframe, размеры, смещения в бинаре, kid_seq_off). Замер: _CODE 24881 -> 21634 (-3247 Б), куча W2 2076 -> 5315 Б, W3 без изменений. Проверено в MAME скриптом: стойка -> бег (кадр 8) -> стоп, перемещение и коллизия у кромки работают, спрайт рисуется. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
e1846369ad |
PoP roomtest: блеск лежащего меча, клинок в руке, вспышка фона
Три расхождения с оригиналом на сцене подъёма меча (комната 15): 1. Лежащий меч не блестел. Порт animate_sword (seg007:0425) + start_anim_sword (seg007:087C): при входе в комнату тайлу даётся случайная фаза (prandom & 0x1F), каждый кадр счётчик вниз, на 0 — новый период 0x28..0x67. Кадр блеска рисуется РОВНО на modif==1 ((modif==1)+10 в draw_tile), т.е. одиночная вспышка раз в 40..103 тика. Перерисовка тайла — по смене видимого кадра, схемой кнопки (текущая страница сразу, вторая через rest-цикл). 2. В кадрах «нашёл меч» клинка не было видно. Порт add_sword_to_objtable (seg006:1798): клинок — ОТДЕЛЬНЫЙ спрайт chtab_0 поверх Kid со смещением из sword_tbl. Смещения в ЭКРАННЫХ пикселях (оригинал применяет их после calc_screen_x_coord), поэтому берём уже масштабированный obj_x. Из таблицы взяты только строки 35..42 (кадры 229..236) — бой не портирован. Новый атлас sword.atl (8 спрайтов, 1.6 КБ) + палитра chtab_0 в слотах 0x80..0x8F. Прямоугольник heal расширяется объединением с клинком, иначе он оставлял след за габаритом Kid. 3. Не было вспышки фона. do_flash = set_bg_attr(0, color) — оригинал подменяет НУЛЕВУЮ запись палитры, вспыхивает всё чёрное поле экрана; proc_get_object ставит flash_color=14, flash_time=8. У нас gfx_pal_set на обе страницы. ВАЖНО: вызов gfx_pal_set(0,0,0,0,0) пятью литералами ломает SDCC 4.5 (эмитит невалидный `ld hl, a`) — обёрнуто в функцию с параметрами. Попутно: TROBS_MAX 24 -> 30 (как в оригинале) + при входе в комнату из списка выбрасываются «декоративные» trob ДРУГИХ комнат (факелы/зелья/ меч анимируются только в отрисованной). Без этого отладочный обход всех 24 комнат забивал список, и новые анимации молча не заводились. Проверено в MAME: блеск (брейк на редрое тайла срабатывает), клинок в руке виден, фон вспыхивает жёлтым. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
c127a4b0d2 |
PoP: факелы и зелья (chtab_1) + быстрый блит фона gfx_blit_noclip
Графика chtab_1 (flame/sword/potion) — новый атлас pop_pot.atl: - склянка зелья берётся из chtab_1 (seg008 draw_tile_fore: id 12 малая / 13 большая при типах 2..4), а НЕ chtab_6 id 12 — res212 в VDUNGEON нет, каскад падал на VPALACE и рисовал кусок дворцовой декорации («мусорный объект» в комнате 5); - пакуем chtab_1 целиком (id 1..23): пламя, склянки, кадры пузырька; - MONO-блит (method_3_blit_mono, seg009:3040) красит спрайт цветом из ОБЩЕЙ 16-цветной палитры экрана — она добавлена в 0x30..0x3F, палитра chtab_1 в 0x40..0x4F; пузырёк пакуется силуэтом цвета 12 (красный), его маска — цвета 0; - в env-атлас добавлено основание факела (env 146, seg008:489 — рисует правый сосед); в статический render_room оно не попадало. Анимация (seg007 animate_torch/animate_potion + seg000:0B12 anim_tile_modif): при входе в комнату факелам и зельям задаётся случайная стартовая фаза и заводится trob; факел — get_torch_frame, пламя в ячейке ПРАВОГО соседа (xh = draw_xh+1, y = draw_main_y−40); зелье — bubble_next_frame по младшим 3 битам модификатора (старшие 5 = тип, при загрузке уровня modif <<= 3, seg009). Пламя и пузырёк не запекаются в фон: heal своей области + кадр поверх. Скорость пламени /2 — наш логический кадр короче игрового тика оригинала. Скорость отрисовки (профиль в MAME, кадр = 430 080 тактов): - замер показал, что цена блита почти НЕ зависит от размера — 13 288 тактов на спрайт 32×3 против 4 617 у линейного спрайтового ядра без клипа; платим за проход gfx_blit → gfx_blit_part → _gfx_blit_full; - в libbgi добавлен gfx_blit_noclip() — блит без клипа в ТЕКУЩЕМ банке (putsprite не годится: навязывает GFX_BANK_SPRITE, фону нужен TRANSPARENT ради ОЗУ-копии); pop_bg.blit_b уходит на него, когда спрайт целиком на экране и не нужен g_clip_top → ~2.9× на блит; - W3-скобку ставит САМА libbgi: из модуля с --w3 её вызывать нельзя — после _bgi_begin окно W3 занято видеобанком и код вызывающего исчезает (проявлялось белым экраном); - редрой шва больше не перерисовывает полосу потолка (bar начинается с POP_YOFF+3) — это удваивало стоимость блока при анимации решётки; - docs/size_optimization_plan.md §7–§8: замеры, сделанное и запас (батчинг W3-скобки, решётка одним спрайтом, лишние блиты). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
cd8d566d82 |
applications/PoP: порт Prince of Persia — PoC (roomtest) + пайплайн
Порт PoP на Sprinter. Текущий PoC — applications/PoP/roomtest/: комната 1 (фон-композиция тайлов) + Kid с управлением на raw-клавиатуре и коллизией с картой. - roomtest — pop_bg (фон), pop_kid (спрайты Kid, column-major флип, seqtbl-анимация), pop_ctrl (порт control() PoP на held-state kbd_raw), pop_map (коллизия seg004/005: бег/стоп у стены, падение/приземление, отскок seq_47, вертикальный прыжок K4.1). MEMORY=small (DATA сразу за CODE, ~23КБ кода не лезет в huge). - toolchain (PoP) — pop_pack_kid/pop_pack_bg/render_room/extract — распаковка res-графики MSDOS в атласы + композиция комнат. - toolchain/ (корень) — make_hdd.sh (быстрый HDD-тест вместо FDD), png_strip.py / room_compose.py (ассет-пайплайн). - docs — PORT_PLAN, KID_PLAN, форматы ресурсов (Apple II / MSDOS / DAT). - bgtest/coltest/poc — ранние PoC (фон, коллизия, первый прототип). .gitignore: build-артефакты applications/*/*/*; исключены внешние референс-репозитории (SDLPoP/mininim/PR/Apple-II — свои git-клоны) и оригинальные game-данные MSDOS/ (копирайт, только для реверса форматов). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |