Разгрузка W1: смена уровня и служебный слой — в банк 8

_main занимал 3658 байт резидента, и добрая половина этого — события «раз
в партию». Вынесены целиком:

- pop_level_change — смена уровня (катсцена, гашение, загрузка, палитра,
  звук): 61 строка, наружу осталось одно условие и вызов;
- pop_frame_ui — меню, F10 и QuickSave/Load: 58 строк, плюс туда же уехали
  edge-состояния клавиш и сами коды клавиш (снаружи они больше не нужны).

_CODE 24102 -> 23626, запас W2 вырос с 86 до 559 байт.

Заодно три правки по замечаниям:

- затемнение фона под меню считалось числом «2», а шкала ступеней выросла
  с 4 до 32 — фон почти не гас. Теперь доля от шкалы (POP_PAL_DIM_BLACK/2);
  тем же прошлись по остаткам pop_pal_apply(4);
- в катсценах персонажи появлялись только после fade in: первый кадр
  рисуется ПОД чёрной палитрой, и картинка проявляется целиком, вместе с
  актёрами (cut_scene_8/9/12_short/ending, pre_room_animated,
  intro_pv_animated);
- отладочная полоса по умолчанию выключена.

Host-тесты: 15 наборов. Проверено в MAME: меню открывается и закрывается,
фон под ним затемнён, полосы нет.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-26 11:42:56 +03:00
parent f66fd0e1b6
commit 0b15ed7b8f
7 changed files with 251 additions and 134 deletions
+17 -119
View File
@@ -128,11 +128,6 @@ static void kbd_idle(void)
#define SEQ_STAND 2
#define ROOM_1 1
#define LINK_DOWN 3 /* сторона links: 0=L,1=R,2=U,3=D */
#define KBD_QUICKSAVE 0x0B /* F6, PS/2 set 2 */
#define KBD_QUICKLOAD 0x01 /* F9, PS/2 set 2 */
#define KBD_MENU 0x76 /* Esc — открыть pause menu (edge) */
#define KBD_QUIT 0x09 /* F10 — отладочный немедленный выход (был на Esc,
* пока меню не готово рисовать — см. план MS4) */
/* ОТЛАДОЧНЫЙ ОБХОД КОМНАТ: '+'/'-' (цифровой блок либо '='/'-' основного
@@ -417,63 +412,20 @@ int main(void)
* QuickSave/QuickLoad идут тем же механизмом, что F6/F9 (запрос
* подхватит блок ниже в этой же границе кадра), рестарты исполняются
* после вычисления back-страницы (как респавн), QUIT рвёт цикл. */
/* Меню, F10 и QuickSave/Load — целиком в банке 8 (pop_frame_ui).
* Всё это события «раз в партию», а кода на них было полсотни строк
* в резиденте; наружу остаются флаги, которые нужны самому циклу. */
if (!is_demo) {
uint8_t e = kbd_raw_down(KBD_MENU);
uint8_t menu_opened;
int8_t mrc = POP_MENU_NONE;
if (e && !esc_prev) pop_menu_request = 1;
esc_prev = e;
menu_opened = pop_menu_request;
if (menu_opened) {
if (!pop_app_dispatch(POP_APP_EV_PAUSE)) {
failed = 1; quit = 1; break;
}
mrc = pop_menu_process();
}
if (mrc == POP_MENU_QUICKSAVE) pop_qsave_request_save();
else if (mrc == POP_MENU_QUICKLOAD) pop_qsave_request_load();
else if (mrc == POP_MENU_QUICKLOAD_BACKUP)
pop_qsave_request_load_backup();
else if (mrc == POP_MENU_RESTART_LEVEL) menu_restart = 1;
else if (mrc == POP_MENU_RESTART_GAME) {
if (!pop_app_dispatch(POP_APP_EV_RESTART_INTRO)) failed = 1;
uint8_t want_restart = 0, dead_reset = 0;
int8_t urc = pop_frame_ui(&want_restart, &dead_reset);
if (want_restart) menu_restart = 1;
if (dead_reset) dead_frames = 0;
if (urc) {
if (urc < 0) failed = 1;
if (urc != 1) quit = 1; /* 1 — restart game, без quit */
break;
}
else if (mrc == POP_MENU_QUIT) {
if (!pop_app_dispatch(POP_APP_EV_QUIT)) failed = 1;
quit = 1;
break;
}
if (menu_opened) {
/* Меню переписало обе страницы целиком. Снимки слотов об
* этом не знают и продолжают считать, что персонаж на
* странице уже нарисован, — стоящий Кид оставался бы
* невидимым до первого своего движения. */
pop_cd_forget();
pop_status_invalidate();
if (!pop_app_dispatch(POP_APP_EV_RESUME)) {
failed = 1; quit = 1; break;
}
}
}
/* F10 — отладочный немедленный выход (раньше был на Esc, но Esc
* теперь открывает меню; уйдёт вместе с отладкой). */
if (!is_demo && kbd_raw_down(KBD_QUIT)) {
if (!pop_app_dispatch(POP_APP_EV_QUIT)) failed = 1;
quit = 1;
break;
}
/* F6/F9 только ставят запрос; файловая операция выполняется здесь,
* на границе кадров — вне окон Char и до heal/tick/draw. */
if (!is_demo) {
uint8_t s = kbd_raw_down(KBD_QUICKSAVE);
uint8_t l = kbd_raw_down(KBD_QUICKLOAD);
int8_t qrc;
if (s && !qs_save_prev) pop_qsave_request_save();
if (l && !qs_load_prev) pop_qsave_request_load();
qs_save_prev = s; qs_load_prev = l;
qrc = pop_qsave_process();
if (qrc > 0) dead_frames = 0;
}
if (frozen) {
PROF(0); /* чёрный: ждём vsync */
@@ -761,66 +713,12 @@ int main(void)
* или доиграли запись — показать текущий кадр и затем вернуться в
* title. В частности, не вызываем level_switch(0->1). */
if (is_demo && (kid_room == 24 || pop_kid_dead)) demo_finished = 1;
if (!is_demo && pop_next_level != pop_current_level) {
pop_pre_cutscene_t pre = pop_flow_pre_cutscene(pop_next_level,
pop_timer_minutes);
/* Смена уровня целиком — в банке (pop_level_switch): грузится
* файл, спрайты соперника и тайлсет. До 2/4/6/8/9/12 таблица
* flow запускает отдельное состояние CUTSCENE, как play_level()
* SDLPoP: следующий уровень не трогаем до завершения/skip. */
if (pre != POP_PRE_NONE) {
if (!pop_app_dispatch(POP_APP_EV_CUTSCENE_STARTED)) {
failed = 1;
quit = 1;
break;
}
pop_pre_cutscene_show(pre);
if (!pop_app_dispatch(POP_APP_EV_CUTSCENE_FINISHED)) {
failed = 1;
quit = 1;
break;
}
/* Катсцена оставляет экран чёрным. Новый уровень будет
* открыт одним применением его готовой палитры. */
reveal_pending = 1;
} else {
/* CBL глушим ДО первой же операции с палитрой. Палитра идёт
* через BIOS ($A4) с запрещёнными прерываниями: насос не
* успевает долить блок, железо крутит хвост своего буфера —
* на слух скрежет. Раньше pop_sfx_pause() звался только
* внутри pop_level_switch, то есть УЖЕ ПОСЛЕ гашения, и
* переход без катсцены (в т.ч. телепорт Shift+L) скрежетал
* почти всегда — поймано пользователем 2026-08-25. Ветка с
* катсценой свободна от этого: pop_pre_cutscene_show глушит
* звук первым действием. */
pop_sfx_pause();
/* Без fade: старый кадр гасим сразу и обязательно даём
* показать один чёрный растр ДО диска/перерисовки. */
pop_pal_apply(4);
pop_wait_edge();
reveal_pending = 1;
if (!pop_app_dispatch(POP_APP_EV_LEVEL_FINISHED)) {
failed = 1;
quit = 1;
break;
}
}
if (pop_level_switch() != 0 ||
!pop_app_dispatch(POP_APP_EV_LEVEL_READY)) {
failed = 1;
quit = 1;
break;
}
if (reveal_pending) {
/* Обе страницы и snapshot уже принадлежат новому уровню:
* включить его корректную палитру одним переходом. */
pop_pal_apply(0);
reveal_pending = 0;
}
/* level_switch оставляет CBL закрытым: palette/fade идут через
* BIOS и при открытом буфере давали скрежет на первом кадре. */
(void)pop_sfx_start();
gfx_set_draw_page(back);
/* Смена уровня целиком уехала в банк 8 (pop_level_change): это
* событие раз в несколько минут, а кода на нём было под сотню строк
* в самом горячем месте программы — резидент W1 у нас в дефиците. */
if (!is_demo && pop_next_level != pop_current_level &&
pop_level_change(back) != 0) {
failed = 1; quit = 1; break;
}
if (!is_demo && pop_kid_dead) { /* demo после смерти завершается, без respawn */
dead_frames++;