roomtest: L2 — переход в комнату снизу (провал/спуск) + фиксы окклюзии

При пересечении нижней границы комнаты Kid переходит в комнату links.down,
продолжая падать/спускаться (порт SDLPoP goto_other_room/leave_room).

- pop_map check_leave_below (порт leave_room, seg002): триггер по Kid.y>=211
  (не curr_row) — единый для провала И спуска-зацепа/climbdown; репроекция
  координат в кадр комнаты снизу (y-=189, curr_row=y_to_row_mod4) + сигнал
  pop_fell_out.  Убран прежний преждевременный триггер y>=189 из do_fall
  (из-за него climbdown с curr_row=3 ждал y_land[4]=244 → пролёт через комнату).
- roomtest: enter_room(room) — загрузка комнаты в рабочие массивы + отрисовка
  фона в обе страницы; обработчик pop_fell_out переключает комнату по
  pop_room_link(down) или респавнит (нет комнаты снизу).  cur_room-трекинг.
- pop_loose_reset (pop_map) + pop_loose_mob_reset (pop_bg): сброс loose-состояния
  (индексировано позицией тайла) при смене комнаты — иначе течёт в новую.

Фиксы окклюзии при висе/спуске (сверено с SDLPoP):
- other_overlay_tile: пустой тайл НЕ окклюдирует — draw_tile для него рисует
  лишь фоновую сетку (BLUELINE «силуэт кладки»), которая в оригинале уходит в
  y-сортируемый midtable ПОЗАДИ Kid; у нас клалась поверх.  Пропускаем.
- pop_fore_over_kid: порт set_char_collision (seg006:0723) — char_bottom_row =
  y_to_row_mod4(obj_y), обёртка -1 → ряд 3 (Kid НИЖЕ комнаты при спуске), а не
  ряд 0.  Прежний кламп в 0 давал rT>rB → цикл окклюзии не выполнялся, нога
  рисовалась поверх пола.  single-room: ряд 3 клампится в 2 (передняя грань пола).

Проверено в MAME: провал вниз, спуск-зацеп/climbdown с кромки, окклюзия ноги.
Известно (позже): осколки упавшего loose в комнате снизу (перенос mob),
per-room modified-tile tracking (re-entry восстанавливает исходные тайлы), HP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 20:22:52 +03:00
parent 21f978d441
commit 7f3e32ddd6
5 changed files with 114 additions and 32 deletions
+30 -5
View File
@@ -493,6 +493,10 @@ void pop_loose_mob_spawn(int row, int col)
#define MOB_X0(mx) ((mx) - 4)
#define MOB_W 64 /* mob_x-4 .. mob_x+59 (крышка +57 с запасом 2px) */
/* Остановить падающий кусок (при смене комнаты — состояние per-tile не
* должно утечь в новую комнату). */
void pop_loose_mob_reset(void) { mob_active = 0; }
void pop_loose_mob_tick(void)
{
if (!mob_active) return;
@@ -586,7 +590,7 @@ static int8_t col_from_x(int xpos) /* get_tile_div_mod: колонка (
return (int8_t)xh;
}
static int8_t y_to_row(int y) /* y_to_row_mod4 (TILE_SIZEY=63) */
static int8_t y_to_row(int y) /* y_to_row_mod4 (seg006, TILE_SIZEY=63) */
{
return (int8_t)((y + 60) / 63 % 4 - 1);
}
@@ -642,8 +646,16 @@ static void climb_overlay_tile(int row, int col, uint8_t fidx)
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))
uint8_t left;
/* Пустой тайл НЕ окклюдирует: у него нет тела пола/стены, а draw_tile для
* него рисует лишь ФОНОВУЮ сетку (BLUELINE «силуэт кладки» — правая грань
* пустого соседа). В оригинале она уходит в y-сортируемый midtable и
* ложится ПОЗАДИ Kid; у нас draw_tile кладёт её прямо поверх → сетка на
* висящем/падающем Kid. Фон запечён один раз в pop_room_draw — над
* спрайтом его перерисовывать не нужно. */
if (code == 0) return;
left = tile_code(row, col - 1);
if (left == 0 || (col > 0 && tile_code(row, col - 2) == 0))
draw_tile(row, col);
}
@@ -658,8 +670,21 @@ void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
xr = xl + wh;
cL = col_from_x(xl); if (cL < 0) cL = 0; else if (cL > 9) cL = 9;
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;
/* Порт set_char_collision (seg006:0723): нижний ряд = y_to_row_mod4(obj_y),
* причём обёртка -1 (Kid НИЖЕ комнаты — climbdown/провал, obj_y в полосе
* «ряд 3») трактуется как ряд 3, НЕ ряд 0. Верхний ряд считается от
* char_top_y с ресетом в 0, если весь спрайт ниже комнаты (>=192). */
{
int top_y = obj_y - (int)h + 1;
if (top_y >= 192) top_y = 0;
rT = y_to_row(top_y);
rB = y_to_row(obj_y);
if (rB == -1) rB = 3; /* обёртка → ряд 3 (ниже комнаты) */
}
/* single-room roomtest: ряд 3 (комната снизу) в этом виде не рисуется →
* окклюдер спереди = ряд 2 (передняя грань пола, за которой нога). */
if (rB < 0) rB = 0; else if (rB > 2) rB = 2;
if (rT < 0) rT = 0; else if (rT > 2) rT = 2;
/* Голова/руки почти всегда торчат в ряд НАД опорным (Kid выше тайла).
* y_to_row(верх спрайта) из-за +60-сдвига может схлопнуться в опорный
* ряд (rT==rB) — тогда ряд над ним не попадёт в перебор и его окклюдер