Доски приведены в соответствие с кодом; bug_list/bug_closed → BUGS_OPEN/BUGS_CLOSED

Доска отставала от кода на три задачи — планировать по ней было нельзя.
Сверка проведена грепом по исходникам, а не по записям:

- L4-MIRROR ЗАКРЫТА: шаги 1-5 сделаны и проверены пользователем в MAME
  (зеркало в атласе, постановка тайла, отражение, прыжок сквозь зеркало с
  рождением тени, левый клип тени).  Протокол с разбором решений — в архиве;
- L3-CHOMP и L3-SKEL закрыты ещё 2026-08-08/07 (коммиты dc0bd47, 4d4323f,
  db4106a, 1461ed5), на доске значились как предстоящие;
- тайлсет palace (шаг 2 levels_plan) в коде есть целиком — pop_bg_load(type),
  pal_*.atl, дворцовый wall_pattern, решётки 25-29 и в tile_table, и в
  коллизии (tile_is_floor совпадает с seg006:0628);
- в GUARD-PHYS остаток пересобран по факту: check_chomped_guard сделан,
  скелет в check_guard_fallout сделан, ветки ТЕНИ нет — она уехала в L5-SHADOW.

Приёмки: по решению пользователя уровни 1-4 приняты SMOKE-тестами, полные
обходы всех комнат делаются по готовности ВСЕХ уровней — L3-PASS/L4-PASS как
отдельные задачи отменены, вместо них политика приёмок в архиве.

Новая цель — уровень 5.  Инвентарь res2005.bin: НИ ОДНОГО нового тайла, всё
портировано на уровнях 1-4.  Единственная новая механика — спецсобытие «тень
крадёт зелье» (комната 24): заведена задача L5-SHADOW с портом по SDLPoP
(check_shadow / do_init_shad / do_auto_moves + shad_drink_move /
autocontrol_shadow_level5 + ветка тени в check_guard_fallout), включая
готовые константы и то, что у нас уже есть под это.

Заведён MIRROR-FG-STALE (низкий): place_mirror пишет тайл в данные уровня, но
не в снимок room_fg, по которому работает коллизия — если зеркало поставлено,
пока игрок В комнате 4, оно невидимо для коллизии (тень не родится).  В
обычном прохождении недостижимо: дверь выхода в другой комнате.  Записан
точный сценарий воспроизведения читом ROOMNAV и фикс на несколько строк.

Правило «в _OPEN только незакрытое» теперь выполняется буквально:
- bug_list.md → BUGS_OPEN.md, bug_closed.md → BUGS_CLOSED.md (ссылки
  обновлены во всех документах и в комментарии pop_trob.c);
- из TASKS_OPEN убраны блоки закрытых задач (L3-CHOMP, L3-SKEL, L3-PASS,
  L4-MIRROR, DRAW-CHAR), справка по связности комнат уехала в архив;
- из BUGS_OPEN убраны 8 строк таблицы закрытых багов, закрытый T-2 (уехал в
  BUGS_CLOSED) и раздел «уровень 3 — неначатые задачи» (обе записи закрыты);
  сводная таблица пересобрана по реально открытым записям.

Все внутренние ссылки проверены скриптом: битых якорей 0.  make size-check
OK (65 программ), tests-host 5/5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 16:10:10 +03:00
parent c0075c7754
commit 5348feb5f4
20 changed files with 488 additions and 654 deletions
+163 -22
View File
@@ -1,4 +1,4 @@
# roomtest — ЗАКРЫТЫЕ задачи (архив досок, обновлено 2026-08-08)
# roomtest — ЗАКРЫТЫЕ задачи (архив досок, обновлено 2026-08-11)
Сделанное — с протоколами замеров, граблями и причинами решений. Файл
существует не ради истории: половина записей ниже — это ЧИСЛА (сколько тактов
@@ -6,13 +6,27 @@
перечень того, что делать НЕЛЬЗЯ, потому что уже пробовали.
Открытые задачи — [`TASKS_OPEN.md`](TASKS_OPEN.md); открытые баги —
[`bug_list.md`](bug_list.md), закрытые с разбором корней —
[`bug_closed.md`](bug_closed.md).
[`BUGS_OPEN.md`](BUGS_OPEN.md), закрытые с разбором корней —
[`BUGS_CLOSED.md`](BUGS_CLOSED.md).
---
## Приёмка уровней
<a id="pass-policy"></a>
### ПОЛИТИКА ПРИЁМОК — решение пользователя 2026-08-11
**Уровни 1-4: smoke-тесты пройдены.** Полные прогоны (обход всех комнат
каждого уровня) делаются **по готовности ВСЕХ уровней**, а не по одному за
этапом — отдельных задач `L3-PASS`/`L4-PASS` больше нет.
Основание: сквозные обходы дорогие, а половина находок на неполном наборе
уровней всё равно оказывается «механики ещё нет». Smoke (пройти уровень от
старта до двери) остаётся обязательным на каждом новом уровне — он снимает
блокеры, а не косметику.
Уже сделанные полные обходы уровней 1 и 2 (ниже) остаются регресс-базой.
<a id="l1-pass"></a>
### L1-PASS. Сквозное прохождение уровня 1 — **ЗАКРЫТ 2026-08-07**
@@ -23,30 +37,30 @@
> регресс-базой для уровня 2.
>
> **Полный обход комнат 2026-08-07 (пользователь): крупных багов нет.** Этим
> же прогоном закрыта [таблица обхода 24 комнат](bug_closed.md#обход-всех-24-комнат-уровня-1)
> и [чек-листы ручной перепроверки фиксов](bug_closed.md#ручная-перепроверка-2026-08-03)
> — обе уехали в `bug_closed.md`.
> же прогоном закрыта [таблица обхода 24 комнат](BUGS_CLOSED.md#обход-всех-24-комнат-уровня-1)
> и [чек-листы ручной перепроверки фиксов](BUGS_CLOSED.md#ручная-перепроверка-2026-08-03)
> — обе уехали в `BUGS_CLOSED.md`.
Приёмка этапа 1 и одновременно регресс-база для уровня 2: от старта до двери
уровня одним заходом — подбор меча, страж, кнопки/ворота, пики, loose-полы,
зелье, падения. Точки, где смотрели внимательно, — закрытая косметика
окклюзии (потолок при прыжке вверх, шов при анимации решётки, грани дальней
колонны) и подъём на тайл-кнопку (оговорка к BUG-3 в `bug_closed.md`).
колонны) и подъём на тайл-кнопку (оговорка к BUG-3 в `BUGS_CLOSED.md`).
<a id="l2-pass"></a>
### L2-PASS. Приёмка уровня 2 — **ЗАКРЫТ 2026-08-07**
> **Smoke 2026-08-05 (пользователь): уровень 2 пройден.** Из smoke пришли
> BUG-LOOSE-3 и BUG-GUARD-DEAF-1 — оба закрыты (`bug_closed.md`). Следом
> BUG-LOOSE-3 и BUG-GUARD-DEAF-1 — оба закрыты (`BUGS_CLOSED.md`). Следом
> прогнан smoke уровня 3.
>
> **Полный обход комнат 2026-08-07 (пользователь): крупных багов нет.**
> Открытыми с этого прогона остались три записи в
> [`bug_list.md`](bug_list.md): [BUG-GUARD-COLOR-1](bug_list.md#bug-guard-color-1)
> [`BUGS_OPEN.md`](BUGS_OPEN.md): [BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1)
> (страж всегда одного цвета), BUG-GUARD-SPLASH-1 (нет брызг при попадании по
> стражу — **закрыт 2026-08-07**, разбор в
> [`bug_closed.md`](bug_closed.md#bug-guard-splash-1)) и
> [BUG-CHEAT-FIGHT-1](bug_list.md#bug-cheat-fight-1) (наш чит `+`/`` в бою
> [`BUGS_CLOSED.md`](BUGS_CLOSED.md#bug-guard-splash-1)) и
> [BUG-CHEAT-FIGHT-1](BUGS_CLOSED.md#bug-cheat-fight-1) (наш чит `+`/`` в бою
> отнимает управление). Ни одна играть не мешает.
Ниже — **карта содержимого уровня, снятая прямо с `res2002.bin`**. Она
@@ -56,7 +70,7 @@
**Стражи — 5, в комнатах 4, 7, 11, 15, 24** (skill 1/2/1/1/3, цвета
1/3/1/1/6 — цвет мы пока игнорируем, атлас один, см.
[BUG-GUARD-COLOR-1](bug_list.md#bug-guard-color-1)). Заметить: страж
[BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1)). Заметить: страж
комнаты 24 со skill 3 — первый по-настоящему опасный.
**Кнопки и что они открывают** (декодировано из LINKLOC/LINKMAP):
@@ -100,8 +114,135 @@
---
### <a id="rooms-graph"></a>Справка: связность комнат уровней 1–3 (снято 2026-08-05)
Обход графа `roomlinks` (@1952, по 4 байта на комнату: L, R, U, D; 0 = нет
соседа) от стартовой комнаты — тем же методом, которым на уровне 1 нашлись
13/18/24 (см. «НЕ БАГИ» в [`BUGS_CLOSED.md`](BUGS_CLOSED.md)). Скрипт разовый,
в репозиторий не клался: чтение трёх массивов, BFS и проверка симметрии.
| уровень | старт | недостижимы | признак |
|---------|-------|-------------|---------|
| 1 | к.1 (0,0) | **13, 18, 24** | ссылки наружу есть, обратных нет |
| 2 | к.5 (1,3) | **нет** | граф полностью симметричен, все 24 достижимы |
| 3 | к.9 (2,4) | **23, 24** | то же, что на 1: односторонние ссылки, обе комнаты **полностью пустые** |
```
ур.3: 23 L→4, у 4 R=22 | 24 L→2, у 2 R=7
23 R→22, у 22 L=4 | 24 R→7, у 7 L=2
| 24 U→16, у 16 D=0
на 23 ссылается только 24, на 24 — только 23; тайлы обеих = все empty
```
То есть на уровне 3 это даже более чистый случай, чем на уровне 1: там в
брошенных комнатах была геометрия, здесь — пустота. Практический вывод тот
же: **в 23/24 возможен «мусор в шве»** (наш рендер кромки читает крайнюю
колонку соседа ПО ССЫЛКЕ, а сосед соседом себя не считает), приоритет багов
там низкий, в игре их не видно.
**Уровень 2 — недостижимых нет, но есть три КОЛОДЦА без выхода** (единственная
связь — вверх, откуда Кид падает):
```
к.10 U→4 пики(2,2) + пол — падение из комнаты 4
к.14 U→21 шахта 2 тайла шириной, дно = обломки
к.17 U→15 то же
```
Это не баги данных: в 14/17 попадают только падением насмерть, а из 10
(если выжил) выхода нет вовсе — так в оригинале. При приёмке не считать
«застрял» багом.
---
## Уровни и механика
<a id="l4-mirror"></a>
### L4-MIRROR. Зеркало уровня 4 и тень — **ЗАКРЫТА 2026-08-11**
Пять шагов из шести сделаны и **проверены пользователем в MAME 2026-08-11**;
шестой (вид тени) осознанно отложен.
| шаг | что | коммит |
|---|---|---|
| 1 | зеркало в атласе: `tile_table[0x0D]` база 75 / фронт 77, `MIRROR_ENV_IDS` + `FORE_ENV_IDS` в `pop_pack_bg.py` | `1b2111f` |
| 2 | постановка тайла: `place_mirror` в `pop_trob.c` по переходу `pop_leveldoor_open` 0/2 → 1 (`animate_leveldoor`, seg007:0457) | `1b2111f` |
| 3 | отражение (`check_mirror`, seg003:0798) — отдельной функцией `pop_mirror_draw`, со своим heal | `8f0362f`, `3913f1e` |
| 4 | прыжок сквозь зеркало и рождение тени (seg004:0239 + seg003:0798..08A9 + seg002:081D/1131) | `844fa6d` |
| 5 | клип тени слева `obj_clip_left = 137 + (mirror_column4)*32` (seg008:1699) + новый примитив `gfx_blit_cols_part_wx` в libbgi | `8f0362f` |
**Почему зеркало пришлось добавлять в атлас явно:** тайла 13 нет ни в одном
уровне статически — проверено перебором всех 15 `res200N.bin`, ноль
попаданий, поэтому `render_room.py` его не видит и на месте зеркала был бы
чёрный провал (memory `pop_atlas_dynamic_ids`).
**Почему отражение — отдельная функция, а не третий слот `Char`:** это
структура самого оригинала — отражение идёт сокращённым путём
`load_frame_to_obj` + `add_objtable(4)`, без клинка, брызг, пропуска кадра;
гейтить всё это в общем теле `pop_cdraw` значило бы добавить ветки в самый
горячий путь. Побочный эффект — [FORE-DUP](BUGS_OPEN.md#fore-dup): передний
слой тайла рисуется дважды, когда футпринты Кида и отражения накрывают один
тайл (всегда, они стоят на одном тайле). Картинку не портит, тратит такты.
**Почему левый клип сделан примитивом libbgi, а не «нарисовать и вернуть фон
поверх лишнего»** (подсказка пользователя): для column-major левая обрезка
стоит ровно столько же, сколько правая — колонка это непрерывный кусок ОЗУ,
меняются стартовая колонка источника и экранная X. Внутри это уже было (так
клипается левый край экрана), наружу не было выведено.
**Отложено — вид тени** (шаг 6): в оригинале она рисуется ДВУМЯ блитами
одного спрайта, `blitters_2_or` на месте и `blitters_3_xor` со сдвигом +1 px
(seg008:1602); у нас пока обычная копия, то есть тень выглядит вторым Кидом.
Разбор, замеры и варианты — [`../docs/shadow_render.md`](../docs/shadow_render.md);
блочные операции акселератора под это в libbgi уже есть (`tests/accop`).
**Открытый краевой случай** — [MIRROR-FG-STALE](BUGS_OPEN.md#mirror-fg-stale):
`place_mirror` пишет тайл в данные уровня, но не в снимок комнаты `room_fg`,
по которому работает коллизия.
<a id="l3-chomp"></a>
### L3-CHOMP. Чомперы — **СДЕЛАНЫ 2026-08-08**
Коммиты `dc0bd47` (анимация, отрисовка, смерть в челюстях), `4d4323f`
(передние зубья через `pop_fore_b` + ветка в `draw_tile`), `db4106a`
(перед чомпером Кид разбегается сразу, без осторожного шага — `safe_step` по
оригиналу), `1461ed5` (фикс регрессии, см. ниже).
Портировано: `animate_chomper` (seg007:0448), `next_chomper_timing`
(seg007:0F9A — 15,12,9,6,13,10,7,14,11,8 по кругу), `start_anim_chomper`
(seg007:08C7), `start_chompers` (seg007:0F13) — все в `pop_trob.c`,
состояние в `room_modif`, как у пик и ворот. Смерть: `SEQ_54_CHOMPED` /
`FRAME_178_CHOMPED` + `check_chomped_guard` для соперника (`pop_map.c`).
Отрисовка — `pop_chomp_pose` (`pop_bg.c`) и холодная перерисовка тайла в
`pop_room.c`. Ассеты упакованы явным набором кадров (`CHOMPER_BOT_IDS`
101-105, `TOP` 111-113, `FORE` 106-110 + кровь 114-123 mono-силуэтом).
**Грабли, стоившие регрессии (`1461ed5`):** `start_chompers` вызывается на
смене ряда персонажа, а у нас `seqtbl` читается ЧЕРЕЗ ОКНО W0 — прямой вызов
из `play_seq` переключал окно посреди чтения байткода. Вызов отложен через
флаг `chomp_pending` (`pop_kid.c`) и делается ПОСЛЕ цикла интерпретатора.
Хвосты в багах: [BUG-CHOMP-JUMP-1](BUGS_OPEN.md#bug-chomp-jump-1) (низкий,
маловоспроизводим) и закрытые BUG-TORCH-CHOMP-1/2 (пламя факела и застывший
чомпер — [`BUGS_CLOSED.md`](BUGS_CLOSED.md)).
<a id="l3-skel"></a>
### L3-SKEL. Скелет уровня 3 — **СДЕЛАН 2026-08-07, принят smoke-прогоном**
В данных уровня 3 стражей нет вообще (`guards_tile` пуст во всех 24
комнатах) — единственный враг это скелет, и он не «страж из данных», а
**спецсобытие** `check_skel` (seg002:1044): в комнате 1, когда
`Kid.curr_col` == 2 или 3 и дверь уровня открыта, тайл `tiles_21_skeleton`
(комната 1, тайлпос 15) стирается в пол, а на его месте поднимается
персонаж — `charid_4_skeleton`, меч сразу вынут, `seq_88_skel_wake_up`,
skill 2, HP 3. Ещё два тайла скелета (комнаты 17 и 19) — декорация, они не
оживают. Атлас — `pop_pack_guard.py SKEL``poc/res/skel/g0..g3.atl`.
Проверено в MAME: бой, падение в пропасть, окклюзия (2026-08-07); принят
smoke-прогоном уровней 1-4 (2026-08-11). **Этот же механизм**
«спецсобытие порождает персонажа в слоте соперника» — база для тени уровня 5
([L5-SHADOW](TASKS_OPEN.md#l5-shadow)).
<a id="l3-chkp"></a>
### L3-CHKP. Чекпойнт уровня 3 — **СДЕЛАНО 2026-08-06** (коммит 0cd6b2d)
@@ -119,7 +260,7 @@ SDLPoP: смерть В комнате 7 вернула его в стартов
**Проверено в MAME:** вход в 7 флаг не ставит, уход влево — ставит; респавн в
комнате 2; после обычной смерти (без чекпойнта) плиты восстанавливаются как
раньше. Вживую в игре (не читом) — потрогать на приёмке уровня 3
([L3-PASS](TASKS_OPEN.md#l3-pass)).
([L3-PASS](TASKS_CLOSED.md#pass-policy)).
### L2. Переход на уровень 2 и его игра — **МАШИНЕРИЯ СДЕЛАНА 2026-08-04**
@@ -163,7 +304,7 @@ Shift+L → уровень 3 (комната 9). То есть цепочка
Контент уровня 2 (сквозное прохождение, выход через дверь) закрыт отдельно —
[L2-PASS](#l2-pass). Цвет стража из данных остался открытым багом —
[BUG-GUARD-COLOR-1](bug_list.md#bug-guard-color-1).
[BUG-GUARD-COLOR-1](BUGS_CLOSED.md#bug-guard-color-1).
### L1-START. Старт по данным уровня — **СДЕЛАНО 2026-08-01**
@@ -203,30 +344,30 @@ Shift+L → уровень 3 (комната 9). То есть цепочка
✅ Косметика тоже закрыта: BUG-DOOR-CLIP (обрезка силуэта правым косяком
проёма) — недоставало второй половины `clip_char` (`obj_clip_right`) и
обрезки СПРАВА у колоночного блита. В libbgi добавлен
`gfx_blit_cols_part_w(..., maxw)`; разбор — в [`bug_closed.md`](bug_closed.md).
`gfx_blit_cols_part_w(..., maxw)`; разбор — в [`BUGS_CLOSED.md`](BUGS_CLOSED.md).
### L1-TRIAGE. Ревизия багов — **ЗАКРЫТА** (часть 1 — 2026-08-01, хвост — 2026-08-07)
**Три Critical'а прогнаны в MAME и закрыты** (протокол с числами — в
[`bug_closed.md`](bug_closed.md), раздел «Проверено в MAME 2026-08-01»):
[`BUGS_CLOSED.md`](BUGS_CLOSED.md), раздел «Проверено в MAME 2026-08-01»):
BUG-1 (провал на row 1 при переходе через открытые ворота) и BUG-2
(ping-pong при возврате) **не воспроизводятся**, BUG-3 (окклюзия climb-up на
кнопке) закрыт фиксом `tile_code_drawn` от 2026-07-28. Заодно снят неверный
диагноз BUG-1: репроекция Y при БОКОВОМ переходе — не наш пробел, а точное
поведение `goto_other_room` (`SDLPoP/src/seg002.c:390` меняет только `x`).
Список разделён на [`bug_list.md`](bug_list.md) (открытое) и
[`bug_closed.md`](bug_closed.md) (закрытое + разбор корней).
Список разделён на [`BUGS_OPEN.md`](BUGS_OPEN.md) (открытое) и
[`BUGS_CLOSED.md`](BUGS_CLOSED.md) (закрытое + разбор корней).
**Косметика окклюзии тоже закрыта** — BUG-CEIL-1/2/3 и BUG-OCCL-1 были
починены кодом ещё в июле, а записи никто не снял: `ceil_over_kid_tile`
(`pop_bg.c:1275`), `pop_ceil_modif` + `pop_ceil_shake_draw`/`_bake_empty`
(`:547,815,827`), `bar` с `POP_YOFF+3` в `pop_room_redraw_seam_left` (`:793`),
разделение слоёв по `add_backtable` vs `ptr_add_table` в `overlay_mid_tile`
(`:1367`). Разбор — в [`bug_closed.md`](bug_closed.md).
(`:1367`). Разбор — в [`BUGS_CLOSED.md`](BUGS_CLOSED.md).
**Хвост закрыт 2026-08-07:** таблица обхода 24 комнат уровня 1 — прогоном
всех комнат уровней 1 и 2 (крупных багов нет), см.
[`bug_closed.md`](bug_closed.md#обход-всех-24-комнат-уровня-1).
[`BUGS_CLOSED.md`](BUGS_CLOSED.md#обход-всех-24-комнат-уровня-1).
---
@@ -301,7 +442,7 @@ BANK4 pop_cdraw 4 625 5 905 +1 280 (сюда переехала отрис
То есть слияние двух копий дало ~3.2 КБ, но **главный выигрыш не в банке 2,
а в резиденте**: отрисовка Кида уехала из W1 в банк, и куча выросла втрое
(2 023 → 6 333 Б). Банку 2 при этом досталось всего +265 Б свободного
места — **под [L3-CHOMP](TASKS_OPEN.md#l3-chomp) этого мало**, разгрузку
места — **под [L3-CHOMP](TASKS_CLOSED.md#l3-chomp) этого мало**, разгрузку
`pop_bg` придётся делать отдельно (свободные номера банков — 7+).
**Проверено:** `tests-host` — все 5 наборов зелёные, трассы физики не
@@ -615,7 +756,7 @@ make ALLOCS=100000 — как раньше: минимальный код, д
> переполняется» отпадает; сам ФИКС (плотный опрос `kbd_raw_poll`) остаётся
> верным и нужным — переполнение вызывает не Shift, а короткая жизнь
> импульса IRQ (пункт 3 гипотезы) плюс DI-окна графики. Разбор и следствия
> — [BUG-KBD-5](bug_closed.md).
> — [BUG-KBD-5](BUGS_CLOSED.md).
>
> **Итог (2026-08-01).** Причина — не наш код и не DI-окна графики: импульс
> запроса прерывания здесь теряется примерно в 44 % случаев, и трёхбайтовый