/* * t_grab.c — прыжок с места через провал в три тайла с зацепом за дальнюю * кромку (уровень 2, комната 9: Кид на краю (1,1) прыгает и должен * повиснуть на (1,5)). * * Зачем отдельный набор. Трюк требует точной фазы и вручную выходит далеко * не всегда — в SDLPoP тоже. Поэтому проверяем не «получилось с первого * раза», а то, что действительно важно: **окно существует и достижимо**. * Разница между «сложно» и «невозможно» — это разница между «так в * оригинале» и «баг у нас», и глазами в MAME её не различить. * * Два утверждения: * 1. есть хотя бы одна стартовая X, из которой зацеп удаётся; * 2. короткий шаг (Shift+вперёд) приводит Кида В ЭТО окно — иначе окно * было бы недостижимо для игрока, что равно его отсутствию. * * При провале в отчёт уходит карта исходов по всей ширине плиты: * G — зацепился, L — приземлился на дальнюю плиту, . — сорвался. * * Механику зацепа задаёт check_grab (seg006:0A28): она работает ТОЛЬКО на * кадрах 102..105 (начало падения) и только при зажатом Shift. mininim, * для сравнения, разрешает повиснуть прямо в дуге прыжка (kid-jump.c, * кадры 7..10) — но это переработка, а не порт, и источник истины у нас * SDLPoP. */ #include "tcheck.h" #include "scene.h" #include "stubs.h" #include "pop_kid.h" #include "pop_map.h" #define E 0 #define F 1 #define P 3 #define W 20 /* Уровень 2, комната 9, ряд 1: факел(0), кнопка(1), провал(2,3,4), * колонна(5). Ряд 2 под провалом пуст — падение уходит из комнаты. */ static const uint8_t room9[30] = { E, E, E, E, E, W, W, W, W, W, F, F, E, E, E, P, F, F, F, P, W, W, E, E, E, P, F, W, W, W, }; /* Судить по curr_row нельзя: у улетающего вниз Кида ряд заворачивается * (0,1,2,0,…), и падение выглядит как «остался наверху». Надёжный признак * — ДЕЙСТВИЕ: 2 = вис/подтягивание, 6 = вис у стены. */ #define ACT_STAND 0 #define ACT_RUN_JUMP 1 #define ACT_HANG_CLIMB 2 #define ACT_HANG_STRAIGHT 6 #define X_LO 76 #define X_HI 99 static char probe(uint8_t x, uint8_t k1, uint8_t k2) { uint8_t i; sc_room(room9, 9); sc_kid_at_x(1, 1, x, 0 /* лицом вправо */); sc_trace_clear(); sc_run(k1, 4); /* фаза 1: ↑+вперёд — прыжок с места */ sc_run(k2, 26); /* фаза 2: Shift — попытка зацепа */ for (i = 0; i < sc_len; i++) { uint8_t a = sc_trace[i].action; if (a == ACT_HANG_CLIMB || a == ACT_HANG_STRAIGHT) return 'G'; } i = sc_len ? (uint8_t)(sc_len - 1) : 0; if (sc_trace[i].col >= 5 && (sc_trace[i].action == ACT_STAND || sc_trace[i].action == ACT_RUN_JUMP)) return 'L'; return '.'; } static void put(char c) { if (tc_result.logn < TC_LOGSZ) tc_result.log[tc_result.logn++] = (uint8_t)c; } static void puts_(const char *s) { while (*s) put(*s++); } /* Карта исходов — только когда что-то сломалось: по ней сразу видно, * окно съехало или исчезло. */ static void dump_map(const char *tag, uint8_t k1, uint8_t k2) { uint8_t x; puts_(tag); for (x = X_LO; x <= X_HI; x++) put(probe(x, k1, k2)); put('\n'); } /* Куда встаёт Кид после n-го короткого шага от x0. */ static uint8_t step_x(uint8_t x0, uint8_t nsteps) { uint8_t n, fx = x0; sc_room(room9, 9); sc_kid_at_x(1, 1, x0, 0); for (n = 0; n < nsteps; n++) { sc_trace_clear(); sc_run(SC_SHIFT | SC_R, 4); /* шаг = нажатие, не удержание */ sc_run(0, 6); /* отпустить, дать доиграть */ if (sc_len) fx = sc_trace[sc_len - 1].x; } return fx; } TC_TEST(grab_window_exists) { uint8_t x, n = 0, lo = 0, hi = 0; for (x = X_LO; x <= X_HI; x++) if (probe(x, SC_U | SC_R, SC_SHIFT) == 'G') { if (!n) lo = x; hi = x; n++; } /* Окно обязано существовать: без него трюк непроходим в принципе, а * уровень 2 без него не пройти. */ TC_TRUE(n > 0); if (!n) { dump_map("no-window: ", SC_U | SC_R, SC_SHIFT); } else { /* Границы фиксируем: если поедут — это осознанное изменение физики, * а не случайность. */ TC_EQ(lo, 91); TC_EQ(hi, 95); } } TC_TEST(grab_window_reachable_by_safe_step) { /* Игрок доходит до кромки короткими шагами. Если они не попадают в * окно, существование окна ничего не стоит. */ TC_EQ(step_x(80, 1), 91); TC_EQ(step_x(80, 2), 95); } TC_TEST(grab_same_for_all_key_patterns) { /* Живой игрок держит «вперёд» и жмёт Shift по-разному. Исход не должен * зависеть от того, отпущена ли стрелка и когда нажат Shift. */ uint8_t x; for (x = X_LO; x <= X_HI; x++) { char a = probe(x, SC_U | SC_R, SC_SHIFT); char b = probe(x, SC_U | SC_R, SC_SHIFT | SC_R); char c = probe(x, SC_U | SC_R | SC_SHIFT, SC_SHIFT | SC_R); TC_EQ(b, a); TC_EQ(c, a); } } int main(void) { sc_init(); TC_RUN(grab_window_exists); TC_RUN(grab_window_reachable_by_safe_step); TC_RUN(grab_same_for_all_key_patterns); return 0; }