Катсцены: тайминги, часы и «время вышло» — по оригиналу; тумблер музыки
ТАЙМИНГИ. Треки заставок между уровнями длиннее самих сцен (27 — 10,7 с против 2,6 с картинки), и оригинал их не обрывает: load_intro гасит экран и возвращает управление, а звук продолжает играть поверх загрузки уровня и первых секунд игры (seg001:690). Мы глушили CBL на выходе — музыка обрывалась на полуслове. Больше не глушим. «ВРЕМЯ ВЫШЛО». Оригинал (seg001:04D3) не зовёт ни одного init_*: на экране пустая комната и ОСТАНОВИВШИЕСЯ часы (state 7, струйки песка нет). Мы показывали готовую композицию с принцессой и полными часами — противоположное по смыслу. Теперь это живая сцена без персонажей, 102 кадра, и трек 36 дослушивается под чёрным экраном, как в оригинале. ПЕСОЧНЫЕ ЧАСЫ. Сцена «времени мало» (cutscene_12) шла статической композицией, куда упаковщик запёк res953 — ПОЛНЫЕ часы. Отсюда и наблюдение «чем меньше времени, тем полнее часы»: картинка была одна и та же независимо от таймера. Сделана живой (cut_scene_12_short: принцесса стоит правее, через два кадра оборачивается), часы берутся от pre_hourglass_state. Заодно: при state 7 струйка песка не рисуется. МУЗЫКА — СВОЙ ТУМБЛЕР. Settings -> MUSIC, Ctrl+M и статус в debug bar. Флаг pop_mus_want отдельный от звукового: музыка у нас поток с диска, и выключают её по другим причинам, чем эффекты; выключение обрывает текущий трек, эффекты продолжают звучать. Строка-заглушка GAMEPLAY PROFILE («VANILLA ONLY») уступила место MUSIC — на десятую строку экрана не хватает. Подписи тумблеров в debug bar сокращены до S:/M:/I:. БАГИ. После меню персонаж оставался невидимым до первого движения: снимок слота продолжал утверждать, что кадр на странице уже нарисован. Общая pop_cd_forget() теперь зовётся после меню, QuickLoad и переворота экрана. «QUICKLOAD» мигал через кадр — pop_status_show_now снимал бит видимой страницы, а её успевало переписать восстановление комнаты. Host-тесты: 15 наборов. Проверено в MAME: debug bar, выход из меню. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -892,13 +892,19 @@ static uint8_t pre_hourglass_state(void)
|
||||
* Статична только поза Princess; draw_princess_room_bg оригинала
|
||||
* каждый логический кадр заново обновляет факелы, звёзды, корпус часов и
|
||||
* одну из трёх фаз сыплющегося песка. */
|
||||
static int pre_princess_animated(pop_pre_cutscene_t scene)
|
||||
/* Живая комната принцессы БЕЗ актёрской механики: готовая композиция (или
|
||||
* чистый фон, если picture == PV_ROOM_BARE), поверх неё пламя факелов,
|
||||
* звёзды в окне и песочные часы заданного состояния. На этом держатся и
|
||||
* сцены перед уровнями, и экран «время вышло» — разница только в картинке,
|
||||
* состоянии часов и длине.
|
||||
*
|
||||
* hg_state: 0 — часов нет, 7 — время вышло (пустые, без струйки песка). */
|
||||
#define PV_ROOM_BARE 0xFF
|
||||
|
||||
static int pre_room_animated(uint8_t picture, uint8_t state, uint8_t frames)
|
||||
{
|
||||
atlas_t hourglass, torch;
|
||||
gfx_rect_t full;
|
||||
uint8_t picture = (uint8_t)(scene == POP_PRE_4 ?
|
||||
INTRO_PV_LYING : INTRO_PV_STAND);
|
||||
uint8_t state = pre_hourglass_state();
|
||||
uint8_t torch_left = 1, torch_right = 6, sand = 0, frame;
|
||||
uint8_t star_phase[6] = { 1, 1, 1, 1, 1, 1 };
|
||||
intro_skip_t skip;
|
||||
@@ -908,8 +914,12 @@ static int pre_princess_animated(pop_pre_cutscene_t scene)
|
||||
intro_commit_palette();
|
||||
/* Обе shadow-страницы содержат чистую статическую сцену. Динамика ниже
|
||||
* рисуется GFX_BANK_SPRITE и потому не оседает в этом источнике. */
|
||||
if (intro_draw_page(picture, 0) != 0 ||
|
||||
intro_draw_page(picture, 1) != 0) return -1;
|
||||
if (picture == PV_ROOM_BARE) {
|
||||
if (intro_pv_draw_base() != 0) return -1;
|
||||
} else if (intro_draw_page(picture, 0) != 0 ||
|
||||
intro_draw_page(picture, 1) != 0) {
|
||||
return -1;
|
||||
}
|
||||
{
|
||||
uint8_t tbl[ARC_PV_COUNT * POP_ARC_REC];
|
||||
pop_arc_t arc;
|
||||
@@ -927,7 +937,7 @@ static int pre_princess_animated(pop_pre_cutscene_t scene)
|
||||
result = 0;
|
||||
full.x = 0; full.y = 0; full.w = GFX_WIDTH; full.h = GFX_HEIGHT;
|
||||
intro_skip_begin(&skip);
|
||||
for (frame = 0; frame < 26; frame++) {
|
||||
for (frame = 0; frame < frames; frame++) {
|
||||
uint8_t back, hold, star;
|
||||
|
||||
if (intro_skip_requested(&skip)) { result = 1; break; }
|
||||
@@ -938,8 +948,13 @@ static int pre_princess_animated(pop_pre_cutscene_t scene)
|
||||
pv_blit_floor(&torch, torch_left, 93, 116);
|
||||
pv_blit_floor(&torch, torch_right, 211, 116);
|
||||
pv_draw_stars(star_phase);
|
||||
pv_blit_floor(&hourglass, state, 152, 168);
|
||||
pv_blit_floor(&hourglass, (uint8_t)(8 + sand), 160, 164);
|
||||
if (state) {
|
||||
pv_blit_floor(&hourglass, state, 152, 168);
|
||||
/* Струйка песка есть, пока часы идут: при state 7 время уже
|
||||
* вышло (draw_hourglass, sandflow = -1). */
|
||||
if (state < 7)
|
||||
pv_blit_floor(&hourglass, (uint8_t)(8 + sand), 160, 164);
|
||||
}
|
||||
pv_blit_floor(&hourglass, 0, 240, 167); /* передняя колонна */
|
||||
gfx_wait_vsync();
|
||||
gfx_set_visible_page(back);
|
||||
@@ -1042,7 +1057,7 @@ static void intro_restore_game_palette(void)
|
||||
* 17..29 в a2. Номер кадра при этом остаётся исходным image-id. */
|
||||
#define CUT_PV2_SPLIT 17
|
||||
/* Один логический кадр катсцены = 5 vsync, как у остальных PV-сцен
|
||||
* (cutscene_frame_time SDLPoP; см. pre_princess_animated). */
|
||||
* (cutscene_frame_time SDLPoP; см. pre_room_animated). */
|
||||
#define CUT_FRAME_VSYNC 5
|
||||
|
||||
static atlas_t cut_pv1, cut_pv2a, cut_pv2b, cut_hg, cut_torch;
|
||||
@@ -1237,10 +1252,13 @@ static uint8_t cut_run(uint16_t frames, uint8_t kid_visible)
|
||||
pv_draw_stars(cut_star_phase);
|
||||
/* hourglass_state == 0 — часов в комнате нет вовсе (reset_cutscene
|
||||
* SDLPoP ставит state=0 и sandflow=-1). Так идёт финальная встреча
|
||||
* с принцессой: время уже не считается, это победа. */
|
||||
* с принцессой: время уже не считается, это победа.
|
||||
* state 7 — время ВЫШЛО: часы стоят пустыми, и струйки песка в них
|
||||
* тоже нет (draw_hourglass: `if (hourglass_state >= 7) return;`). */
|
||||
if (cut_hg_state) {
|
||||
pv_blit_floor(&cut_hg, cut_hg_state, 152, 168);
|
||||
pv_blit_floor(&cut_hg, (uint8_t)(8 + cut_sand), 160, 164);
|
||||
if (cut_hg_state < 7)
|
||||
pv_blit_floor(&cut_hg, (uint8_t)(8 + cut_sand), 160, 164);
|
||||
}
|
||||
cut_blit_princess(&Guard, &pr_fr);
|
||||
if (kid_visible) cut_blit_kid(&Kid, &kid_fr);
|
||||
@@ -1342,6 +1360,27 @@ static int cut_scene_9(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* cutscene_12 (seg001:0525), ветка «времени мало»: принцесса стоит правее
|
||||
* обычного, через два кадра оборачивается — и всё. Живой её делает та же
|
||||
* машинка, что сцены 8/9: часы здесь обязаны показывать ОСТАВШЕЕСЯ время,
|
||||
* а не запечённую в композицию картинку. */
|
||||
static int cut_scene_12_short(void)
|
||||
{
|
||||
uint8_t aborted;
|
||||
|
||||
if (cut_begin(pre_hourglass_state()) != 0) return -1;
|
||||
/* init_princess_x156 (seg001:0230) = init_princess + x = 156. */
|
||||
cut_shad_init(156, 166, 94);
|
||||
pop_pal_fade_in(INTRO_FADE);
|
||||
aborted = cut_run(2, 0);
|
||||
if (!aborted) {
|
||||
cut_shad_seq(98); /* princess turn around [PV1] */
|
||||
aborted = cut_run(24, 0);
|
||||
}
|
||||
cut_end(aborted);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* end_sequence_anim (seg001:041C): Кид вбегает к принцессе, она
|
||||
* поворачивается и обнимает его; следом появляется мышь. */
|
||||
static int cut_ending(void)
|
||||
@@ -1522,7 +1561,15 @@ void pop_pre_cutscene_show(pop_pre_cutscene_t scene) __banked
|
||||
const pop_cs_cmd_t *script = 0;
|
||||
|
||||
pop_sfx_pause();
|
||||
/* Музыка сцены — как в оригинале: 27 для 2/4/6/12 (seg001:401),
|
||||
/* МУЗЫКА ПЕРЕЖИВАЕТ СЦЕНУ. Треки заставок между уровнями длиннее самих
|
||||
* сцен (27 — 10,7 с против 2,6 с картинки), и оригинал их не обрывает:
|
||||
* load_intro просто гасит экран и возвращает управление, а звук
|
||||
* продолжает играть уже поверх загрузки уровня и первых секунд игры
|
||||
* (seg001:690). Мы глушили CBL на выходе — музыка обрывалась на
|
||||
* полуслове. Теперь не глушим: доиграет сама, а следующая загрузка
|
||||
* трека всё равно освободит слот.
|
||||
*
|
||||
* Какой именно трек — как в оригинале: 27 для 2/4/6/12 (seg001:401),
|
||||
* 35 для 8/9 (seg001:298), 40 для короткой «времени мало» (seg001:388).
|
||||
* Экран в этот момент чёрный, поэтому читаем трек целиком: пауза на
|
||||
* диск здесь никому не мешает. */
|
||||
@@ -1537,20 +1584,22 @@ 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_princess_animated(scene) >= 0) {
|
||||
pop_music_free();
|
||||
pop_sfx_pause();
|
||||
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();
|
||||
return;
|
||||
}
|
||||
/* Неполный HDD: статический fallback сохраняет корректные 130 тиков. */
|
||||
}
|
||||
if (scene == POP_PRE_12_SHORT && cut_scene_12_short() == 0) {
|
||||
intro_restore_game_palette();
|
||||
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) {
|
||||
pop_music_free();
|
||||
pop_sfx_pause();
|
||||
intro_restore_game_palette();
|
||||
return;
|
||||
}
|
||||
@@ -1560,27 +1609,35 @@ 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) {
|
||||
pop_music_free();
|
||||
pop_sfx_pause();
|
||||
return;
|
||||
}
|
||||
if (!script) return;
|
||||
|
||||
intro_run(script);
|
||||
pop_music_free();
|
||||
pop_sfx_pause();
|
||||
intro_restore_game_palette();
|
||||
}
|
||||
|
||||
void pop_time_expired_show(void) __banked
|
||||
{
|
||||
pop_sfx_pause();
|
||||
/* time_expired (seg001:365) начинается с play_sound(sound_36). */
|
||||
/* time_expired (seg001:04D3) начинается с play_sound(sound_36). */
|
||||
if (pop_music_load(36) == 0) {
|
||||
(void)pop_sfx_start();
|
||||
pop_music_play();
|
||||
}
|
||||
intro_run(time_expired_script);
|
||||
/* ПРИНЦЕССЫ ЗДЕСЬ НЕТ. Оригинал не зовёт ни одного init_*: экран —
|
||||
* это пустая комната и ОСТАНОВИВШИЕСЯ часы (state 7, струйки песка
|
||||
* нет). Мы показывали готовую композицию с принцессой и полными
|
||||
* часами — то есть ровно противоположное по смыслу.
|
||||
* Длина — 2 + 100 кадров cutscene, как в оригинале. */
|
||||
if (pre_room_animated(PV_ROOM_BARE, 7, 102) < 0)
|
||||
intro_run(time_expired_script); /* нет ресурсов — старый путь */
|
||||
/* Оригинал ждёт КОНЦА ТРЕКА уже под чёрным экраном (seg001:374). */
|
||||
{
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user