Уровень 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:
2026-08-10 17:52:04 +03:00
parent 6a824dd7ab
commit 4718bff767
13 changed files with 364 additions and 47 deletions
+28 -5
View File
@@ -355,9 +355,11 @@ static void draw_tile(int row, int col)
} else if (lcode == 16) { /* дверь уровня */
draw_leveldoor(lmod, xh, dmy);
}
/* Пламя факела (seg008:560, chtab_1 id modifier+1) в фон НЕ запекаем:
* оно анимируется каждый кадр, а heal вернул бы запечённый кадр из-под
* нового. Рисует pop_torch_draw (динамика, pop_process_trobs). */
/* Пламя факела (seg008:560, chtab_1 id modifier+1) здесь, в статичной
* отрисовке тайла, не рисуем — его ведёт pop_torch_draw из
* pop_process_trobs каждый кадр. Но ЗАПЕКАЕТСЯ оно всё равно (там
* GFX_BANK_NORMAL): факел — чистый задний план, а самонакрытие кадров
* делает запекание безопасным. Подробности — в шапке pop_torch_draw. */
}
/* --- draw_tile_bottom (seg008.c:570) --- */
@@ -608,8 +610,22 @@ void pop_torch_draw(int row, int col, uint8_t modif) __banked
/* heal НЕ нужен: оригинал рисует пламя blitters_0_no_transp (seg008
* draw_tile_anim_right), и все 9 кадров лежат на общем канвасе 16x18 —
* непрозрачный блит сам полностью накрывает предыдущий кадр. Кадры
* упакованы с opaque=True (pop_pack_bg POT_FLAME_IDS). */
gfx_set_bank(GFX_BANK_SPRITE);
* упакованы с opaque=True (pop_pack_bg POT_FLAME_IDS).
*
* И ровно поэтому пламя ЗАПЕКАЕТСЯ (GFX_BANK_NORMAL: видео-ОЗУ + ОЗУ-копия),
* а не рисуется в SPRITE поверх фона. Запечь его безопасно именно из-за
* самонакрытия — следующий кадр сотрёт предыдущий сам, «протухнуть» в
* копии нечему. А выигрыш принципиальный: точечные перерисовки соседних
* тайлов начинаются с heal из ОЗУ-копии, и незапечённое пламя они стирали
* — чомпер (0,7) комнаты 23 уровня 4 healит x 224..255 / y 30..93 и
* съедал факел (0,6), который рисуется в ячейке правого соседа. С
* запеканием heal возвращает огонь сам, а челюсти ложатся ПОВЕРХ него —
* тот же z-порядок, что у оригинала, где draw_tile_anim_right (пламя
* левого соседа) идёт до draw_tile_anim (сам чомпер).
*
* Пузырёк ЗЕЛЬЯ так нельзя: он ползёт вверх и себя не накрывает, ему
* нужен heal — pop_potion_draw остаётся в SPRITE. */
gfx_set_bank(GFX_BANK_NORMAL);
pop_pot_b((uint8_t)(modif + 1), x, yb);
gfx_set_bank(GFX_BANK_NORMAL);
}
@@ -889,6 +905,13 @@ static void mob_tick_one(mob_t *m, uint8_t pg)
}
}
if (m->y - 27 >= POP_PLAYFIELD_H) { /* весь ниже поля (улетел из комнаты) */
/* Приземление в комнате СНИЗУ разбирает главный цикл — но ИМЕННО
* сейчас, когда кусок долетел, а не в момент отрыва плиты: иначе
* кнопка внизу срабатывала, пока плита ещё в воздухе. */
if (!pop_loose_exit) {
pop_loose_exit = (uint8_t)(m->col + 1);
pop_loose_exit_room = m->room;
}
m->active = 0; m->clean = (uint8_t)(here ? 2 : 0);
return;
}