Зацеп за кромку нижнего ряда: do_fall пускает ряд до 3 + check_grab в midair
Уровень 7, комната 14: спуск с ряда 0 на ряд 2 через зацеп (повис на кромке кнопки (0,2), отпустил, взялся за (2,2)) не работал ни при какой фазе и задержке — Кид пролетал мимо и разгонялся до fall_y = 33, после чего окно зацепа закрыто уже по скорости. Корень: do_fall не давал curr_row выйти за 2, а check_grab целится в тайл ряда curr_row-1 — то есть ряд 2 своей комнаты становится целью только при curr_row == 3. В оригинале (seg005:0030) inc_curr_row безусловный, а get_tile для ряда 3 уходит по links.down (find_room_of_tile, seg006:005D). - pop_map.c do_fall: inc_curr_row теперь 2 -> 3; ветка «достиг y_land» гейтится по curr_row <= 2 (при ряде 3 ни in_wall, ни land звать нельзя — тайлов своей комнаты там нет, а до y_land[4] дело не доходит: комнату меняет check_leave_below на y >= 211). - pop_map.c check_action: восстановлена ветка ACT_MIDAIR (кадры 102..105, seg006:0619) — первые четыре кадра падения, где fall_y ещё не разогнан, зацеп не работал вовсе. Отсюда же «иногда цепляется, иногда нет». - tests-host/t_grab.c: регресс grab_below_room_edge_window_exists — сцена комнаты 14 + переход в 15 по pop_fell_out. До фикса ни одного зацепа, после — окно из 8 фаз X при задержках 0..5 кадров; проверяется и играбельность (зацеп при «отпустил и сразу зажал Shift»). - roomtest.c + pop_guard.h: чит-навигация ставит Кида на верхний ряд в комнате 14 уровня 7 (GRAB_DOWN_LEVEL/ROOM) — снизу этот спуск не проверить. Общее правило (снизу вверх) не тронуто: в верхнем ряду пола чаще нет и Кид проваливается сразу после телепорта. Проверено: tests-host 5/5 (55 в [grab]), size-check OK, живьём в MAME — Кид повис на кромке (2,2): frame=91 y=55 row=0 room=15. Доски: GRAB-BELOW-ROOM в BUGS_CLOSED, GRAB-KBD-TIMING в BUGS_OPEN (отложен пользователем до готовности всех уровней), план L7-FEATHER в TASKS_OPEN. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,69 @@
|
||||
|
||||
---
|
||||
|
||||
<a id="grab-below-room"></a>
|
||||
## GRAB-BELOW-ROOM. Зацеп за кромку НИЖНЕГО ряда при пролёте вниз не работал вовсе
|
||||
|
||||
**Наблюдение (пользователь, 2026-08-12).** Уровень 7, комната 14: по сценарию
|
||||
уровня Кид спускается с ряда 0 на ряд 2 через зацеп — виснет на кромке кнопки
|
||||
`(0,2)`, отпускает и в падении цепляется за кромку `(2,2)`. «Пытался много
|
||||
раз — Кид летит и не цепляется ни за `(2,2)` комнаты 14, ни за `(1,2)` комнаты
|
||||
15». При этом вис на `(2,2)` с последующим перелётом на `(1,2)` **работает
|
||||
стабильно** — то есть сам зацеп в падении жив.
|
||||
|
||||
**Корень.** `do_fall` (порт seg005:0030) не давал ряду персонажа выйти за
|
||||
нижнюю границу комнаты:
|
||||
|
||||
```c
|
||||
else if (Char.curr_row < 2)
|
||||
inc_curr_row(); /* «следующий ряд ВНУТРИ комнаты» */
|
||||
```
|
||||
|
||||
В оригинале `inc_curr_row()` безусловный, и `curr_row` доходит до **3** — ряда
|
||||
«за нижней кромкой»; `get_tile` для него уходит по `links.down` в комнату снизу
|
||||
(`find_room_of_tile`, seg006:005D). Ряд 3 нужен не сам по себе, а потому, что
|
||||
`check_grab` целится в тайл **спереди-сверху**, то есть в ряд `curr_row − 1`:
|
||||
только при `curr_row == 3` целью становится ряд 2 своей комнаты.
|
||||
|
||||
С залипшим рядом 2 происходило вот что: пока Кид летел вдоль нижнего ряда,
|
||||
`do_fall` каждый кадр уходил в ветку «достиг `y_land`», где `check_grab` не
|
||||
вызывается вовсе. К моменту, когда переход в комнату снизу открывал окно
|
||||
зацепа заново (там цель — «ряд −1», уже реализованный `g_above`), скорость
|
||||
падения успевала дойти до `fall_y = 33`, а `check_grab` требует `< 32`. Окно
|
||||
закрывалось **по скорости** — отсюда «летит и не цепляется никак».
|
||||
|
||||
Второй дефект, найденный там же: ветка `action == ACT_MIDAIR` в `check_action`
|
||||
стояла ПУСТОЙ заглушкой с комментарием «frames 102..105: check_grab — K4». В
|
||||
оригинале (seg006:0619) это первые четыре кадра падения, где `fall_y` ещё не
|
||||
разгоняется (`fall_accel` работает только в `ACT_FREEFALL`), — самое широкое
|
||||
окно зацепа. У нас его не было.
|
||||
|
||||
**Фикс** (`pop_map.c`): `inc_curr_row()` теперь безусловный внутри ветки
|
||||
`curr_row <= 2` (то есть 2 → 3 разрешён), а сама ветка «достиг `y_land`»
|
||||
гейтится по `curr_row <= 2` — при ряде 3 ни `in_wall`, ни `land` звать нельзя
|
||||
(тайлов своей комнаты там нет, `get_tile` отдаёт `WALL`-сентинел; до
|
||||
`y_land[4] = 244` дело не доходит, `check_leave_below` переводит в комнату
|
||||
снизу на `y >= 211`). Плюс восстановлена ветка `ACT_MIDAIR` с кадрами 102..105.
|
||||
|
||||
**Подтверждение.**
|
||||
- Хост-тест `tests-host/t_grab.c` → `grab_below_room_edge_window_exists`:
|
||||
сцена комнаты 14 + переход в 15 по `pop_fell_out` (как в главном цикле).
|
||||
Карта исходов по фазе X и задержке Shift **до** фикса — сплошные точки (ни
|
||||
одного зацепа), **после** — окно из 8 фаз X при задержках 0..5 кадров.
|
||||
Тест проверяет и играбельность: зацеп обязан удаваться при самом
|
||||
естественном вводе (отпустил вис и сразу зажал Shift).
|
||||
- Живьём в MAME (мост, 2026-08-12): Кид повис на кромке `(2,2)` — в памяти
|
||||
`frame=91 y=55 row=0 room=15 act=6`, на экране держится за правый край плиты.
|
||||
|
||||
**Грабли теста, стоившие часа.** Первая версия сцены засчитывала как успех
|
||||
ЛЮБОЙ вис — а Кид на первых кадрах падения цепляется обратно за ту же верхнюю
|
||||
кромку, и тест проходил даже на сломанном коде. Признак цели пришлось делать
|
||||
позиционным (`went_below` + `curr_row == 0` после перехода). Вторая ловушка:
|
||||
кадры спуска `seq_68` идут с `action == 3`, то есть «дождаться падения» по
|
||||
одному лишь `action` нельзя — ждать надо ВИСА, и только потом отпускать Shift.
|
||||
|
||||
---
|
||||
|
||||
<a id="bug-cheat-fight-1"></a>
|
||||
## BUG-CHEAT-FIGHT-1. Чит `+`/`−` в бою: Кид остаётся в режиме боя и теряет управление
|
||||
|
||||
|
||||
Reference in New Issue
Block a user