Fore потолка поверх падающей плиты: в разборе пометок не было ряда -1

Найдено пользователем (2026-08-17, уровень 1 комната 6): падающая плита
(-1,5) перекрывает собой кромку потолка (-1,6), чего физически быть не может.

Дыра архитектурная: pop_fore_needed обходил только ряды 0..2, ряда -1 в
переднем слое не было вовсе.

Сверено с оригиналом.  redraw_needed_tiles (seg008:1B06) обходит ряды 2,1,0, а
ПОТОМ отдельным проходом ряд 2 комнаты сверху (redraw_needed_above), и его
draw_tile_fore кладёт куски в FOREtable.  Падающая плита идёт в MIDtable
(draw_mobs).  draw_tables рисует back -> mid -> fore (seg008:1373), поэтому у
оригинала кромка потолка оказывается поверх плиты сама собой.

Порт:

- pop_fore_needed: проход по ряду -1 добавлен и идёт ПОСЛЕДНИМ, как в
  оригинале.  Свой набор пометок (rdfa/rdfa_pending) — как и у самих
  перерисовок ряда -1 (rda_*), это отдельный проход, а не 11-я колонка;
- новый лист pop_ceil_fore_tile_b (pop_bg.c) — тот же redraw_needed_above,
  что уже рисовался над персонажем (ceil_over_kid_tile), плюс окно клипа
  ровно на полосу столбца и ov_mark (полоса идёт банком без тени, на второй
  странице её восстановит pop_fore_heal);
- mob_mark_neighbour помечает ряд -1, пока кусок достаёт до кромки.  Кромка
  живёт в трёх верхних строках поля (dby = 2 при клипе по POP_YOFF), спрайт
  куска занимает mob_y-16 .. mob_y, отсюда условие mob_y <= 18 — три кадра
  после отрыва (y = 2, 5, 11 при ускорении 3).  Помечаются ОБА столбца,
  которые кусок накрывает по x (mob_x .. mob_x+62 = col и col+1); именно
  поэтому страдал сосед.

По бюджету работа появляется только в эти три кадра на кусок и попадает в
циановую фазу, где сейчас запас (270 тыс. из 400 тыс.).  Замер на ур.13 —
следующим шагом.

8 наборов tests-host зелёные.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 17:17:04 +03:00
parent bb7cf30910
commit 7ae691b070
5 changed files with 78 additions and 1 deletions
+25
View File
@@ -901,6 +901,31 @@ void pop_fore_tile_b(int row, int col) __banked
pop_t_fclip_on = saved;
}
/* Передний слой полосы кладки У ПОТОЛКА (ряд −1) для ОДНОГО столбца — вход для
* прохода по пометкам (pop_fore_needed, ряд −1 идёт там последним).
*
* Тот же redraw_needed_above (seg008:0221), что и над персонажем: обход ряда −1
* стоит в redraw_needed_tiles ПОСЛЕДНИМ, а его draw_tile_fore кладёт куски в
* foretable, то есть поверх midtable (там лежат падающие плиты) и поверх
* объектов. Без этого кусок, пролетающий у потолка, накрывает собой кромку
* потолка — физически невозможная картинка (найдено пользователем 2026-08-17,
* уровень 1 комната 6: плита (−1,5) перекрывала потолок (−1,6)).
*
* Окно — ровно полоса этого столбца: передние куски потолка живут в трёх
* верхних строках (dby = 2 при клипе по POP_YOFF), рисовать шире незачем.
* ov_mark — как у персонажа: полоса рисуется банком без тени, и на второй
* странице её надо будет восстановить (pop_fore_heal). */
void pop_ceil_fore_tile_b(int col) __banked
{
int x = POP_COL_XH[col] * 8;
ov_mark(x, 0, 64, CEIL_BAND_H);
pop_t_clip_top = POP_YOFF;
pop_t_win_set(x, POP_YOFF, 64, CEIL_BAND_H);
ceil_over_kid_tile(col);
pop_t_win_clear();
pop_t_clip_top = 0;
}
/* Банк на ВЕСЬ проход пометок, а не на каждый тайл: помеченных тайлов за
* кадр бывает больше десятка, и две смены банка на каждый — заметная доля
* прохода, который сам по себе дешёвый. */
+5
View File
@@ -173,6 +173,11 @@ void pop_loose_mob_reset(void) __banked; /* убрать все куски (С
* первый же тайл с тем же номером получил бы копию вместо честной запечки. */
void pop_bake_forget(void) __banked;
/* Передний слой полосы кладки у потолка (ряд −1) для одного столбца — зовёт
* проход по пометкам, ряд −1 в нём последний (порт redraw_needed_above +
* порядка redraw_needed_tiles, seg008:1B06). */
void pop_ceil_fore_tile_b(int col) __banked;
/* Смена комнаты: куски продолжают падать в СВОИХ комнатах (do_mobs крутит
* mobs[] независимо от drawn_room), сбрасывается только heal-история. */
void pop_loose_mob_room_changed(void) __banked;
+27 -1
View File
@@ -23,6 +23,13 @@ static uint8_t rd_pending;
* страницам дабл-буфера (запекают фон), а эти транзиентны. */
static uint8_t rdf[NTILES];
static uint8_t rdf_pending;
/* Передний слой РЯДА −1 (полоса кладки у потолка). Отдельным набором, как и
* сами перерисовки ряда −1 (rda_*): у оригинала это вообще другой проход —
* redraw_needed_tiles сначала обходит ряды 2,1,0, а ПОТОМ ряд 2 комнаты сверху
* (seg008:1B06), и его draw_tile_fore кладёт куски в foretable, то есть поверх
* midtable, где лежат падающие плиты, и поверх объектов. */
static uint8_t rdfa[NCOLS];
static uint8_t rdfa_pending;
void pop_redraw_reset(void) __banked
{
@@ -31,8 +38,10 @@ void pop_redraw_reset(void) __banked
for (i = 0; i < NCOLS; i++) { rda_kind[i] = 0; rda_cnt[i] = 0; }
rd_pending = 0;
for (i = 0; i < NTILES; i++) rdf[i] = 0;
for (i = 0; i < NCOLS; i++) rdfa[i] = 0;
pop_fore_pass_end();
rdf_pending = 0;
rdfa_pending = 0;
}
void pop_set_redraw_fore(uint8_t tilepos) __banked
@@ -42,19 +51,36 @@ void pop_set_redraw_fore(uint8_t tilepos) __banked
rdf[tilepos] = 1;
}
/* То же для полосы кладки У ПОТОЛКА (ряд −1). Ставит падающий кусок, когда
* его спрайт достаёт до кромки потолка: иначе кусок накрывает эту кромку
* собой, чего быть не может (найдено пользователем 2026-08-17, ур.1 к.6 —
* плита (−1,5) перекрывала потолок (−1,6)). */
void pop_set_redraw_fore_above(uint8_t col) __banked
{
if (col >= NCOLS) return;
if (!rdfa[col]) rdfa_pending++;
rdfa[col] = 1;
}
/* Разбор пометок переднего слоя: тайлы обходятся в том же порядке, что и
* фон, и рисуются ПОСЛЕ всех объектов кадра — как foretable у оригинала
* (draw_tables, seg008:1373: back -> mid -> fore). */
void pop_fore_needed(void) __banked
{
uint8_t i, row = 0, col = 0;
if (!rdf_pending) return;
if (!rdf_pending && !rdfa_pending) return;
pop_fore_pass_begin();
for (i = 0; i < NTILES; i++) {
if (rdf[i]) { rdf[i] = 0; pop_fore_tile_b(row, col); }
if (++col == 10) { col = 0; row++; }
}
rdf_pending = 0;
/* Ряд −1 — ПОСЛЕДНИМ, как в redraw_needed_tiles (seg008:1B06). */
if (rdfa_pending) {
for (i = 0; i < NCOLS; i++)
if (rdfa[i]) { rdfa[i] = 0; pop_ceil_fore_tile_b(i); }
rdfa_pending = 0;
}
}
void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked
+4
View File
@@ -50,6 +50,10 @@ void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked;
*
* Разбор (pop_fore_needed) звать ПОСЛЕ отрисовки всех объектов кадра. */
void pop_set_redraw_fore(uint8_t tilepos) __banked;
/* Пометить передний слой полосы у потолка (ряд −1), столбец col. Ставит
* падающий кусок, достающий до кромки потолка. */
void pop_set_redraw_fore_above(uint8_t col) __banked;
void pop_fore_needed(void) __banked;
void pop_set_redraw_above(uint8_t col, uint8_t kind, uint8_t pages) __banked;
+17
View File
@@ -1449,6 +1449,23 @@ static void mob_mark_neighbour(const mob_t *m)
int8_t r = pop_y_to_row((int16_t)m->y);
int8_t rt = pop_y_to_row((int16_t)(m->y - 18));
int8_t c = (int8_t)(m->col + 1);
/* ПОЛОСА У ПОТОЛКА (ряд −1). Её передние куски (нижняя кромка потолка и
* кадр плиты-потолка) живут в трёх верхних строках поля — dby = 2 при
* клипе по POP_YOFF, — а спрайт куска занимает mob_y−16 .. mob_y. Значит
* кусок закрывает кромку, пока mob_y − 16 <= 2, то есть mob_y <= 18: три
* кадра после отрыва (y = 2, 5, 11 при ускорении 3). Помечаем ОБА
* столбца, которые кусок накрывает по x (mob_x .. mob_x+62 — это col и
* col+1).
*
* Без этого кусок оказывался ПОВЕРХ кромки потолка соседней колонки —
* физически невозможно (найдено пользователем 2026-08-17, ур.1 к.6:
* плита (−1,5) перекрывала потолок (−1,6)). В оригинале порядок даёт
* сама структура таблиц: кусок идёт в midtable (draw_mobs), а полоса
* потолка — в foretable, и та рисуется позже (draw_tables, seg008:1373). */
if (m->y <= 18) {
pop_set_redraw_fore_above((uint8_t)m->col);
if (m->col + 1 <= 9) pop_set_redraw_fore_above((uint8_t)(m->col + 1));
}
if (c > 9) return;
if (r >= 0 && r <= 2) pop_set_redraw_fore((uint8_t)(r * 10 + c));
if (rt != r && rt >= 0 && rt <= 2) pop_set_redraw_fore((uint8_t)(rt * 10 + c));