/* * pop_title.c — оригинальная графическая часть show_title(), банк 9 (FG6). * * Готовые композиции создаёт toolchain/pop_pack_title.py непосредственно из * TITLE/res51..55.png SDLPoP. Каждый 320x200 экран режется на пять лент: * загружаем одну в EMM, блитим непрозрачно в normal bank, освобождаем. Это * сознательно не держит title-ресурсы рядом с игровыми атласами. */ #include #include #include #include #include #include "pop_bg.h" /* POP_YOFF: title занимает 200px по центру 256px */ #include "pop_sfx.h" #include "pop_settings.h" #include "pop_pal.h" #include "pop_title.h" #include "pop_ui.h" #define TITLE_W 320 #define TITLE_H 200 #define TITLE_Y POP_YOFF #define TITLE_PARTS 5 #define TITLE_FADE 4 enum { TITLE_MAIN = 0, TITLE_PRESENTS, TITLE_GAME, TITLE_LOGO }; /* Имена короче 8.3; строка = композиция, столбец = полоса 49/49/49/49/4. */ static const char * const title_part[4][TITLE_PARTS] = { { "TITLE\\t0_0.atl", "TITLE\\t0_1.atl", "TITLE\\t0_2.atl", "TITLE\\t0_3.atl", "TITLE\\t0_4.atl" }, { "TITLE\\t1_0.atl", "TITLE\\t1_1.atl", "TITLE\\t1_2.atl", "TITLE\\t1_3.atl", "TITLE\\t1_4.atl" }, { "TITLE\\t2_0.atl", "TITLE\\t2_1.atl", "TITLE\\t2_2.atl", "TITLE\\t2_3.atl", "TITLE\\t2_4.atl" }, { "TITLE\\t3_0.atl", "TITLE\\t3_1.atl", "TITLE\\t3_2.atl", "TITLE\\t3_3.atl", "TITLE\\t3_4.atl" } }; /* Последовательность и интервалы буквально из show_title(): * main --0x82--> presents --0xCD--> main --0x41--> game * --0x10E--> main --0xEB--> logo+Mechner. * SDLPoP BASE_FPS = 60, поэтому один счёт = один Sprinter vsync. */ static const uint8_t title_scene[] = { TITLE_MAIN, TITLE_PRESENTS, TITLE_MAIN, TITLE_GAME, TITLE_MAIN, TITLE_LOGO }; static const uint16_t title_wait_ticks[] = { 0x82, 0xCD, 0x41, 0x10E, 0xEB, 0xEB }; static uint8_t title_shown; static uint8_t title_skip_pressed(void) { /* Как и в attract-demo, любой удерживаемый raw-код означает «не ждать». * Это заодно не дублирует список клавиш у title, intro и cutscene. */ return kbd_raw_any_down(); } /* Нарисовать одну из готовых 320x200 композиций в скрытую страницу. * Сопоставление W0 охватывает ТОЛЬКО atlas_image/blit: BIOS/vsync сюда * не попадает, значит не конфликтует с EMM и CBL. Возвращает номер * подготовленной страницы; show=0 оставляет её скрытой для palette commit. */ static int title_draw(uint8_t scene, uint8_t show) { uint8_t page = pop_screen_begin(); pop_screen_fill(BLACK); if (pop_screen_blit_atlas_strips(title_part[scene], TITLE_PARTS, 0, TITLE_Y) != 0) return -1; if (show) pop_screen_present(page); return page; } static uint8_t title_wait(uint16_t ticks) { while (ticks--) { if (title_skip_pressed()) return 1; gfx_wait_vsync(); } return 0; } /* Новый источник не становится видимым сам: cold boot уже чёрный, а * последующий fade_in выбирает яркость только после сборки t0. */ static void title_load_palette_dimmed(void) { pop_pal_title_load(); pop_pal_apply(4); } uint8_t pop_title_show(void) __banked { uint8_t step; uint8_t skipped = 0; int prepared; uint8_t cold_start = (uint8_t)(!title_shown && !pop_settings.show_build_info); /* До музыки title дойдём в FG8; игровые SFX не должны звучать поверх * немой заставки и не должны обслуживаться во время дисковых чтений. */ pop_sfx_pause(); if (!cold_start) pop_pal_fade_out(TITLE_FADE); /* Видимая VRAM могла сохраниться от предыдущей программы. Обнулить её * при чёрной палитре, затем собрать t0 СКРЫТО: загрузка title.pal тогда * освещает только индекс 0, а не старый экран или недорисованный title. */ gfx_set_draw_page(gfx_get_visible_page()); gfx_set_bank(GFX_BANK_NORMAL); pop_screen_fill(BLACK); prepared = title_draw(TITLE_MAIN, 0); if (prepared < 0) return 0; title_load_palette_dimmed(); pop_screen_present((uint8_t)prepared); title_shown = 1; for (step = 0; step < sizeof(title_scene); step++) { if (step && title_draw(title_scene[step], 1) < 0) break; if (step == 0) pop_pal_fade_in(TITLE_FADE); if (title_wait(title_wait_ticks[step])) { skipped = 1; break; } } return skipped; }