Files
Sprinter-SDCC/applications/PoP/roomtest/bug_list.md
T
Александр Петров 5353bdaaec docs: приёмка уровня 2 — первым приоритетом, карта содержимого уровня
Порядок работ по решению пользователя: L2-PASS -> L3-CHOMP/SKEL/CHKP.
Причина техническая — чомперы лягут в банк 2, где живёт отрисовка, и
чинить баги фона поверх свежей механики дороже.

TASKS: запись L2-PASS с картой уровня 2, снятой с res2002.bin —
стражи (5, комнаты 4/7/11/15/24), ловушки и зелья по комнатам, и
декодированные из LINKLOC/LINKMAP цепочки «кнопка -> что открывает»
(в т.ч. кнопка к.9 @1,1, открывающая дверь выхода в к.23).  Плюс
отдельный список того, что сделано именно в L2 и на уровне 1 не
проверялось: большая склянка, меч с начала уровня, выход через дверь,
респавн на своём уровне.

bug_list: заведён раздел «Уровень 2» под список багов отрисовки,
который пользователь подаст отдельно.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 11:46:12 +03:00

287 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# roomtest — ОТКРЫТЫЕ баги и незакрытые оптимизации
Только то, что **не закрыто**. Всё закрытое (и, что важнее, разбор корней)
переехало в [`bug_closed.md`](bug_closed.md): прежде чем заводить новый баг,
грепни там по симптому.
Приоритеты работ — в [`TASKS.md`](TASKS.md), а не здесь. Правило проекта:
механику сверять с `../SDLPoP/src/` ДО кодинга.
Ревизия списка: **2026-08-03** (прогон всех комнат уровня 1). Одиннадцать
наблюдений с прогона свелись к шести корням; **все шесть закрыты и проверены
в MAME** — разбор в [`bug_closed.md`](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 (Кид поверх тела стража) и BUG-LOOSE-2 (осколки только от одной
из двух плит). У BUG-DRAWORDER-1 остался артефакт «ноги поверх головы»,
сверенный с SDLPoP и записанный в «НЕ БАГИ». Тогда же закрыт как
невоспроизводящийся BUG-SEAM-DRAW-1 (Кид в шве рисуется) и заведён
BUG-GATE-PASS-1 — однократное наблюдение прохода сквозь закрывшуюся решётку,
которое ЖДЁТ надёжного сценария.
| ID | что | тип | статус |
|----|-----|-----|--------|
| [Уровень 2](#уровень-2) | баги отрисовки с приёмки | — | **ждут списка от пользователя** |
| [BUG-GATE-PASS-1](#bug-gate-pass-1) | проход сквозь закрывшуюся решётку (0,9) комнаты 5 | Major | **ждёт сценария воспроизведения** |
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация | открыт |
| [T-2](#t-2) | Кид перерисовывается в покое | оптимизация | открыт |
| [обход 24 комнат](#обход-всех-24-комнат-уровня-1) | таблица заполнена на 5 строк из 24 | ревизия | открыт |
---
<a id="уровень-2"></a>
# Уровень 2 — баги с приёмки (заведено 2026-08-04)
Уровень 2 заиграл 2026-08-04 (см. [`TASKS.md`](TASKS.md) запись **L2**).
Пользователь при первом прогоне увидел **несколько багов отрисовки** и
подаст их списком отдельно — раздел заведён под них заранее, чтобы
наблюдения не растеклись по переписке.
Карта содержимого уровня (что где стоит по данным `res2002.bin`, какие
кнопки какие ворота открывают) — в [`TASKS.md`](TASKS.md#l2-pass),
запись **L2-PASS**: по ней видно, «механика не сработала» это или
«так и задумано».
Перед заведением записи — грепнуть [`bug_closed.md`](bug_closed.md) по
симптому: почти вся косметика окклюзии уровня 1 уже разбиралась
(BUG-CEIL-1/2/3, BUG-OCCL-1, BUG-DOOR-CLIP, BUG-DRAWORDER-1), и уровень 2
вводит ровно три новых фоновых тайла — **большая колонна (низ 8 / верх 9)
и верх двери (12)**; если артефакт рядом с ними, это первый подозреваемый.
*(записей пока нет)*
---
<a id="ручная-перепроверка-2026-08-03"></a>
# Ручная перепроверка фиксов (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 в `bug_closed.md`) |
| 8 | комната 6: бег справа налево от (0,9), длинный прыжок (0,6)→(0,7) | не влетает внутрь стены |
| 9 | комната 5: с кнопки (0,6) падение на (2,7), присед, вставание | остаётся в комнате 5 (проверено трассой: `fr=111 x=177``seq_47``fr=15 x=173`) |
| 10 | комната 5: нажать кнопку (0,4) | поднимаются ОБЕ решётки — (0,5) видно на экране, (0,9) проверять из комнаты 1 |
| 11 | страж (комнаты 3, 21) убивает Кида | смерть доигрывается, респавн в стартовой позиции уровня; цикла «убил-воскрес» нет |
| 12 | клавиатура: долгая игра с Shift+стрелка | ↑ и Shift не залипают; осторожный шаг не превращается в бег (доработано во второй волне — п.17) |
## Вторая волна (2026-08-03, вечер)
| # | что проверить | ожидаемо |
|---|---------------|----------|
| 17 | **[BUG-KBD-4]** долго играть Shift+стрелка, много тапов подряд | Shift не «отваливается»; если однажды залипнет — снимается первым же нажатием стрелки |
| 18 | **[BUG-RESPAWN-2]** убить стража (комнаты 3/21), умереть, вернуться в ту же комнату | страж снова жив, стоит на исходном тайле, HP полные |
Отдельно стоит посмотреть **на регрессии от порта `check_collisions`** — он
трогает всю горизонтальную коллизию: бамп в стену на бегу и в прыжке,
осторожный шаг у стены, проход через ОТКРЫТЫЕ ворота, разворот в проёме
решётки, переходы через швы (старый BUG-SEAM-PINGPONG).
---
# Открытые баги уровня 1
<a id="bug-gate-pass-1"></a>
## BUG-GATE-PASS-1. Проход сквозь закрывшуюся решётку — ЖДЁТ СЦЕНАРИЯ
**Статус: наблюдался один раз, воспроизвести повторно НЕ удалось.** Заведён,
чтобы наблюдение не потерялось; закрывать нельзя — ни как исправленный, ни как
«не баг», пока нет надёжного сценария.
**Что наблюдалось (2026-08-03).** Комната 5: Кид стоял НА тайле решётки (0,9)
и ждал, пока она опустится. После закрытия пошёл вправо — прошёл в комнату 1
и упал на (1,1).
**Что уже измерено и в чём загвоздка.** Сразу после наблюдения повторить не
получилось: в том же месте Кид стоит на `x = 196`, `col = 9`, и решётка его
ДЕРЖИТ — то есть штатно.
Арифметика оригинала объясняет разницу. `is_obstacle` (seg004) ставит
плоскость блокировки в `x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_MIDX`, для
колонки 9 это **x = 205**. При этом «колонка 9» по `get_tile_div_mod_m7`
это `x ∈ [191, 205)`. Пока `curr_col == 9`, Кид гарантированно левее
плоскости и обязан блокироваться; чтобы пройти, он должен оказаться **правее
205**, то есть уже на дальней стороне решётки, — и тогда уход вправо законен:
решётка закрылась у него за спиной, в оригинале она блокирует плоскость, а не
весь тайл.
Отсюда рабочая гипотеза: в момент наблюдения Кид стоял правее 205 (успел зайти
по тайлу дальше, пока решётка была поднята), и поведение штатное. Но повторить
эту позу и снять `x` пока не удалось, поэтому гипотеза НЕ подтверждена.
**Что снять в следующий раз** (без этих чисел вопрос не закрыть):
1. `Kid.x` и `Kid.curr_col` в момент, когда решётка уже закрылась, а Кид ещё
стоит на её тайле — до шага вправо;
2. модификатор решётки (openness) комнаты 5, тайл 9 — `can_bump_into_gate()`
считает её препятствием только пока `(modif >> 2) + 6 < char_height`, то
есть пока она опустилась достаточно низко относительно РОСТА кадра;
3. `Kid.x` покадрово на самом шаге вправо — где именно перестал блокировать.
Быстрый способ снять первое: отладочный стоп-кадр (**1** заморозить, **2**
продолжить), затем чтение `_Kid` из отладчика MAME.
**Возможный корень, если гипотеза не подтвердится.** Проверка идёт по колонке,
которая на шве уже принадлежит СОСЕДНЕЙ комнате (`curr_row_coll_room[]` в
оригинале); у нас межкомнатная коллизия на шве — исторически проблемное место
(ср. закрытый BUG-SEAM-PINGPONG). Второй кандидат — `char_height` в
`can_bump_into_gate()`: если он берётся не от того кадра, решётка может
перестать считаться препятствием раньше времени.
# Оптимизация отрисовки (записано 2026-07-29)
Не баги — план работ. Оба пункта про одно: у оригинала пометка тайла к
перерисовке стоит копейки (бит в таблице, которая всё равно чистится каждый
кадр), а у нас каждая такая пометка превращается в реальный heal (копию из
ОЗУ-копии акселератора) плюс блиты. Поэтому буквальный порт «перерисовываем
безусловно» корректен, но дорог.
<a id="t-1"></a>
## 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, отдельно не окупается.
<a id="t-2"></a>
## 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`](TASKS.md)).
---
# Обход всех 24 комнат уровня 1 — поиск багов отрисовки
Инструмент: `#define ROOMNAV` в `roomtest.c``+`/`-` (цифровой блок либо
`=`/`-` основного ряда) переключают комнату по номеру (1..24, с обёрткой),
Kid ставится на первый пол, `pop_trob_reset()` возвращает пики/ворота в
исходное. Номер комнаты — полосками в верхнем борте: слева десятки, справа
единицы (`||` `||||` = 24). Убирается закомментированием одной строки.
Комнаты **13, 18, 24 недостижимы** в обычной игре — это свойство данных
уровня, разбор в [`bug_closed.md`](bug_closed.md) («НЕ БАГИ»). Приоритет
багов в них низкий.
Таблица заполнена на 5 строк из 24 — доделать в рамках L1-TRIAGE
([`TASKS.md`](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`).
---
<a id="сырые-наблюдения-прогон-2026-08-03"></a>
## Сырые наблюдения (прогон 2026-08-03) → корень
Формулировки с прогона, как записаны. Закрытые корни — в
[`bug_closed.md`](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 | закрыт |