diff --git a/applications/PoP/roomtest/TASKS_OPEN.md b/applications/PoP/roomtest/TASKS_OPEN.md index 1326f60..b194b82 100644 --- a/applications/PoP/roomtest/TASKS_OPEN.md +++ b/applications/PoP/roomtest/TASKS_OPEN.md @@ -258,6 +258,52 @@ MEM-BANK2, MEM-BANK5, CLIP-1, KBD-1, DBG-CHEATS. ## P0 — делаем сейчас +### HEAL-WIDTH. Ширина точечных heal'ов не совпадает со следом перерисовки — ОБЯЗАТЕЛЬНО + +**Постановка (пользователь, 2026-08-18).** Ширины heal'ов взяты «на глаз по +клеткам», а не по реальному следу. Сжатие до фактического следа даёт около +**10 % площади** самой дорогой операции акселератора; по оценке пользователя +5-6 % реального выигрыша. Задача обязательная. + +**Измерено** (каталоги атласов, спрайты плиты): + +| часть плиты | подземелье | дворец | +|---|---|---| +| верх (`41/69/70`) | 32 × 13-14 | 32 × 13-14 | +| правая грань (`42/71/72`) | **26** × 15-16 | **25** × 15-16 | + +То есть анимированный след ОДНОЙ плиты — `32 + 26 = 58` пикселей в +подземелье и `57` во дворце. + +**Что стоит сейчас.** В коде уживаются ДВЕ разные договорённости: + +- **60** = «свой тайл + свес соседа» — `pop_floor_bake` (там это записано + комментарием); +- **64** = «две клетки» — `pop_loose_shake_draw`, `pop_spike_redraw` + («ширина 64 = обе ячейки»). + +Для трясущейся плиты 64 избыточны: хватает 58 (подземелье) / 57 (дворец), +то есть **6 пикселей из 64 — впустую, 9,4 % площади heal'а**. + +**И обратная сторона, которую тоже надо разобрать.** Ни 60, ни 64 не +покрывают того, что перерисовка РИСУЕТ: два полных `draw_tile` красят до +**90** пикселей (32 свои + 32 соседа + 26 правой грани соседа, уходящей в +третью клетку). Видимого артефакта отсюда пока нет — содержимое третьей +клетки статично и перерисовывается одинаково, а трясущийся сосед накрывает +эту область своим heal'ом, — но несоответствие «heal уже, чем след» надо +либо закрыть, либо записать как осознанное. + +**Что сделать.** Свести ВСЕ точечные heal'ы (`pop_loose_shake_draw`, +`pop_spike_redraw`, `pop_chomp_redraw`, `pop_floor_bake`, `pop_loose_bake_empty`, +`pop_ceil_*`, коридор `mob_heal_*`) в таблицу «что рисует / что чистит» и +привести ширину каждого к фактическому следу. Ширины брать ИЗ АТЛАСА, а не +из номера клетки — они разные у двух тайлсетов (26 против 25). + +**Проверка:** замер 13/23 до/после (heal'ы — зелёная фаза) плюс визуальный +прогон комнат с плитами, пиками и чомперами: сужение heal'а — самый прямой +способ получить «недочищенный хвост». + + ### L12-SHADOW. Уровень 12: встреча с тенью, бой, слияние > **Статус 2026-08-13: код написан, хост-тесты зелёные (45 проверок, diff --git a/applications/PoP/roomtest/pop_bg.c b/applications/PoP/roomtest/pop_bg.c index 3a5bb64..a474904 100644 --- a/applications/PoP/roomtest/pop_bg.c +++ b/applications/PoP/roomtest/pop_bg.c @@ -781,20 +781,10 @@ static void overlay_mid_tile(int row, int col) } else if (t->bottom_id) { pop_env_b(t->bottom_id, x, dby); } - /* draw_loose(0) — ПЕРЕДНИЙ ТОРЕЦ плиты, последний вызов draw_tile2 - * (seg008: draw_tile_right, draw_tile_anim_right, draw_tile_base, - * draw_tile_anim, draw_tile_bottom, draw_loose). У loose в - * pop_tile_table bottom_id = 0, поэтому ветка выше его НЕ рисует, и в - * оверлее торца не было вовсе: падающий кусок оставался ПОВЕРХ торца - * соседней плиты (найдено пользователем 2026-08-18, ур.11 к.14 — - * трясущаяся плита (2,6) и кусок, отвалившийся от (2,5)). - * - * Кадр берётся по ЖИВОМУ modif, как и верх плиты выше: оверлей рисуется - * поверх уже готовой картинки, и дрожащая плита обязана остаться - * дрожащей. */ - if (code == 11 && row >= 0) - pop_env_b(POP_LOOSE_FRAM_BOTTOM[pop_loose_frame(pop_loose_modif[row * 10 + col])], - x, dby); + /* draw_loose тут НЕТ, и это не забывчивость: в оригинале (seg008 + * draw_loose) нижняя грань плиты идёт в add_backtable + add_foretable, + * мимо ptr_add_table — то есть в midtable не попадает никогда. Перекрыть + * ею падающий кусок должен ПЕРЕДНИЙ слой (fore_only_tile), а не оверлей. */ } /* draw_tile_fore (seg008:690) — ТОЛЬКО передний слой (add_foretable): он diff --git a/applications/PoP/roomtest/pop_redraw.c b/applications/PoP/roomtest/pop_redraw.c index 5b23a3b..a0eba7c 100644 --- a/applications/PoP/roomtest/pop_redraw.c +++ b/applications/PoP/roomtest/pop_redraw.c @@ -107,7 +107,8 @@ void pop_set_redraw_above(uint8_t col, uint8_t kind, uint8_t pages) __banked * аргументов (30*2 делений за кадр даже когда рисовать нечего). */ void pop_redraw_needed(void) __banked { - uint8_t i, row = 0, col = 0, left = 0; + uint8_t i, row = 0, col = 0, left = 0, cnt = 0; /* cnt — ВРЕМЕННО, замер */ + pop_dbg_rdmax_tot = 0; if (!rd_pending) return; for (i = 0; i < NTILES; i++) { if (rd_cnt[i]) { @@ -121,6 +122,7 @@ void pop_redraw_needed(void) __banked case POP_RD_CHOMP: pop_chomp_redraw(row, col); break; default: break; } + cnt++; /* ВРЕМЕННО: замер */ if (--rd_cnt[i]) left++; else rd_kind[i] = 0; } @@ -130,9 +132,12 @@ void pop_redraw_needed(void) __banked if (rda_cnt[i]) { if (rda_kind[i] == POP_RDA_CEIL) pop_ceil_shake_draw(i); else pop_ceil_bake_empty(i); + cnt++; /* ВРЕМЕННО: замер */ if (--rda_cnt[i]) left++; else rda_kind[i] = 0; } } rd_pending = left; + pop_dbg_rdmax_tot = cnt; /* ВРЕМЕННО: замер */ + if (cnt > pop_dbg_rdmax[0]) pop_dbg_rdmax[0] = cnt; } diff --git a/applications/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c index 02fbcf9..4ab7fe9 100644 --- a/applications/PoP/roomtest/pop_room.c +++ b/applications/PoP/roomtest/pop_room.c @@ -1203,6 +1203,13 @@ static void mob_spawn(uint8_t room, int row, int col) m->defer = 0; m->prev_y[0] = m->prev_y[1] = MOB_Y_NONE; m->draw_y = MOB_Y_NONE; + { /* ВРЕМЕННО: журнал решения оверлея — копим за весь полёт */ + uint8_t k = (uint8_t)(m - mobs); + pop_dbg_pass[k] = 0; + pop_dbg_ovl[k * 4 + 2] = 0; + pop_dbg_ovl[k * 4 + 3] = 0; + pop_dbg_tiles[k] = 0; + } } /* Отрыв куска в ТЕКУЩЕЙ комнате (обычный случай — pop_loose_tick). */ @@ -1581,7 +1588,24 @@ static void mob_overlay_neighbour(const mob_t *m) int8_t rt = pop_y_to_row((int16_t)(m->draw_y - 18)); int8_t c = (int8_t)(m->col + 1); int ytop, ybot; + uint8_t *dbg = &pop_dbg_ovl[(m - mobs) * 4]; /* ВРЕМЕННО: журнал решения */ + { /* ВРЕМЕННО: перебор ВСЕХ тайлов, чьи габариты пересекаются со спрайтом + * куска — чтобы понять, скольким тайлам реально нужно лечь поверх. */ + int8_t rr, kk; + int t0 = m->draw_y - (int)mob_spr[2] + 1, t1 = m->draw_y; + for (rr = -1; rr <= 2; rr++) + for (kk = 0; kk <= 1; kk++) { + int8_t cc = (int8_t)(m->col + kk); + if (cc > 9) continue; + if (pop_tile_code(rr, cc) && + t1 >= 63 * rr + 2 && t0 <= 63 * rr + 66) + pop_dbg_tiles[m - mobs] |= (uint8_t)(1 << ((rr + 1) * 2 + kk)); + } + } + dbg[0] = (uint8_t)(r + 128); dbg[1] = (uint8_t)(rt + 128); + dbg[2] |= 1; if (c > 9) return; + dbg[2] |= 2; /* Габарит куска — по ЭКРАННОЙ координате, как и ряд выше: у куска, * видимого из соседней комнаты, draw_y отличается от y на 192, и * сравнение с габаритом тайла по y давало заведомо ложный ответ. */ @@ -1598,12 +1622,20 @@ static void mob_overlay_neighbour(const mob_t *m) * Габарит тайла берём ПОЛНЫМ (63*row+2 .. +66) — какая именно часть * соседа окажется поверх куска, решит уже окно клипа внутри. Сужать по * реальной высоте кусков нельзя: у колонн и зеркала база высокая. */ + if (r >= 0 && r <= 2 && pop_tile_code(r, c)) dbg[2] |= 4; + if (r >= 0 && r <= 2 && ybot >= 63 * r + 2 && ytop <= 63 * r + 66) dbg[2] |= 8; if (r >= 0 && r <= 2 && pop_tile_code(r, c) && - ybot >= 63 * r + 2 && ytop <= 63 * r + 66) + ybot >= 63 * r + 2 && ytop <= 63 * r + 66) { + dbg[2] |= 0x10; dbg[3]++; pop_mob_overlay_tile(r, c, r, (int8_t)m->col, m->x, m->draw_y, mob_spr[2]); + } + if (rt != r && rt >= 0 && rt <= 2 && pop_tile_code(rt, c)) dbg[2] |= 0x20; + if (rt != r && rt >= 0 && rt <= 2 && ybot >= 63 * rt + 2 && ytop <= 63 * rt + 66) dbg[2] |= 0x40; if (rt != r && rt >= 0 && rt <= 2 && pop_tile_code(rt, c) && - ybot >= 63 * rt + 2 && ytop <= 63 * rt + 66) + ybot >= 63 * rt + 2 && ytop <= 63 * rt + 66) { + dbg[2] |= 0x80; dbg[3]++; pop_mob_overlay_tile(rt, c, r, (int8_t)m->col, m->x, m->draw_y, mob_spr[2]); + } } static void mob_draw_pass(uint8_t pg, uint8_t want_defer) @@ -1618,6 +1650,10 @@ static void mob_draw_pass(uint8_t pg, uint8_t want_defer) mob_t *m = &mobs[i]; /* Отбор по draw_y, а НЕ по комнате: кусок, только что провалившийся * в комнату снизу, ещё виден из нашей (см. mob_tick_one). */ + pop_dbg_pass[i] |= 1; + if (m->active) pop_dbg_pass[i] |= 2; + if (m->draw_y != MOB_Y_NONE) pop_dbg_pass[i] |= 4; + if ((uint8_t)(m->defer != 0) == want_defer) pop_dbg_pass[i] |= 8; if (!m->active || m->draw_y == MOB_Y_NONE) continue; if ((uint8_t)(m->defer != 0) != want_defer) continue; for (j = n; j > 0 && mobs[order[j - 1]].draw_y < m->draw_y; j--) @@ -1634,8 +1670,10 @@ static void mob_draw_pass(uint8_t pg, uint8_t want_defer) * в кадр пересечения границы пометка указывала на прежний тайл, и * передняя часть колонны кусок не перекрывала — ровно ОДИН кадр, * как и наблюдалось (прогон 2026-08-13, комната 23, колонка 8). */ + pop_dbg_pass[order[i]] |= 0x10; mob_mark_neighbour(&mobs[order[i]]); mob_render(&mobs[order[i]], pg); + pop_dbg_pass[order[i]] |= 0x20; mob_overlay_neighbour(&mobs[order[i]]); } } diff --git a/applications/PoP/roomtest/pop_state.c b/applications/PoP/roomtest/pop_state.c index 27f8bb1..e97c9cf 100644 --- a/applications/PoP/roomtest/pop_state.c +++ b/applications/PoP/roomtest/pop_state.c @@ -87,6 +87,20 @@ void pop_dbg_trap(void) { pop_dbg_hits++; } * смена комнаты видна по скачку tilepos, а нам удобнее явно). */ int8_t pop_dbg_obj[9]; +/* ВРЕМЕННО (разбор оверлея падающего куска): по слоту mob. + * pop_dbg_pass[i] — что было в проходе отрисовки: + * bit0 рассмотрен, bit1 active, bit2 draw_y задан, bit3 defer совпал, + * bit4 нарисован, bit5 позван mob_overlay_neighbour. + * pop_dbg_ovl[i*4 + 0..3] — что решил mob_overlay_neighbour: + * [0] r+128, [1] rt+128, [2] флаги, [3] col. + * Флаги: bit0 вошли, bit1 c<=9, bit2 tile(r,c), bit3 габарит r, + * bit4 ВЫЗОВ по r, bit5 tile(rt,c), bit6 габарит rt, bit7 ВЫЗОВ по rt. */ +uint8_t pop_dbg_pass[14]; +uint8_t pop_dbg_ovl[14 * 4]; +/* ВРЕМЕННО: какие тайлы РЕАЛЬНО перекрывают спрайт куска. Бит + * (ряд+1)*2 + (0 своя колонка / 1 соседняя), ряды −1..2. */ +uint8_t pop_dbg_tiles[14]; + /* Зеркало уровня 4 — см. pop_state.h. */ int8_t pop_jumped_mirror; diff --git a/applications/PoP/roomtest/pop_state.h b/applications/PoP/roomtest/pop_state.h index 2cd512e..d3a5f87 100644 --- a/applications/PoP/roomtest/pop_state.h +++ b/applications/PoP/roomtest/pop_state.h @@ -101,4 +101,8 @@ void pop_dbg_wh(uint16_t wh); * 0 obj_tilepos, 1 frame, 2 action, 3 curr_col, 4 char_col_left, * 5 char_col_right, 6 char_top_row, 7 char_bottom_row, 8 комната. */ extern int8_t pop_dbg_obj[9]; +/* ВРЕМЕННО: разбор оверлея падающего куска (см. pop_state.c). */ +extern uint8_t pop_dbg_pass[14]; +extern uint8_t pop_dbg_ovl[14 * 4]; +extern uint8_t pop_dbg_tiles[14]; void pop_dbg_kidobj(void) __banked;