Files
Sprinter-SDCC/applications/PoP/roomtest/bug_list.md
T
Александр Петров e86f254b87 L1-START + L1-EXIT: старт по данным уровня и выход через дверь уровня
L1-START.  Старт и оба рестарта (смерть, выпадение из уровня) сведены в
pop_start_level() — порт start_level + do_startpos + set_start_pos (seg003).
Комната/тайл/направление берутся из pop_level_start_*, направление
инвертируется (~start_dir), поза входа — из tbl_entry_pose: у уровня 1 это
падение внутрь (seq_7_fall) плюс нажатие кнопки room5(0,2), то самое, что
захлопывает решётку за спиной.  Жёсткие START_ROOM/COL/ROW убраны.
Проверено в MAME: старт даёт room 1, col 0, падение на row 1 — как по данным.

L1-EXIT.  Ветка двери уровня из up_pressed + go_up_leveldoor (seg005:0482/
0574): тайлы и геометрия — pop_leveldoor_enter() в pop_map, последовательность
seq_70 — в pop_ctrl.  Опкод 0xF1 END_LEVEL в play_seq инкрементит
pop_next_level (порт next_level), главный цикл по нему перезапускает уровень
— ровно та точка, куда levels_plan §2.2 подключит загрузку уровня 2.
Открытость двери проверяется по modifier >= 42 (ветка fix_exit_door), а не по
ванильному leveldoor_open: иначе можно войти в ещё ползущую створку.

Отдельно стоило разбора: go_up_leveldoor сначала писал Char.x/Char.direction,
и оба присваивания молча терялись — окно Char вокруг диспетчера возвращает
назад только curr_seq и sword (pop_savekid_state).  Направление оставалось
«вправо», а все DX в seq_70 отрицательные, поэтому Кид уходил ИЗ проёма
влево (поймано стоп-кадром).  Геометрию персонажа в этом порте меняет
pop_map, пишет в Kid — как pop_down_action и pop_jump_up_seq.

Известный остаток — BUG-DOOR-CLIP в bug_list.md: нет обрезки силуэта правым
косяком проёма (недопортирован obj_clip_right в clip_char); нужен вариант
колоночного блита с ограничением ширины.  ROOMNAV пока оставлен включённым —
он нужен, чтобы попадать в комнату 9 для этой работы.

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

13 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 что тип
BUG-DOOR-CLIP Кид не обрезается правым косяком двери уровня Medium
T-1 пики перерисовываются безусловно оптимизация
T-2 Кид перерисовывается в покое оптимизация
обход 24 комнат таблица заполнена на 5 строк из 24 ревизия

BUG-DOOR-CLIP. Подъём по лестнице двери уровня: нет обрезки по правому косяку — Medium

Симптом (2026-08-01, найден пользователем сразу после L1-EXIT): при подъёме по лестнице за дверью уровня (seq_70, кадры 224..228) силуэт Кида вылезает ПРАВЕЕ правого косяка проёма — должен уходить за него. По ВЫСОТЕ обрезка корректна.

Причина — недопортированная половина clip_char (seg006:1231). Для кадров двери уровня оригинал ставит ДВА клипа, а мы портировали только первый:

if (frame >= frame_224_exit_stairs_8 && frame < 229) {
    obj_clip_top   = leveldoor_ybottom + 1;   // ЕСТЬ (pop_clip_char_top)
    obj_clip_right = leveldoor_right;          // НЕТ
}

leveldoor_right = (draw_xh << 3) + 48 считается в draw_leveldoor (seg008:1D29) от колонки ЛЕВОЙ половины двери; для palace-уровней (+8, tbl_level_type) — на уровне 1 не применяется.

Обратить внимание: комментарий в SDLPoP говорит «frames 217..228», а КОД проверяет >= frame_224_exit_stairs_8, то есть 224..228. Портировать по коду.

Чего не хватает в движке: обрезки СПРАВА у колоночного блита. gfx_blit_cols_part (libbgi) умеет только обрезку сверху (skip/rows) плюс клип по краю ЭКРАНА (w = _bgi_maxx + 1 - x). Для column-major спрайта обрезка справа — это ровно уменьшение w, то есть внутри ядра она уже есть, не хватает способа задать её снаружи.

План (не начат):

  1. libbgi: тело gfx_blit_cols_part вынести в вариант с ограничением ширины (напр. gfx_blit_cols_part_w(..., uint8_t maxw)), а нынешнюю функцию оставить тонкой обёрткой — тем же приёмом, каким gfx_blit_cols уже обёрнут вокруг gfx_blit_cols_part. Флип тут не мешает: в кадрах двери направление всегда dir_FF_left, то есть flip=0.
  2. PoP: pop_clip_char_top дополнить правым клипом (или завести pop_clip_char_right), считать leveldoor_right от колонки двери.
  3. make size-check (трогаем libbgi) + проверка стоп-кадром в MAME: поймать кадр 224..228 клавишей 1 и сверить кромку.

Пока не починено, дефект косметический — вход на следующий уровень работает.


Оптимизация отрисовки (записано 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 в START_ROOM.
  • START_ROOM=1 / COL 3 / ROW 0 — жёстко зашитый тестовый старт; перевести на pop_level_start_* (задача L1-START в TASKS.md).