diff --git a/applications/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c index b609d20..e7c6325 100644 --- a/applications/PoP/roomtest/pop_room.c +++ b/applications/PoP/roomtest/pop_room.c @@ -686,6 +686,29 @@ void pop_room_redraw_seam_left(uint8_t rows) __banked } } +/* Ширина прямоугольника запечки, ОБРЕЗАННАЯ по правому краю экрана. + * + * Точечные запечки берут ширину «свой тайл + свес соседа» (40, 60 или 64 при + * шаге тайла 32) — для последней колонки это уезжает за 320. А заливка и + * копия по контракту НЕ КЛИПУЮТ (см. шапку _gfx_recthfill256: «Клиппинга НЕТ, + * координаты обязаны быть валидны») — и правильно, что не клипуют: проверка + * координат стоила бы дороже самой заливки. Значит обрезать обязан + * вызывающий. + * + * Чем это было: падение плиты (1,8) помечает соседа (1,9), pop_floor_bake + * заливал x=288 шириной 60, то есть 28 пикселей за экраном, и эти 28 всплывали + * ЧЁРНОЙ ПОЛОСОЙ у левого края — поверх стража в (1,0), причём только на одной + * странице дабл-буфера, то есть с мерцанием (найдено пользователем + * 2026-08-17, уровень 2 комната 4; трасса заливок: + * BAR x=288 y=117 w=60 h=39, а расхождение страниц — ровно x 0..27, y 117..155). + * + * Обрезка ничего не теряет: правее 320 восстанавливать нечего. */ +static uint8_t bake_w(int x, uint8_t w) +{ + if (x >= 320) return 0; /* весь прямоугольник за экраном */ + return (x + (int)w > 320) ? (uint8_t)(320 - x) : w; +} + /* ---- ЗАПЕЧКА НА ВТОРУЮ СТРАНИЦУ — копией, а не пересчётом ----------- * * Точечные запечки ставятся с pages = 2, то есть срабатывают ДВА кадра подряд * — по разу на каждую страницу дабл-буфера, — и оба раза считают одно и то же. @@ -709,6 +732,11 @@ static uint8_t bake_copy(uint8_t *slot, int x, int ytop, int w, int h) { uint8_t pg = (uint8_t)(gfx_get_draw_page() & 1); gfx_rect_t r; +#ifdef POP_DBG_NO_BAKE_COPY + (void)x; (void)ytop; (void)w; (void)h; /* A/B: копия выключена целиком */ + *slot = (uint8_t)(pg + 1); + return 0; +#endif if (!*slot || (uint8_t)(*slot - 1) == pg || gfx_get_draw_page() == gfx_get_visible_page()) { *slot = (uint8_t)(pg + 1); /* это ПЕРВАЯ запечка */ @@ -788,19 +816,20 @@ void pop_ceil_shake_draw(int col) __banked void pop_ceil_bake_empty(int col) __banked { int x = POP_COL_XH[col] * 8; + uint8_t bw = bake_w(x, 64); /* обрезка по краю экрана — см. bake_w */ /* Вторая страница — копией с первой (см. bake_copy). */ - if (bake_copy(&bake_pg_above[col], x, POP_YOFF, 64, CEIL_BAND_H + 1)) return; + if (bake_copy(&bake_pg_above[col], x, POP_YOFF, bw, CEIL_BAND_H + 1)) return; gfx_set_bank(GFX_BANK_NORMAL); /* Отдельного pop_cd_touch тут не надо: pop_bar_black метит сам, и ровно * этот же прямоугольник (было два вызова на одну область). */ - pop_bar_black(x, POP_YOFF, 64, CEIL_BAND_H + 1); /* стереть плиту в ОЗУ-копии */ + if (bw) pop_bar_black(x, POP_YOFF, bw, CEIL_BAND_H + 1); /* стереть плиту в ОЗУ-копии */ gfx_set_bank(GFX_BANK_TRANSPARENT); pop_t_clip_top = POP_YOFF; /* Окно = ровно стёртая полоса. Главный выигрыш здесь не в отсеве, а в * КЛИПЕ: куски ряда 0 высотой 63 px рисовались целиком (клип был только * сверху, по pop_t_clip_top), хотя восстановить надо девять строк — * отсюда 21 447 такта на блит против 14 384 в pop_floor_bake. */ - pop_t_win_set(x, POP_YOFF, 64, CEIL_BAND_H + 1); + pop_t_win_set(x, POP_YOFF, bw, CEIL_BAND_H + 1); pop_t_bake_rest = 1; /* фон = покой (см. pop_t_bake_rest) */ /* Восстанавливаем ВСЁ, чья графика попадает в стёртую полосу, а не только * два тайла ряда −1 (BUG-LOOSE-3). Куски тайла рисуются ВВЕРХ от своей @@ -907,9 +936,12 @@ void pop_torch_wipe(int row, int col) __banked { int x = (col + 1 < 10 ? POP_COL_XH[col + 1] : POP_COL_XH[col] + 4) * 8 + 8; int yb = POP_YOFF + 63 * row + 62 - 40; + /* У факела в колонке 9 канвас пламени лежит на x = 328, то есть ЦЕЛИКОМ за + * правым краем — заливать нечего, а bar не клипует (см. bake_w). */ + uint8_t tw = bake_w(x, 16); pop_cd_touch(x, yb - 17, 16, 18); /* bar мимо pop_blit_b */ gfx_set_bank(GFX_BANK_NORMAL); - pop_bar_black(x, yb - 17, 16, 18); + if (tw) pop_bar_black(x, yb - 17, tw, 18); gfx_set_bank(GFX_BANK_TRANSPARENT); pop_t_bake_rest = 1; /* фон = покой */ draw_tile(row, col); @@ -926,15 +958,16 @@ void pop_floor_bake(int row, int col) __banked { int x = POP_COL_XH[col] * 8; int yb = POP_YOFF + 63 * row; + uint8_t bw = bake_w(x, 60); /* обрезка по краю экрана — см. bake_w */ /* Вторая страница — копией с первой (см. bake_copy). */ - if (bake_copy(&bake_pg[row * 10 + col], x, yb + 26, 60, 39)) return; + if (bake_copy(&bake_pg[row * 10 + col], x, yb + 26, bw, 39)) return; /* Пометку ставит сам pop_bar_black — по тому же прямоугольнику; отдельный * pop_cd_touch тут был вторым вызовом на одну область (4 502 такта). */ /* Wipe СВОЕЙ ячейки + правой грани (заходит в соседа) + перерисовка пола. * НИЗ строго yb+64 (≈ низ передней грани пола): ниже — СТЕНА ряда ниже * (высокий спрайт), которую тут не перерисовываем → иначе чёрный бар. */ gfx_set_bank(GFX_BANK_NORMAL); - pop_bar_black(x, yb + 26, 60, 39); + if (bw) pop_bar_black(x, yb + 26, bw, 39); gfx_set_bank(GFX_BANK_TRANSPARENT); /* ОКНО КЛИПА ЗДЕСЬ ОБЯЗАТЕЛЬНО — и не ради скорости. * @@ -954,7 +987,7 @@ void pop_floor_bake(int row, int col) __banked * * Что бар покрывает ВСЁ изменившееся — это инвариант самого бара (иначе * запечка не работала бы и без копии). */ - pop_t_win_set(x, yb + 26, 60, 39); + pop_t_win_set(x, yb + 26, bw, 39); pop_t_bake_rest = 1; /* фон = покой (см. pop_t_bake_rest) */ draw_tile(row, col); if (col + 1 < 10) draw_tile(row, col + 1); @@ -1012,7 +1045,9 @@ void pop_gate_redraw(int row, int col) __banked void pop_loose_bake_empty(int row, int col) __banked { int x = POP_COL_XH[col] * 8; - int xr = POP_COL_XH[col + 1] * 8; + /* col+1 читаем ТОЛЬКО в границах массива: у последней колонки соседа нет, + * а POP_COL_XH[10] — чтение за концом (10 элементов). */ + int xr = (col + 1 < 10) ? POP_COL_XH[col + 1] * 8 : 0; int yb = 63 * row + 46; /* Чёрная плита (opaque NORMAL — пишет в VRAM И ОЗУ-копию акселератора): * стирает ЗАПЕЧЁННЫЙ пиксель-в-пиксель loose-пол с обоих тайлов, иначе @@ -1030,9 +1065,9 @@ void pop_loose_bake_empty(int row, int col) __banked pop_cd_touch(x, yb, 72, POP_YOFF + 63 * row + 66 - yb); gfx_set_bank(GFX_BANK_NORMAL); setfillstyle(SOLID_FILL, BLACK); - pop_bar_black(x, yb, 40, POP_YOFF + 63 * row + 65 - yb + 1); /* сам тайл: +40 (кусок пола заходит вправо; сосед перерисуется) */ + if (bake_w(x, 40)) pop_bar_black(x, yb, bake_w(x, 40), POP_YOFF + 63 * row + 65 - yb + 1); /* сам тайл: +40 (кусок пола заходит вправо; сосед перерисуется) */ if (col + 1 < 10) - pop_bar_black(xr, yb, 32, POP_YOFF + 63 * row + 65 - yb + 1); /* сосед: РОВНО 32 — не залезть в col+2 */ + if (bake_w(xr, 32)) pop_bar_black(xr, yb, bake_w(xr, 32), POP_YOFF + 63 * row + 65 - yb + 1); /* сосед: РОВНО 32 — не залезть в col+2 */ /* Новый статич. фон: сам тайл (empty → «стена вниз» topright комнаты * снизу) + правый сосед (его левая грань: был loose-край pop_env_b(42), * теперь пусто). TRANSPARENT — 0xFF не забивает чёрный, пишет и в