Смерть на кнопке ломает её насовсем (died_on_button, seg007:776)

Уровень 7 начинается падением; разбившись на кнопке открытия решётки
(комната 3, тайл 2,1), Кид в оригинале открывает решётку НАСОВСЕМ, а у нас
она закрывалась обратно.  Чинить пришлось три места.

1. pop_phys_tick выходил по pop_kid_dead, то есть check_press до трупа не
   доходил вовсе.  У оригинала play_kid_frame гейтится только Char.room
   != 0.  Кнопка получала одно нажатие — в кадре смерти, потому что флаг
   ставит land() уже внутри цепочки.  «Труп не шевелится» держит внутренний
   выход в kid_phys, он остался.

2. Портирован died_on_button: открывалка → пол + связь дёргается типом
   «щебень» (открыть насовсем), прочие кнопки → TILE_STUCK.  Ветки по
   Char.alive в check_press не было вовсе.

3. Рестарт уровня восстанавливал только foretable, а died_on_button
   оставляет таймер связи нажатым (trob кнопки умирает сразу — тайл уже
   пол).  Остаток переживал респавн, и кнопка рисовалась нажатой с первого
   кадра.  pop_level_reset_tiles теперь возвращает и LINKMAP.

Проверено в MAME по всему циклу: смерть → FF (открыта навсегда), респавн →
кнопка цела и не нажата, второе падение → снова ломается.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 15:27:36 +03:00
parent b49ba15666
commit 6feab5d2f2
4 changed files with 182 additions and 71 deletions
-67
View File
@@ -17,7 +17,6 @@
| [SPRITE-ZERO-W0](#sprite-zero-w0) | кадр персонажа изредка отдаёт спрайт 0x0 (залипание уже вылечено) | **редкий** | открыт: нужна трасса маппинга W0 |
| [GATE-FORE-KID](#gate-fore-kid) | Кид в проёме ворот виден поверх решётки | окклюзия | **фикс есть, ждёт проверки в MAME** (2026-08-13) |
| [FORE-DUP](#fore-dup) | передний слой тайла рисуется дважды при перекрытии объектов | оптимизация | открыт: **сначала замерить**, потом чинить |
| [DIED-ON-BUTTON](#died-on-button) | `died_on_button` (seg007:776) не портирован | порт | открыт |
| [TORCH-ANIM-RIGHT](#torch-anim-right) | под запечённым пламенем застывают не только челюсти чомпера | **низкий** | открыт: на уровнях 1-4 такого соседства нет |
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация | открыт |
| [BUG-SPIKE-1](#bug-spike-1) | пики залипают выдвинутыми рядом с Кидом | **низкий** | маловоспроизводим: ни сценарием, ни попиксельной подгонкой X не поднимается |
@@ -303,72 +302,6 @@ check_spiked (seg006:0658): убивает при h>=2 на кадрах б
приоритет багов в них низкий. Аналогично 23/24 на уровне 3.
---
<a id="died-on-button"></a>
## DIED-ON-BUTTON. `died_on_button` (seg007:776) не портирован
Обнаружено при разборе [BUG-LOOSE-BUTTON-1](BUGS_CLOSED.md#bug-loose-button-1).
`check_press` (seg006:1707) разбирает ЛЮБОГО мёртвого `Char`, не только Кида:
```c
if (curr_tile2 == tiles_15_opener || curr_tile2 == tiles_6_closer) {
if (Char.alive < 0) trigger_button(1, 0, -1); /* жив */
else died_on_button(); /* мёртв */
}
```
`died_on_button` на **opener** делает тайл обычным полом и форсирует
`button_type = tiles_14_debris` — ворота открываются насовсем; на любой другой
кнопке ставит `tiles_5_stuck` (заклинена, `link_timer == 0x1F`, связь мертва).
У Кида эффект живёт до перезапуска уровня (смерть → `is_restart_level`
`play_level` заново зовёт `load_level()`, тайлы перечитываются), а вот когда на
кнопке умирает СТРАЖ — перезапуска нет, и кнопка заклинена до конца уровня.
Что нужно: сам порт `died_on_button`, константа `TILE_STUCK = 5` и тайл
заклиненной кнопки в атласе фона (сейчас его там нет).
### Вторая половина: до `check_press` труп вообще не доходит (2026-08-18)
Найдено на живом сценарии (пользователь): уровень 7 начинается падением, и
если не зацепиться за кромку, Кид разбивается на `(2,1)` комнаты 3 — а это
кнопка открытия решётки. В оригинале решётка после этого открыта НАСОВСЕМ,
у нас отжимается.
Оказалось, порта `died_on_button` мало — до него нет пути:
```c
void pop_phys_tick(void) __banked
{
if (pop_kid_dead) return; /* <- вся физика трупа выключена */
```
У оригинала `play_kid_frame` (seg000:1231) гейт ровно один — `Char.room != 0`;
весь список, включая `check_press`, крутится и на трупе (по `alive` разведены
только `check_spiked`/`check_chomped_kid`, и то через `resurrect_time`).
Отсюда наблюдаемое поведение: кнопка получает РОВНО ОДНО нажатие — в кадре
самой смерти, потому что `pop_kid_dead` ставит `land()` уже ВНУТРИ цепочки,
после раннего возврата. Решётка успевает приоткрыться и потом закрывается.
Снято в MAME: Кид `frame 185, action 5 (bumped), alive 0, (2,1) room 3`,
`pop_kid_dead = 1`, тайл `(2,1)` = `0x0F` opener, modif `0x12` (индекс связи),
openness ворот `(2,6)` ползёт вниз (146 → 112).
Кадр смерти проходит проверки `check_press` штатно (`action == ACT_BUMPED`,
флаги кадра 185 = `0x49`, бит `FRAME_NEEDS_FLOOR` есть) — то есть после снятия
раннего возврата труп начнёт давить кнопку КАЖДЫЙ кадр, как живой. Это ещё
не оригинал: там на `alive >= 0` уходит `died_on_button`, который делает
эффект ПОСТОЯННЫМ и снимает кнопку с тайла. Значит чинить надо обе половины
разом, иначе получится «кнопка держится, пока труп лежит» — поведение,
которого нет ни в оригинале, ни сейчас.
**Побочное наблюдение, НЕ проверено:** openness ворот остановился на 112 и
дальше не убывал, хотя `animate_door` (у нас совпадает с seg007:0522) обязан
досчитать до 0 и снять trob. Довести проверку не удалось — в сцене залип
`frozen` (клавиша фриза осталась в raw-битмапе, `2` перестала сниматься).
Если подтвердится — это отдельный баг, к `died_on_button` отношения не имеющий.
---
<a id="torch-anim-right"></a>