BUG-GATE-PASS-1: воспроизведён и разобран; SDLPoP собран с трассой

Сценарий: ур.1 комн.8, ряд 0, x=170, лицом вправо, решётка шва закрыта —
разбежаться вправо и не отпускать.  Кид проходит сквозь решётку.

Корень (две трассы, наша и оригинала, стартовая позиция совпала до пикселя):
смена комнаты в этой точке ШТАТНАЯ и у нас, и в оригинале — leave_room вправо
блокируют только doortop.  Оригинал держит Кида бампом СРАЗУ ПОСЛЕ перехода,
потому что check_collisions хранит флаги по паре (колонка в СВОЕЙ комнате,
номер комнаты) и сравнивает prev/curr только при совпадении номеров: решётка
комнаты 8 и до, и после перехода лежит в слоте 9 с room=8, история не рвётся.
У нас индекс — колонка относительно отрисованной комнаты, при переходе все
индексы уезжают на 10, поэтому enter_room зовёт pop_coll_invalidate, тот
подавляет бамп на кадре входа, а дальше перехода флага 0->1 уже не будет.

Что делать — порт индексации оригинала (10 слотов по колонке своей комнаты +
массив её номера); тогда pop_coll_invalidate не нужен вовсе.  Осторожно: это
сердце коллизии из BUG-SEAM-PINGPONG.

SDLPoP инструментирован для сверки: POP_TRACE=1 включает покадровую печать
Char + обе пары краёв, плюс маркеры BUMPED и LEAVE.  В bug_list записана и
команда сборки на macOS (штатный Makefile требует pkg-config, которого нет).
This commit is contained in:
2026-08-08 20:35:41 +03:00
parent 463f35d440
commit c93a348b4a
+84 -2
View File
@@ -18,7 +18,7 @@
| ID | что | тип | статус |
|----|-----|-----|--------|
| [BUG-CHEAT-FIGHT-1](#bug-cheat-fight-1) | `+`/`` в бою с вынутым мечом → Кид теряет управление | Major (чит) | открыт |
| [BUG-GATE-PASS-1](#bug-gate-pass-1) | проход сквозь закрывшуюся решётку (0,9) комнаты 5 | Major | **ждёт сценария**: прогоном 2026-08-07 не воспроизведён |
| [BUG-GATE-PASS-1](#bug-gate-pass-1) | проход сквозь закрытую решётку шва (комн. 8 -> 6) | Major | **воспроизведён, корень найден 2026-08-08**: история флагов коллизии не привязана к комнате |
| [BUG-SPIKE-1](#bug-spike-1) | пики залипают выдвинутыми рядом с Кидом | **низкий** | маловоспроизводимый: ни сценарием, ни попиксельной подгонкой X не поднимается |
| [BUG-CHOMP-JUMP-1](#bug-chomp-jump-1) | прыжок с места вплотную к чомперу: кадр с отступом назад | **низкий** | маловоспроизводимый: на повторе не поднялся; на пререлиз |
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация | открыт |
@@ -193,7 +193,89 @@ Smoke-прогон уровня 3 (пользователь, 2026-08-05) дал
# Открытые баги уровня 1
<a id="bug-gate-pass-1"></a>
## BUG-GATE-PASS-1. Проход сквозь закрывшуюся решётку — ЖДЁТ СЦЕНАРИЯ
## BUG-GATE-PASS-1. Проход сквозь закрывшуюся решётку — **КОРЕНЬ НАЙДЕН 2026-08-08**
> **ВОСПРОИЗВЕДЁН И РАЗОБРАН.** Сценарий: уровень 1, комната 8, ряд 0,
> `Kid.x = 170`, лицом ВПРАВО, решётка шва (комната 8, колонка 9) ЗАКРЫТА.
> Разбежаться вправо и НЕ ОТПУСКАТЬ клавишу. Кид проходит сквозь решётку и
> убегает вглубь комнаты 6.
>
> Ниже — новый разбор; всё, что было раньше (гипотеза про x > 205), не
> подтвердилось и оставлено в конце для истории.
### Корень: история флагов коллизии индексируется НЕ ТАК, как в оригинале
Сняты две трассы — наша (брейкпоинт на резидентном `kid_tick`) и оригинала
(SDLPoP, собран с `POP_TRACE=1`, печать в `play_kid_frame` + маркеры в
`bumped` и `exit_room`). Стартовая позиция совпала до пикселя, поэтому
сравнение построчное.
**Оригинал:**
```
LEAVE dir=1 x=61 cxl=184 cxr=201 room=6 смена комнаты — ШТАТНАЯ
BUMPED dx=-2 push=-1 x=63 tile=4 col=9 и СРАЗУ бамп о решётку, откат
KID f=15 x=60 act=0 col=-1 room=6 drawn=6 сел за решёткой, стоит
```
**Мы:**
```
x 170 -> 196 бегом, col 7 -> 8 -> 9, НИ ОДНОГО бампа
x=198 -> room 8 -> 6
при удержании ВПРАВО доезжает до col 1 комнаты 6 и бежит дальше
```
Смена комнаты в этой точке правильна и у нас, и у оригинала: `leave_room`
(seg002:423) вправо блокируют ТОЛЬКО `doortop`-тайлы, решётки в списке нет, а
порог `char_x_right >= 201` совпадает с плоскостью ворот
(`x_bump[9+FIRST_ONSCREEN_COLUMN] + TILE_MIDX + wall_dist_from_left[1]`
= 191 + 10). Оригинал полагается не на запрет ухода, а на бамп СРАЗУ ПОСЛЕ
перехода.
**Почему бамп есть у них и нет у нас.** `check_collisions` (seg004:0004)
хранит флаги так:
```c
row_coll_flags_ptr[tile_col] = curr_flags; // индекс — колонка В РАЗРЕШЁННОЙ комнате
row_coll_room_ptr [tile_col] = curr_room; // и рядом — НОМЕР ЭТОЙ КОМНАТЫ
...
if (curr_row_coll_room[column] >= 0 &&
prev_coll_room[column] == curr_row_coll_room[column]) { /* только тогда сравниваем */ }
```
То есть история привязана к паре **(колонка внутри своей комнаты, комната)**.
Решётка комнаты 8 и до перехода, и после лежит в слоте 9 с `room = 8`
переход 8->6 историю НЕ рвёт, переход флага 0->1 виден, бамп срабатывает.
У нас массив `coll_prev/coll_curr` индексируется колонкой ОТНОСИТЕЛЬНО
отрисованной комнаты (−2…11). При смене комнаты все индексы уезжают на 10,
сопоставить прошлый кадр с текущим нечем, и `enter_room` вынужден звать
`pop_coll_invalidate()`; тот ставит «прошлые флаги = уже перекрывал» (3), то
есть ПОДАВЛЯЕТ бамп на кадре входа. А к следующему кадру Кид уже за
плоскостью ворот — перехода 0->1 не будет никогда.
### Что делать
Порт индексации оригинала: хранить флаги в 10 слотах ПО КОЛОНКЕ РАЗРЕШЁННОЙ
комнаты плюс параллельный массив её номера, и сравнивать prev/curr только при
совпадении номеров. Тогда `pop_coll_invalidate()` не нужен вовсе — история
сама «не совпадает» там, где колонка сменила комнату.
**Осторожно:** это сердце коллизии, вокруг которого разбирался
BUG-SEAM-PINGPONG (см. `bug_closed.md`). После правки обязателен прогон
швов: комнаты 6/8 первого уровня в обе стороны, осторожный шаг и разбег, плюс
проверка, что пинг-понг не вернулся.
**Инструмент для сверки готов:** SDLPoP собран с трассой, включается
`POP_TRACE=1` (правки в `seg000.c` play_kid_frame, `seg004.c` bumped,
`seg002.c` exit_room, чтение переменной — `main.c`). Сборка на macOS:
`make CFLAGS="-std=gnu99 -O2 -I/opt/homebrew/include -Wno-implicit-function-declaration" LIBS="-L/opt/homebrew/lib -lSDL2 -lSDL2_image"`
(в системе нет pkg-config, штатный Makefile без этого не собирается).
---
### История: прежний разбор (гипотеза НЕ подтвердилась)
**Статус: наблюдался один раз (2026-08-03), воспроизвести не удалось ни
тогда, ни прогоном всех комнат уровня 1 (2026-08-07).** Заведён, чтобы