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

187 lines
13 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 — ОТКРЫТЫЕ баги и незакрытые оптимизации
Только то, что **не закрыто**. Всё закрытое (и, что важнее, разбор корней —
odd-pixel арифметика `char_x`, подстановка тайла нажатой кнопки, баг
кодогенератора SDCC) переехало в [`bug_closed.md`](bug_closed.md): прежде чем
заводить новый баг, грепни там по симптому.
Приоритеты работ — в [`TASKS.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`](bug_closed.md)).
| ID | что | тип |
|----|-----|-----|
| [BUG-DOOR-CLIP](#bug-door-clip) | Кид не обрезается правым косяком двери уровня | Medium |
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация |
| [T-2](#t-2) | Кид перерисовывается в покое | оптимизация |
| [обход 24 комнат](#обход-всех-24-комнат-уровня-1) | таблица заполнена на 5 строк из 24 | ревизия |
---
<a id="bug-door-clip"></a>
## BUG-DOOR-CLIP. Подъём по лестнице двери уровня: нет обрезки по правому косяку — **Medium**
**Симптом (2026-08-01, найден пользователем сразу после L1-EXIT):** при
подъёме по лестнице за дверью уровня (seq_70, кадры 224..228) силуэт Кида
вылезает ПРАВЕЕ правого косяка проёма — должен уходить за него. По ВЫСОТЕ
обрезка корректна.
**Причина — недопортированная половина `clip_char`** (seg006:1231). Для
кадров двери уровня оригинал ставит ДВА клипа, а мы портировали только
первый:
```c
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 (копию из
ОЗУ-копии акселератора) плюс блиты. Поэтому буквальный порт «перерисовываем
безусловно» корректен, но дорог.
<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 в `START_ROOM`.
- `START_ROOM=1 / COL 3 / ROW 0` — жёстко зашитый тестовый старт; перевести
на `pop_level_start_*` (задача L1-START в [`TASKS.md`](TASKS.md)).