Уровни 12 и 13: тень, Джафар, падающие плиты + разбор слоёв

Уровень 12 (тень) — порт seg002/seg006:
- check_shadow: подъём тени в комнате 15 по условию «меч подобран»,
  init_shad_12, вход падением (seq 7);
- autocontrol_shadow_level12: ждать Кида, бой, сближение, СЛИЯНИЕ;
- общий урон (ранил тень — ранил себя) и check_killed_shadow
  (убил тень — убил себя);
- таймер вспышки слияния 42 -> -1, sword_disappears при уходе из
  комнаты 18, появление плит в комнатах 2/13 после слияния.

Переход 12 -> 13 бесшовный: двери у уровня нет, он кончается фактом
попадания в комнату 23 (tbl_seamless_exit); флаг pop_seamless не даёт
сбросить HP.  Чит навигации по комнатам этот триггер придерживает —
иначе комнату 23 двенадцатого уровня не посмотреть в принципе.

Уровень 13 (Джафар): guard_notice_timer (фора после встречи),
on_guard_killed + Jaffar_exit, check_fall_flo (гряда плит сверху) и три
исключения loose-механики.  Плюс спрайты визиря VIZIER.DAT — без них он
рисовался обычным стражем; заодно цвет из данных комнаты применяется
только к обычному стражу, как в оригинале.

Падающие плиты — семь дефектов, найденных прогонами:
- MOB_MAX 4 -> 14: check_fall_flo роняет шесть плит разом, лишние
  терялись без щебня;
- одиночные сигналы посадки/провала больше не затираются в кадре;
- честный loose_fall: сбитая плита рождается в том же кадре, от места
  удара, с половинной скоростью;
- при снятии плиты метится и сосед справа (висел передний торец);
- heal и отрисовка кусков разнесены на два прохода;
- куски рисуются ПОСЛЕ фона, порядок между собой — по убыванию y
  (compare_curr_objs для пары 0x80 сортирует наоборот);
- wall_pattern убран из ceil_over_kid_tile: у оригинала узор из
  draw_tile_bottom идёт в фон, а не в передний слой.

Хост-тесты: два новых набора, t_shadow (45 проверок) и t_jaffar (44).
TK_ROOMS 8 -> 24 — спецсобытия адресуют комнаты по реальным номерам.

Осознанные расхождения (docs/impl_diff.md): нет мигания Кида спрайтами
тени при слиянии, чит навигации не запускает бесшовный переход, чит K
убивает через штатный путь смерти.

Открытым остаётся разбор слоёв: MOB-CLIP-RIGHT, MID-OVERLAY-LAYER,
GUARD-FALLOUT-VICTORY, BG-ONCE (BUGS_OPEN.md / TASKS_OPEN.md).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 18:21:52 +03:00
parent 716f6f9273
commit 67d62e3e8b
37 changed files with 3169 additions and 79 deletions
+145
View File
@@ -231,6 +231,151 @@ MEM-BANK2, MEM-BANK5, CLIP-1, KBD-1, DBG-CHEATS.
## P0 — делаем сейчас
### <a id="l12-shadow"></a>L12-SHADOW. Уровень 12: встреча с тенью, бой, слияние
> **Статус 2026-08-13: код написан, хост-тесты зелёные (45 проверок,
> `tests-host/t_shadow.c`), ЖИВОЙ ПРОВЕРКИ В MAME ЕЩЁ НЕ БЫЛО.** Для неё
> нужна сборка с `FIRST_LEVEL=12` и полный рестарт MAME после `make hdd`.
План и сверка констант с оригинальными данными —
[`../docs/levels_12_15_plan.md`](../docs/levels_12_15_plan.md) §1 и §7.
Сделано ровно по нему:
| кусок | где | порт |
|---|---|---|
| подъём тени в комнате 15 (условие «меч уже подобран») | `guards.c` `pop_check_shadow` | seg002:0070 |
| `init_shad_12` + вход ПАДЕНИЕМ (`seq_7`) | `guards.c` | data:0EFA |
| ИИ тени: ждать Кида / драться / сойтись / слиться | `guards.c` `autocontrol_shadow_level12` | seg002:1184 |
| «ранил тень — ранил себя» | `pop_guard.c` `pop_do_delta_hp` | seg000:1518 |
| «убил тень — убил себя» | `guards.c` `pop_check_killed_shadow` | seg006:2033 |
| таймер вспышки слияния (0 → счётчик → −1) | `pop_guard.c` `pop_shadow_timer` | seg003:0735 |
| меч исчезает при уходе вправо из комнаты 18 | `guards.c` `pop_sword_disappears` | seg002:0536 |
| появление плит в комнатах 2/13 после слияния | `pop_map.c` `floors_appear` | seg006:1063 |
| бесшовный переход 12 → 13 (комната 23, без двери) | `roomtest.c` + `pop_start_level` | seg000:0900 |
Осознанное расхождение: мигания Кида спрайтами тени во время вспышки нет —
запись в [`../docs/impl_diff.md`](../docs/impl_diff.md).
<a id="bg-once"></a>
### BG-ONCE. Задний фон рисуется ОДИН раз; вместо чёрных баров — heal
**Предложение пользователя 2026-08-13. Не делать сейчас — обязательно к
проверке и реализации на этапе полиша/оптимизации.**
Суть: элементы САМОГО заднего фона — факелы без пламени, окна, узор кладки,
решётчатая кладка и что ещё найдётся — впечатываются в фон один раз и больше
не перерисовываются никогда. Ни один объект за ними оказаться не может, а
значит и возвращать их поверх чего-либо не нужно. Если такой элемент оказался
затёрт, это САМО ПО СЕБЕ сигнал об ошибке: либо бар/прямоугольник слишком
велик, либо что-то рисуется не в своём слое.
Вторая половина предложения — и она про скорость: там, где мы гасим область
чёрным баром перед выводом спрайта, звать вместо бара **heal по координатам
бара**. Бар всё равно приходится потом закрывать фоном, то есть выходит две
операции; heal делает то же одной и сразу правильным содержимым.
**Оценка: идея верная по направлению, но есть два известных препятствия.**
1. **Узкий heal уже пробовали — и откатили.** В `pop_room.c` (`pop_clip_sprite`)
записано: per-спрайтовый heal не используется, потому что выравнивание
блока акселератора оставляет 1-2 px слайверы правой грани, и на одной
странице дабл-буфера это мерцает. Поэтому борта чистит полоса
`pop_room_clip_borders` с гейтом по флагу. Прежде чем менять бары на heal,
надо решить именно эту задачу — иначе получим мерцание вместо ускорения.
2. **Полный запрет «не перерисовывать фон» противоречит foretable.**
`draw_tile_fore` (seg008:715) кладёт в передний слой ГЛАВНУЮ ГРАНЬ стены
вместе с её узором — она обязана перекрывать персонажа. То есть запрет
должен различать «декор задней стены» и «передняя грань», а это ровно
различие backtable/foretable оригинала. Фактически предложение сводится к
«привести наши слои в соответствие с таблицами оригинала» — тот же вывод,
к которому пришёл разбор `overlay_mid_tile` (см. ниже).
**Известное исключение**, которое надо не сломать: Кид, уходящий по лестнице
в дверь уровня, действительно уезжает за передний план — но он и рисуется
отдельно (`clip_char`, правая грань doortop; см. memory `pop_clip_char_todo`).
**Критерий приёмки этапа:** пройти уровни с падающими плитами (13) и с
факелами/окнами (10-12) и убедиться, что ни один фоновый элемент не
перерисовывается в кадре, где его тайл не менялся; замерить кадр до и после.
<a id="l12-shadow-spr"></a>
### L12-SHADOW-SPR. Тень дерётся спрайтами СТРАЖА, а не своими
**Найдено прогоном 2026-08-13.** `tbl_guard_type[12] = 4` — это SHADOW.DAT,
отдельный набор спрайтов (силуэт Кида), а `pop_guard_load` (`pop_cdraw.c`)
разбирает только тип 2 (SKEL) и 3 (VIZIER); тип 4 сваливается в набор
обычного стража. Ассеты есть: `SDLPoP/data/SHADOW` (32 кадра + `res750.pal`).
Правка по образцу VIZIER (набор в `pop_pack_guard.py`, каталог в Makefile,
ветка в `pop_guard_load`), НО сначала проверить две вещи, иначе спрайты
поедут: (1) какую таблицу кадров получает `CHARID_1_SHADOW` в
`pop_frame_tbl_is_guard` — Кида или стража, и (2) сколько кадров в наборе
(32 против 34 у стража) и не выходит ли индекс за его пределы.
Это НЕ полный «вид тени» (OR/XOR-блиттеры, [`../docs/shadow_render.md`]) —
только правильный набор спрайтов вместо чужого.
<a id="l12-seam-polish"></a>
### L12-SEAM-POLISH. Переход 12 -> 13 выглядит коряво
Механика работает (комната 23, без заставки и без сброса HP), но сама
анимация прохода рваная — замечено пользователем 2026-08-13. Полишинг,
берётся вместе с остальной шлифовкой переходов.
<a id="l13-jaffar"></a>
### L13-JAFFAR. Уровень 13: Джафар, победа, выход, падающая гряда
> **Статус 2026-08-13: код написан, хост-тесты зелёные (44 проверки,
> `tests-host/t_jaffar.c`), ЖИВОЙ ПРОВЕРКИ В MAME ЕЩЁ НЕ БЫЛО.**
Своего ИИ у Джафара нет — это обычный страж (seg002:085A), skill 9 и 6 HP уже
лежат в `pop_level_cold.c`. Сделаны только события и данные:
| кусок | где | порт |
|---|---|---|
| фора после встречи (`guard_notice_timer`) | `guards.c` `pop_meet_jaffar` + `autocontrol_guard_inactive` | seg002:0544 / 0736 |
| победа: белая вспышка + «выход разрешён» | `guards.c` `on_guard_killed` | seg006:1927 |
| выход: кнопка комнаты 24 при уходе ВЛЕВО | `guards.c` `pop_jaffar_exit` | seg002:0517 |
| гряда плит сверху при входе в комнаты 23/16 | `pop_map.c` `pop_check_fall_flo` | seg000:1317 |
| фазу со старшим битом на 13-м НЕ гасить | `pop_map.c` `pop_loose_tick`, `pop_trob.c` `animate_loose` | seg007:823 |
| сотрясение на 13-м плиты НЕ трясёт | `pop_map.c` `do_knock` | seg007:949 |
| плита бьёт и В БЕГЕ | `pop_map.c` `fell_on_your_head` | seg007:1218 |
| СПРАЙТЫ Джафара (VIZIER.DAT, своя палитра) | `pop_pack_guard.py`, `pop_cdraw.c`, `Makefile` | seg000:1092 |
По ходу нашлась и починена смежная ошибка: цвет стража из данных комнаты
применялся к ЛЮБОМУ набору, хотя оригинал берёт его только у обычного стража
(`curr_guard_color = 0` при `tbl_guard_type != 0`, seg002:183) — скелета это
не задевало случайно, Джафара покрасило бы.
Осознанное расхождение (представление фазы отложенного старта) — запись в
[`../docs/impl_diff.md`](../docs/impl_diff.md).
**Не портировано намеренно:** `is_show_time` в `on_guard_killed` — это показ
ОСТАВШЕГОСЯ ВРЕМЕНИ, а часов на 60 минут у нас пока нет вовсе.
**Что проверять в MAME** (`make LEVEL=13`):
1. комната 23 (стартовая): плиты сверху сыплются ВРАЗНОБОЙ с первых кадров,
и они бьют, даже если пробегать под ними;
2. приземление Кида плиты НЕ трясёт (иначе гряда посыплется разом);
3. уход вправо из комнаты 3: Джафар виден СВОИМИ спрайтами и своей палитрой,
~2 секунды стоит, не доставая клинок;
4. смерть Джафара — белая вспышка; уход ВЛЕВО открывает дверь уровня.
**Что проверять в MAME** (по порядку прохождения уровня):
1. комната 15: пока меч лежит — тени нет; подобрал меч, вышел и вернулся —
тень СВАЛИВАЕТСЯ сверху, а не стоит готовая;
2. бой: каждый удар по тени отнимает HP и у Кида; добить её нельзя — на её
смерти умирает и Кид (белая вспышка);
3. слияние: убрать меч (`Shift`+вниз), подойти — белая вспышка, тень исчезла,
потолок HP вырос на единицу;
4. после слияния в комнатах 2 и 13 (верхний ряд) под ногами появляются плиты;
5. уход ВПРАВО из комнаты 18 убирает меч из комнаты 15 (вернуться и взять
второй раз нельзя);
6. комната 23 — уровень молча становится 13-м, HP НЕ сбрасывается.
### <a id="l7-feather"></a>L7-FEATHER. Зелье МЕДЛЕННОГО ПАДЕНИЯ (уровень 7, комната 1)
> **Статус 2026-08-12: код написан, ждёт живой проверки в MAME.** Сделаны все