roomtest: fore-окклюзия пола/стены над Kid (порт redraw_at_char/char2)

Kid — высокий спрайт: голова/руки торчат в ряд выше опорного, floor/wall
там должны перекрывать его.  Сверено с SDLPoP (seg003 redraw_at_char2 +
seg008 draw_tile_fore/draw_other_overlay/draw_floor_overlay):

- fore_tile (draw_tile_fore): стена рисует и WALL_FRAM_BOTTOM (нижняя
  грань-решётка), не только MAIN — руки при прыжке в потолок [2,7]->[1,7]
  уходят за стену;
- диапазон рядов форсит включение ряда над опорным (y_to_row верха спрайта
  мог схлопнуться из-за +60-сдвига);
- other_overlay_tile (draw_other_overlay): на КРОМКЕ пола (сосед слева пуст)
  перерисовать весь тайл поверх Kid — но ТОЛЬКО в позах захвата (78-79),
  виса (action 2/6), полёта (3/4), старта падения (bumped 102-106) и начала
  подъёма (135/136, до SEQ_UP на 141 Kid ещё в ряду ПОД полом).  Гейтинг
  как в redraw_at_char2 — при стоянии/беге ложной окклюзии нет;
- отладочный стоп-кадр: '1' заморозить / '2' продолжить (для разбора поз).

Проверено в MAME (пользователь): прыжок-в-потолок, короткий прыжок,
спрыгивание, подъём на [0,3] — окклюзия корректна.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 20:41:55 +03:00
parent 023b45eb85
commit 237f780e0e
3 changed files with 65 additions and 10 deletions
+48 -5
View File
@@ -420,11 +420,17 @@ static void fore_tile(int row, int col) /* передний слой одно
const piece *t = &tile_table[code];
int x = COL_XH[col] * 8;
int dmy = 63 * row + 62;
if (code == 20) { /* стена: основная грань (foretable, */
uint8_t wmod = wall_modifier(row, col); /* draw_tile_fore seg008:690) — */
wall_b(WALL_FRAM_MAIN[wmod], x, dmy); /* перекрывает Kid как передний */
int dby = dmy + 3; /* = 63*row+65 (draw_tile_bottom) */
if (code == 20) { /* стена: ОБЕ грани перекрывают Kid */
uint8_t wmod = wall_modifier(row, col);
wall_b(WALL_FRAM_BOTTOM[wmod], x, dby); /* нижняя грань-решётка (draw_tile_bottom): */
wall_pattern(row, col, 0); /* перекрывает голову/руки в НИЖНЕЙ части ячейки */
wall_b(WALL_FRAM_MAIN[wmod], x, dmy); /* основная грань (draw_tile_fore) */
wall_pattern(row, col, 1);
} else if (t->fore_id) {
} else {
if (t->bottom_id) /* передняя кромка пола (draw_tile_bottom) */
env_b(t->bottom_id, x, dby);
if (t->fore_id) /* передние объекты: колонны/ворота/щебень */
fore_b(t->fore_id, t->fore_x * 8 + x, t->fore_y + dmy);
}
}
@@ -451,8 +457,21 @@ static void climb_overlay_tile(int row, int col, uint8_t fidx)
if (t->bottom_id) env_b(t->bottom_id, x, dby); /* draw_tile_bottom */
}
/* draw_other_overlay (seg008:1492): для поз виса/прыжка/падения/захвата на
* КРОМКЕ (сосед слева пуст, либо тайл через один слева пуст) — перерисовать
* ВЕСЬ тайл поверх Kid. Тело пола перекрывает висящего/прыгающего/падающего
* ПОД ним. Срабатывает только в этих позах (см. redraw_at_char2 ниже) —
* поэтому при стоянии/залезании ложной окклюзии нет. */
static void other_overlay_tile(int row, int col)
{
uint8_t code = tile_code(row, col);
uint8_t left = tile_code(row, col - 1);
if (left == 0 || (code != 0 && col > 0 && tile_code(row, col - 2) == 0))
draw_tile(row, col);
}
void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
int8_t dir, uint8_t frame)
int8_t dir, uint8_t frame, uint8_t action)
{
int wh = (int)((w + 1) >> 1); /* char_width_half */
int xl = obj_x / 2 + 58; /* char_x_left (58-база) */
@@ -464,6 +483,12 @@ void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
cR = col_from_x(xr); if (cR < 0) cR = 0; else if (cR > 9) cR = 9;
rB = y_to_row(obj_y); if (rB < 0) rB = 0; else if (rB > 2) rB = 2;
rT = y_to_row(obj_y - (int)h + 1); if (rT < 0) rT = 0; else if (rT > 2) rT = 2;
/* Голова/руки почти всегда торчат в ряд НАД опорным (Kid выше тайла).
* y_to_row(верх спрайта) из-за +60-сдвига может схлопнуться в опорный
* ряд (rT==rB) — тогда ряд над ним не попадёт в перебор и его окклюдер
* (нижняя грань стены / кромка пола) не перерисуется поверх Kid. Форсим
* включение ряда rB-1. */
if (rB > 0 && rT >= rB) rT = rB - 1;
gfx_set_bank(GFX_BANK_SPRITE); /* видео-ОЗУ, heal восстановит */
for (r = rT; r <= rB; r++)
for (c = cL; c <= cR; c++)
@@ -473,6 +498,24 @@ void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
for (r = rT; r <= rB; r++)
for (c = cL; c <= cR; c++)
climb_overlay_tile(r, c, fidx);
} else {
/* redraw_at_char2 (seg003:449) — путь set_redraw2/draw_other_overlay:
* тело пола на кромке перекрывает Kid в позах захвата (78-79), виса
* (action 2/6), полёта (3/4) и старта падения (bumped 102-106).
* Верхний ряд — всегда; опорный — кроме action 2 (hang_climb). */
uint8_t do_ov = 0;
if (frame >= 78 && frame < 80) do_ov = 1; /* захват */
else if (action == 2 || action == 3 || action == 4 || action == 6) do_ov = 1;
else if (action == 5 && frame >= 102 && frame <= 106) do_ov = 1;
else if (frame == 135 || frame == 136) do_ov = 1; /* начало подъёма (SEQ_UP */
/* на 141 — до него Kid ещё */
/* в ряду 1 ПОД полом [0,3]) */
if (do_ov) {
for (c = cR; c >= cL; c--) {
if (action != 2) other_overlay_tile(rB, c); /* опорный ряд */
if (rT != rB) other_overlay_tile(rT, c); /* верхний ряд */
}
}
}
gfx_set_bank(GFX_BANK_NORMAL);
}
+1 -1
View File
@@ -34,6 +34,6 @@ void pop_room_draw(uint8_t room_num,
* спрайта, w/h — размер кадра, dir — направление Kid (<0 влево). Метрики
* берутся из pop_kid (kid_fp_*). См. redraw_at_char/set_char_collision. */
void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
int8_t dir, uint8_t frame);
int8_t dir, uint8_t frame, uint8_t action);
#endif
+15 -3
View File
@@ -23,7 +23,7 @@ int main(void)
/* Дабл-буфер: front — видимая страница, back — куда рисуем. dbuf=1
* по умолчанию (без мерцания); SPACE переключает в однобуфер для
* отладки багов рисования (edge по sp_prev, чтоб не дёргать). */
uint8_t pg, front = 0, back, dbuf = 1, sp_prev = 0;
uint8_t pg, front = 0, back, dbuf = 1, sp_prev = 0, frozen = 0;
if (pop_bg_load() != 0) { /* атласы — ДО initgraph */
puts("pop_bg_load failed");
@@ -58,7 +58,8 @@ int main(void)
kid_init(SEQ_STAND, 1, 1, 0);
kid_draw();
pop_fore_over_kid(kid_fp_obj_x(), kid_fp_obj_y(),
kid_fp_width(), kid_fp_height(), Kid.direction, Kid.frame);
kid_fp_width(), kid_fp_height(),
Kid.direction, Kid.frame, Kid.action);
if (pop_ctrl_init() != 0) { /* raw-канал клавиатуры (забирает ввод у DSS) */
closegraph();
@@ -79,6 +80,16 @@ int main(void)
}
sp_prev = sp;
/* Отладочный стоп-кадр: '1' (PS/2 0x16) — заморозить, '2' (0x1E) —
* продолжить. В заморозке пропускаем весь tick/draw/flip: картинка
* стоит, можно спокойно разбирать позу Kid и окклюзию. */
if (kbd_raw_down(0x16)) frozen = 1;
if (kbd_raw_down(0x1E)) frozen = 0;
if (frozen) {
gfx_wait_vsync();
continue;
}
/* Рисуем в СКРЫТУЮ страницу (при dbuf); в однобуфере — в 0. */
back = dbuf ? (uint8_t)(front ^ 1) : 0;
gfx_set_draw_page(back);
@@ -93,7 +104,8 @@ int main(void)
}
kid_draw(); /* нарисовать новый кадр */
pop_fore_over_kid(kid_fp_obj_x(), kid_fp_obj_y(),
kid_fp_width(), kid_fp_height(), Kid.direction, Kid.frame);
kid_fp_width(), kid_fp_height(),
Kid.direction, Kid.frame, Kid.action);
/* Пейсинг 3 кадра, затем tear-free флип на vblank: draw уже
* сделан (скрытая страница), wait_vsync ловит начало кадра,