# roomtest — ОТКРЫТЫЕ баги и незакрытые оптимизации Только то, что **не закрыто**. Всё закрытое (и, что важнее, разбор корней) переехало в [`bug_closed.md`](bug_closed.md): прежде чем заводить новый баг, грепни там по симптому. Приоритеты работ — в [`TASKS.md`](TASKS.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 — однократное наблюдение прохода сквозь закрывшуюся решётку, которое ЖДЁТ надёжного сценария. | ID | что | тип | статус | |----|-----|-----|--------| | [Уровень 2](#уровень-2) | остальные баги с приёмки | — | принимаются по ходу | | [BUG-GATE-PASS-1](#bug-gate-pass-1) | проход сквозь закрывшуюся решётку (0,9) комнаты 5 | Major | **ждёт сценария** (BUG-GATEMOD-1, из-за которого решётка стартовала не в том состоянии, закрыт — перепроверить) | | [T-1](#t-1) | пики перерисовываются безусловно | оптимизация | открыт | | [T-2](#t-2) | Кид перерисовывается в покое | оптимизация | открыт | | [обход 24 комнат](#обход-всех-24-комнат-уровня-1) | таблица заполнена на 5 строк из 24 | ревизия | открыт | | [BUG-SPIKE-1](#bug-spike-1) | пики залипают выдвинутыми рядом с Кидом | **низкий** | маловоспроизводимый: ни сценарием, ни попиксельной подгонкой X не поднимается | --- # Уровень 2 — баги с приёмки (заведено 2026-08-04) Уровень 2 заиграл 2026-08-04 (см. [`TASKS.md`](TASKS.md) запись **L2**). Пользователь при первом прогоне увидел **несколько багов отрисовки** и подаст их списком отдельно — раздел заведён под них заранее, чтобы наблюдения не растеклись по переписке. **Закрыто с этой волны:** [BUG-LOOSE-3](bug_closed.md#bug-loose-3) — чёрный бар под упавшей плитой-потолком (запечка восстанавливала не всё, что стёрла). **Порядок работы по уровню 2 (2026-08-05):** наблюдения СНАЧАЛА копятся здесь целиком — фиксы берём, когда уровень пройден до конца. Причина: косметика отрисовки почти всегда сводится к общему корню (ср. волну BUG-CEIL-1/2/3 + BUG-OCCL-1), и чинить по одному наблюдению — значит чинить один и тот же корень трижды. Карта содержимого уровня (что где стоит по данным `res2002.bin`, какие кнопки какие ворота открывают) — в [`TASKS.md`](TASKS.md#l2-pass), запись **L2-PASS**: по ней видно, «механика не сработала» это или «так и задумано». Перед заведением записи — грепнуть [`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)**; если артефакт рядом с ними, это первый подозреваемый. # Уровень 3 — баги с приёмки (заведено 2026-08-05) Smoke-прогон уровня 3 (пользователь, 2026-08-05) дал четыре наблюдения. Два из них — **не баги, а неначатые задачи**, и записаны как таковые: | наблюдение | что это на самом деле | |------------|------------------------| | к.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 | Остальные два были настоящими багами и **закрыты в тот же день** — разбор корней в [`bug_closed.md`](bug_closed.md): **BUG-JUMPWALL-1** (флаги коллизий считались для одного ряда вместо трёх — бамп терялся на кадре смены ряда) и **BUG-SEAM-WEDGE-1** (сосед угла (2,0) безусловно считался стеной). ## BUG-SPIKE-1. Пики залипают выдвинутыми — НИЗКИЙ ПРИОРИТЕТ, МАЛОВОСПРОИЗВОДИМ > **Статус 2026-08-05, вечер.** Пользователь **повторить не смог**, а замер > (ниже) показал, что чистый пробег по убранным пикам убивает штатно. То > есть в смертельности бага, похоже, нет вовсе: наблюдался частный случай — > пики, УЖЕ выдвинутые полностью (h = 1), для бегущего безвредны и в > оригинале. Запись оставлена открытой ровно из-за визуального расхождения > со скриншотом SDLPoP (у нас острия торчат, у него убраны) — см. конец. > > **Как получить состояние нарочно:** подойти к пикам вплотную (выдвинутся), > отступить на полшага, НЕ выходя из зоны срабатывания, и пробежать по ним. > Пользователь пробовал и это, и попиксельную подгонку X читом `[`/`]` — > не поднялось. Вывод для будущего разбора: состояние **не чисто > позиционное**, одной шириной габарита его не объяснить; следующий > подозреваемый — момент, в который `process_trobs` застаёт модификатор > относительно кадра Кида. **Наблюдение (пользователь, 2026-08-05).** Уровень 2, комната 6, пики (1,3): | действие | что происходит | |----------|----------------| | длинный прыжок с ряда 0 на пики | **смерть — правильно** (путь `fell_on_spikes`) | | пробег по ряду 1 прямо по пикам | **урона нет** | | после уборки пик | **на экране остаются белые остатки остриёв** (в оригинале чисто) | | прыжок на месте, стоя на пиках | урона нет | | просто стоять на выдвинутых пиках | можно сколько угодно | **Замер (MAME, чтение `room_modif` комнаты 6).** Пока Кид стоит на тайле, модификатор пики (индекс 13) = **0x8E**, то есть «пики ПОЛНОСТЬЮ вышли и идёт обратный отсчёт». Дальше вся арифметика сходится с оригиналом: ``` is_spike_harmful (seg007:1178): 0/-1 → 0; <0 → 1; 1..4 → 2; >=5 → 0 check_spiked (seg006:0658): убивает при h>=2 на кадрах бега 7..14 и при h!=0 на кадрах приземления 43/26 ``` То есть **при h = 1 (пики уже вышли) бегущий не гибнет и в оригинале** — смертельно только окно ВЫДВИЖЕНИЯ (модификатор 1..4, 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).** Чистый пробег по УБРАННЫМ пикам убивает штатно: ``` запись modif=1 : кадр 11 (беговой), x=112, curr_col=2 ← пики пошли вверх запись modif=2 : кадр 12 (беговой), x=117, curr_col=3 ← Кид уже НА тайле, h=2 запись modif=3 : кадр 177 (frame_177_spiked) ← напоролся ``` То есть `check_spike_below`, `check_spiked`, `is_spike_harmful` и тайминг выдвижения работают правильно, и «раннего» триггера нет. **Настоящий корень — пики ЗАЛИПАЮТ выдвинутыми.** Пока габарит Кида накрывает колонку пики, `check_spike_below` каждый кадр зовёт `start_anim_spike`, а тот при отрицательном модификаторе переставляет его обратно в 0x8F — отсчёт до уборки не доходит. А выдвинутые пики (h = 1) для бегущего БЕЗВРЕДНЫ по правилам оригинала. Отсюда обе жалобы: пробег по уже вышедшим пикам не убивает, и они же остаются торчать на экране. **Что осталось выяснить (и это единственный открытый вопрос).** Код `start_anim_spike` у нас с оригиналом совпадает дословно, значит оригинал тоже удерживал бы пики, стой Кид там же. На скриншоте SDLPoP в похожей позе пики УБРАНЫ — то есть его Кид стоит чуть левее и его габарит колонку пики уже не задевает. Разница в 2–3 пикселя посадки, а у нас такие расхождения по X уже ловились (см. заметку в BUG-GRAB-1: после касания площадки SDLPoP уводит Кида на 134, мы — на 141). **Как закрывать:** инструментировать SDLPoP (печать `char_x_left/right`, `left/right_checked_col` и модификатора пики каждый кадр), проиграть ту же сцену — падение плиты-потолка в комнате 6 и остановку на щебне — и сверить с нашей трассой ПОЗИЦИЮ КИДА после приземления. Если позиции совпадут, а диапазоны колонок разойдутся — виноват габарит (`kid_fp` против `set_char_collision` текущего кадра); если разойдутся позиции — это отдельный баг посадки, а пики — его следствие. --- # Ручная перепроверка фиксов (2026-08-03) Шесть корней закрыты автоматической проверкой в MAME (мост `mame-z80`: чтение `_Kid`, потиковые трассы, скриншоты) — этого достаточно, чтобы показать, что конкретный сценарий больше не воспроизводится, но НЕ достаточно, чтобы поймать регрессии в соседней механике. Ниже — сценарии ровно в тех формулировках, в которых баги были заведены. Собрано и лежит в образе (`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). --- # Открытые баги уровня 1 ## BUG-GATE-PASS-1. Проход сквозь закрывшуюся решётку — ЖДЁТ СЦЕНАРИЯ **Статус: наблюдался один раз, воспроизвести повторно НЕ удалось.** Заведён, чтобы наблюдение не потерялось; закрывать нельзя — ни как исправленный, ни как «не баг», пока нет надёжного сценария. **Что наблюдалось (2026-08-03).** Комната 5: Кид стоял НА тайле решётки (0,9) и ждал, пока она опустится. После закрытия пошёл вправо — прошёл в комнату 1 и упал на (1,1). **Что уже измерено и в чём загвоздка.** Сразу после наблюдения повторить не получилось: в том же месте Кид стоит на `x = 196`, `col = 9`, и решётка его ДЕРЖИТ — то есть штатно. Арифметика оригинала объясняет разницу. `is_obstacle` (seg004) ставит плоскость блокировки в `x_bump[col + FIRST_ONSCREEN_COLUMN] + TILE_MIDX`, для колонки 9 это **x = 205**. При этом «колонка 9» по `get_tile_div_mod_m7` — это `x ∈ [191, 205)`. Пока `curr_col == 9`, Кид гарантированно левее плоскости и обязан блокироваться; чтобы пройти, он должен оказаться **правее 205**, то есть уже на дальней стороне решётки, — и тогда уход вправо законен: решётка закрылась у него за спиной, в оригинале она блокирует плоскость, а не весь тайл. Отсюда рабочая гипотеза: в момент наблюдения Кид стоял правее 205 (успел зайти по тайлу дальше, пока решётка была поднята), и поведение штатное. Но повторить эту позу и снять `x` пока не удалось, поэтому гипотеза НЕ подтверждена. **Что снять в следующий раз** (без этих чисел вопрос не закрыть): 1. `Kid.x` и `Kid.curr_col` в момент, когда решётка уже закрылась, а Кид ещё стоит на её тайле — до шага вправо; 2. модификатор решётки (openness) комнаты 5, тайл 9 — `can_bump_into_gate()` считает её препятствием только пока `(modif >> 2) + 6 < char_height`, то есть пока она опустилась достаточно низко относительно РОСТА кадра; 3. `Kid.x` покадрово на самом шаге вправо — где именно перестал блокировать. Быстрый способ снять первое: отладочный стоп-кадр (**1** заморозить, **2** продолжить), затем чтение `_Kid` из отладчика MAME. **Возможный корень, если гипотеза не подтвердится.** Проверка идёт по колонке, которая на шве уже принадлежит СОСЕДНЕЙ комнате (`curr_row_coll_room[]` в оригинале); у нас межкомнатная коллизия на шве — исторически проблемное место (ср. закрытый BUG-SEAM-PINGPONG). Второй кандидат — `char_height` в `can_bump_into_gate()`: если он берётся не от того кадра, решётка может перестать считаться препятствием раньше времени. # Оптимизация отрисовки (записано 2026-07-29) Не баги — план работ. Оба пункта про одно: у оригинала пометка тайла к перерисовке стоит копейки (бит в таблице, которая всё равно чистится каждый кадр), а у нас каждая такая пометка превращается в реальный heal (копию из ОЗУ-копии акселератора) плюс блиты. Поэтому буквальный порт «перерисовываем безусловно» корректен, но дорог. ## T-1. Пики: перерисовывать по причине, а не безусловно **Сейчас:** `pop_process_trobs` зовёт `pop_spike_redraw` каждый кадр для каждой живой пики в комнате (порт `redraw_21h`, который `animate_spike` вызывает вне всяких `if`). Это корректно, но лишнее для пик, до которых Киду дела нет. **Надо:** перерисовывать тайл пики, только если 1. **сменился её видимый кадр** (шаг выдвижения/уборки), ЛИБО 2. **её кто-то стёр** — а стереть у нас может только heal, то есть тайл попал в прямоугольник `kid_heal` этого кадра. Это и есть модель оригинала, просто выраженная флагами: `redraw_at_char` (seg003:0576) каждый кадр помечает `set_redraw_fore` тайлы персонажа, причём **объединение текущего и предыдущего** прямоугольника (`MIN(char_top_row, prev_char_top_row)` и т.д.), а `animate_spike` помечает свой тайл. Итог = {тайл сменил кадр} ∪ {тайлы Кида}. **Как:** слой Кида и так считает `cL..cR`/`rT..rB` в `pop_fore_over_kid` — пусть публикует их (плюс предыдущие, как в оригинале), а цикл trob'ов сравнивает `tilepos` с диапазоном целочисленно. Никаких пересечений прямоугольников (см. память `manual_hints_over_auto_detect`). **Приоритет:** отдаётся почти бесплатно ПОСЛЕ T-2, отдельно не окупается. ## T-2. Idle-skip: не перерисовывать Кида, когда ничего не происходит **Сейчас:** `kid_heal` → `kid_draw` → `pop_fore_over_kid` идут каждый кадр, даже когда Kid стоит и в его тайлах ничего не меняется. Это ровно поведение оригинала (`draw_game_frame`, seg000:917 — `draw_moving()` + `draw_tables()` безусловно), но у него это дёшево, а у нас нет. **Надо:** пропускать heal+draw Кида, когда кадр/поза/координаты не менялись и в его тайлах нет активной анимации. **Осторожно (дабл-буфер):** пропускать можно **не раньше второго подряд** неизменного кадра — иначе одна из двух страниц останется со старым содержимым. Условие «обе страницы уже получили это состояние». **Связь с T-1:** после T-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_y−3), подъём по Shift (check_get_item/get_item/do_pickup: присед → seq_91 pickupsword → меч исчезает с пола) и статус `pop_have_sword` | | 16 | | | | 17 | | | | 18 | недостижима в игре | | | 19 | | | | 20 | | | | 21 | | | | 22 | | | | 23 | | | | 24 | недостижима в игре | | --- ## Заметки (отладка) - Тестовые клавиши осторожного шага: **J** = шаг влево, **L** = шаг вправо (эмуляция Shift+стрелка), см. `pop_ctrl.c` `KBD_DBG_STEP*`. Первый шаг в сторону = разворот (как в оригинале safe_step), движение со второго. - Читы (`pop_cheat.h`): **K** — убить стража, **I** — бессмертие (toggle), **S** — выдать меч. - Респавн после смерти — по **↑** (или авто через `RESPAWN_DELAY`); ставит Kid в стартовую позицию УРОВНЯ (`pop_start_level`, порт do_startpos). - **ROOMNAV (`=`/`-`) — тоже наш чит**, которого в оригинале не было, как и `S`. Все они со временем съедутся в общий блок читов, разрешаемый в настройках; пока просто включены (`pop_cheats = 1` в `roomtest.c`). --- ## Сырые наблюдения (прогон 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 | закрыт |