Катсцены: fade длиной как в оригинале, музыка дослушивается до уровня

ДЛИТЕЛЬНОСТЬ. fade_in_1/fade_out_1 у оригинала — 64 шага палитры по два
тика, то есть 128 тиков = 2,13 с каждый; сцена перед уровнем 2 идёт с ними
около семи секунд. Наши четыре кадра укладывались в восемь сотых секунды,
и сцена выходила втрое короче. INTRO_FADE и TITLE_FADE теперь POP_T60(128).

Ступеней яркости стало восемь вместо четырёх (7/8, 3/4, 5/8, 1/2, 3/8,
1/4, 1/8, 0 — каждая парой сдвигов, умножения на Z80 не нужно): растянуть
четыре ступени на две секунды значило бы получить четыре скачка яркости.

МУЗЫКА. Прошлый коммит отдавал трек доигрывать уже в игре — так делает
оригинал. На слух вышло хуже: музыка спотыкается, потому что загрузка
уровня не даёт насосу долить блок вовремя (у DOS-версии такой проблемы
нет). Дослушиваем под чёрным экраном, до отрисовки уровня — тем более что
следующий load_intro у оригинала всё равно начинается с ожидания тишины
(seg001:681). Пропуск сцены обрывает и музыку.

Заодно: WAIT перед PV пересчитан (584 вместо 707 — наш fade больше не
короче), а тема титров пускается ПОСЛЕ fade_in, как в show_title.

Host-тесты: 15 наборов. Проверено в MAME: плавное появление титров.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-26 09:47:16 +03:00
parent 4a939e42f8
commit 0db4f94707
6 changed files with 128 additions and 48 deletions
+58 -18
View File
@@ -27,7 +27,13 @@
#include "pop_guard.h" /* Guard, CHARID_*, pop_gframe */
#define INTRO_PARTS 5
#define INTRO_FADE 4
/* ДЛИТЕЛЬНОСТЬ FADE. У оригинала это 64 шага палитры по два тика —
* 128 тиков, то есть 2,13 с (fade_in_1/fade_out_1 -> fade_*_frame ->
* do_simple_wait(1) при wait_time = 2). Наши четыре кадра укладывались в
* восемь сотых секунды, и вся сцена выходила втрое короче оригинальной.
* Число здесь — кадры луча, ступени яркости внутри распределяет
* pop_ui_fade_*. */
#define INTRO_FADE POP_T60(128)
/* Оцифрованные эффекты pv_scene() (SDLPoP seg001:422): створка ворот
* закрывается, затем открывается дверь покоев, из которой входит Джафар. */
@@ -104,13 +110,14 @@ enum {
* SDLPoP: transition_ltr 80 тиков + pop_wait 0x258 + fade_out_2, а тот
* гасит палитру за 0x40 шагов по 2 тика = 128 тиков;
* у нас: полосовой переход 80 кадров луча (= 96 тиков) + WAIT + fade
* всего в четыре ступени палитры (= 5 тиков).
* Отсюда 80 + 600 + 128 - 96 - 5 = 707. Дальше и там и там экран уже
* чёрный, а трек доигрывает — этой паузой сцена и стыкуется с PV. */
* той же длины (INTRO_FADE = 128 тиков).
* Теперь, когда наш fade идёт те же 128 тиков, остаётся поправка на один
* лишь переход: 80 + 600 + 128 - 96 - 128 = 584. Дальше и там и там экран
* уже чёрный, а трек доигрывает — этой паузой сцена и стыкуется с PV. */
static const pop_cs_cmd_t intro_before_pv[] = {
/* absence собирается прямо поверх последнего title-кадра полосовым
* transition_ltr(), как в SDLPoP; оба экрана используют title.pal. */
{ POP_CS_WAIT, POP_T60(707) }, /* story 1: In the absence */
{ POP_CS_WAIT, POP_T60(584) }, /* story 1: In the absence */
{ POP_CS_FADE_OUT, 0 },
{ POP_CS_END, 0 }
};
@@ -328,7 +335,7 @@ static void intro_prepare_palette(uint8_t pal)
{
intro_pending_pal = pal;
intro_pal_pending = 1;
pop_pal_apply(4);
pop_pal_apply(POP_PAL_DIM_BLACK);
intro_black_pages();
}
@@ -336,7 +343,7 @@ static void intro_commit_palette(void)
{
if (intro_pending_pal == INTRO_PAL_PV) pop_pal_pv_load();
else pop_pal_story_load();
pop_pal_apply(4);
pop_pal_apply(POP_PAL_DIM_BLACK);
intro_pal_pending = 0;
}
@@ -1032,7 +1039,7 @@ static void intro_restore_game_palette(void)
* переходом в demo. */
intro_black_pages();
pop_pal_game_load();
pop_pal_apply(4);
pop_pal_apply(POP_PAL_DIM_BLACK);
}
/* ================= ПЕРСОНАЖИ КАТСЦЕН ================================= *
@@ -1303,8 +1310,13 @@ static int cut_begin(uint8_t hg_state)
return 0;
}
/* Прервали ли последнюю живую сцену клавишей — нужно вызывающему: по
* пропуску музыку обрываем, а не дослушиваем. */
static uint8_t cut_aborted;
static void cut_end(uint8_t aborted)
{
cut_aborted = aborted;
if (!aborted) pop_pal_fade_out(INTRO_FADE);
cut_free();
}
@@ -1556,6 +1568,31 @@ uint8_t pop_intro_show(void) __banked
return 0;
}
/* Хвост любой сцены перед уровнем.
*
* ДОСЛУШИВАЕМ ПОД ЧЁРНЫМ ЭКРАНОМ, а не «пусть играет в игре». Оригинал
* так и делает: следующий load_intro начинается с ожидания тишины
* (seg001:681), и к отрисовке уровня трек уже смолк. Мы сначала отдали
* музыку в игру — и она там спотыкалась: загрузка уровня (ESTEX + сборка
* комнаты) не даёт насосу долить блок вовремя. Ждать здесь дешевле, чем
* слушать заикание.
*
* Пропуск сцены обрывает и музыку: игрок нажал клавишу, чтобы идти
* дальше, а не чтобы досидеть до конца трека. */
static void pre_cut_finish(uint8_t aborted)
{
if (!aborted) {
intro_skip_t tail;
intro_skip_begin(&tail);
while (pop_music_busy() && !intro_skip_requested(&tail))
pop_wait_edge();
}
pop_music_free();
pop_sfx_pause();
intro_restore_game_palette();
}
void pop_pre_cutscene_show(pop_pre_cutscene_t scene) __banked
{
const pop_cs_cmd_t *script = 0;
@@ -1584,23 +1621,24 @@ void pop_pre_cutscene_show(pop_pre_cutscene_t scene) __banked
}
}
if (scene == POP_PRE_2_6 || scene == POP_PRE_4 || scene == POP_PRE_12) {
if (pre_room_animated((uint8_t)(scene == POP_PRE_4 ? INTRO_PV_LYING
: INTRO_PV_STAND),
pre_hourglass_state(), 26) >= 0) {
intro_restore_game_palette();
int r = pre_room_animated((uint8_t)(scene == POP_PRE_4 ? INTRO_PV_LYING
: INTRO_PV_STAND),
pre_hourglass_state(), 26);
if (r >= 0) {
pre_cut_finish((uint8_t)(r > 0));
return;
}
/* Неполный HDD: статический fallback сохраняет корректные 130 тиков. */
}
if (scene == POP_PRE_12_SHORT && cut_scene_12_short() == 0) {
intro_restore_game_palette();
pre_cut_finish(cut_aborted);
return;
}
/* Сцены 8/9 — живая анимация мыши и принцессы (cutscene_8/cutscene_9). */
if (scene == POP_PRE_8 || scene == POP_PRE_9) {
int done = (scene == POP_PRE_8) ? cut_scene_8() : cut_scene_9();
if (done == 0) {
intro_restore_game_palette();
pre_cut_finish(cut_aborted);
return;
}
}
@@ -1609,10 +1647,12 @@ void pop_pre_cutscene_show(pop_pre_cutscene_t scene) __banked
else if (scene == POP_PRE_8) script = pre_8;
else if (scene == POP_PRE_9) script = pre_9;
else if (scene == POP_PRE_12_SHORT) script = pre_12_short;
if (!script) return;
if (!script) {
pre_cut_finish(1);
return;
}
intro_run(script);
intro_restore_game_palette();
pre_cut_finish(intro_run(script));
}
void pop_time_expired_show(void) __banked
@@ -1650,7 +1690,7 @@ void pop_ending_show(void) __banked
* начинается с draw_rect(&screen_rect, color_0_black). Иначе загрузка
* ресурсов сцены идёт поверх ЖИВОЙ комнаты уровня 14, и она «мелькает»
* перед встречей. */
pop_pal_apply(4);
pop_pal_apply(POP_PAL_DIM_BLACK);
/* Встреча с принцессой — живая сцена (end_sequence_anim). Дальше по
* скрипту идёт только текст HAIL, поэтому статический сценарий ниже
* начинается с него; при нехватке ресурсов отыгрывается целиком. */