PoP roomtest: непрозрачные нижние грани + edge-триггер бампа + 2 колонки шва
Три фикса, найденные разбором в MAME. 1. Просвечивание Кида сквозь торец кнопки (подъём/спуск у кромки). draw_tile_bottom (seg008:570) рисует нижнюю грань блиттером blitters_0_no_transp: пиксель индекса 0 заливается ЧЁРНЫМ, а не пропускается. У граней кнопок (env 96/149) и нижних кадров loose (73/74) последняя строка целиком нулевая — наш упаковщик переводил 0 в 0xFF для ВСЕХ спрайтов, и она становилась дырой, сквозь которую был виден Kid за тайлом (на запечённом фоне не видно — там и так чёрное). pop_pack_bg.py: NO_TRANSP_ENV_IDS пакуется с сохранением индекса 0 как ЦВЕТА (0x50 = чёрный в палитре env). Ноль тактов в рантайме. 2. Разворот в проёме опущенной решётки выкидывал Кида на другую её сторону. check_bumped проверял столкновение УРОВНЕМ («край зашёл за грань»), поэтому разворот на месте внутри тайла-стены менял, какую грань мы меряем, и это читалось как новое столкновение → Kid.x = char_dx_forward(d) швырял его сквозь решётку. Оригинал (check_collisions + get_row_collision_data, seg004:0004) считает флаги перекрытия от ГАБАРИТА персонажа, БЕЗ направления, и зовёт bumped() только на переходе 0 -> 1 (вправо смотрит на 0x0F, влево на 0xF0). Порт этого edge-триггера: флаги от габарита + память прошлого кадра по колонке, сброс при смене комнаты (в оригинале это несовпадение prev_coll_room/curr_row_coll_room). Проверено: теперь разворот даёт уход в комнату 8 (leave_room, взгляд влево, char_x_left<=54) — то есть туда, где Kid физически и стоит. 3. Снимок шва расширен до ДВУХ колонок с каждой стороны (lcol_fg[6]/ rcol_fg[6]: col9+col8 и col0+col1). Kid, стоящий В шве (curr_col=-1), смотрит вперёд на колонку -2, где раньше была мнимая стена (get_tile -> TILE_WALL). Оригинал резолвит любую колонку через find_room_of_tile (seg006:005D). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -108,12 +108,23 @@ static uint8_t *g_fg; /* fg[30], row*10+col (МУТАБЕЛЬНАЯ
|
||||
static uint8_t g_curr_tile; /* = curr_tile2: тайл последнего get_tile */
|
||||
static uint8_t g_obj_xl; /* позиция внутри тайла (get_tile_div_mod) */
|
||||
static uint8_t grab_timer; /* после зацепа (12): блокирует climb-up */
|
||||
/* Связи комнаты (0 = нет соседа) + кромки швов: g_lcol = col9 левого соседа
|
||||
* (за col0), g_rcol = col0 правого соседа (за col9). Для коллизии на швах —
|
||||
/* Состояние edge-триггера бампа (порт prev_coll_flags, seg004): колонка, для
|
||||
* которой сохранены флаги перекрытия, и сами флаги (бит0 = левая грань стены,
|
||||
* бит1 = правая). BUMP_COL_NONE — данных нет (смена комнаты/старт). */
|
||||
#define BUMP_COL_NONE ((int8_t)-128)
|
||||
static int8_t bump_prev_col;
|
||||
static uint8_t bump_prev_flags;
|
||||
/* Связи комнаты (0 = нет соседа) + кромки швов: g_lcol[0..2] = col9 левого
|
||||
* соседа (за col0), g_lcol[3..5] = его col8 (колонка −2); g_rcol симметрично
|
||||
* (col0 / col1 правого соседа, колонки 10 и 11). Для коллизии на швах —
|
||||
* иначе Kid, войдя в соседнюю комнату у самой кромки (curr_col=-1/10), стоял
|
||||
* бы «в стене» уровня и in_wall выбрасывал бы его. Задаются pop_map_set_edges. */
|
||||
* бы «в стене» уровня и in_wall выбрасывал бы его. ВТОРАЯ колонка нужна,
|
||||
* когда Kid стоит В шве и смотрит НАРУЖУ: передняя колонка тогда −2 (или 11),
|
||||
* и мнимая стена там выпихивала его сквозь решётку (баг разворота у ворот).
|
||||
* Оригинал резолвит любую колонку через find_room_of_tile (seg006:005D).
|
||||
* Задаются pop_map_set_edges. */
|
||||
static uint8_t g_link_l, g_link_r, g_link_u, g_link_d;
|
||||
static uint8_t g_lcol[3], g_rcol[3];
|
||||
static uint8_t g_lcol[6], g_rcol[6];
|
||||
|
||||
/* ---- Плита-ПОТОЛОК: loose ряда 2 комнаты СВЕРХУ ------------------- *
|
||||
* В оригинале это тот же loose, просто get_tile ушёл в комнату сверху
|
||||
@@ -150,8 +161,12 @@ void pop_map_set_edges(uint8_t left, uint8_t right, uint8_t up, uint8_t down,
|
||||
const uint8_t *lcol_fg, const uint8_t *rcol_fg)
|
||||
{
|
||||
g_link_l = left; g_link_r = right; g_link_u = up; g_link_d = down;
|
||||
if (lcol_fg) { g_lcol[0] = lcol_fg[0]; g_lcol[1] = lcol_fg[1]; g_lcol[2] = lcol_fg[2]; }
|
||||
if (rcol_fg) { g_rcol[0] = rcol_fg[0]; g_rcol[1] = rcol_fg[1]; g_rcol[2] = rcol_fg[2]; }
|
||||
if (lcol_fg) { uint8_t i; for (i = 0; i < 6; i++) g_lcol[i] = lcol_fg[i]; }
|
||||
if (rcol_fg) { uint8_t i; for (i = 0; i < 6; i++) g_rcol[i] = rcol_fg[i]; }
|
||||
/* смена комнаты — колонки перепроецированы, прошлые флаги перекрытия
|
||||
* недействительны (в оригинале prev_coll_room != curr_row_coll_room и
|
||||
* бампа на кадре входа нет). */
|
||||
bump_prev_col = BUMP_COL_NONE;
|
||||
}
|
||||
|
||||
static uint8_t get_tile(int8_t col, int8_t row)
|
||||
@@ -165,8 +180,12 @@ static uint8_t get_tile(int8_t col, int8_t row)
|
||||
g_curr_tile = TILE_WALL;
|
||||
else if (col == -1 && g_link_l) /* левый шов: тайл col9 комнаты слева */
|
||||
g_curr_tile = g_lcol[row] & 0x1F;
|
||||
else if (col == -2 && g_link_l) /* на тайл глубже в комнату слева (col8) */
|
||||
g_curr_tile = g_lcol[3 + row] & 0x1F;
|
||||
else if (col == 10 && g_link_r) /* правый шов: тайл col0 комнаты справа */
|
||||
g_curr_tile = g_rcol[row] & 0x1F;
|
||||
else if (col == 11 && g_link_r) /* на тайл глубже в комнату справа (col1) */
|
||||
g_curr_tile = g_rcol[3 + row] & 0x1F;
|
||||
else if (col < 0 || col > 9)
|
||||
g_curr_tile = TILE_WALL; /* край уровня / нет соседа = стена */
|
||||
else
|
||||
@@ -767,7 +786,41 @@ static void check_bumped(void)
|
||||
tcol = get_tile_div_mod_m7(char_front_coll());
|
||||
t = get_tile(tcol, Kid.curr_row);
|
||||
wt = wall_type(t);
|
||||
if (!wt) return;
|
||||
/* EDGE-триггер (порт check_collisions + get_row_collision_data, seg004:0004).
|
||||
* Оригинал считает флаги перекрытия от ГАБАРИТА персонажа, БЕЗ направления:
|
||||
* 0x0F = left_wall_xpos < char_x_right_coll («в левую грань стены»)
|
||||
* 0xF0 = right_wall_xpos > char_x_left_coll («в правую грань»)
|
||||
* и зовёт bumped() ТОЛЬКО когда нужный флаг перешёл 0 -> 1 (взгляд вправо
|
||||
* смотрит на 0x0F, влево — на 0xF0). Без этого проверка «уровнем» ловит
|
||||
* ЛЮБОЕ перекрытие: Kid, стоящий В проёме опущенной решётки, при развороте
|
||||
* на месте получал ложный бамп и выпихивался сквозь неё в соседнюю
|
||||
* комнату. С флагами от габарита разворот их не меняет — бампа нет. */
|
||||
{
|
||||
uint8_t f = 0, prev, need;
|
||||
if (wt) {
|
||||
int coll_left = x_bump[tcol + FIRST_ONSCREEN_COLUMN] + TILE_MIDX;
|
||||
int lw = wall_dl[wt] + coll_left; /* get_left_wall_xpos */
|
||||
int rw = coll_left - wall_dr[wt] + TILE_RIGHTX; /* get_right_wall_xpos */
|
||||
int e = char_x_forward_edge(), wh = ((int)kid_fp_width() + 1) >> 1;
|
||||
int xl, xr;
|
||||
if (Kid.direction < 0) { xl = e; xr = e + wh; } /* габарит персонажа */
|
||||
else { xr = e; xl = e - wh; }
|
||||
if (kid_cur_flags() & FRAME_THIN) { xl += 4; xr -= 4; }
|
||||
if (lw < xr) f |= 1;
|
||||
if (rw > xl) f |= 2;
|
||||
}
|
||||
/* прошлый кадр: та же колонка — её флаги; другая — считаем, что не
|
||||
* перекрывали; «нет данных» (смена комнаты) — считаем, что перекрывали
|
||||
* всё, чтобы на кадре входа бампа не было (в оригинале это даёт
|
||||
* несовпадение prev_coll_room и curr_row_coll_room). */
|
||||
prev = (bump_prev_col == BUMP_COL_NONE) ? 3u
|
||||
: (bump_prev_col == tcol ? bump_prev_flags : 0u);
|
||||
bump_prev_col = tcol;
|
||||
bump_prev_flags = f;
|
||||
if (!wt) return;
|
||||
need = (Kid.direction < 0) ? 2u : 1u;
|
||||
if (!(f & need) || (prev & need)) return; /* не перекрыл / уже перекрывал */
|
||||
}
|
||||
if (t == TILE_GATE && gate_passable(tcol, Kid.curr_row))
|
||||
return; /* открытые ворота (шов/внутри) — Kid проходит (не бамп) */
|
||||
d = dist_from_wall_forward(t, tcol);
|
||||
|
||||
Reference in New Issue
Block a user