Files
Sprinter-SDCC/applications/PoP/roomtest/bug_list.md
T
Александр Петров a89b8b30c6 docs: BUG-DRAWORDER-1 закрыт; остаток «ноги поверх головы» — в НЕ БАГИ
Сверено в SDLPoP: Кид, стоящий колонкой правее лежащего трупа, и там
рисуется поверх его головы.  Значит порт верен, а артефакт врождённый:
set_objtile_at_char приписывает персонажа РОВНО ОДНОМУ тайлу, ширина
спрайта на выбор тайла не влияет, и всё, что стоит правее, перекрывает
выступающую часть тела.  Механизма «широкий объект в нескольких тайлах» в
оригинале нет — сверено с draw_objtable_items_at_tile, sort_curr_objs и
веткой tile_object_redraw == 0xFF (та про оверлеи пола).

Закрытая запись перечисляет все четыре корня, которые пришлось снять по
очереди: порядок по роли вместо тайла; своя регрессия с общим окном
fore-клипа; колонка трупа из тайла вместо X; непортированная ветка
actions_1_run_jump.  Записано и то, как отличать остаток от этих багов,
и цена «починки» остатка (тайл по центру габарита = отход от эталона).

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

22 KiB
Raw Blame History

roomtest — ОТКРЫТЫЕ баги и незакрытые оптимизации

Только то, что не закрыто. Всё закрытое (и, что важнее, разбор корней) переехало в bug_closed.md: прежде чем заводить новый баг, грепни там по симптому.

Приоритеты работ — в TASKS.md, а не здесь. Правило проекта: механику сверять с ../SDLPoP/src/ ДО кодинга.

Ревизия списка: 2026-08-03 (прогон всех комнат уровня 1). Одиннадцать наблюдений с прогона свелись к шести корням; все шесть закрыты и проверены в MAME — разбор в bug_closed.md: BUG-LVLSTATE-1 (уровень стал мутабельным), BUG-RESPAWN-1 (респавн перезагружает уровень), BUG-DEATH-1 (смерть от меча), BUG-GATE-ANIM-1 (анимация ворот), BUG-COLL-1 (полный порт check_collisions), BUG-STANDUP-1 (лишний guard в bumped_floor). Клавиатурный BUG-KBD-3 доведён до конца во второй волне (см. ниже); из первой волны открытым остался косметический BUG-SEAM-DRAW-1.

Ревизия 2026-08-03, вечер (вторая волна с прогона): четыре наблюдения → BUG-KBD-4 (фантомный отжим Shift), BUG-RESPAWN-2 (стражи не оживают) и BUG-DRAWORDER-1 (Кид поверх тела стража) — закрыты; у последнего остался артефакт «ноги поверх головы», сверенный с SDLPoP и записанный в «НЕ БАГИ». BUG-LOOSE-2 (осколки только от одной из двух плит) — исправлен по SDLPoP, но гонка автоматикой не воспроизведена (см. таблицу ниже).

ID что тип статус
BUG-LOOSE-2 осколки только от одной из двух падающих плит (комната 12) Minor фикс есть, гонка не воспроизведена
BUG-SEAM-DRAW-1 у решётки на шве экран переключается, а Кид в шве НЕ рисуется Minor открыт
T-1 пики перерисовываются безусловно оптимизация открыт
T-2 Кид перерисовывается в покое оптимизация открыт
обход 24 комнат таблица заполнена на 5 строк из 24 ревизия открыт

Ручная перепроверка фиксов (2026-08-03)

Шесть корней закрыты автоматической проверкой в MAME (мост mame-z80: чтение _Kid, потиковые трассы, скриншоты) — этого достаточно, чтобы показать, что конкретный сценарий больше не воспроизводится, но НЕ достаточно, чтобы поймать регрессии в соседней механике. Ниже — сценарии ровно в тех формулировках, в которых баги были заведены.

Собрано и лежит в образе (make + make hdd уже прогнаны; libc пересобран, make size-check без роста).

# что проверить ожидаемо
1 комната 22: выпить зелье (0,6), выйти в 16/23 и вернуться кувшина нет, пузырька над пустым местом нет
2 то же для комнат 14 (0,5) и 17 (2,3) так же
3 комната 15: подобрать меч (2,2), выйти и вернуться меча нет; кладка на дальней стене НЕ мигает
4 комната 12: разбить плиты (0,1)/(0,2) и потолок в 16, выйти-вернуться остаются разбитыми, проём не закрывается
5 комната 17 из 23: разбить (1,5)/(1,6), выпить зелье (2,3), вернуться всё остаётся
6 после смерти зайти в те же комнаты ВСЁ восстановлено (в оригинале смерть = load_level)
7 комната 12: разбег в закрытую решётку (0,9) с полушага не проходит насквозь; экран может перелистнуться — это BUG-SEAM-DRAW-1
8 комната 6: бег справа налево от (0,9), длинный прыжок (0,6)→(0,7) не влетает внутрь стены
9 комната 5: с кнопки (0,6) падение на (2,7), присед, вставание остаётся в комнате 5 (проверено трассой: fr=111 x=177seq_47fr=15 x=173)
10 комната 5: нажать кнопку (0,4) поднимаются ОБЕ решётки — (0,5) видно на экране, (0,9) проверять из комнаты 1
11 страж (комнаты 3, 21) убивает Кида смерть доигрывается, респавн в стартовой позиции уровня; цикла «убил-воскрес» нет
12 клавиатура: долгая игра с Shift+стрелка ↑ и Shift не залипают; осторожный шаг не превращается в бег (доработано во второй волне — п.17)

Вторая волна (2026-08-03, вечер)

# что проверить ожидаемо
14 [BUG-LOOSE-2] комната 12: разбежаться по плитам (0,1)/(0,2) и уйти в 15 ДО того, как долетит вторая; вернуться осколки на ОБОИХ — (1,1) и (1,2)
15 то же в комнате 7 (плиты 0,5/0,6 → 2,5/2,6) не сломалось: по-прежнему двое осколков
16 плита, улетевшая из комнаты вниз, при возврате лишнего щебня не появилось
17 [BUG-KBD-4] долго играть Shift+стрелка, много тапов подряд Shift не «отваливается»; если однажды залипнет — снимается первым же нажатием стрелки
18 [BUG-RESPAWN-2] убить стража (комнаты 3/21), умереть, вернуться в ту же комнату страж снова жив, стоит на исходном тайле, HP полные

Отдельно стоит посмотреть на регрессии от порта check_collisions — он трогает всю горизонтальную коллизию: бамп в стену на бегу и в прыжке, осторожный шаг у стены, проход через ОТКРЫТЫЕ ворота, разворот в проёме решётки, переходы через швы (старый BUG-SEAM-PINGPONG).


Открытые баги уровня 1

BUG-LOOSE-2. Осколки только от одной из двух падающих плит

Симптом. Комната 12, соседние падающие плиты (0,1) и (0,2): после пробежки осколки появляются только на (1,2), на (1,1) — нет. В комнате 7 (плиты 0,5/0,6 → 2,5/2,6) обе дают осколки.

Корень (гипотеза автора отчёта подтвердилась чтением кода). Наш падающий кусок был привязан к ТЕКУЩЕЙ комнате: pop_loose_reset() на смене комнаты звал pop_loose_mob_reset() и гасил всё, что ещё летит, а тайл под куском читался из карты отрисованной комнаты. Кид убегает в комнату 15 раньше, чем долетает плита (0,1) — кусок уничтожается, loose_land не случается, щебень не кладётся. В комнате 7 обе плиты успевают сесть до ухода, поэтому там всё выглядело правильно.

В оригинале mobs[] чистится ТОЛЬКО в start_level (seg003:88), do_mobs (seg007:1063) прокручивает все куски независимо от drawn_room, move_loose работает по curmob.room, а redraw_at_cur_mob (seg007:132C) сверяется с drawn_room лишь для ОТРИСОВКИ.

Фикс. У куска появилось поле room (curmob.room); на смене комнаты зовётся pop_loose_mob_room_changed() — сбрасывает только heal-историю; тайл читается через mob_tile_at() (своя комната — tile_code, чужая — pop_level_tile); приземление в покинутой комнате пишет щебень прямо в данные уровня; отрисовка и check_loose_fall_on_kid (там оригинал начинает с Char.room == curmob.room) — только для своей комнаты. Настоящий mobs_count = 0 переехал в pop_start_level.

Не проверено. Гонка «уйти из комнаты раньше, чем долетит плита» автоматикой не воспроизведена. Ручной прогон — пп. 14–16 таблицы выше.

BUG-SEAM-DRAW-1. Кид, упершийся в решётку на шве, не рисуется

Симптом. Комната 5: Кид добегает до закрытой решётки (0,9), решётка его ДЕРЖИТ (проверено: x встаёт на 60 и дальше не растёт), но экран переключается на комнату 1, и в ней Кид не виден — он стоит на x = 57..60, левее кромки комнаты (col 0 начинается с 58).

Разбор. Переключение экрана само по себе штатное: leave_room (seg002:423) уводит вправо при char_x_right >= 201, а левая грань решётки как раз 201 — то есть в оригинале экран тоже перелистнётся, если персонаж встанет ровно на грань. Не штатно то, что персонажа в шве не рисуют: update_kid_render_dx даёт смещение только когда kid_room != cur_room, а enter_room сразу выравнивает kid_room = cur_room. Это остаток непортированной модели straddle (S3 в pop_kid_plan).

Что делать. Довести S3: держать Kid.room отдельно от drawn_room и рисовать персонажа со смещением ±140, как xpos_in_drawn_room (seg004:0405).

Оптимизация отрисовки (записано 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).

Сырые наблюдения (прогон 2026-08-03) → корень

Формулировки с прогона, как записаны. Закрытые корни — в bug_closed.md.

# наблюдение (кратко) корень статус
1 кувшин выпит, а пузырёк рисуется / кувшин возвращается (14, 22, 17) BUG-LVLSTATE-1 закрыт
2 меч возвращается в 15 / мигает контур кладки BUG-LVLSTATE-1 закрыт
3 комната 12: пробегает сквозь закрытую решётку (0,9) BUG-COLL-1 закрыт
4 после respawn плиты остаются разбитыми, зелья выпитыми BUG-RESPAWN-1 закрыт
5 плита/зелье возвращаются и БЕЗ respawn (12→16, 17, 22) BUG-LVLSTATE-1 закрыт
6 комната 6: длинный прыжок (0,6)→(0,7) — влёт в стену, респавн BUG-COLL-1 закрыт
7 залипает ↑ BUG-KBD-3 закрыт (см. bug_closed.md)
8 залипает Shift BUG-KBD-3 / BUG-KBD-4 закрыт (см. bug_closed.md)
9 комната 5: кнопка (0,4) не открывает решётку (0,5) BUG-GATE-ANIM-1 закрыт
10 комната 5: с кнопки (0,6) на (2,7), присед — провал в комнату 6 BUG-STANDUP-1 закрыт
11 страж убил Кида → Кид воскресает на месте и его убивают снова BUG-DEATH-1 закрыт