Разгрузка 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:
@@ -30,6 +30,16 @@
|
||||
#include "pop_pace.h" /* pop_speed_mode — режим скорости в отладочной метке */
|
||||
#include "pop_settings.h" /* pop_settings_defaults — стартовый профиль VANILLA */
|
||||
#include "pop_cfg.h" /* pop_cfg_load — POP.CFG поверх дефолтов */
|
||||
#include "pop_flow.h" /* таблица level-flow: какая сцена перед уровнем */
|
||||
#include "pop_intro.h" /* pop_pre_cutscene_show — сцена перед уровнем */
|
||||
#include "pop_app.h" /* pop_app_dispatch — состояния приложения */
|
||||
#include "pop_pal.h" /* pop_pal_apply — гашение перед загрузкой */
|
||||
#include "pop_timer.h" /* pop_timer_minutes — выбор сцены «времени мало» */
|
||||
#include "pop_ui.h" /* POP_PAL_DIM_BLACK */
|
||||
#include "pop_menu.h" /* пауза-меню: коды выбора */
|
||||
#include "pop_qsave.h" /* QuickSave/QuickLoad по F6/F9 */
|
||||
#include "pop_status.h" /* pop_status_invalidate после меню */
|
||||
#include <kbd_raw.h>
|
||||
#include "pop_status.h" /* статус-строка и отладочная полоса — банк 9 */
|
||||
#include "pop_menu.h" /* pop_menu_init/free — атлас UI в bank 9 */
|
||||
#include "pop_ui.h" /* ранняя чёрная палитра + snapshot стартового экрана */
|
||||
@@ -73,6 +83,134 @@ static void find_start_level_door(uint8_t room)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* МЕНЮ, F10 И QUICKSAVE/LOAD — граница кадра, но не горячий путь.
|
||||
*
|
||||
* Всё это события «раз в партию», а в главном цикле они занимали полсотни
|
||||
* строк резидента W1. Здесь же держим и edge-состояния клавиш: снаружи они
|
||||
* никому не нужны.
|
||||
*
|
||||
* restart_level — «пользователь выбрал Restart Level» (главный цикл сделает
|
||||
* это сам, после вычисления back-страницы, как респавн);
|
||||
* dead_reset — «QuickLoad удался, счётчик кадров смерти сбросить».
|
||||
* Возврат: 0 — продолжаем кадр, 1 — выйти из игрового цикла (Restart Game),
|
||||
* 2 — выйти и завершить работу (Quit), -1 — сбой диспетчера. */
|
||||
int8_t pop_frame_ui(uint8_t *restart_level, uint8_t *dead_reset) __banked
|
||||
{
|
||||
static uint8_t esc_prev, qs_save_prev, qs_load_prev;
|
||||
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)) return -1;
|
||||
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) *restart_level = 1;
|
||||
else if (mrc == POP_MENU_RESTART_GAME)
|
||||
return (int8_t)(pop_app_dispatch(POP_APP_EV_RESTART_INTRO) ? 1 : -1);
|
||||
else if (mrc == POP_MENU_QUIT)
|
||||
return (int8_t)(pop_app_dispatch(POP_APP_EV_QUIT) ? 2 : -1);
|
||||
if (menu_opened) {
|
||||
/* Меню переписало обе страницы целиком. Снимки слотов об этом не
|
||||
* знают и продолжают считать, что персонаж на странице уже
|
||||
* нарисован, — стоящий Кид оставался бы невидимым до первого своего
|
||||
* движения. */
|
||||
pop_cd_forget();
|
||||
pop_status_invalidate();
|
||||
if (!pop_app_dispatch(POP_APP_EV_RESUME)) return -1;
|
||||
}
|
||||
/* F10 — отладочный немедленный выход (раньше был на Esc, но Esc теперь
|
||||
* открывает меню; уйдёт вместе с отладкой). */
|
||||
if (kbd_raw_down(KBD_QUIT))
|
||||
return (int8_t)(pop_app_dispatch(POP_APP_EV_QUIT) ? 2 : -1);
|
||||
/* F6/F9 только ставят запрос; файловая операция выполняется здесь, на
|
||||
* границе кадров — вне окон Char и до heal/tick/draw. */
|
||||
{
|
||||
uint8_t s = kbd_raw_down(KBD_QUICKSAVE);
|
||||
uint8_t l = kbd_raw_down(KBD_QUICKLOAD);
|
||||
|
||||
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;
|
||||
if (pop_qsave_process() > 0) *dead_reset = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* СМЕНА УРОВНЯ — целиком здесь, а не в главном цикле.
|
||||
*
|
||||
* Событие редкое (раз в несколько минут игры), а кода на нём была без
|
||||
* малого сотня строк — и все они лежали в _main, то есть в резиденте W1,
|
||||
* где у нас счёт на десятки байт. Наружу остаётся одно условие и один
|
||||
* вызов.
|
||||
*
|
||||
* back — страница, в которую главный цикл рисует кадр: после загрузки
|
||||
* уровня её надо вернуть себе (level_switch показывает свою).
|
||||
* Возврат: 0 — уровень сменён, -1 — сбой, главный цикл завершает работу. */
|
||||
int8_t pop_level_change(uint8_t back) __banked
|
||||
{
|
||||
uint8_t reveal_pending = 0;
|
||||
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))
|
||||
return -1;
|
||||
pop_pre_cutscene_show(pre);
|
||||
if (!pop_app_dispatch(POP_APP_EV_CUTSCENE_FINISHED))
|
||||
return -1;
|
||||
/* Катсцена оставляет экран чёрным. Новый уровень будет
|
||||
* открыт одним применением его готовой палитры. */
|
||||
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(POP_PAL_DIM_BLACK);
|
||||
pop_wait_edge();
|
||||
reveal_pending = 1;
|
||||
if (!pop_app_dispatch(POP_APP_EV_LEVEL_FINISHED))
|
||||
return -1;
|
||||
}
|
||||
if (pop_level_switch() != 0 ||
|
||||
!pop_app_dispatch(POP_APP_EV_LEVEL_READY))
|
||||
return -1;
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* start_level (seg003:0055) + do_startpos (seg003:01A3) + set_start_pos
|
||||
* (seg003:028A).
|
||||
*
|
||||
@@ -937,7 +1075,7 @@ int pop_boot(void) __banked
|
||||
* понадобится как источник обычного fade-in. Для build-info экран
|
||||
* остаётся штатный яркий игровой фон. */
|
||||
if (!pop_settings.show_build_info) {
|
||||
pop_pal_apply(4);
|
||||
pop_pal_apply(POP_PAL_DIM_BLACK);
|
||||
}
|
||||
|
||||
/* Стартовая комната до title НЕ НУЖНА: LEVEL_LOAD всё равно заново
|
||||
@@ -1091,7 +1229,7 @@ int pop_new_game_load(void) __banked
|
||||
/* Title мог завершиться на яркой title-палитре. Сначала погасить её,
|
||||
* затем восстановить полный game snapshot; level_switch собирает обе
|
||||
* страницы уже невидимо, и первый игровой кадр проявляется. */
|
||||
pop_pal_apply(4);
|
||||
pop_pal_apply(POP_PAL_DIM_BLACK);
|
||||
gfx_set_draw_page(0);
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
pop_screen_fill_game();
|
||||
@@ -1099,7 +1237,7 @@ int pop_new_game_load(void) __banked
|
||||
pop_screen_fill_game();
|
||||
gfx_set_visible_page(1);
|
||||
pop_pal_game_load();
|
||||
pop_pal_apply(4);
|
||||
pop_pal_apply(POP_PAL_DIM_BLACK);
|
||||
if (pop_level_switch() != 0) return -1;
|
||||
/* Первый уровень появляется сразу, без fade, но только после того как
|
||||
* обе страницы и его собственная палитра полностью готовы. */
|
||||
|
||||
Reference in New Issue
Block a user