diff --git a/applications/PoP/roomtest/TASKS_CLOSED.md b/applications/PoP/roomtest/TASKS_CLOSED.md
index 34d3deb..ebb3610 100644
--- a/applications/PoP/roomtest/TASKS_CLOSED.md
+++ b/applications/PoP/roomtest/TASKS_CLOSED.md
@@ -968,3 +968,205 @@ make-бита для кода `0x6C`. Скратч-байты — хвост `o
работает при EI» из `docs/new/06-accel.md §6.6` в этой прошивке/эмуляции
недоступен — вопрос закрыт артефактом, а не рассуждением.
- Дробить DI-окна по колонкам смысла тоже нет: см. вывод 1.
+
+---
+
+### L5-SHADOW. Тень уровня 5: крадёт зелье — **СДЕЛАНА 2026-08-11**
+
+Единственная новая механика уровня 5 (тайлов новых нет вовсе, см. цель выше).
+Тень появляется в комнате 24, дожидается, пока откроется дверь, идёт к зелью,
+**выпивает его** и уходит за левый край. Боя нет.
+
+**Как это в оригинале** (всё сверено по коду, `custom->*` — это дефолты 1.0):
+
+| что | где | суть |
+|---|---|---|
+| появление | `check_shadow`, seg002:0064 | при СМЕНЕ КОМНАТЫ: если `current_level == 5` и `drawn_room == 24`, и тайл (кол 3, ряд 0) всё ещё `tiles_10_potion` — породить тень |
+| порождение | `do_init_shad`, seg002:0000 | `memcpy(&Char, init_shad_5, 7)` + `seqtbl_offset_char(2 /*stand*/)`, `charid = charid_1_shadow`, `demo_time = 0`, `guard_skill = 3`, `guardhp_* = 4`, `saveshad()` |
+| данные | `init_shad_5` | `{0x0F, 0x37, 0x37, 0, 0xFF, 0, 0}` = frame 15, x 55, y 55, direction 0, curr_col −1, curr_row 0, action 0 |
+| поведение | `autocontrol_shadow_level5`, seg002:1157 | в комнате 24: пока `demo_time == 0` — ждать, пока дверь (кол 1, ряд 0) не откроется (`modif >= 80`), затем `demo_index = 0`; дальше каждый кадр `do_auto_moves(shad_drink_move)`; при `Char.x < 15` — `clear_char()` |
+| движения | `do_auto_moves`, seg002:1089 | крошечный интерпретатор: `demo_time++`, по таблице `{time, move}` выбирается запись, `move` = 0 nothing / 1 forward / 2 backward / 3 up / 4 down / 5 up+forward / 6 shift / 7 move_7; −1 = ничего, −2 = конец |
+| таблица | `shad_drink_move` (data.h:866) | `{0x00,0} {0x01,1} {0x0E,0} {0x12,6} {0x1D,7} {0x2D,2} {0x31,1} {0xFF,−2}` — 8 записей по 2 байта |
+
+**Что из этого у нас уже есть:**
+
+- **механизм «спецсобытие порождает персонажа в слоте соперника»** —
+ `pop_check_skel` (`guards.c`, зовётся из `roomtest.c` в тике); тень
+ уровня 5 садится на тот же шов, только условие другое;
+- **тень как `charid_1_shadow`** — заведена под уровень 4
+ ([L4-MIRROR](TASKS_CLOSED.md#l4-mirror)): своя ветка ИИ
+ (`autocontrol_shadow` + `autocontrol_shadow_level4`), выбор таблицы кадров
+ Кида (`pop_frame_tbl_is_guard`), отрисовка спрайтами Кида;
+- **питьё зелья** — `SEQ_78_DRINK` и `get_item` в `pop_ctrl.c` (Кид уже
+ умеет); тень «нажимает» те же кнопки через автодвижения;
+- **зелья как trob** — фаза пузырька, тип в старших битах (`pop_trob.c`).
+
+**Что писать:**
+
+1. `do_auto_moves` + таблица `shad_drink_move` + `demo_time`/`demo_index` —
+ интерпретатор ~30 строк, кладётся рядом с `autocontrol_shadow` в
+ `guards.c` (банк 1). «Движения» — это те же переменные управления, что
+ заполняет `read_user_control` (`pop_ctrl.c`), так что `move_*` сводятся к
+ присваиваниям.
+2. `do_init_shad(init_shad_5, seq stand)` — общий порождатель тени; пригодится
+ и на уровнях 6 и 12 (`init_shad_6`, `init_shad_12` — те же 7 байт).
+3. Ветка `check_shadow` для уровня 5 — по образцу `pop_check_skel`, вызов из
+ того же места тика.
+4. `autocontrol_shadow_level5`.
+5. **Ветка ТЕНИ в `check_guard_fallout`** (seg002:0241): тень падает, только
+ если она в свободном полёте (`action == 4`), и тогда
+ `loadshad(); clear_char(); saveshad()`. Сейчас в `pop_guard_fallout`
+ (`pop_guard.c`) есть ветки стража и скелета, а тени нет — комментарий там
+ обещает её «вместе с L3-SKEL», но она относится именно к тени.
+
+**Чем подтверждать:** smoke уровня 5 — дойти до комнаты 24, увидеть, как
+тень выходит после открытия двери, выпивает зелье (тайл зелья исчезает) и
+уходит влево. Сверять последовательность движений с живым SDLPoP на том же
+месте — таблица `shad_drink_move` короткая, расхождение будет видно сразу.
+
+**Оговорка по виду:** тень пока рисуется обычной копией спрайтов Кида, то
+есть выглядит вторым Кидом — это [отложенный](../docs/shadow_render.md)
+вопрос, к механике уровня 5 отношения не имеет.
+
+---
+
+### GUARD-PHYS. Страж живёт по тем же правилам, что Кид — **ЗАКРЫТА 2026-08-11**
+
+> **Что уже работает** (решение пользователя: переносим физику на `Char`,
+> без предварительных замеров — иначе третий-четвёртый экземпляр той же
+> логики неизбежен).
+>
+> - **физика переведена на `Char`**: `pop_map.c` целиком работает с активным
+> персонажем, а кто в `Char` — решают окна `loadkid`/`savekid` и
+> `loadshad`/`saveshad`, как в оригинале (seg006:809). Два входа:
+> `pop_phys_tick` (порт хвоста play_kid_frame) и `pop_guard_phys_tick`
+> (порт play_guard_frame) — списки вызовов отличаются ровно тем, чем в
+> оригинале;
+> - **`take_hp` стал общим** (`pop_take_hp` в резиденте `pop_guard.c`): урон
+> идёт тому, кто в `Char`, по `charid`. Раньше у боёвки и у физики были
+> свои копии, причём у физики неверная — правила `hitp_curr` мимо дельты и
+> про не-Кидов не знала;
+> - **порт веток по charid в `land()`** (seg005:173): страж гибнет с двух
+> рядов, тень падает как с одного, у не-Кида приземление даёт боевую
+> стойку; `check_guard_bumped` (seg004:0522), `droppedout` +
+> `guard_follows_kid_down` (seg002:09F8), `check_guard_fallout`
+> (seg002:0241);
+> - **`pop_savekid_state` снова полное `Kid = Char`**, а
+> `pop_load_fram_det_col` пересчитывает колонку ЛЮБОМУ персонажу — обе
+> заплатки существовали только потому, что физика знала один `Kid`.
+>
+> **Проверено:** `tests-host` — трассы Кида не изменились (`[phys] ok: 1723`,
+> тот же эталон), новый набор `t_char` (32 проверки) покрывает ветки по
+> персонажам; в MAME проверено, что игра жива (респавн, бег, падение,
+> приземление). Цена: `_CODE` +80 Б, банк 3 (`pop_map`) 7973 → 8485
+> (51.8 %), банк 1 (`guards`) 2042 → 2156.
+>
+> **Проверено пользователем 2026-08-07:** страж СПРЫГИВАЕТ ЗА КИДОМ на ряд
+> ниже — связка «ИИ + физика» работает вживую, не только в тестах.
+>
+> **`follow_guard` портирован и проверен в MAME (2026-08-07):** уровень 1,
+> бой в комнате 3, Кид отступает влево — страж приходит следом (`Guard.room`
+> 3 → 2, X перенесён через шов), ровно как в SDLPoP. Условия отбора
+> покрыты тестами `t_char` (7 сценариев: пороги 91/165, «не бой», мёртвый,
+> вверх/вниз, занятая соседняя комната). **Сцена вскрыла отдельный баг —
+> [BUG-SWORD-GHOST-1](BUGS_CLOSED.md#bug-sword-ghost-1): при переходе в бою Кид
+> прячет меч и дальше дерётся пустой рукой.**
+>
+> **Осталось (потому и запись открыта) — ревизия 2026-08-11 по коду:**
+> 1. ~~`check_chomped_guard`~~ — **сделан** вместе с
+> [L3-CHOMP](TASKS_CLOSED.md#l3-chomp) (`pop_map.c`);
+> 2. ветки `check_guard_fallout`: **скелет сделан** (возрождается в комнате 3,
+> `pop_guard_fallout` в `pop_guard.c`), **ветки ТЕНИ нет** — падает только
+> в свободном полёте, `loadshad/clear_char/saveshad`; идёт в
+> [L5-SHADOW](#l5-shadow) п. 5 (комментарий в коде обещает её «вместе с
+> L3-SKEL» — устарел, это про тень);
+> 3. страж, нажимающий напольную кнопку, вживую не проверялся (код —
+> общий `check_press`).
+
+**Почему это была ОДНА физика, а не «сделаем стражу свою».** В оригинале
+слот `Guard` — не «стражи», а все НЕ-Киды: тем же `play_guard_frame` ходят
+**страж** (charid 2), **скелет** (4, ур. 3), **тень** (1, ур. 4/5/6/12),
+**визирь-Джаффар** (ур. 13), **толстяк** (FAT, ур. 12) и **мышь** (0x18,
+ур. 8) — `tbl_guard_type` = {0,0,0,2,0,0,1,0,0,0,0,0,4,3,−1,−1}, а
+`autocontrol_opponent` (seg002:628) разводит их ТОЛЬКО по ИИ. То есть
+второй экземпляр логики пришлось бы делать не один раз, а пять.
+
+**Гард по X: страж не уходит САМ — но его МОГУТ ПЕРЕНЕСТИ.** Поправка к
+формулировке, которая была здесь раньше («страж не покидает комнату ни в
+каком виде») — она неверна, контрпример дал пользователь: в SDLPoP страж из
+комнаты 3 оказывается в комнате 2 вслед за отступающим Кидом.
+
+Разделять надо два разных механизма:
+
+- **своим ходом — не может.** Физика персонажа слота Guard обёрнута
+ `Char.room == drawn_room` и `Char.x >= 44 && Char.x < 211` (seg000:1252),
+ и никакого `check_leave` в его списке вызовов нет. Провалившегося ниже
+ комнаты убирает `check_guard_fallout` (seg002:0241) — вниз он не уходит.
+- **следом за Кидом — переносит движок.** `exit_room` (seg002:03C7)
+ вызывается ПОСЛЕ того, как комнату сменил Кид, и решает судьбу стража:
+
+ ```c
+ if (Guard.alive < 0 && Guard.sword == sword_2_drawn) { // жив и В БОЮ
+ if (guards_tile[kid_room−1] >= 30 || // в новой комнате
+ guards_seq_hi[kid_room−1] != 0) { // своего живого нет
+ if (ушёл ВЛЕВО) { if (Guard.x >= 91) leave = 1; } // страж далеко — остаётся
+ else if (ВПРАВО) { if (Guard.x < 165) leave = 1; }
+ else if (ВВЕРХ) { if (Guard.curr_row >= 0) leave = 1; } // всегда → не идёт
+ else { if (Guard.curr_row < 3) leave = 1; } // вниз → не идёт
+ } else leave = 1;
+ } else leave = 1;
+ leave ? leave_guard() : follow_guard();
+ ```
+
+ `follow_guard` (seg002:039E) стирает `guards_tile` в ОБЕИХ комнатах
+ (0xFF — «стража здесь нет», чтобы он не раздвоился) и гонит стража через
+ `goto_other_room` в окне `loadshad`/`saveshad`.
+
+**Что это значит для нас.** У нас в `enter_room` (`roomtest.c:265`) стоит
+безусловный `pop_guard_leave()` — то есть всегда ветка `leave_guard`, и
+страж ВСЕГДА остаётся. Портировать надо сам `exit_room`-выбор: условия
+«жив + меч вынут + в целевой комнате нет своего стража + он у нужного
+края» и `follow_guard`. Пороги 91/165 — это «страж у того края, в который
+ушёл Кид»; вверх и вниз оригинал не пускает никогда.
+
+**Чем подтверждать:** уровень 1, комната 3 — начать бой, отступить влево в
+комнату 2: страж обязан прийти следом и продолжить бой (как в SDLPoP на
+скриншотах пользователя). Обратная проверка: если в соседней комнате СВОЙ
+живой страж — переход не происходит.
+
+---
+
+### L6-SHADOW. Тень уровня 6 роняет решётку — СДЕЛАНА 2026-08-11
+
+**Сцена (уровень 6, комната 1).** Кид жмёт `opener` (1,7) — решётка (1,2)
+поднимается; он разбегается, прыгает через четырёхтайловую пропасть и
+цепляется за порожек решётки, чтобы подтянуться. В этот момент тень делает
+ОДИН осторожный шаг с границы (1,0)-(1,1) на `closer` (1,1), и решётка
+падает. Обе плиты комнаты ведут на один тайл 12 (проверено по LINKLOC).
+
+**Порт.** Две точки, обе в `seg002.c`:
+
+- `check_shadow` (seg002:0090): условий на содержимое комнаты нет — тень
+ встаёт при КАЖДОМ входе в комнату 1. `do_init_shad` переписан под
+ таблицы (`init_shad_5`/`init_shad_6` — первые 7 полей `Char`, как
+ `memcpy(&Char, source, 7)` у оригинала);
+- `autocontrol_shadow_level6` (seg002:1064): `Kid.frame == 43` (кадр
+ бег-прыжка) и `Kid.x < 128` -> `move_6_shift()` + `move_1_forward()`.
+ Это Shift+вперёд, то есть ОСТОРОЖНЫЙ ШАГ, а не прыжок; кадр 43 —
+ условие на КИДА, тень им только триггерится.
+
+**Звук** `sound_25_presentation` не портирован (звука нет вовсе); флаг
+оригинала `leveldoor_open = 0x4D` не воспроизводим — чужую переменную
+занимать незачем.
+
+**Проверено в MAME (пользователь, 2026-08-11):** «Кид прыгнул, зацепился,
+Тень сделал шаг, решётка упала». Тем самым закрыт и остаток
+[GUARD-PHYS](TASKS_CLOSED.md#guard-phys): нажатие плиты НЕ-Кидом работает
+(`check_press` в `guard_phys`, без гейта по `charid`).
+
+**Фон комнаты сверен с оригиналом** методом из
+[`BUGS_CLOSED.md`](BUGS_CLOSED.md#bug-lattice-doortop): 539 пикселей
+расхождения, и все — силуэты персонажей (тень пока рисуется палитрой Кида,
+см. [`../docs/shadow_render.md`](../docs/shadow_render.md)).
+
+**Цена:** банк 1 2755 -> 2877 Б, резидент без изменений.
diff --git a/applications/PoP/roomtest/TASKS_OPEN.md b/applications/PoP/roomtest/TASKS_OPEN.md
index 89b4eb3..c85b6d7 100644
--- a/applications/PoP/roomtest/TASKS_OPEN.md
+++ b/applications/PoP/roomtest/TASKS_OPEN.md
@@ -55,8 +55,6 @@
| # | Задача | Что | Блокирует |
|---|--------|-----|-----------|
-| 1 | [L5-SHADOW](#l5-shadow) | **СЛЕДУЮЩАЯ**: тень уровня 5 (крадёт зелье в комнате 24) + движок автодвижений | прохождение ур. 5 |
-| 2 | [GUARD-PHYS](#guard-phys) | физика стража = физика Кида — ядро сделано, остаток: ветка ТЕНИ в `check_guard_fallout` и живая проверка кнопки под стражем | ур. 5+ (тень) |
| — | [DRAW-COST](#draw-cost) | **кадр уложился в бюджет 2026-08-09**: 470 964 -> 425 600 тактов, период цикла 4 растровых кадра -> **3**. Дальнейшее — запас, не срочность | плавность на ВСЕХ уровнях |
| — | [L1-SPEED](#l1-speed) | игра на ~39 % быстрее оригинала | ощущение от ВСЕХ уровней; берётся в любой момент |
| — | [TUNE-1](#tune-1) | параметры движка → cfg-файл (сейчас `pop_tune.h`) | отладка таймингов и моды; берётся по мере надобности |
@@ -89,167 +87,6 @@ MEM-BANK2, MEM-BANK5, CLIP-1, KBD-1, DBG-CHEATS.
## P0 — делаем сейчас
-### L5-SHADOW. Тень уровня 5: крадёт зелье — **СЛЕДУЮЩАЯ**
-
-Единственная новая механика уровня 5 (тайлов новых нет вовсе, см. цель выше).
-Тень появляется в комнате 24, дожидается, пока откроется дверь, идёт к зелью,
-**выпивает его** и уходит за левый край. Боя нет.
-
-**Как это в оригинале** (всё сверено по коду, `custom->*` — это дефолты 1.0):
-
-| что | где | суть |
-|---|---|---|
-| появление | `check_shadow`, seg002:0064 | при СМЕНЕ КОМНАТЫ: если `current_level == 5` и `drawn_room == 24`, и тайл (кол 3, ряд 0) всё ещё `tiles_10_potion` — породить тень |
-| порождение | `do_init_shad`, seg002:0000 | `memcpy(&Char, init_shad_5, 7)` + `seqtbl_offset_char(2 /*stand*/)`, `charid = charid_1_shadow`, `demo_time = 0`, `guard_skill = 3`, `guardhp_* = 4`, `saveshad()` |
-| данные | `init_shad_5` | `{0x0F, 0x37, 0x37, 0, 0xFF, 0, 0}` = frame 15, x 55, y 55, direction 0, curr_col −1, curr_row 0, action 0 |
-| поведение | `autocontrol_shadow_level5`, seg002:1157 | в комнате 24: пока `demo_time == 0` — ждать, пока дверь (кол 1, ряд 0) не откроется (`modif >= 80`), затем `demo_index = 0`; дальше каждый кадр `do_auto_moves(shad_drink_move)`; при `Char.x < 15` — `clear_char()` |
-| движения | `do_auto_moves`, seg002:1089 | крошечный интерпретатор: `demo_time++`, по таблице `{time, move}` выбирается запись, `move` = 0 nothing / 1 forward / 2 backward / 3 up / 4 down / 5 up+forward / 6 shift / 7 move_7; −1 = ничего, −2 = конец |
-| таблица | `shad_drink_move` (data.h:866) | `{0x00,0} {0x01,1} {0x0E,0} {0x12,6} {0x1D,7} {0x2D,2} {0x31,1} {0xFF,−2}` — 8 записей по 2 байта |
-
-**Что из этого у нас уже есть:**
-
-- **механизм «спецсобытие порождает персонажа в слоте соперника»** —
- `pop_check_skel` (`guards.c`, зовётся из `roomtest.c` в тике); тень
- уровня 5 садится на тот же шов, только условие другое;
-- **тень как `charid_1_shadow`** — заведена под уровень 4
- ([L4-MIRROR](TASKS_CLOSED.md#l4-mirror)): своя ветка ИИ
- (`autocontrol_shadow` + `autocontrol_shadow_level4`), выбор таблицы кадров
- Кида (`pop_frame_tbl_is_guard`), отрисовка спрайтами Кида;
-- **питьё зелья** — `SEQ_78_DRINK` и `get_item` в `pop_ctrl.c` (Кид уже
- умеет); тень «нажимает» те же кнопки через автодвижения;
-- **зелья как trob** — фаза пузырька, тип в старших битах (`pop_trob.c`).
-
-**Что писать:**
-
-1. `do_auto_moves` + таблица `shad_drink_move` + `demo_time`/`demo_index` —
- интерпретатор ~30 строк, кладётся рядом с `autocontrol_shadow` в
- `guards.c` (банк 1). «Движения» — это те же переменные управления, что
- заполняет `read_user_control` (`pop_ctrl.c`), так что `move_*` сводятся к
- присваиваниям.
-2. `do_init_shad(init_shad_5, seq stand)` — общий порождатель тени; пригодится
- и на уровнях 6 и 12 (`init_shad_6`, `init_shad_12` — те же 7 байт).
-3. Ветка `check_shadow` для уровня 5 — по образцу `pop_check_skel`, вызов из
- того же места тика.
-4. `autocontrol_shadow_level5`.
-5. **Ветка ТЕНИ в `check_guard_fallout`** (seg002:0241): тень падает, только
- если она в свободном полёте (`action == 4`), и тогда
- `loadshad(); clear_char(); saveshad()`. Сейчас в `pop_guard_fallout`
- (`pop_guard.c`) есть ветки стража и скелета, а тени нет — комментарий там
- обещает её «вместе с L3-SKEL», но она относится именно к тени.
-
-**Чем подтверждать:** smoke уровня 5 — дойти до комнаты 24, увидеть, как
-тень выходит после открытия двери, выпивает зелье (тайл зелья исчезает) и
-уходит влево. Сверять последовательность движений с живым SDLPoP на том же
-месте — таблица `shad_drink_move` короткая, расхождение будет видно сразу.
-
-**Оговорка по виду:** тень пока рисуется обычной копией спрайтов Кида, то
-есть выглядит вторым Кидом — это [отложенный](../docs/shadow_render.md)
-вопрос, к механике уровня 5 отношения не имеет.
-
-### GUARD-PHYS. Страж живёт по тем же правилам, что Кид — **ЯДРО СДЕЛАНО 2026-08-07**
-
-> **Что уже работает** (решение пользователя: переносим физику на `Char`,
-> без предварительных замеров — иначе третий-четвёртый экземпляр той же
-> логики неизбежен).
->
-> - **физика переведена на `Char`**: `pop_map.c` целиком работает с активным
-> персонажем, а кто в `Char` — решают окна `loadkid`/`savekid` и
-> `loadshad`/`saveshad`, как в оригинале (seg006:809). Два входа:
-> `pop_phys_tick` (порт хвоста play_kid_frame) и `pop_guard_phys_tick`
-> (порт play_guard_frame) — списки вызовов отличаются ровно тем, чем в
-> оригинале;
-> - **`take_hp` стал общим** (`pop_take_hp` в резиденте `pop_guard.c`): урон
-> идёт тому, кто в `Char`, по `charid`. Раньше у боёвки и у физики были
-> свои копии, причём у физики неверная — правила `hitp_curr` мимо дельты и
-> про не-Кидов не знала;
-> - **порт веток по charid в `land()`** (seg005:173): страж гибнет с двух
-> рядов, тень падает как с одного, у не-Кида приземление даёт боевую
-> стойку; `check_guard_bumped` (seg004:0522), `droppedout` +
-> `guard_follows_kid_down` (seg002:09F8), `check_guard_fallout`
-> (seg002:0241);
-> - **`pop_savekid_state` снова полное `Kid = Char`**, а
-> `pop_load_fram_det_col` пересчитывает колонку ЛЮБОМУ персонажу — обе
-> заплатки существовали только потому, что физика знала один `Kid`.
->
-> **Проверено:** `tests-host` — трассы Кида не изменились (`[phys] ok: 1723`,
-> тот же эталон), новый набор `t_char` (32 проверки) покрывает ветки по
-> персонажам; в MAME проверено, что игра жива (респавн, бег, падение,
-> приземление). Цена: `_CODE` +80 Б, банк 3 (`pop_map`) 7973 → 8485
-> (51.8 %), банк 1 (`guards`) 2042 → 2156.
->
-> **Проверено пользователем 2026-08-07:** страж СПРЫГИВАЕТ ЗА КИДОМ на ряд
-> ниже — связка «ИИ + физика» работает вживую, не только в тестах.
->
-> **`follow_guard` портирован и проверен в MAME (2026-08-07):** уровень 1,
-> бой в комнате 3, Кид отступает влево — страж приходит следом (`Guard.room`
-> 3 → 2, X перенесён через шов), ровно как в SDLPoP. Условия отбора
-> покрыты тестами `t_char` (7 сценариев: пороги 91/165, «не бой», мёртвый,
-> вверх/вниз, занятая соседняя комната). **Сцена вскрыла отдельный баг —
-> [BUG-SWORD-GHOST-1](BUGS_CLOSED.md#bug-sword-ghost-1): при переходе в бою Кид
-> прячет меч и дальше дерётся пустой рукой.**
->
-> **Осталось (потому и запись открыта) — ревизия 2026-08-11 по коду:**
-> 1. ~~`check_chomped_guard`~~ — **сделан** вместе с
-> [L3-CHOMP](TASKS_CLOSED.md#l3-chomp) (`pop_map.c`);
-> 2. ветки `check_guard_fallout`: **скелет сделан** (возрождается в комнате 3,
-> `pop_guard_fallout` в `pop_guard.c`), **ветки ТЕНИ нет** — падает только
-> в свободном полёте, `loadshad/clear_char/saveshad`; идёт в
-> [L5-SHADOW](#l5-shadow) п. 5 (комментарий в коде обещает её «вместе с
-> L3-SKEL» — устарел, это про тень);
-> 3. страж, нажимающий напольную кнопку, вживую не проверялся (код —
-> общий `check_press`).
-
-**Почему это была ОДНА физика, а не «сделаем стражу свою».** В оригинале
-слот `Guard` — не «стражи», а все НЕ-Киды: тем же `play_guard_frame` ходят
-**страж** (charid 2), **скелет** (4, ур. 3), **тень** (1, ур. 4/5/6/12),
-**визирь-Джаффар** (ур. 13), **толстяк** (FAT, ур. 12) и **мышь** (0x18,
-ур. 8) — `tbl_guard_type` = {0,0,0,2,0,0,1,0,0,0,0,0,4,3,−1,−1}, а
-`autocontrol_opponent` (seg002:628) разводит их ТОЛЬКО по ИИ. То есть
-второй экземпляр логики пришлось бы делать не один раз, а пять.
-
-**Гард по X: страж не уходит САМ — но его МОГУТ ПЕРЕНЕСТИ.** Поправка к
-формулировке, которая была здесь раньше («страж не покидает комнату ни в
-каком виде») — она неверна, контрпример дал пользователь: в SDLPoP страж из
-комнаты 3 оказывается в комнате 2 вслед за отступающим Кидом.
-
-Разделять надо два разных механизма:
-
-- **своим ходом — не может.** Физика персонажа слота Guard обёрнута
- `Char.room == drawn_room` и `Char.x >= 44 && Char.x < 211` (seg000:1252),
- и никакого `check_leave` в его списке вызовов нет. Провалившегося ниже
- комнаты убирает `check_guard_fallout` (seg002:0241) — вниз он не уходит.
-- **следом за Кидом — переносит движок.** `exit_room` (seg002:03C7)
- вызывается ПОСЛЕ того, как комнату сменил Кид, и решает судьбу стража:
-
- ```c
- if (Guard.alive < 0 && Guard.sword == sword_2_drawn) { // жив и В БОЮ
- if (guards_tile[kid_room−1] >= 30 || // в новой комнате
- guards_seq_hi[kid_room−1] != 0) { // своего живого нет
- if (ушёл ВЛЕВО) { if (Guard.x >= 91) leave = 1; } // страж далеко — остаётся
- else if (ВПРАВО) { if (Guard.x < 165) leave = 1; }
- else if (ВВЕРХ) { if (Guard.curr_row >= 0) leave = 1; } // всегда → не идёт
- else { if (Guard.curr_row < 3) leave = 1; } // вниз → не идёт
- } else leave = 1;
- } else leave = 1;
- leave ? leave_guard() : follow_guard();
- ```
-
- `follow_guard` (seg002:039E) стирает `guards_tile` в ОБЕИХ комнатах
- (0xFF — «стража здесь нет», чтобы он не раздвоился) и гонит стража через
- `goto_other_room` в окне `loadshad`/`saveshad`.
-
-**Что это значит для нас.** У нас в `enter_room` (`roomtest.c:265`) стоит
-безусловный `pop_guard_leave()` — то есть всегда ветка `leave_guard`, и
-страж ВСЕГДА остаётся. Портировать надо сам `exit_room`-выбор: условия
-«жив + меч вынут + в целевой комнате нет своего стража + он у нужного
-края» и `follow_guard`. Пороги 91/165 — это «страж у того края, в который
-ушёл Кид»; вверх и вниз оригинал не пускает никогда.
-
-**Чем подтверждать:** уровень 1, комната 3 — начать бой, отступить влево в
-комнату 2: страж обязан прийти следом и продолжить бой (как в SDLPoP на
-скриншотах пользователя). Обратная проверка: если в соседней комнате СВОЙ
-живой страж — переход не происходит.
-
### L3-COLOR. Палитра КЛАДКИ уровня 3 (в оригинале он зелёный)
**Наблюдение (пользователь, 2026-08-07, со сравнением карт VGA).** В
diff --git a/applications/PoP/roomtest/guards.c b/applications/PoP/roomtest/guards.c
index 15f7947..d2dd3e5 100644
--- a/applications/PoP/roomtest/guards.c
+++ b/applications/PoP/roomtest/guards.c
@@ -224,18 +224,25 @@ static uint8_t demo_index; /* текущая запись
#define SEQ_2_STAND 2
#define TILE_POTION 10
-/* init_shad_5 (data:0EF2): frame 15, x 55, y 55, direction 0 (вправо),
- * curr_col −1 (ещё за левым краем), curr_row 0, action 0 (стоит). */
-static void do_init_shad_5(uint8_t room)
+/* Таблицы init_shad_N (data:0EEA/0EF2) — ПЕРВЫЕ СЕМЬ полей Char в том же
+ * порядке, в каком их копирует оригинал (`memcpy(&Char, source, 7)`):
+ * frame, x, y, direction, curr_col, curr_row, action.
+ * 5: x 55, y 55, вправо, колонка −1 (ещё за левым краем экрана), ряд 0;
+ * 6: x 81, y 118, вправо, колонка 0, ряд 1 — тень стоит на границе
+ * (1,0)-(1,1), одного осторожного шага хватает, чтобы встать на плиту. */
+static const uint8_t init_shad_5[7] = { 0x0F, 0x37, 0x37, 0, 0xFF, 0, 0 };
+static const uint8_t init_shad_6[7] = { 0x0F, 0x51, 0x76, 0, 0x00, 1, 0 };
+
+static void do_init_shad(const uint8_t *tbl, uint8_t room)
{
pop_loadshad();
- Char.frame = 0x0F;
- Char.x = 0x37;
- Char.y = 0x37;
- Char.direction = 0;
- Char.curr_col = -1;
- Char.curr_row = 0;
- Char.action = 0;
+ Char.frame = tbl[0];
+ Char.x = tbl[1];
+ Char.y = tbl[2];
+ Char.direction = (int8_t)tbl[3];
+ Char.curr_col = (int8_t)tbl[4];
+ Char.curr_row = (int8_t)tbl[5];
+ Char.action = tbl[6];
Char.room = room;
Char.charid = CHARID_1_SHADOW;
Char.alive = Kid.alive; /* наследование от Кида, см. шапку */
@@ -254,12 +261,22 @@ static void do_init_shad_5(uint8_t room)
uint8_t pop_check_shadow(uint8_t room) __banked
{
+ /* Уровень 6 (seg002:0090): условий на содержимое комнаты нет вовсе —
+ * тень встаёт при КАЖДОМ входе в комнату 1. Оригинал здесь ещё играет
+ * sound_25_presentation один раз, отмечая это `leveldoor_open = 0x4D`
+ * (переменная двери используется как флаг); звука у нас нет, а чужую
+ * переменную портить незачем — TODO вместе со звуком. */
+ if (pop_current_level == SHADOW_STEP_LEVEL) {
+ if (room != SHADOW_STEP_ROOM) return 0;
+ do_init_shad(init_shad_6, room);
+ return 1;
+ }
if (pop_current_level != SHADOW_STEAL_LEVEL) return 0;
if (room != SHADOW_STEAL_ROOM) return 0;
/* Зелья уже нет — тень своё дело сделала. Возвращаем 1 и здесь: стража
* комнаты 24 оригинал не поднимает НИКОГДА (return до enter_guard). */
if (pop_level_tile(room, SHADOW_POTION_TILEPOS) != TILE_POTION) return 1;
- do_init_shad_5(room);
+ do_init_shad(init_shad_5, room);
return 1;
}
@@ -604,12 +621,30 @@ static void autocontrol_shadow_level5(void)
if (Char.x < 15) clear_char();
}
-/* autocontrol_shadow (seg002:081D) — по уровням. Ветки 6 (шагает по плите)
- * и 12 появятся со своими уровнями. */
+/* autocontrol_shadow_level6 (seg002:1064). Вся логика — один осторожный шаг
+ * (Shift + вперёд) в тот кадр, когда Кид прыгает к решётке: тень встаёт на
+ * closer (1,1) и роняет решётку (1,2), за порожек которой Кид в этот момент
+ * цепляется. Условие смотрит на КИДА: кадр 43 — четвёртый кадр бег-прыжка,
+ * x < 128 отсекает прыжки в правой половине комнаты (там Кид ещё разбегается
+ * и до решётки ему далеко). */
+#define FRAME_43_RUNJUMP 43
+
+static void autocontrol_shadow_level6(void)
+{
+ if (Char.room != SHADOW_STEP_ROOM) return;
+ if (Kid.frame != FRAME_43_RUNJUMP) return;
+ if (Kid.x >= 128) return;
+ move_6_shift();
+ move_1_forward();
+}
+
+/* autocontrol_shadow (seg002:081D) — по уровням. Ветка 12 появится со своим
+ * уровнем. */
static void autocontrol_shadow(void)
{
if (pop_current_level == MIRROR_LEVEL) autocontrol_shadow_level4();
if (pop_current_level == SHADOW_STEAL_LEVEL) autocontrol_shadow_level5();
+ if (pop_current_level == SHADOW_STEP_LEVEL) autocontrol_shadow_level6();
}
/* autocontrol_guard (seg002:701) + autocontrol_opponent (seg002:628). */
diff --git a/applications/PoP/roomtest/pop_guard.h b/applications/PoP/roomtest/pop_guard.h
index 94c91be..6e9dc40 100644
--- a/applications/PoP/roomtest/pop_guard.h
+++ b/applications/PoP/roomtest/pop_guard.h
@@ -94,6 +94,15 @@
#define SHADOW_DOOR_TILEPOS 1 /* (кол 1, ряд 0) — ворота */
#define SHADOW_DOOR_OPEN_MIN 80 /* openness, при которой тень идёт */
+/* Спецсобытие «тень захлопывает решётку» (custom->shadow_step_*, дефолты
+ * SDLPoP): уровень 6, комната 1. Кид жмёт opener (1,7), решётка (1,2)
+ * поднимается, он прыгает через пропасть и цепляется за её порожек — и
+ * ровно в этот момент тень делает ОДИН осторожный шаг с границы (1,0)-(1,1)
+ * на closer (1,1), роняя решётку. Обе плиты комнаты ведут на тот же тайл
+ * 12 (проверено по LINKLOC/LINKMAP уровня). */
+#define SHADOW_STEP_LEVEL 6
+#define SHADOW_STEP_ROOM 1
+
/* Единственный уровень, где тень — БОЕВОЙ персонаж (seg003:702). На всех
* остальных (4 зеркало, 5 зелье, 6 плита) она Кида «не видит»: см.
* pop_check_can_guard_see_kid. */
diff --git a/applications/PoP/roomtest/pop_level.c b/applications/PoP/roomtest/pop_level.c
index 088a18d..c0eb5ed 100644
--- a/applications/PoP/roomtest/pop_level.c
+++ b/applications/PoP/roomtest/pop_level.c
@@ -226,7 +226,14 @@ int pop_room_load(uint8_t room, uint8_t *fg, uint8_t *bg,
? (uint8_t)(room_fg_ptr(dl)[9] & 0x1F)
: 20; /* кромка уровня */
} else {
- for (i = 0; i < 11; i++) below_fg[i] = 20; /* нет комнаты снизу = стена */
+ /* Комнаты снизу НЕТ. Оригинал (load_rowbelow, seg008:368) в этом
+ * случае подставляет РАЗНЫЕ кромки: колонкам 1..9 — `tiles_0_empty`,
+ * и только левому краю (колонка 0, тайл из room_BL) — `tiles_20_wall`.
+ * У нас тут стояла стена на всё, и её грань (topright стены) лезла
+ * жёлтыми треугольниками под пустые тайлы нижнего ряда — видно в
+ * шахте падения, уровень 6 комната 3 (BUG-BELOWROW-WALL). */
+ for (i = 0; i < 10; i++) below_fg[i] = 0; /* tiles_0_empty */
+ below_fg[10] = 20; /* левая кромка уровня */
}
gfx_w0_unmap();
diff --git a/applications/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c
index 8a57840..1e42cc2 100644
--- a/applications/PoP/roomtest/pop_room.c
+++ b/applications/PoP/roomtest/pop_room.c
@@ -274,7 +274,7 @@ static void draw_tile(int row, int col)
* «стена» там стоит, только если такой комнаты нет — как в
* load_rowbelow (seg008:368). См. BUG-SEAM-WEDGE-1. */
if (lc < 0) rbl_code = pop_t_below ? (uint8_t)(pop_t_below[10] & 0x1F) : 20;
- else rbl_code = pop_t_below ? (uint8_t)(pop_t_below[lc] & 0x1F) : 1;
+ else rbl_code = pop_t_below ? (uint8_t)(pop_t_below[lc] & 0x1F) : 0;
/* Модификатора ряда снизу у нас нет (pop_t_below — только fg), а он
* нужен паласной панели над воротами. Ставим 0 = «панели нет»:
* случай узкий (ворота в комнате СНИЗУ, по диагонали от пустого