kid_heal/pop_guard_heal звали gfx_heal всегда, хотя рисуют ровно тот прямоугольник, который блит в большинстве кадров кладёт noclip-ядром. Все три места heal (+ heal_off фона) сведены к общему pop_heal_fast. Замер в MAME, счётчики totalcycles на входах kid_heal и kid_tick (вся группа heal за кадр), комната 1, Кид стоит: клипающее ядро 26 200 тактов/кадр (149 кадров) noclip 15 848 тактов/кадр (239 кадров) −10 352 такта, −39.5 %. A/B в одном прогоне: вторая половина снята с пропатченным в памяти условием (jr nz → jr), то есть на той же геометрии. Размер СУММАРНО −362 Б: _CODE +17, BANK2 −116 (свободно 2708 — это тесный банк из рисков levels_plan §5), BANK3 −263, BANK4 без изменений. Грабли по дороге: первым заходом хелпер был static inline в pop_bg.h — SDCC 4.5 И встраивает тело (181 Б) в каждый вызов, И оставляет копию в каждом TU, который видит заголовок. pop_guard_heal раздулся с ~60 до 663 Б, итого +1091 Б в _CODE и +636 Б в банке стража. Отсюда pop_draw.c: обычная функция в резиденте W1, из банков это прямой call без трамплина. pop_room_clip_borders оставлен клипающим осознанно (320 не лезет в 8 бит, гейт border_dirty редкий) — причина записана в коде. Проверено визуально: ходьба, прыжок, спуск, позиция за решёткой шва (straddle — там работает клипающий фолбэк) — артефактов нет. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
11 KiB
roomtest — ОТКРЫТЫЕ баги и незакрытые оптимизации
Только то, что не закрыто. Всё закрытое (и, что важнее, разбор корней —
odd-pixel арифметика char_x, подстановка тайла нажатой кнопки, баг
кодогенератора SDCC) переехало в bug_closed.md: прежде чем
заводить новый баг, грепни там по симптому.
Приоритеты работ — в TASKS.md, а не здесь. Правило проекта:
механику сверять с ../SDLPoP/src/ ДО кодинга.
Ревизия списка: 2026-08-01 (задача L1-TRIAGE). Закрыты и перенесены в
архив семь записей: BUG-1, BUG-2, BUG-3 (стояли как Critical с 2026-07-21,
прогнаны в MAME) и вся косметика окклюзии BUG-CEIL-1/2/3, BUG-OCCL-1 (была
починена кодом ещё в июле, список просто отстал — чем именно закрыта каждая,
расписано в bug_closed.md).
Открытых багов сейчас нет — только две незакрытые оптимизации и незаконченная ревизия комнат:
| ID | что | тип |
|---|---|---|
| T-1 | пики перерисовываются безусловно | оптимизация |
| T-2 | Кид перерисовывается в покое | оптимизация |
| обход 24 комнат | таблица заполнена на 5 строк из 24 | ревизия |
Это НЕ значит «багов нет» — значит, что известные найдены и закрыты, а следующая порция придёт из обхода 24 комнат и сквозного прохождения (L1-PASS).
Оптимизация отрисовки (записано 2026-07-29)
Не баги — план работ. Оба пункта про одно: у оригинала пометка тайла к перерисовке стоит копейки (бит в таблице, которая всё равно чистится каждый кадр), а у нас каждая такая пометка превращается в реальный heal (копию из ОЗУ-копии акселератора) плюс блиты. Поэтому буквальный порт «перерисовываем безусловно» корректен, но дорог.
T-1. Пики: перерисовывать по причине, а не безусловно
Сейчас: pop_process_trobs зовёт pop_spike_redraw каждый кадр для
каждой живой пики в комнате (порт redraw_21h, который animate_spike
вызывает вне всяких if). Это корректно, но лишнее для пик, до которых
Киду дела нет.
Надо: перерисовывать тайл пики, только если
- сменился её видимый кадр (шаг выдвижения/уборки), ЛИБО
- её кто-то стёр — а стереть у нас может только heal, то есть тайл
попал в прямоугольник
kid_healэтого кадра.
Это и есть модель оригинала, просто выраженная флагами: redraw_at_char
(seg003:0576) каждый кадр помечает set_redraw_fore тайлы персонажа, причём
объединение текущего и предыдущего прямоугольника
(MIN(char_top_row, prev_char_top_row) и т.д.), а animate_spike помечает
свой тайл. Итог = {тайл сменил кадр} ∪ {тайлы Кида}.
Как: слой Кида и так считает cL..cR/rT..rB в pop_fore_over_kid —
пусть публикует их (плюс предыдущие, как в оригинале), а цикл trob'ов
сравнивает tilepos с диапазоном целочисленно. Никаких пересечений
прямоугольников (см. память manual_hints_over_auto_detect).
Приоритет: отдаётся почти бесплатно ПОСЛЕ T-2, отдельно не окупается.
T-2. Idle-skip: не перерисовывать Кида, когда ничего не происходит
Сейчас: kid_heal → kid_draw → pop_fore_over_kid идут каждый кадр,
даже когда Kid стоит и в его тайлах ничего не меняется. Это ровно поведение
оригинала (draw_game_frame, seg000:917 — draw_moving() + draw_tables()
безусловно), но у него это дёшево, а у нас нет.
Надо: пропускать heal+draw Кида, когда кадр/поза/координаты не менялись и в его тайлах нет активной анимации.
Осторожно (дабл-буфер): пропускать можно не раньше второго подряд неизменного кадра — иначе одна из двух страниц останется со старым содержимым. Условие «обе страницы уже получили это состояние».
Связь с T-1: после T-2 пики отпадают сами — раз Кида не перерисовываем, heal'а нет, стирать пики нечем, редрой не нужен.
Связь с KBD-1: это ещё и минус DI-окна в самых спокойных кадрах — ровно
там, где тапают Shift+стрелку (см. остаток KBD-1 в TASKS.md).
Обход всех 24 комнат уровня 1 — поиск багов отрисовки
Инструмент: #define ROOMNAV в roomtest.c — +/- (цифровой блок либо
=/- основного ряда) переключают комнату по номеру (1..24, с обёрткой),
Kid ставится на первый пол, pop_trob_reset() возвращает пики/ворота в
исходное. Номер комнаты — полосками в верхнем борте: слева десятки, справа
единицы (|| |||| = 24). Убирается закомментированием одной строки.
Комнаты 13, 18, 24 недостижимы в обычной игре — это свойство данных
уровня, разбор в bug_closed.md («НЕ БАГИ»). Приоритет
багов в них низкий.
Таблица заполнена на 5 строк из 24 — доделать в рамках L1-TRIAGE
(TASKS.md).
| комната | статус | что не так |
|---|---|---|
| 1 | пофикшено | падающая плита (2,6): правая грань видна через пол (2,7) и перекрывает его переднюю грань — mob_render брал ряд соседа из m->row (счётчик, уже ушедший на ряд вперёд), а не из координаты |
| 2 | ||
| 3 | ||
| 4 | ||
| 5 | пофикшено | прыжок в решётку: Kid оставался стоять на 6 px ВЫШЕ пола и без приземления-приседания — от bumped() (seg004) был портирован только хвост (seq_47), не хватало bumped_floor (прижатие Y к полу + seq_46_hardbump на кадрах прыжка 24/25/40..42/102..106) и bumped_fall |
| 6 | ||
| 7 | ||
| 8 | ||
| 9 | сделано | дверь уровня (1,3)-(1,4) рисовалась чёрным проёмом: не был портирован draw_leveldoor (seg008:1D29) — створка (слайсы 33 + верх 34), лестница за ней (99/144) и анимация подъёма по кнопке (animate_leveldoor, seg007:05F1, modif 0→43). Спрайты 33/34/99/144 добавлены в атлас явным набором (render_room.py дверь не рисует) |
| 10 | ||
| 11 | ||
| 12 | пофикшено | вис/подтягивание на кромке loose-плиты: плита рисовалась ПОД Кидом. Не хватало двух кусков draw_tile: (а) draw_loose кладёт нижнюю грань плиты И в foretable (поверх персонажа), (б) draw_tile_base подставляет верх плиты из loose_fram_left, а в нашем midtable-оверлее стоял голый base_id (у loose он 0). Голова Кида поверх падающей на него плиты — см. «НЕ БАГИ» в bug_closed.md |
| 13 | недостижима в игре | |
| 14 | ||
| 15 | сделано | меч (2,2) не рисовался: тайл 22 в draw_tile_anim не был портирован. Добавлены отрисовка предмета (chtab_1 id 10/11 на draw_main_y−3), подъём по Shift (check_get_item/get_item/do_pickup: присед → seq_91 pickupsword → меч исчезает с пола) и статус pop_have_sword |
| 16 | ||
| 17 | ||
| 18 | недостижима в игре | |
| 19 | ||
| 20 | ||
| 21 | ||
| 22 | ||
| 23 | ||
| 24 | недостижима в игре |
Заметки (отладка)
- Тестовые клавиши осторожного шага: J = шаг влево, L = шаг вправо
(эмуляция Shift+стрелка), см.
pop_ctrl.cKBD_DBG_STEP*. Первый шаг в сторону = разворот (как в оригинале safe_step), движение со второго. - Читы (
pop_cheat.h): K — убить стража, I — бессмертие (toggle), S — выдать меч. - Респавн после смерти — по ↑ (или авто через
RESPAWN_DELAY); ставит Kid вSTART_ROOM. START_ROOM=1 / COL 3 / ROW 0— жёстко зашитый тестовый старт; перевести наpop_level_start_*(задача L1-START вTASKS.md).