PoP: скелет уровня 3, цвета стражей, окклюзия соперника; кэш соседних комнат

Скелет (L3-SKEL, ассеты + механика):
- pop_pack_guard.py получил параметр набора (GUARD/SKEL): атлас скелета
  poc/res/skel/g0..g3.atl (28 кадров), палитра — из его res750.pal (на ур. 3
  curr_guard_color = 0, оригинал палитру не подменяет);
- pop_guard_load выбирает набор по tbl_guard_type и перезагружается ПРИ СМЕНЕ
  УРОВНЯ (load_lev_spr, seg000:1092) — без этого скелет рисовался атласом
  стража и был невидим;
- load_frame: charid_4_skeleton идёт по таблице стража (seg006:529), тень —
  только в кадрах 150..189.  Пока ветка была одна (charid_2_guard), скелет
  получал image из таблицы Кида (180 при 28 спрайтах) и не рисовался;
- check_skel (seg002:1042), ветка charid_4 в enter_guard, возрождение в
  комнате 3 при падении (seg002:252), autocontrol_skeleton;
- leveldoor_open (seg007:456) — новый флаг, сбрасывается стартом уровня.

Цвета стражей (BUG-GUARD-COLOR-1, закрыт):
- все 7 палитр res10.bin -> pop_guard_pal.h, заливка 16 слотов по
  guards_color комнаты перед отрисовкой (set_chtab_palette, seg003:257).
  Проверено в MAME: ур. 2 комн. 11 = цвет 1, комн. 7 = цвет 3, полоса HP
  меняется вместе со стражем.  Грабля: gfx_pal_load отдаёт указатель в BIOS,
  а тот читает только #4000-#BFFF — таблицу из банка копируем в стек.

Кэш соседних комнат (BUG-SWORD-GHOST-1, закрыт):
- pop_map кэширует fg соседей слева/справа ЦЕЛИКОМ и резолвит col -10..19.
  Было -2..11, дальше мнимая стена: луч видимости упирался в неё (страж
  после follow_guard в col 12), Кид прятал меч посреди боя и не мог достать
  обратно.  +48 байт W2.

Окклюзия соперника:
- pop_fore_over_char получил проход other_overlay_tile (порядок midtable,
  seg008:1B06) и расширение перебора объединённым прямоугольником
  «персонаж + клинок + брызги» — падающий скелет больше не рисуется поверх
  кладки и верхней грани пола;
- клип полем 192 строк (reset_obj_clip, seg006:0507) для спрайта, клинка
  (общий pop_sword_draw) и брызг — спрайт не залезает на полосу HP;
- ROOMNAV после смерти Кида делает честный pop_start_level: телепорт
  «оживлял» мёртвого мимо старта уровня, оставляя живого скелета рядом с
  вернувшейся кучей костей.

Ассеты чомпера (под L3-CHOMP): весь набор кадров в атласе явным списком
(101-105 низ, 111-113 верх, 106-110 фронт, 114-123 кровь mono-силуэтом) —
render_room анимированные тайлы пропускает, и в атласе не было ни одного.
Число EMM-страниц не изменилось.

Тесты: tests-host все 5 наборов зелёные, t_char вырос до 65 проверок
(резолв колонок за краем комнаты, возрождение скелета); в testkit добавлен
гард «код наехал на данные» (DATA_LOC).

Доски: TASKS.md разнесён на TASKS_OPEN/TASKS_CLOSED, закрытые баги с
разбором корней — в bug_closed.md; заведены DRAW-CHAR (отрисовка одна на всех
Char, как физика после GUARD-PHYS) и L3-COLOR (зелёная кладка уровня 3:
level_var_palettes = ресурс 20, есть в MSDOS/PRINCE.DAT).

В roomtest.c временно оставлен автостоп на падении соперника (отладка
падений скелета) — помечен ВРЕМЕННО.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 22:11:03 +03:00
parent 18d177407b
commit 6673279cef
55 changed files with 3340 additions and 1304 deletions
+93 -185
View File
@@ -4,82 +4,84 @@
переехало в [`bug_closed.md`](bug_closed.md): прежде чем заводить новый баг,
грепни там по симптому.
Приоритеты работ — в [`TASKS.md`](TASKS.md), а не здесь. Правило проекта:
механику сверять с `../SDLPoP/src/` ДО кодинга.
Приоритеты работ — в [`TASKS_OPEN.md`](TASKS_OPEN.md) (закрытые задачи с
протоколами — [`TASKS_CLOSED.md`](TASKS_CLOSED.md)), а не здесь. Правило
проекта: механику сверять с `../SDLPoP/src/` ДО кодинга.
Ревизия списка: **2026-08-03** (прогон всех комнат уровня 1). Одиннадцать
наблюдений с прогона свелись к шести корням; **все шесть закрыты и проверены
в MAME** — разбор в [`bug_closed.md`](bug_closed.md): BUG-LVLSTATE-1 (уровень
стал мутабельным), BUG-RESPAWN-1 (респавн перезагружает уровень),
BUG-DEATH-1 (смерть от меча), BUG-GATE-ANIM-1 (анимация ворот), BUG-COLL-1
(полный порт `check_collisions`), BUG-STANDUP-1 (лишний guard в
`bumped_floor`). Клавиатурный BUG-KBD-3 доведён до конца во второй волне
(см. ниже); из первой волны открытым остался косметический BUG-SEAM-DRAW-1.
Ревизия **2026-08-03, вечер** (вторая волна с прогона): все четыре наблюдения
закрыты — BUG-KBD-4 (фантомный отжим Shift), BUG-RESPAWN-2 (стражи не оживают),
BUG-DRAWORDER-1 (Кид поверх тела стража) и BUG-LOOSE-2 (осколки только от одной
из двух плит). У BUG-DRAWORDER-1 остался артефакт «ноги поверх головы»,
сверенный с SDLPoP и записанный в «НЕ БАГИ». Тогда же закрыт как
невоспроизводящийся BUG-SEAM-DRAW-1 (Кид в шве рисуется) и заведён
BUG-GATE-PASS-1 — однократное наблюдение прохода сквозь закрывшуюся решётку,
которое ЖДЁТ надёжного сценария.
**Ревизия списка: 2026-08-07 — прогон ВСЕХ комнат уровней 1 и 2**
(пользователь). Крупных багов нет. Поэтому в [`bug_closed.md`](bug_closed.md)
уехали разом: чек-листы ручной перепроверки фиксов (2026-08-03, обе волны),
таблица обхода 24 комнат уровня 1 и таблица сырых наблюдений того же прогона —
все они закрыты этим проходом. С прогона пришли **три новые записи**, все по
уровню 2 (сырые формулировки — [`bugs_level2.md`](bugs_level2.md)).
| ID | что | тип | статус |
|----|-----|-----|--------|
| [Уровень 2](#уровень-2) | остальные баги с приёмки | — | принимаются по ходу |
| [BUG-GATE-PASS-1](#bug-gate-pass-1) | проход сквозь закрывшуюся решётку (0,9) комнаты 5 | Major | **ждёт сценария** (BUG-GATEMOD-1, из-за которого решётка стартовала не в том состоянии, закрыт — перепроверить) |
| [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-SPIKE-1](#bug-spike-1) | пики залипают выдвинутыми рядом с Кидом | **низкий** | маловоспроизводимый: ни сценарием, ни попиксельной подгонкой X не поднимается |
| [T-1](#t-1) | пики перерисовываются безусловно | оптимизация | открыт |
| [T-2](#t-2) | Кид перерисовывается в покое | оптимизация | открыт |
| [обход 24 комнат](#обход-всех-24-комнат-уровня-1) | таблица заполнена на 5 строк из 24 | ревизия | открыт |
| [BUG-SPIKE-1](#bug-spike-1) | пики залипают выдвинутыми рядом с Кидом | **низкий** | маловоспроизводимый: ни сценарием, ни попиксельной подгонкой X не поднимается |
---
<a id="уровень-2"></a>
# Уровень 2 — баги с приёмки (заведено 2026-08-04)
# Уровень 2 — баги с приёмки
Уровень 2 заиграл 2026-08-04 (см. [`TASKS.md`](TASKS.md) запись **L2**).
Пользователь при первом прогоне увидел **несколько багов отрисовки** и
подаст их списком отдельно — раздел заведён под них заранее, чтобы
наблюдения не растеклись по переписке.
Приёмка уровня 2 закрыта ([L2-PASS](TASKS_CLOSED.md#l2-pass)): smoke
2026-08-05 + обход всех комнат 2026-08-07. Карта содержимого уровня (что где
стоит по `res2002.bin`, какие кнопки какие ворота открывают) — там же, по ней
видно, «механика не сработала» это или «так и задумано».
**Закрыто с этой волны:** [BUG-LOOSE-3](bug_closed.md#bug-loose-3) — чёрный
бар под упавшей плитой-потолком (запечка восстанавливала не всё, что стёрла).
**Закрыто с этой волны:** [BUG-LOOSE-3](bug_closed.md) — чёрный бар под
упавшей плитой-потолком; [BUG-GUARD-DEAF-1](bug_closed.md) — страж не
оборачивался на вернувшегося Кида;
[BUG-GUARD-SPLASH-1](bug_closed.md#bug-guard-splash-1) — «брызги» при
попадании по стражу; [BUG-SWORD-GHOST-1](bug_closed.md#bug-sword-ghost-1) —
меч, спрятанный посреди боя после перехода комнаты (корень — мнимая стена за
краем комнаты в `get_tile`; кэш соседей расширен до полных комнат);
[BUG-GUARD-COLOR-1](bug_closed.md#bug-guard-color-1) — цвет стража и его
полосы HP теперь берётся из `guards_color` уровня (2026-08-07).
**Порядок работы по уровню 2 (2026-08-05):** наблюдения СНАЧАЛА копятся
здесь целиком — фиксы берём, когда уровень пройден до конца. Причина:
косметика отрисовки почти всегда сводится к общему корню (ср. волну
BUG-CEIL-1/2/3 + BUG-OCCL-1), и чинить по одному наблюдению — значит
чинить один и тот же корень трижды.
Ниже — то, что осталось открытым после прогона 2026-08-07.
Карта содержимого уровня (что где стоит по данным `res2002.bin`, какие
кнопки какие ворота открывают) — в [`TASKS.md`](TASKS.md#l2-pass),
запись **L2-PASS**: по ней видно, «механика не сработала» это или
«так и задумано».
<a id="bug-cheat-fight-1"></a>
## BUG-CHEAT-FIGHT-1. Чит `+`/`−` в бою: Кид остаётся в режиме боя и теряет управление
Перед заведением записи — грепнуть [`bug_closed.md`](bug_closed.md) по
симптому: почти вся косметика окклюзии уровня 1 уже разбиралась
(BUG-CEIL-1/2/3, BUG-OCCL-1, BUG-DOOR-CLIP, BUG-DRAWORDER-1), и уровень 2
вводит ровно три новых фоновых тайла — **большая колонна (низ 8 / верх 9)
и верх двери (12)**; если артефакт рядом с ними, это первый подозреваемый.
**Наблюдение (пользователь, 2026-08-07).** Если нажать `+`/`` (ROOMNAV,
переход по комнатам) в момент, когда Кид вытащил меч для битвы, — в новой
комнате Кид не управляется: нажатия стрелок игнорируются.
<a id="уровень-3"></a>
# Уровень 3 — баги с приёмки (заведено 2026-08-05)
**Корень (прочитан по коду, сверен с seg005).** Чит ROOMNAV
(`roomtest.c:576`) телепортирует и сбрасывает позу и HP —
`enter_room()`, `kid_init(SEQ_STAND, …)`, `pop_kid_hp_reset()`, — но **не
трогает состояние боя**: `Kid.sword` остаётся `SWORD_2_DRAWN`. Диспетчер
`pop_control` (`pop_ctrl.c:578`) при вынутом мече уходит в
`control_with_sword`, а там единственный выход из режима боя —
Smoke-прогон уровня 3 (пользователь, 2026-08-05) дал четыре наблюдения. Два
из них — **не баги, а неначатые задачи**, и записаны как таковые:
```c
if (Char.frame == FRAME_171_STAND_WITH_SWORD) { /* seg005:987 */
Char.sword = SWORD_0_SHEATHED;
seqtbl_offset_char(SEQ_92_PUT_SWORD_AWAY);
}
```
| наблюдение | что это на самом деле |
|------------|------------------------|
| к.22: чомпер (2,6) не анимируется и **вообще не рисуется** | [L3-CHOMP](TASKS.md#l3-chomp) — механики чомперов НЕТ. В таблице тайлов `pop_bg.c:211` строка `12 chomper` рисует только основание, правую грань и низ; самих челюстей (спрайт из `chtab`, `draw_tile_anim` seg008) нет вовсе. Так и должно выглядеть до порта |
| к.10: скелет не оживает | [L3-SKEL](TASKS.md#l3-skel) — `check_skel` (seg002:0E1F) не портирован. **И комната другая:** тайл `skeleton(21)`, который оживает, лежит в **к.1 (1,5)** — это `skeleton_room=1, skeleton_column=5, skeleton_row=1`. Ещё два скелета уровня (к.17 (2,7), к.19 (2,2)) — просто декорация, они не оживают никогда. Плюс условие: скелет встаёт, только когда **дверь уровня уже открыта** и Кид стоит в колонке 2 или 3 |
Стража в новой комнате нет (`can_guard_see_kid` = 0), кадр после
`kid_init(SEQ_STAND)` — обычная стойка, а не 171, поэтому не срабатывает ни
`swordfight()`, ни ветка «убрать меч»: `control_with_sword` каждый кадр
не делает НИЧЕГО, и ввод не доходит до движения. Заклинивание вечное.
Остальные два были настоящими багами и **закрыты в тот же день** — разбор
корней в [`bug_closed.md`](bug_closed.md): **BUG-JUMPWALL-1** (флаги
коллизий считались для одного ряда вместо трёх — бамп терялся на кадре
смены ряда) и **BUG-SEAM-WEDGE-1** (сосед угла (2,0) безусловно считался
стеной).
Это баг **нашего чита**, а не порта: в оригинале телепорта между комнатами
нет, и в режим боя без соперника попасть нечем.
**Как чинить.** В ветке ROOMNAV телепорт трактовать как выход из боя (то же
самое, что делает `pop_start_level`): `Kid.sword = SWORD_0_SHEATHED`,
`holding_sword = 0`, сбросить `offguard`/`guard_refrac` и состояние стража
(`pop_guard_reset()` вызывается по входу в комнату — проверить, что он
обнуляет `Opp`). Меч в инвентаре (`pop_have_sword`) при этом НЕ терять.
Второй кандидат на ту же болезнь — чит `K` (убить стража) в момент, когда
Кид в стойке с мечом, но не в кадре 171: там оригинал сам доводит до 171
через `swordfight`, так что проверить сценарием, а не менять вслепую.
<a id="bug-spike-1"></a>
## BUG-SPIKE-1. Пики залипают выдвинутыми — НИЗКИЙ ПРИОРИТЕТ, МАЛОВОСПРОИЗВОДИМ
@@ -124,28 +126,13 @@ check_spiked (seg006:0658): убивает при h>=2 на кадрах б
`animate_spike`, `start_anim_spike`, `is_spike_harmful`, `check_spiked`
сверены с seg006/seg007 построчно и совпадают дословно.
**Значит расходится ТАЙМИНГ, а не логика:** у нас пики успевают полностью
выйти ДО того, как Кид на них вбежит, и он проскакивает мимо смертельного
окна 1..4. Триггер — `check_spike_below` (seg006:1199), и у нас он берёт
габарит спрайта из `kid_fp` — метрик ПОСЛЕДНЕГО отрисованного кадра, а
оригинал берёт `char_x_left/right` ТЕКУЩЕГО кадра (`set_char_collision`).
Это первый подозреваемый; второй — момент вызова `process_trobs`
относительно `play_frame` (сколько кадров модификатор успевает пройти,
пока Кид добегает).
**ВТОРОЕ НАБЛЮДЕНИЕ (то же место, сравнение с оригиналом) — оно и уточняет
гипотезу.** Кид уронил плиту-потолок и спрыгнул вниз; пики выдвинулись и
«спрятались не все — часть артефактов осталась». Скриншоты рядом:
**ВТОРОЕ НАБЛЮДЕНИЕ (то же место, сравнение с оригиналом).** Кид уронил
плиту-потолок и спрыгнул вниз; пики выдвинулись и «спрятались не все — часть
артефактов осталась». Скриншоты рядом:
[наш](bugscreens/l2-r6-spikes-ours.png) и
[SDLPoP](bugscreens/l2-r6-spikes-sdlpop.png) в той же позе. У нас из-под
щебня торчат белые острия, у оригинала пик не видно ВООБЩЕ.
Это стыкуется с замером: пока Кид стоит рядом, `check_spike_below` каждый
кадр зовёт `start_anim_spike`, а тот при отрицательном модификаторе
переставляет его обратно в 0x8F — то есть пики у нас «залипают выдвинутыми»
и отсчёт до уборки не доходит. В оригинале в той же позе они убраны,
значит его `check_spike_below` эту колонку УЖЕ не задевает.
**ГИПОТЕЗА «ТРИГГЕР СРАБАТЫВАЕТ РАНО» ПРОВЕРЕНА И ОПРОВЕРГНУТА (замер
2026-08-05, MAME, watchpoint на `room_modif[13]` комнаты 6).** Чистый
пробег по УБРАННЫМ пикам убивает штатно:
@@ -184,45 +171,21 @@ check_spiked (seg006:0658): убивает при h>=2 на кадрах б
---
<a id="уровень-3"></a>
# Уровень 3 — не баги, а неначатые задачи
<a id="ручная-перепроверка-2026-08-03"></a>
# Ручная перепроверка фиксов (2026-08-03)
Smoke-прогон уровня 3 (пользователь, 2026-08-05) дал четыре наблюдения. Два
были настоящими багами и **закрыты в тот же день** (разбор — в
[`bug_closed.md`](bug_closed.md): **BUG-JUMPWALL-1** и **BUG-SEAM-WEDGE-1**).
Оставшиеся два — не баги, а неначатые задачи:
Шесть корней закрыты автоматической проверкой в MAME (мост `mame-z80`:
чтение `_Kid`, потиковые трассы, скриншоты) — этого достаточно, чтобы
показать, что конкретный сценарий больше не воспроизводится, но НЕ
достаточно, чтобы поймать регрессии в соседней механике. Ниже — сценарии
ровно в тех формулировках, в которых баги были заведены.
| наблюдение | что это на самом деле |
|------------|------------------------|
| к.22: чомпер (2,6) не анимируется и **вообще не рисуется** | [L3-CHOMP](TASKS_OPEN.md#l3-chomp) — механики чомперов НЕТ. В таблице тайлов `pop_bg.c:211` строка `12 chomper` рисует только основание, правую грань и низ; самих челюстей (спрайт из `chtab`, `draw_tile_anim` seg008) нет вовсе. Так и должно выглядеть до порта |
| к.10: скелет не оживает | [L3-SKEL](TASKS_OPEN.md#l3-skel) — `check_skel` (seg002:0E1F) не портирован. **И комната другая:** тайл `skeleton(21)`, который оживает, лежит в **к.1 (1,5)** — это `skeleton_room=1, skeleton_column=5, skeleton_row=1`. Ещё два скелета уровня (к.17 (2,7), к.19 (2,2)) — просто декорация, они не оживают никогда. Плюс условие: скелет встаёт, только когда **дверь уровня уже открыта** и Кид стоит в колонке 2 или 3 |
Собрано и лежит в образе (`make` + `make hdd` уже прогнаны; libc пересобран,
`make size-check` без роста).
| # | что проверить | ожидаемо |
|---|---------------|----------|
| 1 | комната 22: выпить зелье (0,6), выйти в 16/23 и вернуться | кувшина нет, пузырька над пустым местом нет |
| 2 | то же для комнат 14 (0,5) и 17 (2,3) | так же |
| 3 | комната 15: подобрать меч (2,2), выйти и вернуться | меча нет; кладка на дальней стене НЕ мигает |
| 4 | комната 12: разбить плиты (0,1)/(0,2) и потолок в 16, выйти-вернуться | остаются разбитыми, проём не закрывается |
| 5 | комната 17 из 23: разбить (1,5)/(1,6), выпить зелье (2,3), вернуться | всё остаётся |
| 6 | **после смерти** зайти в те же комнаты | ВСЁ восстановлено (в оригинале смерть = `load_level`) |
| 7 | комната 12: разбег в закрытую решётку (0,9) с полушага | не проходит насквозь; перелистывание экрана штатно (см. BUG-SEAM-DRAW-1 в `bug_closed.md`) |
| 8 | комната 6: бег справа налево от (0,9), длинный прыжок (0,6)→(0,7) | не влетает внутрь стены |
| 9 | комната 5: с кнопки (0,6) падение на (2,7), присед, вставание | остаётся в комнате 5 (проверено трассой: `fr=111 x=177``seq_47``fr=15 x=173`) |
| 10 | комната 5: нажать кнопку (0,4) | поднимаются ОБЕ решётки — (0,5) видно на экране, (0,9) проверять из комнаты 1 |
| 11 | страж (комнаты 3, 21) убивает Кида | смерть доигрывается, респавн в стартовой позиции уровня; цикла «убил-воскрес» нет |
| 12 | клавиатура: долгая игра с Shift+стрелка | ↑ и Shift не залипают; осторожный шаг не превращается в бег (доработано во второй волне — п.17) |
## Вторая волна (2026-08-03, вечер)
| # | что проверить | ожидаемо |
|---|---------------|----------|
| 17 | **[BUG-KBD-4]** долго играть Shift+стрелка, много тапов подряд | Shift не «отваливается»; если однажды залипнет — снимается первым же нажатием стрелки |
| 18 | **[BUG-RESPAWN-2]** убить стража (комнаты 3/21), умереть, вернуться в ту же комнату | страж снова жив, стоит на исходном тайле, HP полные |
Отдельно стоит посмотреть **на регрессии от порта `check_collisions`** — он
трогает всю горизонтальную коллизию: бамп в стену на бегу и в прыжке,
осторожный шаг у стены, проход через ОТКРЫТЫЕ ворота, разворот в проёме
решётки, переходы через швы (старый BUG-SEAM-PINGPONG).
Приёмки уровня 3 (полного обхода комнат) ещё не было — она осмысленна только
после L3-CHOMP/L3-SKEL.
---
@@ -231,11 +194,14 @@ check_spiked (seg006:0658): убивает при h>=2 на кадрах б
<a id="bug-gate-pass-1"></a>
## BUG-GATE-PASS-1. Проход сквозь закрывшуюся решётку — ЖДЁТ СЦЕНАРИЯ
**Статус: наблюдался один раз, воспроизвести повторно НЕ удалось.** Заведён,
чтобы наблюдение не потерялось; закрывать нельзя — ни как исправленный, ни как
«не баг», пока нет надёжного сценария.
**Статус: наблюдался один раз (2026-08-03), воспроизвести не удалось ни
тогда, ни прогоном всех комнат уровня 1 (2026-08-07).** Заведён, чтобы
наблюдение не потерялось; закрывать нельзя — ни как исправленный, ни как
«не баг», пока нет надёжного сценария. Оговорка: BUG-GATEMOD-1, из-за
которого решётка стартовала не в том состоянии, с тех пор закрыт, и
наблюдение могло быть его следствием.
**Что наблюдалось (2026-08-03).** Комната 5: Кид стоял НА тайле решётки (0,9)
**Что наблюдалось.** Комната 5: Кид стоял НА тайле решётки (0,9)
и ждал, пока она опустится. После закрытия пошёл вправо — прошёл в комнату 1
и упал на (1,1).
@@ -266,7 +232,8 @@ check_spiked (seg006:0658): убивает при h>=2 на кадрах б
3. `Kid.x` покадрово на самом шаге вправо — где именно перестал блокировать.
Быстрый способ снять первое: отладочный стоп-кадр (**1** заморозить, **2**
продолжить), затем чтение `_Kid` из отладчика MAME.
продолжить), затем чтение `_Kid` из отладчика MAME; подгонка позы по пикселю —
читы `[`/`]`.
**Возможный корень, если гипотеза не подтвердится.** Проверка идёт по колонке,
которая на шве уже принадлежит СОСЕДНЕЙ комнате (`curr_row_coll_room[]` в
@@ -275,6 +242,8 @@ check_spiked (seg006:0658): убивает при h>=2 на кадрах б
`can_bump_into_gate()`: если он берётся не от того кадра, решётка может
перестать считаться препятствием раньше времени.
---
# Оптимизация отрисовки (записано 2026-07-29)
Не баги — план работ. Оба пункта про одно: у оригинала пометка тайла к
@@ -328,51 +297,8 @@ check_spiked (seg006:0658): убивает при h>=2 на кадрах б
heal'а нет, стирать пики нечем, редрой не нужен.
**Связь с KBD-1:** это ещё и минус DI-окна в самых спокойных кадрах — ровно
там, где тапают Shift+стрелку (см. остаток KBD-1 в [`TASKS.md`](TASKS.md)).
---
# Обход всех 24 комнат уровня 1 — поиск багов отрисовки
Инструмент: `#define ROOMNAV` в `roomtest.c``+`/`-` (цифровой блок либо
`=`/`-` основного ряда) переключают комнату по номеру (1..24, с обёрткой),
Kid ставится на первый пол, `pop_trob_reset()` возвращает пики/ворота в
исходное. Номер комнаты — полосками в верхнем борте: слева десятки, справа
единицы (`||` `||||` = 24). Убирается закомментированием одной строки.
Комнаты **13, 18, 24 недостижимы** в обычной игре — это свойство данных
уровня, разбор в [`bug_closed.md`](bug_closed.md) («НЕ БАГИ»). Приоритет
багов в них низкий.
Таблица заполнена на 5 строк из 24 — доделать в рамках L1-TRIAGE
([`TASKS.md`](TASKS.md)).
| комната | статус | что не так |
|---------|--------|------------|
| 1 | пофикшено | падающая плита (2,6): правая грань видна через пол (2,7) и перекрывает его переднюю грань — `mob_render` брал ряд соседа из `m->row` (счётчик, уже ушедший на ряд вперёд), а не из координаты |
| 2 | | |
| 3 | | |
| 4 | | |
| 5 | пофикшено | прыжок в решётку: Kid оставался стоять на 6 px ВЫШЕ пола и без приземления-приседания — от `bumped()` (seg004) был портирован только хвост (`seq_47`), не хватало `bumped_floor` (прижатие Y к полу + `seq_46_hardbump` на кадрах прыжка 24/25/40..42/102..106) и `bumped_fall` |
| 6 | | |
| 7 | | |
| 8 | | |
| 9 | сделано | дверь уровня (1,3)-(1,4) рисовалась чёрным проёмом: не был портирован `draw_leveldoor` (seg008:1D29) — створка (слайсы 33 + верх 34), лестница за ней (99/144) и анимация подъёма по кнопке (`animate_leveldoor`, seg007:05F1, modif 0→43). Спрайты 33/34/99/144 добавлены в атлас явным набором (render_room.py дверь не рисует) |
| 10 | | |
| 11 | | |
| 12 | пофикшено | вис/подтягивание на кромке loose-плиты: плита рисовалась ПОД Кидом. Не хватало двух кусков `draw_tile`: (а) `draw_loose` кладёт нижнюю грань плиты И в foretable (поверх персонажа), (б) `draw_tile_base` подставляет верх плиты из `loose_fram_left`, а в нашем midtable-оверлее стоял голый `base_id` (у loose он 0). Голова Кида поверх падающей на него плиты — см. «НЕ БАГИ» в `bug_closed.md` |
| 13 | недостижима в игре | |
| 14 | | |
| 15 | сделано | меч (2,2) не рисовался: тайл 22 в draw_tile_anim не был портирован. Добавлены отрисовка предмета (chtab_1 id 10/11 на draw_main_y3), подъём по Shift (check_get_item/get_item/do_pickup: присед → seq_91 pickupsword → меч исчезает с пола) и статус `pop_have_sword` |
| 16 | | |
| 17 | | |
| 18 | недостижима в игре | |
| 19 | | |
| 20 | | |
| 21 | | |
| 22 | | |
| 23 | | |
| 24 | недостижима в игре | |
там, где тапают Shift+стрелку (остаток KBD-1 отложен, см.
[`TASKS_CLOSED.md`](TASKS_CLOSED.md#kbd-1)).
---
@@ -382,32 +308,14 @@ Kid ставится на первый пол, `pop_trob_reset()` возвращ
(эмуляция Shift+стрелка), см. `pop_ctrl.c` `KBD_DBG_STEP*`. Первый шаг в
сторону = разворот (как в оригинале safe_step), движение со второго.
- Читы (`pop_cheat.h`): **K** — убить стража, **I** — бессмертие (toggle),
**S** — выдать меч.
**S** — выдать меч, **Shift+L** — следующий уровень, **`[`/`]`** — сдвиг
Кида на пиксель по X.
- Респавн после смерти — по **↑** (или авто через `RESPAWN_DELAY`); ставит
Kid в стартовую позицию УРОВНЯ (`pop_start_level`, порт do_startpos).
- **ROOMNAV (`=`/`-`) — тоже наш чит**, которого в оригинале не было, как и
`S`. Все они со временем съедутся в общий блок читов, разрешаемый в
настройках; пока просто включены (`pop_cheats = 1` в `roomtest.c`).
---
<a id="сырые-наблюдения-прогон-2026-08-03"></a>
## Сырые наблюдения (прогон 2026-08-03) → корень
Формулировки с прогона, как записаны. Закрытые корни — в
[`bug_closed.md`](bug_closed.md).
| # | наблюдение (кратко) | корень | статус |
|---|---------------------|--------|--------|
| 1 | кувшин выпит, а пузырёк рисуется / кувшин возвращается (14, 22, 17) | BUG-LVLSTATE-1 | закрыт |
| 2 | меч возвращается в 15 / мигает контур кладки | BUG-LVLSTATE-1 | закрыт |
| 3 | комната 12: пробегает сквозь закрытую решётку (0,9) | BUG-COLL-1 | закрыт |
| 4 | после respawn плиты остаются разбитыми, зелья выпитыми | BUG-RESPAWN-1 | закрыт |
| 5 | плита/зелье возвращаются и БЕЗ respawn (12→16, 17, 22) | BUG-LVLSTATE-1 | закрыт |
| 6 | комната 6: длинный прыжок (0,6)→(0,7) — влёт в стену, респавн | BUG-COLL-1 | закрыт |
| 7 | залипает ↑ | BUG-KBD-3 | закрыт (см. `bug_closed.md`) |
| 8 | залипает Shift | BUG-KBD-3 / BUG-KBD-4 | закрыт (см. `bug_closed.md`) |
| 9 | комната 5: кнопка (0,4) не открывает решётку (0,5) | BUG-GATE-ANIM-1 | закрыт |
| 10 | комната 5: с кнопки (0,6) на (2,7), присед — провал в комнату 6 | BUG-STANDUP-1 | закрыт |
| 11 | страж убил Кида → Кид воскресает на месте и его убивают снова | BUG-DEATH-1 | закрыт |
Известный баг этого чита — [BUG-CHEAT-FIGHT-1](#bug-cheat-fight-1).
- Комнаты **13, 18, 24 уровня 1 недостижимы** в обычной игре — это свойство
данных уровня (разбор — «НЕ БАГИ» в [`bug_closed.md`](bug_closed.md));
приоритет багов в них низкий. Аналогично 23/24 на уровне 3.