/* * t_hang.c — ЗАЦЕП ОБЯЗАН ДЕРЖАТЬСЯ, а не срываться на следующем кадре. * * Регресс 2026-09-02. glide_through_wall_guard() (порт fix_glide_through_wall, * взят в билд 2026-08-31) звался в do_fall СРАЗУ ПОСЛЕ check_grab. Беда в * том, что seq_15 называется grab_ledge_MIDAIR: после удачного зацепа * Char.action == 3 (в воздухе), персонаж уже подтянут вплотную к кромке — а * под кромкой в комнатах оригинала стоит КЛАДКА. Guard видел ровно её * (t == TILE_WALL, d == 10), считал это «пролётом сквозь стену», отбрасывал * персонажа на 5 пикселей назад и гасил fall_x. Зацеп РИСОВАЛСЯ и тут же * срывался — уровень 3 (комната 7, длинный прыжок через четыре пролёта) и * attract-демо (комната 2, прыжок с места). * * ПОЧЕМУ ПРЕЖНИЕ НАБОРЫ ЭТОГО НЕ ПОЙМАЛИ. t_grab судит по «действие стало * вис» — а вис-то наступал, он просто не жил. Отсюда мера здесь другая: * вис обязан ДЕРЖАТЬСЯ подряд несколько кадров. Разница между «зацепился» * и «зацепился и держится» — это и есть разница между багом и нормой. * * СЦЕНА (геометрия уровня 3, комната 7, приведённая внутрь одной комнаты, * чтобы шов не примешивался): кромка на (0,0), под ней кладка (1,0), четыре * пролёта (0,1..4), толчок с плиты (0,5), разбег справа. */ #include #include "tcheck.h" #include "scene.h" #include "stubs.h" #include "pop_kid.h" #include "pop_map.h" #define E 0x00 #define F 0x01 #define G 0x04 #define L 0x0B #define T 0x13 #define W 0x14 static uint8_t room[30] = { F, E, E, E, E, L, F, T, F, T, W, E, E, E, E, W, W, W, W, W, W, E, E, E, E, W, W, W, W, W, }; #define ACT_HANG_CLIMB 2 #define ACT_HANG_STRAIGHT 6 #define HOLD_FRAMES 3 /* Вис ДЕРЖИТСЯ: действие «вис» стоит HOLD_FRAMES кадров подряд. */ static uint8_t hang_holds(uint8_t ledge, uint8_t nrun) { uint8_t i, run = 0; room[0] = ledge; sc_room(room, 7); sc_kid_at(9, 0, -1); /* лицом влево, у правого края */ sc_run(SC_L, nrun); /* разбег */ sc_run(SC_L | SC_U, 6); /* толчок в прыжок */ sc_trace_clear(); sc_run(SC_SHIFT, 30); /* полёт, зацеп и жизнь после него */ for (i = 0; i < sc_len; i++) { uint8_t a = sc_trace[i].action; if (a == ACT_HANG_CLIMB || a == ACT_HANG_STRAIGHT) { if (++run >= HOLD_FRAMES) return 1; } else run = 0; } return 0; } /* Сколько фаз разбега из 41 дают ЖИВОЙ вис. До регресса — все, кроме * первой (слишком близкий толчок), то есть 34; при сорванном зацепе — ноль. */ static uint8_t window(uint8_t ledge) { uint8_t k, n = 0; for (k = 6; k <= 46; k++) if (hang_holds(ledge, k)) n++; return n; } TC_TEST(long_jump_hang_survives_wall_under_ledge) { /* Кромка-пол: под ней кладка — та самая, на которую срабатывал guard. */ TC_TRUE(window(F) >= 30); } TC_TEST(long_jump_hang_survives_on_gate) { /* Кромка-решётка: тайл другой, кладка под ним та же. Проверяется * отдельно, потому что зацеп за решётку идёт своей веткой can_grab. */ TC_TRUE(window(G) >= 30); } int main(void) { sc_init(); TC_RUN(long_jump_hang_survives_wall_under_ledge); TC_RUN(long_jump_hang_survives_on_gate); return 0; }