Уровни 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
+81 -1
View File
@@ -66,6 +66,9 @@
#define CHARID_0_KID 0
#define CHARID_1_SHADOW 1
#define CHARID_2_GUARD 2
/* charid_3 не используется: Джафар (уровень 13) — обычный charid_2_guard,
* своим charid оригинал помечает ТОЛЬКО скелета (enter_guard, seg002:196).
* Отличают Джафара набор спрайтов (VIZIER.DAT), skill 9 и три события. */
#define CHARID_4_SKELETON 4
#define CHARID_5_PRINCESS 5
/* МЫШЬ (спецсобытие уровня 8) — тоже персонаж слота Guard: своей таблицы
@@ -129,8 +132,41 @@
/* Единственный уровень, где тень — БОЕВОЙ персонаж (seg003:702). На всех
* остальных (4 зеркало, 5 зелье, 6 плита) она Кида «не видит»: см.
* pop_check_can_guard_see_kid. */
* pop_check_can_guard_see_kid.
*
* Сцена целиком (seg002:0070 + seg002:1184): в комнате 15 лежит МЕЧ (0,1);
* пока он на месте, тень не появляется. Кид его берёт — тень падает в ту
* же комнату, и дальше либо бой (у обоих клинки), либо, если Кид уберёт
* меч, — слияние. Комната 18 при уходе ВПРАВО «съедает» меч с карты
* (sword_disappears), чтобы вернуться и подобрать его второй раз было
* нельзя. Проверено по res2012.bin, ../docs/levels_12_15_plan.md §7.3. */
#define SHADOW_FIGHT_LEVEL 12
#define SHADOW_FIGHT_ROOM 15
#define SHADOW_SWORD_TILEPOS 1 /* (кол 1, ряд 0) — меч уровня 12 */
#define SWORD_GONE_ROOM 18 /* уход ВПРАВО отсюда убирает меч */
/* Слияние (autocontrol_shadow_level12): дистанция, вспышка, её длительность. */
#define SHADOW_UNITE_DIST 10
#define SHADOW_UNITE_TIME 42
/* Уровень 13 — ДЖАФАР (custom->jaffar_*, дефолты SDLPoP). Своего ИИ у него
* нет вовсе: `autocontrol_Jaffar` (seg002:085A) — это `autocontrol_guard`,
* то есть обычный бой стражи. Отличают его только данные (VIZIER.DAT,
* skill 9, 6 HP — всё уже в pop_level_cold.c) и три события ниже.
*
* Встреча: Кид уходит ВПРАВО из комнаты 3 — а `комната 3.right = 1`, где
* Джафар и стоит (проверено по res2013.bin, ../docs/levels_12_15_plan.md
* §7.2). Ему даётся фора JAFFAR_NOTICE_TIME кадров: пока таймер идёт, он не
* встаёт в боевую стойку, и это единственная пауза во всей сцене.
*
* Выход: смерть Джафара сама дверь НЕ открывает — она ставит
* pop_leveldoor_open = 2, а дверь открывает КНОПКА комнаты 24 (tilepos 0,
* реальный opener), «нажимаемая» при уходе Кида ВЛЕВО. */
#define JAFFAR_LEVEL 13
#define JAFFAR_MEET_ROOM 3 /* уход ВПРАВО отсюда будит Джафара */
#define JAFFAR_NOTICE_TIME 28 /* ≈2,3 с форы (28/12) */
#define JAFFAR_EXIT_ROOM 24
#define JAFFAR_EXIT_TILEPOS 0
#define JAFFAR_FLASH_TIME 18
/* Градаций мастерства стража (seg002: таблицы вероятностей по 12 записей). */
#define NUM_GUARD_SKILLS 12
@@ -184,6 +220,23 @@ extern int8_t guard_refrac, justblocked, kid_sword_strike;
* holding_sword — клинок в руке (для индикатора HP стража). */
extern int8_t offguard, holding_sword;
/* Спецсобытие уровня 12 — СЛИЯНИЕ С ТЕНЬЮ (united_with_shadow, seg000:288).
* Значение читается как три разных состояния, и это НЕ флаг:
* 0 — тень ещё отдельный персонаж (или уровень не 12);
* >0 — идёт вспышка слияния, счётчик кадров (42 → 1), убывает в
* pop_shadow_timer; пока он идёт, Кид мигает спрайтами тени;
* −1 — «уже слились», навсегда до конца уровня. Именно по нему
* check_shadow больше не поднимает тень, а в комнатах 2/13
* появляются недостающие плиты пола.
* Переход 0 → −1 идёт ЧЕРЕЗ счётчик: таймер, досчитав до нуля, уводит его
* ещё на единицу вниз (seg003:503) — отсюда и знаковый тип.
*
* pop_shadow_init (shadow_initialized, seg000:309) — «тень уже дождалась
* Кида в комнате 15»: до этого autocontrol_shadow_level12 роняет её заново
* каждый кадр, чтобы Кид застал падение, а не готовую стойку. */
extern int8_t pop_united_shadow;
extern uint8_t pop_shadow_init;
/* Сид бросков боёвки (свой домен последовательности, см. pop_geom.h). */
extern pop_rnd_t pop_fight_seed;
@@ -215,6 +268,33 @@ void pop_check_mouse(void) __banked;
* возвращается и во втором случае тоже. */
uint8_t pop_check_shadow(uint8_t room) __banked;
/* sword_disappears (seg002:0536) — спецсобытие уровня 12: уход ВПРАВО из
* комнаты 18 убирает меч из комнаты 15. Звать при уходе вбок ДО смены
* комнаты, рядом с чекпойнтом уровня 3. */
void pop_sword_disappears(void) __banked;
/* meet_Jaffar (seg002:0544) — уход ВПРАВО из комнаты 3 уровня 13 даёт
* Джафару фору; звать рядом с pop_sword_disappears (та же ветка leave_room).
* Jaffar_exit (seg002:0517) — уход ВЛЕВО при pop_leveldoor_open == 2 жмёт
* кнопку комнаты 24 и открывает дверь уровня. */
void pop_meet_jaffar(void) __banked;
void pop_jaffar_exit(void) __banked;
/* check_killed_shadow (seg006:2033) — спецсобытие уровня 12: смерть тени
* убивает Кида и наоборот. Звать раз в кадр ДО тиков персонажей, рядом с
* pop_check_skel; на прочих уровнях выходит первой строкой. */
void pop_check_killed_shadow(void) __banked;
/* timers (seg003:0735) — кадровые счётчики спецсобытий: вспышка слияния с
* тенью и фора Джафара. Звать раз в кадр из главного цикла. Резидент: два
* вычитания, банк ради них не разворачиваем. */
void pop_frame_timers(void);
/* guard_notice_timer (seg000:307) — фора Джафара после встречи: пока >0, он
* не встаёт в боевую стойку. Ставит pop_meet_jaffar, убавляет
* pop_frame_timers, читает autocontrol_guard_inactive. */
extern int8_t pop_guard_notice_timer;
/* Боёвка: пометить попадания (обе стороны) и применить их. Порядок в
* кадре как в play_frame: hurting -> hurt -> do_delta_hp. */
void pop_check_sword_hurting(void) __banked;