Уровни 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
+71 -3
View File
@@ -464,6 +464,29 @@ static void draw_gate_back(uint8_t modl, int xh, int dby, int dmy)
pop_env_b(DOOR_FRAM_SLICE[gate_frame], x, yb);
}
/* Та же решётка, но в ПЕРЕДНИЙ слой — поверх персонажа, стоящего в проёме
* (draw_gate_fore, seg008:1153). От back-варианта отличается ровно тем, что
* есть в оригинале: только ПРОЗРАЧНЫЕ куски (51 низ + 52 восьмипиксельные
* ломти), без непрозрачного 50 и без хвостового DOOR_FRAM_SLICE — фон под
* баррами уже нарисован, здесь возвращаются только сами прутья.
* Клип по прямоугольнику персонажа делает pop_t_fclip (см. pop_tile.h). */
static void draw_gate_fore(uint8_t modl, int xh, int dby, int dmy)
{
int x = xh * 8;
int gate_top_y = dby - 62;
int openness = (((modl > 188) ? 188 : modl) >> 2) + 1;
int gate_bot_y = dmy - openness;
int yb;
pop_env_b(51, x, gate_bot_y - 2);
yb = gate_bot_y - 12;
if (yb < 192)
while (yb >= 0 && yb > 7 && (yb - 7) > gate_top_y) {
pop_env_b(52, x, yb);
yb -= 8;
}
}
@@ -599,7 +622,17 @@ static void ceil_over_kid_tile(int col)
if (code == 20) { /* draw_tile_bottom: стена */
if (!pop_palace) /* seg008:576 */
pop_wall_b(POP_WALL_FRAM_BOTTOM[pop_wall_modifier(-1, col)], x, dby);
wall_pattern(-1, col, 0);
/* wall_pattern ЗДЕСЬ НЕТ — и это не упущение. draw_tile_bottom
* (seg008:588) кладёт в foretable ТОЛЬКО кадр грани `id`, под своим
* `arg_0`; узор вызывается строкой ниже, вне этой ветки, и уходит в
* текущую таблицу, то есть в ФОН. Мы же звали его прямо в переднем
* проходе — узор задней стены штамповался поверх всего, что уже
* нарисовано, включая падающие плиты (найдено прогоном 2026-08-13).
* В фон он попадает там, где и положено, — при отрисовке тайла.
*
* Не путать с fore_only_tile: там узор в переднем слое ЗАКОННЫЙ,
* это порт draw_tile_fore (seg008:715), где он стоит явно рядом с
* главной гранью стены, обязанной перекрывать персонажа. */
} else if (t->bottom_id) {
pop_env_b(t->bottom_id, x, dby);
}
@@ -948,9 +981,27 @@ void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y,
/* на 141 — до него Kid ещё */
/* в ряду 1 ПОД полом [0,3]) */
if (do_ov) {
/* ВЕСЬ диапазон рядов, а не только два крайних. В оригинале
* (redraw_at_char2, seg003:0645) стоит ровно два вызова —
* char_bottom_row и char_top_row, — и этого хватает, потому что
* там они ВСЕГДА соседние. У нас между ними бывает пропуск:
* char_footprint форсит rT = rB−1 (голова торчит в ряд выше), а
* потом окно fore-клипа расширяет rB ещё на ряд вниз — rT=0,
* rB=2 давали обход рядов 0 и 2 МИМО ряда 1.
*
* Ровно так терялась передняя грань пола у Кида, спрыгнувшего с
* кромки: кадр 103, action 3, ряд по y = 1, а обход шёл по 0 и 2
* (трасса MAME 2026-08-13: rT=0 rB=2 cL=0 cR=1, ни одного вызова
* overlay_mid_tile). Глубже в падении y_to_row давал уже 2, ряд
* 1 попадал в пару крайних — и плита вдруг начинала рисоваться
* правильно, что и было видно глазом (JUMP-FLOOR-LEGS). */
for (c = cR; c >= cL; c--) {
if (action != 2) other_overlay_tile(rB, c); /* опорный ряд */
if (rT != rB) other_overlay_tile(rT, c); /* верхний ряд */
int8_t r2;
for (r2 = rB; r2 >= rT; r2--) {
/* seg003:0645: при hang_climb опорный ряд НЕ трогаем. */
if (r2 == rB && action == 2) continue;
other_overlay_tile(r2, c);
}
}
}
}
@@ -961,6 +1012,23 @@ void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y,
for (r = rT; r <= rB; r++)
for (c = cL; c <= cR; c++)
fore_tile(r, c);
/* Первое условие draw_tile_fore (seg008:0D15): персонаж СТОИТ на тайле
* ворот — их решётка возвращается поверх него. Решение приняла карта
* (pop_gate_over_char, разбор там же); здесь остаётся отрисовка. Бары
* рисуются в колонке curr_col+1: графика ворот принадлежит ПРАВОМУ краю
* своего тайла и в оригинале кладётся в проходе следующей колонки.
*
* Один вызов через трамплин на персонажа за кадр — не в тайловом цикле:
* тайл-кандидат всегда ровно один, а fore-проход и так самый горячий
* кусок кадра (memory pop_fore_layer_cost). */
if (pop_gate_over_char(ch)) {
int8_t gr = ch->curr_row, gc = (int8_t)(ch->curr_col + 1);
if (tile_in_fclip(gr, gc)) {
int dby = 63 * gr + 65;
draw_gate_fore(pop_t_bg ? pop_t_bg[gr * 10 + ch->curr_col] : 0,
POP_COL_XH[gc], dby, dby - 3);
}
}
/* redraw_needed_above (seg008:0221): полоса кладки У ПОТОЛКА (ряд −1 =
* ряд 2 комнаты сверху). В оригинале она рисуется в самом конце обхода
* тайлов — ПОСЛЕ объектов, т.е. поверх персонажа (redraw_frames_above,