SprPoP: зацеп больше не срывается на кадре после захвата
Регресс от 4e12aa5: glide_through_wall_guard() звался в do_fall сразу
после check_grab и отменял ТОЛЬКО ЧТО СОСТОЯВШИЙСЯ зацеп.
Причина в имени последовательности: seq_15 — это grab_ledge_MIDAIR, и
после удачного захвата Char.action == 3, то есть персонаж формально всё
ещё «в воздухе», уже подтянутый вплотную к кромке. А под кромкой в
комнатах оригинала стоит кладка. Guard видел ровно её (замерено на живой
сцене: t == TILE_WALL, d == 10, col 0, row 1), считал это пролётом сквозь
стену, отбрасывал персонажа на 5 пикселей назад и гасил fall_x. Зацеп
РИСОВАЛСЯ и тут же срывался — ловилось на длинном прыжке уровня 3
(комната 7) и в attract-демо (комната 2, прыжок с места на кромку 0,2,
после срыва Кид падал на пики).
ФИКС. check_grab() возвращает признак «зацепился», и при нём guard не
зовётся. Цена — один тест байта на кадр падения; фикс «падение сквозь
стену» цел, t_wall зелёный.
ПОЧЕМУ ПРЕЖНИЕ НАБОРЫ ЭТОГО НЕ ПОЙМАЛИ — и главный урок. t_grab, t_phys
(1733 проверки) и t_wall судят по «действие стало вис», а вис-то
наступал, он просто не жил. Первый A/B guard'а по этой же метрике дал
ЛОЖНО-ОТРИЦАТЕЛЬНЫЙ ответ, и подозрение с него было снято зря. Разница
между «зацепился» и «зацепился и держится» — это и есть разница между
багом и нормой.
Отсюда новый набор t_hang: критерий — вис ДЕРЖИТСЯ три кадра подряд, и в
отчёте различаются «не наступило» и «наступило и сорвалось». Окно — 34
фазы разбега из 41; при намеренно возвращённой поломке 0 из 41, тест
краснеет (проверено). Сцена — геометрия уровня 3 комнаты 7, приведённая
внутрь одной комнаты, чтобы шов не примешивался; кромка-пол и
кромка-решётка проверяются отдельно.
Живая проверка в MAME пользователем: корректно.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018T2k4VZrSyERwk6H97Sfi1
This commit is contained in:
@@ -839,30 +839,35 @@ static uint8_t can_grab_front_above(void)
|
||||
/* check_grab (seg006:0A28): в падении при зажатом Shift — зацепиться за
|
||||
* уступ спереди-сверху (seq_15), если скорость падения ещё мала и высота
|
||||
* подходящая. Выравнивает передний край по кромке, гасит fall_y, взводит
|
||||
* grab_timer (блок climb-up на несколько кадров). */
|
||||
static void check_grab(void)
|
||||
* grab_timer (блок climb-up на несколько кадров).
|
||||
*
|
||||
* ВОЗВРАТ: 1 — зацеп СОСТОЯЛСЯ на этом кадре. Нужен вызывающему (do_fall):
|
||||
* дальше по кадру идёт glide_through_wall_guard, и ему нельзя трогать
|
||||
* персонажа, которого мы только что повесили на кромку (см. там же). */
|
||||
static uint8_t check_grab(void)
|
||||
{
|
||||
uint8_t old_x;
|
||||
if (!pop_ctrl_shift_held()) return; /* Shift не зажат */
|
||||
if ((uint8_t)Char.fall_y >= 32) return; /* падает слишком быстро */
|
||||
if ((uint16_t)pop_y_land[Char.curr_row + 1] > (uint16_t)(Char.y + 25)) return;
|
||||
if (!pop_ctrl_shift_held()) return 0; /* Shift не зажат */
|
||||
if ((uint8_t)Char.fall_y >= 32) return 0; /* падает слишком быстро */
|
||||
if ((uint16_t)pop_y_land[Char.curr_row + 1] > (uint16_t)(Char.y + 25)) return 0;
|
||||
old_x = Char.x;
|
||||
Char.x = (uint8_t)char_dx_forward(-8);
|
||||
determine_col();
|
||||
if (!can_grab_front_above()) {
|
||||
Char.x = old_x; /* не за что — назад */
|
||||
determine_col();
|
||||
} else {
|
||||
Char.x = (uint8_t)char_dx_forward((int8_t)distance_to_edge_weight());
|
||||
Char.y = (uint8_t)pop_y_land[Char.curr_row + 1];
|
||||
Char.fall_y = 0;
|
||||
pop_char_set_seq(SEQ_15_GRAB_LEDGE_MIDAIR);
|
||||
play_seq();
|
||||
determine_col();
|
||||
grab_timer = 12;
|
||||
pop_sfx_play(9); /* seg006 check_grab */
|
||||
is_screaming = 0; /* seg006:1219 */
|
||||
return 0;
|
||||
}
|
||||
Char.x = (uint8_t)char_dx_forward((int8_t)distance_to_edge_weight());
|
||||
Char.y = (uint8_t)pop_y_land[Char.curr_row + 1];
|
||||
Char.fall_y = 0;
|
||||
pop_char_set_seq(SEQ_15_GRAB_LEDGE_MIDAIR);
|
||||
play_seq();
|
||||
determine_col();
|
||||
grab_timer = 12;
|
||||
pop_sfx_play(9); /* seg006 check_grab */
|
||||
is_screaming = 0; /* seg006:1219 */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void make_loose_fall(int pos, uint8_t modifier);
|
||||
@@ -972,8 +977,16 @@ static void do_fall(void)
|
||||
}
|
||||
if (nrow > 4) nrow = 4; /* защита pop_y_land[] от выхода */
|
||||
if ((uint16_t)pop_y_land[nrow] > (uint16_t)Char.y) {
|
||||
check_grab(); /* ещё летит — попытка зацепа */
|
||||
glide_through_wall_guard(); /* и не сквозь кладку (см. выше) */
|
||||
/* ЗАЦЕП ОТМЕНЯЕТ GUARD. seq_15 — «grab ledge MIDAIR», то есть после
|
||||
* удачного зацепа Char.action == 3, персонаж уже подтянут вплотную к
|
||||
* кромке, а под кромкой в оригинальных комнатах стоит КЛАДКА. Guard,
|
||||
* позванный следом, видел ровно её (t == TILE_WALL, d == 10), считал
|
||||
* это «пролётом сквозь стену», отбрасывал Кида на 5 пикселей назад и
|
||||
* гасил fall_x — зацеп рисовался и тут же срывался. Ловилось на
|
||||
* длинном прыжке уровня 3 (комната 7 -> кромка комнаты 2) и в
|
||||
* attract-демо; регресс-набор — tests/host/t_glide.c. */
|
||||
if (!check_grab()) /* ещё летит — попытка зацепа */
|
||||
glide_through_wall_guard(); /* и не сквозь кладку (см. выше) */
|
||||
} else if (Char.curr_row <= 2) {
|
||||
if (get_tile_at_char() == TILE_WALL)
|
||||
in_wall();
|
||||
|
||||
Reference in New Issue
Block a user