Уровень 4: skip по маске тайлов + порт loose_land, ворота 0xFF, пламя в фон
ОПТИМИЗАЦИЯ. Метка «фон трогали» была одним union-прямоугольником на страницу, и три факела комнаты склеивались в полосу x 40..280 на всю комнату — Кид, стоящий между крайними факелами, терял пропуск перерисовки и каждый кадр платил полным fore-проходом. Теперь это маска тайлов (uint16_t pop_cd_dmask[2][3]: бит = колонка, слово = ряд, набор = страница), проверка — три AND через резидентный pop_cd_hit. Гранулярность тайла — это гранулярность оригинала (redraw_frames_anim[tilepos], set_wipe; подтайловое уточнение там только по высоте, wipe_heights). Замер на (1,7): циан 233 515 -> 24 781, работа за кадр 517 609 -> 306 553, период 4 -> 3 растровых кадра. BUG-LOOSE-BUTTON-1. Упавшая плита не нажимала кнопку. Три слоя: порт loose_land не звал trigger_button вовсе; pop_room_col_landing считала площадкой только чистый пол, а у оригинала их семь (пол, пика, обе кнопки, зелье, оба факела); сигнал приходил в момент ОТРЫВА плиты, из-за чего ворота начинали открываться, пока она ещё в воздухе. Нажатие в оригинале ОДНО, но с button_type = tiles_14_debris — это «открыть НАСОВСЕМ» (modifier 0xFF), и кнопка съедается. Посадка в комнате снизу переехала на новый сигнал pop_loose_exit (взводит mob_tick_one, когда кусок ушёл ниже поля). BUG-GATE-FF-1. 0xFF был перегружен: сторожевое «тайла нет» в gate_modif и живое «открыто навсегда» из trigger_gate. gate_passable заворачивал Кида в воротах, нарисованных открытыми. Мёртвая ветка убрана. BUG-TORCH-CHOMP-1. Чомпер (0,7) комнаты 23 healит x 224..255 / y 30..93 и стирал пламя факела (0,6), которое рисуется в ячейке правого соседа. Фикс — запекать пламя (GFX_BANK_NORMAL): у факела все девять кадров на общем канвасе 16x18 и непрозрачны, протухнуть в ОЗУ-копии нечему, а heal чомпера сам возвращает огонь и кладёт челюсти поверх — z-порядок как в оригинале. Пузырёк зелья так нельзя (ползёт вверх, нужен heal) — остаётся в SPRITE. Заведено открытым: died_on_button (seg007:776) не портирован — нужен тайл tiles_5_stuck в атласе. Проверено пользователем в MAME (уровень 4: плита 16(1,1) -> кнопка 17(0,1) -> ворота 23(0,9); комната 23 с чомпером и факелом); make -C tests-host — все 5 наборов. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,96 @@
|
||||
|
||||
---
|
||||
|
||||
<a id="bug-loose-button-1"></a>
|
||||
## BUG-LOOSE-BUTTON-1. Упавшая плита не нажимает кнопку — **ЗАКРЫТ 2026-08-10**
|
||||
|
||||
**Симптом (пользователь, уровень 4).** Плита `16(1,1)` падает вниз, в комнате
|
||||
17 ничего не меняется: кнопка цела, щебня нет, ворота не открылись.
|
||||
|
||||
**Корень — три слоя.**
|
||||
|
||||
1. Порт `loose_land` (seg007:11E8) у нас вообще не звал `trigger_button`: и в
|
||||
своей комнате (`pop_map.c`), и в комнате снизу (`roomtest.c`) он только
|
||||
клал щебень.
|
||||
2. `pop_room_col_landing` (pop_level.c) считала площадкой **только чистый
|
||||
пол** (`t == 1`). У оригинала площадок семь — `tiles_1_floor`,
|
||||
`tiles_2_spike`, `tiles_6_closer`, `tiles_10_potion`, `tiles_15_opener`,
|
||||
`tiles_19_torch`, `tiles_30_torch_with_debris`; на всём прочем кусок просто
|
||||
исчезает. Кнопка в набор не входила, поэтому плита растворялась.
|
||||
3. Сигнал `pop_loose_fell` взводится в момент ОТРЫВА плиты, а по нему главный
|
||||
цикл и щебень клал, и кнопку жал — ворота начинали открываться, пока плита
|
||||
ещё в воздухе.
|
||||
|
||||
**Механика оригинала (важно, интуиция обманывает).** Плита не «постоянно
|
||||
давит» на кнопку. Нажатие ОДНО, но с `button_type = tiles_14_debris`, а это у
|
||||
`trigger_gate` отдельная ветка: возврат 2 → `animate_door` доводит створку и
|
||||
ставит `curr_modifier = 0xFF` → ворота заморожены открытыми навсегда, обычный
|
||||
opener их больше не трогает (`if (modifier == 0xFF) return -1`). Сама кнопка
|
||||
съедается: тайл становится щебнем. На closer — быстрое закрытие и тоже
|
||||
щебень. На факеле остаётся отдельный тайл `tiles_30_torch_with_debris`.
|
||||
|
||||
**Фикс.** Набор площадок расширен до оригинального; `pop_trigger_button`
|
||||
зовётся в обоих местах с правильным `button_type`; посадка в комнате снизу
|
||||
переехала с `pop_loose_fell` на новый сигнал `pop_loose_exit`, который
|
||||
`mob_tick_one` взводит, когда кусок ушёл ниже поля комнаты.
|
||||
|
||||
**Осознанное расхождение.** Оригинал через `mob_down_a_row` продолжает
|
||||
симуляцию куска уже в нижней комнате; у нас он там не летит, а садится сразу.
|
||||
Задержка получается «высота комнаты», а не «высота комнаты плюс путь до пола
|
||||
внизу».
|
||||
|
||||
---
|
||||
|
||||
<a id="bug-gate-ff-1"></a>
|
||||
## BUG-GATE-FF-1. Ворота, открытые навсегда, нарисованы открытыми, но непроходимы — **ЗАКРЫТ 2026-08-10**
|
||||
|
||||
**Симптом.** После BUG-LOOSE-BUTTON-1 ворота `23(0,9)` рисуются поднятыми, но
|
||||
Кид в них упирается.
|
||||
|
||||
**Корень — перегруженное значение.** `gate_modif` (pop_map.c) отдаёт `0xFF`
|
||||
как сторожевое «тайла нет за краем уровня», а `trigger_gate` ставит тот же
|
||||
`0xFF` как живой модификатор «открыто НАСОВСЕМ». В `gate_passable` стояло
|
||||
`if (gm == 0xFF) return 0;`. До сегодняшнего дня значение в игре не
|
||||
возникало — ветку по щебню никто не запускал.
|
||||
|
||||
**Фикс.** Ветка убрана: она мёртвая (нас зовут только когда тайл ТОЧНО
|
||||
ворота), а `(0xFF >> 2) + 6 = 69` и так больше любого `fph`. Остальные
|
||||
читатели `gate_modif` трактуют 63 как «открыто» и правок не потребовали.
|
||||
|
||||
---
|
||||
|
||||
<a id="bug-torch-chomp-1"></a>
|
||||
## BUG-TORCH-CHOMP-1. Чомпер стирает пламя соседнего факела — **ЗАКРЫТ 2026-08-10**
|
||||
|
||||
**Симптом (пользователь).** Комната 23 уровня 4: факел есть, огня нет.
|
||||
|
||||
**Корень.** Пламя рисуется в ячейке ПРАВОГО соседа (`x = COL_XH[col+1]*8 + 8`
|
||||
= 232..248, `y` 33..50), то есть в тайле чомпера (0,7). `pop_chomp_redraw`
|
||||
начинается с `pop_heal_off(224, 2, 32, 64)` — восстанавливает запечённый фон в
|
||||
`x 224..255, y 30..93`. Пламя в фон не запекалось и рисовалось в
|
||||
`GFX_BANK_SPRITE`, поэтому heal его съедал; вдобавок `pop_redraw_needed` идёт
|
||||
ПОСЛЕ `pop_process_trobs`, так что стиралось только что нарисованное.
|
||||
|
||||
**Тупик, в который я сначала свернул.** Вынес отрисовку пламени в отдельный
|
||||
проход после `pop_redraw_needed` — стало хуже: пламя легло ПОВЕРХ челюстей
|
||||
(правильный z-порядок существует только внутри общего обхода тайлов, где
|
||||
`draw_tile_anim_right` идёт до `draw_tile_anim`), плюс проход индексировал
|
||||
`trob_code[i]` параллельно `trobs[i]`, а `pop_process_trobs` в конце уплотняет
|
||||
массив — индексы разъезжались, и чужой trob рисовался как факел (пламя из
|
||||
головы Кида). Проход откачен.
|
||||
|
||||
**Фикс (подсказан пользователем).** Пламя ЗАПЕКАЕТСЯ: `pop_torch_draw` пишет
|
||||
в `GFX_BANK_NORMAL` (видео-ОЗУ + ОЗУ-копия). Это безопасно именно у факела —
|
||||
все девять кадров лежат на общем канвасе 16×18 и упакованы `opaque=True`, то
|
||||
есть кадр полностью накрывает предыдущий, протухнуть в копии нечему; и факел
|
||||
всегда задний план. Тогда heal чомпера возвращает огонь сам, а челюсти
|
||||
ложатся поверх — тот же z-порядок, что в оригинале, и без лишнего прохода.
|
||||
|
||||
Пузырёк ЗЕЛЬЯ так нельзя: он ползёт вверх, себя не накрывает и требует heal —
|
||||
`pop_potion_draw` остаётся в `GFX_BANK_SPRITE`. Столкнуться с чомпером в
|
||||
одном тайле зелье не может.
|
||||
|
||||
|
||||
<a id="bug-guard-ix-1"></a>
|
||||
## BUG-GUARD-IX-1. «Зависание» при бое со стражем — затёртый IX главного цикла — **ЗАКРЫТ 2026-08-10**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user