From 67a4c71138c9287077303763a233b7af049e4574 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Mon, 10 Aug 2026 21:16:58 +0300 Subject: [PATCH] =?UTF-8?q?BUG-TORCH-CHOMP-2:=20=D0=B7=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D1=8B=D0=B2=D1=88=D0=B8=D0=B9=20=D1=87=D0=BE=D0=BC=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=20=D0=BD=D0=B0=D0=BA=D1=80=D1=8B=D0=B2=D0=B0=D0=BB=D1=81?= =?UTF-8?q?=D1=8F=20=D0=BF=D0=BB=D0=B0=D0=BC=D0=B5=D0=BD=D0=B5=D0=BC=20?= =?UTF-8?q?=D1=84=D0=B0=D0=BA=D0=B5=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Регрессия от BUG-TORCH-CHOMP-1 (пламя перевели на запекание в фон). Аргумент «запекать безопасно, кадры пламени самонакрываются» верен для пикселей самого факела, но не для чужой графики в той же ячейке: пламя рисуется в клетке ПРАВОГО СОСЕДА (seg008:560), и челюсти чомпера возвращал поверх огня только его собственный trob — пока анимация жива. SDLPoP так не делает: animate_torch (seg007:0241) заканчивается вызовом set_redraw_anim_right(), который метит правого соседа, а redraw_needed (seg008:0178) рисует его слой строго в порядке draw_tile_anim_topright -> draw_tile_anim_right (пламя) -> draw_tile_anim (СВОЯ графика тайла). То есть челюсти возвращаются поверх огня КАЖДЫЙ кадр факела, независимо от собственной анимации чомпера. У нас пламя рисуется напрямую, минуя механизм пометок, — этой второй половины не было. Фикс: после pop_torch_draw метим правого соседа POP_RD_CHOMP на ОДНУ страницу (факел анимируется каждый кадр -> обе страницы получат свою перерисовку по очереди). Порядок сходится сам: блок факелов идёт до pop_redraw_needed. Код соседа читается в том же префетче кодов тайлов (trob_rcode[]), чтобы не свапать W0 второй раз за кадр. Банк 6 +104 Б. Осознанное расхождение (оригинал метит соседа безусловно, мы — только под чомпера) заведено открытым: TORCH-ANIM-RIGHT в bug_list.md. Слой draw_tile_anim рисует ещё пики/зелье/меч, но такого соседства на уровнях 1-4 не встретилось, а безусловная пометка стоит перерисовки тайла каждый кадр на каждый факел. tests-host: все 5 наборов прошли. Co-Authored-By: Claude Opus 5 --- applications/PoP/roomtest/bug_closed.md | 48 +++++++++++++++++++++++++ applications/PoP/roomtest/bug_list.md | 26 ++++++++++++++ applications/PoP/roomtest/pop_trob.c | 46 +++++++++++++++++++++--- 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/applications/PoP/roomtest/bug_closed.md b/applications/PoP/roomtest/bug_closed.md index 8fe213d..d74e142 100644 --- a/applications/PoP/roomtest/bug_closed.md +++ b/applications/PoP/roomtest/bug_closed.md @@ -2206,3 +2206,51 @@ BUG-SEAM-PINGPONG (см. `bug_closed.md`). Приёмка: (ср. закрытый BUG-SEAM-PINGPONG). Второй кандидат — `char_height` в `can_bump_into_gate()`: если он берётся не от того кадра, решётка может перестать считаться препятствием раньше времени. + +--- + +## BUG-TORCH-CHOMP-2. Застывший чомпер накрывается пламенем факела — ЗАКРЫТ 2026-08-10 + +Симптом (уровень 4, скриншот пользователя): пока чомпер щёлкает, всё в +порядке; как только его анимация кончается, пламя соседнего факела ложится +ПОВЕРХ челюстей. + +Регрессия от фикса [BUG-TORCH-CHOMP-1](#bug-torch-chomp-1): там пламя +перевели на ЗАПЕКАНИЕ в фон (`GFX_BANK_NORMAL`), чтобы `heal` соседнего +чомпера не съедал огонь. Аргумент «запекать безопасно, кадры пламени +самонакрываются» верен для СОБСТВЕННЫХ пикселей факела, но не для чужой +графики в той же ячейке: пламя рисуется в клетке ПРАВОГО СОСЕДА +(`x = (col+1)*32 + 8`, seg008:560), и если сосед — чомпер, огонь ложится на +его челюсти. Пока trob чомпера жив, `pop_set_redraw(tp, POP_RD_CHOMP, ...)` +возвращает их поверх каждый кадр; как только анимация кончилась и trob +умер, возвращать стало нечем. + +**Как это устроено в оригинале** (и почему у него бага нет). +`animate_torch` (seg007:0241) заканчивается вызовом `set_redraw_anim_right()` +— он метит `redraw_frames_anim` у ПРАВОГО СОСЕДА (seg007:0101). А +`redraw_needed` (seg008:0178) рисует помеченный слой строго в порядке + +``` +draw_tile_anim_topright(); +draw_tile_anim_right(); <- пламя ЛЕВОГО соседа (факела) +draw_tile_anim(); <- СВОЯ графика тайла: челюсти чомпера +``` + +То есть челюсти возвращаются поверх огня **каждый кадр факела**, независимо +от того, жива ли собственная анимация чомпера. У нас пламя рисуется +напрямую из `pop_process_trobs`, минуя механизм пометок, — этой второй +половины не было. + +**Фикс** (`pop_trob.c`): после `pop_torch_draw` метим правого соседа +`pop_set_redraw(tp + 1, POP_RD_CHOMP, 1)`, если там чомпер. Одна страница: +факел анимируется каждый кадр, значит обе страницы дабл-буфера получат свою +перерисовку по очереди. Порядок сходится сам — блок факелов стоит ДО +`pop_redraw_needed`. Код соседа читается в том же префетче кодов тайлов +(`trob_rcode[]`), чтобы не свапать W0 второй раз за кадр. Банк 6: +104 Б. + +**Осознанное расхождение** — оригинал метит соседа БЕЗУСЛОВНО, мы только под +чомпера. Безусловная пометка = перерисовка тайла каждый кадр на каждый +факел; слой `draw_tile_anim` рисует ещё пики, зелье и меч (seg008:0644), +поэтому теоретически «застыть под пламенем» могут и они — заведено открытым +в `bug_list.md` (TORCH-ANIM-RIGHT). На уровнях 1–4 такого соседства не +встретилось. diff --git a/applications/PoP/roomtest/bug_list.md b/applications/PoP/roomtest/bug_list.md index e1e80b4..ed25f2d 100644 --- a/applications/PoP/roomtest/bug_list.md +++ b/applications/PoP/roomtest/bug_list.md @@ -303,3 +303,29 @@ if (curr_tile2 == tiles_15_opener || curr_tile2 == tiles_6_closer) { Что нужно: сам порт `died_on_button`, константа `TILE_STUCK = 5` и тайл заклиненной кнопки в атласе фона (сейчас его там нет). + +--- + +## TORCH-ANIM-RIGHT. Под пламенем могут застыть не только челюсти чомпера + +Открыто 2026-08-10 при закрытии +[BUG-TORCH-CHOMP-2](bug_closed.md#bug-torch-chomp-2). + +Пламя факела запекается в фон и рисуется в ячейке ПРАВОГО СОСЕДА. Оригинал +после каждого кадра факела метит этого соседа (`set_redraw_anim_right`, +seg007:0101) и перерисовывает весь его слой `anim` поверх огня; мы метим +только когда сосед — чомпер. Слой `draw_tile_anim` (seg008:0644) рисует +также **пики, зелье и меч** — если такой тайл окажется справа от факела и +будет в статике, пламя накроет и его. + +На уровнях 1–4 такого соседства не встретилось, поэтому расширять пометку +(она стоит перерисовки тайла каждый кадр на каждый факел) заранее не стали. + +**Как проверять:** поставить факел слева от пики/меча/зелья в тестовой +комнате и дождаться статики; либо пройти уровни 5+ и смотреть на клетку +справа от каждого факела. + +**Как чинить, если встретится:** там же, в ветке факела `pop_process_trobs`, +добавить в `trob_rcode`-проверку нужные коды и соответствующий вид пометки +(`POP_RD_SPIKE` / `POP_RD_FLOOR`), а зелье — переставить в порядке обхода +так, чтобы оно рисовалось ПОСЛЕ факела. diff --git a/applications/PoP/roomtest/pop_trob.c b/applications/PoP/roomtest/pop_trob.c index 7d66f32..6e73c35 100644 --- a/applications/PoP/roomtest/pop_trob.c +++ b/applications/PoP/roomtest/pop_trob.c @@ -42,6 +42,11 @@ typedef struct { uint8_t room, tilepos; int8_t type; } trob_t; static trob_t trobs[TROBS_MAX]; static uint8_t trobs_count; static uint8_t trob_code[TROBS_MAX]; /* префетч кодов тайлов на кадр (1 map W0) */ +/* Код ПРАВОГО соседа — заполняется только у факелов (0 у остальных). Нужен + * потому, что пламя рисуется в ячейке соседа и запекается в фон: если у + * соседа своя anim-графика, её надо вернуть ПОВЕРХ огня. Читается в том же + * префетче, чтобы не свапать W0 второй раз за кадр. */ +static uint8_t trob_rcode[TROBS_MAX]; /* Последняя НАРИСОВАННАЯ draw-сигнатура тайла (spike: modifier; button: * признак «нажата» = таймер связи >1). Редрой пики/кнопки — ТОЛЬКО при её @@ -499,8 +504,15 @@ void pop_process_trobs(uint8_t cur_room) __banked { uint8_t k; pop_level_access_begin(); - for (k = 0; k < trobs_count; k++) - trob_code[k] = pop_level_tile_raw(trobs[k].room, trobs[k].tilepos); + for (k = 0; k < trobs_count; k++) { + uint8_t tp = trobs[k].tilepos; + uint8_t c = pop_level_tile_raw(trobs[k].room, tp); + trob_code[k] = c; + trob_rcode[k] = 0; + if ((c == TILE_TORCH || c == TILE_TORCH_DEBRIS) && TP_COL[tp] < 9) + trob_rcode[k] = (uint8_t)(pop_level_tile_raw(trobs[k].room, + (uint8_t)(tp + 1)) & 0x1F); + } pop_level_access_end(); } @@ -570,8 +582,34 @@ void pop_process_trobs(uint8_t cur_room) __banked * __divuchar + __moduchar — на два факела это четыре вызова за * кадр (замер MAME). Тайл всегда 0..29. */ uint8_t trow = TP_ROW[tp], tcol = TP_COL[tp]; - if (code == TILE_POTION) pop_potion_draw(trow, tcol, mod[tp]); - else pop_torch_draw(trow, tcol, mod[tp]); + if (code == TILE_POTION) { + pop_potion_draw(trow, tcol, mod[tp]); + } else { + pop_torch_draw(trow, tcol, mod[tp]); + /* Порт set_redraw_anim_right (seg007:0101): animate_torch метит + * ПРАВОГО СОСЕДА, и redraw_needed рисует его слой anim в + * порядке draw_tile_anim_topright -> draw_tile_anim_right + * (пламя) -> draw_tile_anim (СВОЯ графика тайла). То есть в + * оригинале челюсти чомпера возвращаются поверх огня КАЖДЫЙ + * кадр факела, а не только пока жива их собственная анимация. + * + * У нас пламя запекается напрямую (pop_torch_draw), минуя + * механизм пометок, поэтому у ЗАСТЫВШЕГО чомпера возвращать + * его было нечем — огонь накрывал челюсти (баг найден на + * уровне 4). Метим сосед-тайл сами, ОДНУ страницу: факел + * анимируется каждый кадр, значит обе страницы получат свою + * перерисовку по очереди. Порядок сходится: этот блок идёт + * ДО pop_redraw_needed. + * + * РАСХОЖДЕНИЕ С ОРИГИНАЛОМ (осознанное): он метит соседа + * безусловно, мы — только когда там чомпер. Безусловная + * пометка = перерисовка тайла каждый кадр на каждый факел, + * а слой anim рисует ещё пики/зелье/меч (seg008:644). Их + * случай «застыл справа от факела» в bug_list как открытый: + * пики и меч рядом с факелом на уровнях 1-4 не встретились. */ + if (trob_rcode[i] == TILE_CHOMP) + pop_set_redraw((uint8_t)(tp + 1), POP_RD_CHOMP, 1); + } } if (room == cur_room && code == TILE_SPIKE) { /* ПИКИ — редрой КАЖДЫЙ кадр, пока trob жив: animate_spike