Files
Sprinter-SDCC/applications/PoP/roomtest/bug_list.md
T
Александр Петров ba37bd1133 BUG-DOOR-CLIP: обрезка силуэта правым косяком двери уровня
Симптом (нашёл пользователь сразу после L1-EXIT): при подъёме по лестнице за
дверью уровня силуэт Кида вылезал ПРАВЕЕ правого косяка проёма; по высоте
обрезка была корректна.

Причина — недопортированная половина clip_char (seg006:1231).  Для кадров
двери оригинал ставит ДВА клипа, у нас был только первый:
    obj_clip_top   = leveldoor_ybottom + 1;   // было
    obj_clip_right = leveldoor_right;          // не было
Отдельная ловушка: комментарий в SDLPoP говорит «frames 217..228», а КОД
проверяет >= frame_224_exit_stairs_8, то есть 224..228 — портировано по коду.

Fore-слоем это не лечится: створка и косяк уходят в оригинале целиком в
backtable (draw_leveldoor, все add_backtable), рисуются ПОД персонажем и
перекрыть его не могут.  Единственный способ — срезать сам спрайт.

libbgi: gfx_blit_cols_part_w(..., uint8_t maxw) — обрезка СПРАВА у
колоночного блита.  Для column-major это ровно уменьшение числа колонок, то
есть внутри ядра механизм уже был (так же клипается край экрана,
w = _bgi_maxx + 1 - x), наружу не выводился.  Тело блита переехало туда,
gfx_blit_cols_part стал тонкой обёрткой (maxw=0) — тем же приёмом, каким
gfx_blit_cols уже обёрнут вокруг gfx_blit_cols_part.  Работает и при flip:
первые maxw нарисованных колонок всегда ложатся в левую часть футпринта.
make size-check: роста нет.

PoP: pop_leveldoor_right / pop_leveldoor_ybottom (порт одноимённых глобалов)
пишет draw_leveldoor в pop_state — их читает clip_char из другого банка;
pop_clip_char_right() отдаёт границу, kid_draw превращает её в maxw и уводит
эти кадры с noclip-пути на общий.  Прямоугольник heal (kid_lw) сужается тоже
— стираем ровно нарисованное.

Проверено в MAME: pop_leveldoor_right = 176, что есть ровно (draw_xh<<3)+48
для двери комнаты 9; pop_leveldoor_ybottom = 112 у закрытой створки и 69 у
поднятой — сходится с формулой оригинала.  Отрисовку подтвердил пользователь
на живом подъёме.

Заодно: ROOMNAV остаётся включённым осознанно — это наш чит, которого в
оригинале не было, как и S/K/I; позже сведём в общий блок читов.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 23:43:01 +03:00

11 KiB
Raw Blame History

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 ревизия

Оптимизация отрисовки (записано 2026-07-29)

Не баги — план работ. Оба пункта про одно: у оригинала пометка тайла к перерисовке стоит копейки (бит в таблице, которая всё равно чистится каждый кадр), а у нас каждая такая пометка превращается в реальный heal (копию из ОЗУ-копии акселератора) плюс блиты. Поэтому буквальный порт «перерисовываем безусловно» корректен, но дорог.

T-1. Пики: перерисовывать по причине, а не безусловно

Сейчас: pop_process_trobs зовёт pop_spike_redraw каждый кадр для каждой живой пики в комнате (порт redraw_21h, который animate_spike вызывает вне всяких if). Это корректно, но лишнее для пик, до которых Киду дела нет.

Надо: перерисовывать тайл пики, только если

  1. сменился её видимый кадр (шаг выдвижения/уборки), ЛИБО
  2. её кто-то стёр — а стереть у нас может только 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_healkid_drawpop_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_y3), подъём по 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.c KBD_DBG_STEP*. Первый шаг в сторону = разворот (как в оригинале safe_step), движение со второго.
  • Читы (pop_cheat.h): K — убить стража, I — бессмертие (toggle), S — выдать меч.
  • Респавн после смерти — по (или авто через RESPAWN_DELAY); ставит Kid в стартовую позицию УРОВНЯ (pop_start_level, порт do_startpos).
  • ROOMNAV (=/-) — тоже наш чит, которого в оригинале не было, как и S. Все они со временем съедутся в общий блок читов, разрешаемый в настройках; пока просто включены (pop_cheats = 1 в roomtest.c).