Катсцены: тайминги, часы и «время вышло» — по оригиналу; тумблер музыки
ТАЙМИНГИ. Треки заставок между уровнями длиннее самих сцен (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:
@@ -1206,6 +1206,38 @@ N навык M» — отдельная надобность, если пона
|
||||
|
||||
Фазы З1..З5 и список «проверить артефактом до кодинга» — в доке.
|
||||
|
||||
### <a id="pal-arc"></a>PAL-ARC. Палитры — в архив своей группы (полишинг)
|
||||
|
||||
Файлов палитр в игре шесть: `KID\kid.pal` (1024 Б), `PV\story.pal`,
|
||||
`PV\pv.pal` (по 1024), `BG\pop_tile.pal`, `BG\pal_tile.pal` (по 128),
|
||||
`TITLE\title.pal` (64). Всё, что лежит в `poc/res/tiles/` (36 файлов) и
|
||||
`bg/pop_bg.pal`, на образ не попадает и кодом не читается — осадок ранних
|
||||
экспериментов, его стоит просто убрать.
|
||||
|
||||
**Что предлагается.** Класть палитру в архив ТОЙ ЖЕ группы, где её
|
||||
графика: `kid.pal` → `kid.arc`, оба `*_tile.pal` → `bg.arc`, `title.pal` →
|
||||
`title.arc`, `story`/`pv.pal` → `pv.arc`. Тогда правило одно на все
|
||||
ресурсы («файл лежит в архиве своей группы»), и уходят последние шесть
|
||||
открытий: `pop_bg_load` берёт тайлсет и палитру за одно открытие, заставка
|
||||
так же. Отдельно ценно, что `kid.pal` перестанет читаться своим `open` на
|
||||
КАЖДОЙ загрузке уровня (её перезаливает `pop_pal_game_load`).
|
||||
|
||||
**Чего это стоит.** Элемент архива читается только в EMM-страницу,
|
||||
поэтому нужна маленькая функция «палитра из страницы»: `bank_read` по 256
|
||||
байт в буфер и четыре `gfx_pal_load`. Буфер уже есть — `pop_pal_buf` по
|
||||
0x4000.
|
||||
|
||||
**Что теряется.** Возможность подменить палитру файлом рядом с игрой
|
||||
(`pal_file_load` сейчас откатывается на `a:\kid.pal`). Решить явно:
|
||||
либо файл остаётся приоритетным и архив — запасным путём, либо fallback
|
||||
выбрасывается.
|
||||
|
||||
**Почему НЕ отдельная папка `PAL\` и не один общий атлас палитр.** Папка
|
||||
не уменьшает числа файлов и рвёт связь «палитра ↔ её графика». Общий
|
||||
атлас экономил бы открытия только при загрузке всех палитр разом, а они
|
||||
нужны в разное время: `kid.pal` — каждому уровню, заставочные — только
|
||||
заставке.
|
||||
|
||||
### <a id="mus-won"></a>MUS-WON. Финальная тема — кольцевой стриминг с диска
|
||||
|
||||
Трек 56 (`won`, 115 с) — это 1,2 МБ, 78 страниц EMM. Ни в `POP_MUS_PAGES`
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
#define POP_CFG_VERSION 1
|
||||
#define POP_CFG_HEADER_SIZE 7
|
||||
/* Ранний MS0-файл имел семь полей. Его продолжаем принимать: отсутствующие
|
||||
* поля берут исторический дефолт (лимит времени ON, отладочная полоса ON),
|
||||
* а новая запись содержит все девять. */
|
||||
* поля берут исторический дефолт (лимит времени ON, отладочная полоса ON,
|
||||
* музыка ON), а новая запись содержит все десять. */
|
||||
#define POP_CFG_LEGACY_PAYLOAD_SIZE 7
|
||||
#define POP_CFG_TIMELIMIT_PAYLOAD_SIZE 8
|
||||
#define POP_CFG_PAYLOAD_SIZE 9
|
||||
#define POP_CFG_DEBUGBAR_PAYLOAD_SIZE 9
|
||||
#define POP_CFG_PAYLOAD_SIZE 10
|
||||
#define POP_CFG_TAIL_SIZE 2
|
||||
/* CFG остаётся маленьким. Верхняя граница не даёт ручному мусору занять
|
||||
* стек W2, но оставляет запас для будущих полей версии 1. */
|
||||
|
||||
@@ -420,6 +420,25 @@ static void cd_bbox(uint8_t who, uint8_t p, int *x0, int *y0, int *x1, int *y1)
|
||||
}
|
||||
}
|
||||
|
||||
/* ЗАБЫТЬ, ЧТО НАРИСОВАНО, на ОБЕИХ страницах.
|
||||
*
|
||||
* Звать после всего, что переписало экран мимо кадрового конвейера: меню,
|
||||
* QuickLoad, переворот картинки. Иначе снимок слота продолжает утверждать,
|
||||
* что нужный кадр персонажа на странице уже есть, и cd_quiet честно
|
||||
* пропускает отрисовку — персонаж исчезает до первого своего движения
|
||||
* (поймано пользователем на выходе из меню; рядом с факелом бага не видно
|
||||
* ровно потому, что анимация фона метит прямоугольник Кида и пропуск
|
||||
* снимается сама собой). */
|
||||
void pop_cd_forget(void) __banked
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < POP_CH_N; i++) {
|
||||
pop_cd[i].valid[0] = pop_cd[i].valid[1] = 0;
|
||||
pop_cd[i].ovalid[0] = pop_cd[i].ovalid[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* «Тихий» слот: спрайт этой страницы цел (heal его не трогал), фон в его
|
||||
* прямоугольнике не трогали, и все входы отрисовки те же, что в момент,
|
||||
* когда этот кадр на эту страницу и рисовали. valid[p] здесь ключевой: он
|
||||
|
||||
@@ -79,6 +79,11 @@ typedef struct {
|
||||
|
||||
extern pop_cdraw_t pop_cd[POP_CH_N];
|
||||
|
||||
/* Забыть, что нарисовано на обеих страницах: после меню, QuickLoad и любой
|
||||
* другой перерисовки экрана мимо кадрового конвейера. Без этого пропуск
|
||||
* перерисовки «тихого» персонажа оставляет его невидимым. */
|
||||
void pop_cd_forget(void) __banked;
|
||||
|
||||
/* Стереть прошлый кадр персонажа (спрайт + накладные) на ТЕКУЩЕЙ странице. */
|
||||
void pop_char_heal(uint8_t who) __banked;
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ extern uint8_t pop_cheats; /* 0 = выключены, иначе включ
|
||||
/* Ctrl+S (PS/2 set 2: S = 0x1B) — вкл/выкл звук; порт Ctrl+S из SDLPoP
|
||||
* (seg000:657). С Ctrl, а не голая S: у оригинала клавиша именно такая. */
|
||||
#define KBD_SOUND_TOGGLE 0x1B
|
||||
/* Ctrl+M — тумблер МУЗЫКИ, парный к Ctrl+S. Отдельный потому, что музыка
|
||||
* у нас поток с диска: её выключают по другим причинам, чем эффекты. */
|
||||
#define KBD_MUSIC_TOGGLE 0x3A /* M (PS/2 set 2) */
|
||||
#define KBD_CHEAT_NEXTLVL 0x4B /* Shift+L (PS/2 set 2) — следующий уровень */
|
||||
#define KBD_CHEAT_XDEC 0x54 /* [ (PS/2 set 2) — сдвинуть Кида на 1 px влево */
|
||||
#define KBD_CHEAT_XINC 0x5B /* ] (PS/2 set 2) — сдвинуть Кида на 1 px вправо */
|
||||
|
||||
@@ -44,6 +44,7 @@ static uint8_t cfg_checksum(const uint8_t *data, uint16_t size)
|
||||
void pop_settings_defaults(void) __banked
|
||||
{
|
||||
pop_settings.sound_enabled = 1;
|
||||
pop_settings.music_enabled = 1;
|
||||
pop_settings.speed_mode = POP_SPEED_NORMAL;
|
||||
pop_settings.gameplay_profile = POP_PROFILE_VANILLA;
|
||||
pop_settings.cheats_enabled = 1; /* пока roomtest — dev-сборка */
|
||||
@@ -66,6 +67,9 @@ void pop_settings_apply(uint8_t sound_ready) __banked
|
||||
* Файл должен отражать фактически применимое состояние. */
|
||||
pop_settings.sound_enabled = pop_snd_want;
|
||||
}
|
||||
/* Музыке набор эффектов не нужен: она читается с диска отдельно,
|
||||
* поэтому применяется всегда, а не по sound_ready. */
|
||||
pop_mus_want = pop_settings.music_enabled;
|
||||
}
|
||||
|
||||
void pop_cfg_encode(uint8_t *buf)
|
||||
@@ -84,6 +88,7 @@ void pop_cfg_encode(uint8_t *buf)
|
||||
buf[13] = (uint8_t)(pop_settings.enhancement_flags >> 8);
|
||||
buf[14] = pop_settings.time_limit_enabled;
|
||||
buf[15] = pop_settings.show_debug_bar;
|
||||
buf[16] = pop_settings.music_enabled;
|
||||
sum = cfg_checksum(buf + POP_CFG_HEADER_SIZE, POP_CFG_PAYLOAD_SIZE);
|
||||
buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE] = sum;
|
||||
buf[POP_CFG_HEADER_SIZE + POP_CFG_PAYLOAD_SIZE + 1] = (uint8_t)~sum;
|
||||
@@ -113,7 +118,8 @@ uint8_t pop_cfg_decode(const uint8_t *buf, uint16_t size)
|
||||
buf[9] > POP_PROFILE_ENHANCED || buf[10] > 1 || buf[11] > 1)
|
||||
return 0;
|
||||
if (payload_size >= POP_CFG_TIMELIMIT_PAYLOAD_SIZE && buf[14] > 1) return 0;
|
||||
if (payload_size >= POP_CFG_PAYLOAD_SIZE && buf[15] > 1) return 0;
|
||||
if (payload_size >= POP_CFG_DEBUGBAR_PAYLOAD_SIZE && buf[15] > 1) return 0;
|
||||
if (payload_size >= POP_CFG_PAYLOAD_SIZE && buf[16] > 1) return 0;
|
||||
flags = (uint16_t)(buf[12] | ((uint16_t)buf[13] << 8));
|
||||
pop_settings.sound_enabled = buf[7];
|
||||
pop_settings.speed_mode = buf[8];
|
||||
@@ -124,12 +130,14 @@ uint8_t pop_cfg_decode(const uint8_t *buf, uint16_t size)
|
||||
pop_settings.enhancement_flags = flags;
|
||||
/* Старый POP.CFG создавался до настройки таймера (и до тумблера
|
||||
* отладочной полосы): отсутствующее поле означает исторический дефолт —
|
||||
* ограничение 60 минут и включённую полосу, а не случайное значение в
|
||||
* памяти. */
|
||||
* ограничение 60 минут, включённую полосу и музыку, а не случайное
|
||||
* значение в памяти. */
|
||||
pop_settings.time_limit_enabled =
|
||||
(payload_size >= POP_CFG_TIMELIMIT_PAYLOAD_SIZE) ? buf[14] : 1;
|
||||
pop_settings.show_debug_bar =
|
||||
(payload_size >= POP_CFG_PAYLOAD_SIZE) ? buf[15] : 1;
|
||||
(payload_size >= POP_CFG_DEBUGBAR_PAYLOAD_SIZE) ? buf[15] : 1;
|
||||
pop_settings.music_enabled =
|
||||
(payload_size >= POP_CFG_PAYLOAD_SIZE) ? buf[16] : 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -87,13 +87,13 @@
|
||||
9 * SET_STEP + SEL_BOT_DY + 1)
|
||||
#define SET_TITLE_BASE (UI_CENTER_TOP(SET_BLOCK_H) + POP_FONT_BIG_ASCENT)
|
||||
#define SET_HEAD1 (SET_TITLE_BASE + SET_HEAD_GAP)
|
||||
#define SET_HEAD2 (SET_HEAD1 + 3 * SET_STEP + SET_HEAD_GAP)
|
||||
#define SET_HEAD3 (SET_HEAD2 + 4 * SET_STEP + SET_HEAD_GAP)
|
||||
#define SET_HEAD2 (SET_HEAD1 + 4 * SET_STEP + SET_HEAD_GAP)
|
||||
#define SET_HEAD3 (SET_HEAD2 + 3 * SET_STEP + SET_HEAD_GAP)
|
||||
|
||||
/* CONTROLS: число строк зависит от того, включены ли читы, поэтому верх
|
||||
* блока считается в рантайме. */
|
||||
#define CTRL_TITLE_GAP 20
|
||||
#define CTRL_N_BASE 6 /* общие клавиши */
|
||||
#define CTRL_N_BASE 7 /* общие клавиши */
|
||||
#define CTRL_N_CHEAT 5 /* дополнительные строки читов */
|
||||
#define CTRL_COL_KEY 45
|
||||
#define CTRL_COL_DESC 145
|
||||
@@ -267,9 +267,9 @@ static void menu_draw(uint8_t sel)
|
||||
* значение помещается (на голом литерале 218 — молчит). */
|
||||
static const uint8_t settings_y[SETTINGS_N_ITEMS] = {
|
||||
(uint8_t)(SET_HEAD1 + 1 * SET_STEP), (uint8_t)(SET_HEAD1 + 2 * SET_STEP),
|
||||
(uint8_t)(SET_HEAD1 + 3 * SET_STEP),
|
||||
(uint8_t)(SET_HEAD1 + 3 * SET_STEP), (uint8_t)(SET_HEAD1 + 4 * SET_STEP),
|
||||
(uint8_t)(SET_HEAD2 + 1 * SET_STEP), (uint8_t)(SET_HEAD2 + 2 * SET_STEP),
|
||||
(uint8_t)(SET_HEAD2 + 3 * SET_STEP), (uint8_t)(SET_HEAD2 + 4 * SET_STEP),
|
||||
(uint8_t)(SET_HEAD2 + 3 * SET_STEP),
|
||||
(uint8_t)(SET_HEAD3 + 1 * SET_STEP), (uint8_t)(SET_HEAD3 + 2 * SET_STEP)
|
||||
};
|
||||
|
||||
@@ -293,15 +293,15 @@ static void settings_draw(uint8_t sel)
|
||||
"SETTINGS");
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, 60, SET_HEAD1, "GENERAL");
|
||||
settings_line(0, "SOUND", pop_settings.sound_enabled ? "ON" : "OFF", sel == 0);
|
||||
settings_line(1, "DEBUG BAR",
|
||||
pop_settings.show_debug_bar ? "ON" : "OFF", sel == 1);
|
||||
settings_line(2, "RESTORE DEFAULTS...", 0, sel == 2);
|
||||
settings_line(1, "MUSIC", pop_settings.music_enabled ? "ON" : "OFF", sel == 1);
|
||||
settings_line(2, "DEBUG BAR",
|
||||
pop_settings.show_debug_bar ? "ON" : "OFF", sel == 2);
|
||||
settings_line(3, "RESTORE DEFAULTS...", 0, sel == 3);
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, 60, SET_HEAD2, "GAMEPLAY");
|
||||
settings_line(3, "TIME LIMIT",
|
||||
settings_line(4, "TIME LIMIT",
|
||||
pop_settings.time_limit_enabled ? "60 MIN" : "UNLIMITED",
|
||||
sel == 3);
|
||||
settings_line(4, "SPEED", speed_text[pop_settings.speed_mode], sel == 4);
|
||||
settings_line(5, "GAMEPLAY PROFILE", "VANILLA", sel == 5);
|
||||
sel == 4);
|
||||
settings_line(5, "SPEED", speed_text[pop_settings.speed_mode], sel == 5);
|
||||
settings_line(6, "CHEATS", pop_settings.cheats_enabled ? "ON" : "OFF", sel == 6);
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, 60, SET_HEAD3, "CONTROLS");
|
||||
settings_line(7, "SHOW KEY BINDINGS", 0, sel == 7);
|
||||
@@ -313,12 +313,13 @@ static void settings_draw(uint8_t sel)
|
||||
/* Две колонки «клавиша — что делает». Первые CTRL_N_BASE строк общие, за
|
||||
* ними идут читовые: их показываем, только когда читы включены. */
|
||||
static const char * const ctrl_key[CTRL_N_BASE + CTRL_N_CHEAT] = {
|
||||
"ARROWS", "SHIFT", "ESC", "F6 / F9", "CTRL+S", "P",
|
||||
"ARROWS", "SHIFT", "ESC", "F6 / F9", "CTRL+S", "CTRL+M", "P",
|
||||
"K", "I", "SHIFT+L", "U", "F7 / F8"
|
||||
};
|
||||
|
||||
static const char * const ctrl_desc[CTRL_N_BASE + CTRL_N_CHEAT] = {
|
||||
"MOVE / JUMP", "ACTION", "PAUSE MENU", "SAVE / LOAD", "SOUND", "SPEED",
|
||||
"MOVE / JUMP", "ACTION", "PAUSE MENU", "SAVE / LOAD", "SOUND", "MUSIC",
|
||||
"SPEED",
|
||||
"KILL GUARD", "IMMORTAL", "NEXT LEVEL", "FLIP SCREEN", "TIME - / +"
|
||||
};
|
||||
|
||||
@@ -521,25 +522,25 @@ static uint8_t settings_process(void)
|
||||
/* Left/right принадлежат только числовой настройке скорости;
|
||||
* остальные строки меняются Enter либо Space, чтобы стрелкой не
|
||||
* открыть случайно Controls и не сбросить параметры. */
|
||||
if ((ent && !ent_prev) || (sel == 4 && delta)) {
|
||||
if ((ent && !ent_prev) || (sel == 5 && delta)) {
|
||||
if (sel == 0) {
|
||||
pop_settings.sound_enabled ^= 1; change = 1;
|
||||
} else if (sel == 1) {
|
||||
pop_settings.show_debug_bar ^= 1; change = 1;
|
||||
pop_settings.music_enabled ^= 1; change = 1;
|
||||
} else if (sel == 2) {
|
||||
pop_settings.show_debug_bar ^= 1; change = 1;
|
||||
} else if (sel == 3) {
|
||||
if (ui_confirm("RESTORE DEFAULTS?")) {
|
||||
pop_settings_defaults(); change = 1;
|
||||
}
|
||||
redraw = 1; sound = MENU_SND_SELECT;
|
||||
} else if (sel == 3) {
|
||||
pop_settings.time_limit_enabled ^= 1; change = 1;
|
||||
} else if (sel == 4) {
|
||||
pop_settings.time_limit_enabled ^= 1; change = 1;
|
||||
} else if (sel == 5) {
|
||||
uint8_t speed = pop_settings.speed_mode;
|
||||
if (delta < 0) speed = (uint8_t)(speed ? speed - 1 : POP_SPEED_FASTEST);
|
||||
else speed = (uint8_t)(speed == POP_SPEED_FASTEST ? POP_SPEED_NORMAL : speed + 1);
|
||||
pop_settings.speed_mode = speed; change = 1;
|
||||
} else if (sel == 5) {
|
||||
ui_notice("VANILLA ONLY"); redraw = 1; sound = MENU_SND_SELECT;
|
||||
} else if (sel == 6) {
|
||||
pop_settings.cheats_enabled ^= 1; change = 1;
|
||||
} else if (sel == 7) {
|
||||
|
||||
@@ -219,6 +219,13 @@ void pop_music_service(void) __banked
|
||||
/* Один вызов на кадр из ЛЮБОГО цикла — игрового, интро, катсцены.
|
||||
* Заявку кладёт pop_sfx_play (звук без оцифровки = музыка оригинала),
|
||||
* а диск трогаем только здесь. */
|
||||
if (!pop_mus_want) {
|
||||
/* Выключили на ходу: оборвать поток и замолчать. Эффекты при этом
|
||||
* продолжают звучать — у них свой тумблер. */
|
||||
if (pop_mus_req) pop_mus_req = 0;
|
||||
if (ld_busy || pop_mus_left) pop_music_free();
|
||||
return;
|
||||
}
|
||||
if (pop_mus_req) {
|
||||
uint8_t id = pop_mus_req;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
uint8_t sound_enabled; /* 0/1 — зеркало звука (Ctrl+S / pop_sfx) */
|
||||
uint8_t music_enabled; /* 0/1 — зеркало музыки (Ctrl+M / pop_mus_want) */
|
||||
uint8_t speed_mode; /* POP_SPEED_* (pop_pace.h), 0..2 */
|
||||
uint8_t gameplay_profile; /* pop_gameplay_profile_t */
|
||||
uint8_t cheats_enabled; /* 0/1 — активны ли чит-клавиши */
|
||||
|
||||
@@ -59,6 +59,12 @@ volatile uint8_t pop_snd_tick;
|
||||
* (нулевого трека не бывает). Ставит pop_sfx_play, снимает главный цикл. */
|
||||
uint8_t pop_mus_req;
|
||||
|
||||
/* ХОЧЕТ ЛИ ПОЛЬЗОВАТЕЛЬ МУЗЫКУ (Settings / Ctrl+M) — отдельно от звука.
|
||||
* Оригинал такого разделения не знал (там один тумблер на всё), но у нас
|
||||
* музыка это ПОТОК С ДИСКА: кому мешает подкачка или просто не нужен
|
||||
* саундтрек, гасит её, не теряя эффектов. */
|
||||
uint8_t pop_mus_want;
|
||||
|
||||
static volatile uint8_t sfx_pg; /* индекс в pop_snd_page[] */
|
||||
static volatile uint16_t sfx_ptr; /* адрес в окне W0: 0x0000..0x3FFF */
|
||||
static volatile uint16_t sfx_left; /* сколько байт осталось (кратно 128) */
|
||||
@@ -237,7 +243,7 @@ void pop_sfx_play(uint8_t id)
|
||||
* В конкурсе эффектов такой id не участвует: он не глушит живой звук
|
||||
* (порт проверки `if (NULL == sound_pointers[id]) return;`). */
|
||||
if (pop_snd_tbl[id].len == 0) {
|
||||
pop_mus_req = id;
|
||||
if (pop_mus_want) pop_mus_req = id;
|
||||
return;
|
||||
}
|
||||
if (!snd_next1 || snd_prio[id] <= snd_prio[snd_next1 - 1])
|
||||
|
||||
@@ -65,6 +65,11 @@ extern volatile uint8_t pop_snd_tick;
|
||||
* недопустимо. 0 — заявки нет. Снимать обязан тот, кто обработал. */
|
||||
extern uint8_t pop_mus_req;
|
||||
|
||||
/* Тумблер МУЗЫКИ (Settings / Ctrl+M), независимый от тумблера эффектов:
|
||||
* музыка у нас — поток с диска, и выключают её по другим причинам.
|
||||
* 0 — заявки не принимаются, играющий трек глушится (pop_music_service). */
|
||||
extern uint8_t pop_mus_want;
|
||||
|
||||
/* Заявить эффект с номером id (0..56 — нумерация оригинала, см.
|
||||
* sound_plan.md). НЕ запускает его немедленно и НЕ перебивает играющий:
|
||||
* это порт play_sound (seg000:12C5), который лишь НОМИНИРУЕТ кандидата на
|
||||
|
||||
@@ -73,6 +73,8 @@ static const char * const status_text[POP_MSG_N] = {
|
||||
"NO QUICKLOAD",
|
||||
"SOUND ON",
|
||||
"SOUND OFF",
|
||||
"MUSIC ON",
|
||||
"MUSIC OFF",
|
||||
"", /* LEVEL N — собирается с номером */
|
||||
"", /* N MINUTES LEFT */
|
||||
"", /* N SECONDS LEFT */
|
||||
@@ -113,10 +115,15 @@ static uint8_t st_arg; /* номер уровня для POP_MSG_LEVEL
|
||||
#define R_VAL_ROOM 84 /* зона 71..83 */
|
||||
#define X_LBL_SPEED 84 /* ", Speed:" 84..116 */
|
||||
#define R_VAL_SPEED 152 /* зона 117..151 */
|
||||
#define X_LBL_SOUND 152 /* ", Sound:" 152..184 */
|
||||
#define R_VAL_SOUND 202 /* зона 185..201 */
|
||||
#define X_LBL_IMM 202 /* ", Immortal" 202..243 */
|
||||
#define R_VAL_IMM 253 /* зона 244..252: цифре нужен зазор в пробел */
|
||||
/* Три тумблера подряд — звук, музыка, бессмертие. Подписи сокращены до
|
||||
* буквы с двоеточием: полные слова («Sound», «Immortal») занимали столько,
|
||||
* что на музыку места уже не оставалось, а смысл и так читается. */
|
||||
#define X_LBL_SOUND 152 /* ", S:" 152..168 */
|
||||
#define R_VAL_SOUND 185 /* зона 169..184 */
|
||||
#define X_LBL_MUS 185 /* ", M:" 185..201 */
|
||||
#define R_VAL_MUS 218 /* зона 202..217 */
|
||||
#define X_LBL_IMM 218 /* ", I:" 218..234 */
|
||||
#define R_VAL_IMM 245 /* зона 235..244: цифре нужен зазор в пробел */
|
||||
/* Оставшееся время ##:## — отдельным полем у правого края, подписи не
|
||||
* требует: формат читается сам. */
|
||||
#define R_VAL_TIME 310 /* зона 284..309 */
|
||||
@@ -125,7 +132,7 @@ static uint8_t st_arg; /* номер уровня для POP_MSG_LEVEL
|
||||
/* Ширина зоны значения — под самый длинный вариант плюс воздух. */
|
||||
#define W_VAL_NUM 13 /* две цифры */
|
||||
#define W_VAL_SPEED 35 /* FASTEST */
|
||||
#define W_VAL_SOUND 17 /* OFF */
|
||||
#define W_VAL_SOUND 16 /* OFF */
|
||||
#define W_VAL_IMM 9 /* одна цифра */
|
||||
|
||||
enum { /* биты маски «что перерисовать» */
|
||||
@@ -135,12 +142,13 @@ enum { /* биты маски «что перерис
|
||||
DBG_F_SOUND = 0x08,
|
||||
DBG_F_IMM = 0x10,
|
||||
DBG_F_TIME = 0x20,
|
||||
DBG_F_ALL = 0x3F /* + подписи: см. dbg_labels */
|
||||
DBG_F_MUS = 0x40,
|
||||
DBG_F_ALL = 0x7F /* + подписи: см. dbg_labels */
|
||||
};
|
||||
|
||||
static const char * const speed_text[] = { "NORMAL", "FAST", "FASTEST" };
|
||||
|
||||
static uint8_t dbg_lvl, dbg_room, dbg_spd, dbg_snd, dbg_imm;
|
||||
static uint8_t dbg_lvl, dbg_room, dbg_spd, dbg_snd, dbg_mus, dbg_imm;
|
||||
static uint8_t dbg_tmin, dbg_tsec; /* показанное время ##:## */
|
||||
static uint8_t dbg_phase; /* кадры до пересчёта секунд */
|
||||
static uint8_t dbg_dirty[2]; /* маска по странице; DBG_F_ALL => и подписи */
|
||||
@@ -308,7 +316,11 @@ void pop_status_show_now(uint8_t msg) __banked
|
||||
saved_bank = gfx_get_bank();
|
||||
gfx_set_draw_page(visible);
|
||||
status_draw();
|
||||
st_dirty = (uint8_t)(st_dirty & ~(1 << visible));
|
||||
/* Бит видимой страницы НЕ снимаем. За время дисковой операции ту же
|
||||
* страницу успевает переписать восстановление комнаты, и снятый бит
|
||||
* означал бы «здесь уже нарисовано» — надпись оставалась ровно на одной
|
||||
* странице и мигала через кадр (поймано на QUICKLOAD). Лишняя
|
||||
* перерисовка строки стоит дешевле мигания. */
|
||||
gfx_set_draw_page(saved_page);
|
||||
gfx_set_bank(saved_bank);
|
||||
}
|
||||
@@ -334,8 +346,9 @@ static void dbg_labels(void)
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_LEVEL, DBG_BASE, "Level");
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_ROOM, DBG_BASE, ", Room");
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_SPEED, DBG_BASE, ", Speed:");
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_SOUND, DBG_BASE, ", Sound:");
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_IMM, DBG_BASE, ", Immortal");
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_SOUND, DBG_BASE, ", S:");
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_MUS, DBG_BASE, ", M:");
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, X_LBL_IMM, DBG_BASE, ", I:");
|
||||
pop_text_unmap();
|
||||
}
|
||||
|
||||
@@ -359,6 +372,8 @@ static void dbg_draw(uint8_t mask)
|
||||
speed_text[dbg_spd < 3 ? dbg_spd : 0]);
|
||||
if (mask & DBG_F_SOUND)
|
||||
dbg_value(R_VAL_SOUND, W_VAL_SOUND, dbg_snd ? "ON" : "OFF");
|
||||
if (mask & DBG_F_MUS)
|
||||
dbg_value(R_VAL_MUS, W_VAL_SOUND, dbg_mus ? "ON" : "OFF");
|
||||
if (mask & DBG_F_IMM) {
|
||||
buf[0] = (char)('0' + (dbg_imm < 10 ? dbg_imm : 9));
|
||||
buf[1] = 0;
|
||||
@@ -377,7 +392,7 @@ static void dbg_draw(uint8_t mask)
|
||||
static void dbg_scan(uint8_t room)
|
||||
{
|
||||
uint8_t prev_lvl = dbg_lvl, prev_room = dbg_room, prev_spd = dbg_spd;
|
||||
uint8_t prev_snd = dbg_snd, prev_imm = dbg_imm;
|
||||
uint8_t prev_snd = dbg_snd, prev_mus = dbg_mus, prev_imm = dbg_imm;
|
||||
uint8_t prev_tmin = dbg_tmin, prev_tsec = dbg_tsec;
|
||||
uint8_t mask = 0;
|
||||
|
||||
@@ -402,11 +417,13 @@ static void dbg_scan(uint8_t room)
|
||||
dbg_room = room;
|
||||
dbg_spd = pop_speed_mode;
|
||||
dbg_snd = pop_snd_want;
|
||||
dbg_mus = pop_mus_want;
|
||||
dbg_imm = pop_immortal;
|
||||
if (dbg_lvl != prev_lvl) mask |= DBG_F_LEVEL;
|
||||
if (dbg_room != prev_room) mask |= DBG_F_ROOM;
|
||||
if (dbg_spd != prev_spd) mask |= DBG_F_SPEED;
|
||||
if (dbg_snd != prev_snd) mask |= DBG_F_SOUND;
|
||||
if (dbg_mus != prev_mus) mask |= DBG_F_MUS;
|
||||
if (dbg_imm != prev_imm) mask |= DBG_F_IMM;
|
||||
if (dbg_tmin != prev_tmin || dbg_tsec != prev_tsec) mask |= DBG_F_TIME;
|
||||
if (mask) {
|
||||
|
||||
@@ -26,6 +26,8 @@ enum {
|
||||
POP_MSG_NO_QUICKLOAD, /* F9 нечего грузить / битый слот */
|
||||
POP_MSG_SOUND_ON, /* Ctrl+S */
|
||||
POP_MSG_SOUND_OFF,
|
||||
POP_MSG_MUSIC_ON, /* Ctrl+M */
|
||||
POP_MSG_MUSIC_OFF,
|
||||
POP_MSG_LEVEL, /* «LEVEL N» — строка собирается с номером */
|
||||
POP_MSG_MINUTES, /* «N MINUTES LEFT» */
|
||||
POP_MSG_SECONDS, /* «N SECONDS LEFT» / «1 SECOND LEFT» */
|
||||
|
||||
@@ -444,8 +444,16 @@ int main(void)
|
||||
quit = 1;
|
||||
break;
|
||||
}
|
||||
if (menu_opened && !pop_app_dispatch(POP_APP_EV_RESUME)) {
|
||||
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
|
||||
|
||||
@@ -344,7 +344,7 @@ uint8_t pop_dbg_roomnav(uint8_t dir) __banked
|
||||
* Флаги «фронта» стали статиками модуля — раньше они были локалями main.
|
||||
* Смысл прежний: одно нажатие = одно срабатывание.
|
||||
*/
|
||||
static uint8_t cheat_prev, immo_prev, sound_prev, nlvl_prev;
|
||||
static uint8_t cheat_prev, immo_prev, sound_prev, music_prev, nlvl_prev;
|
||||
static uint8_t upsd_prev, xdec_prev, xinc_prev, cfg_prev;
|
||||
static uint8_t time_sub_prev, time_add_prev;
|
||||
|
||||
@@ -379,6 +379,8 @@ void pop_cheat_tick(void) __banked
|
||||
uint8_t i = kbd_raw_down(KBD_CHEAT_IMMO);
|
||||
uint8_t s = (uint8_t)(kbd_raw_down(KBD_SOUND_TOGGLE) &&
|
||||
(kbd_raw_down(KBD_LCTRL) || kbd_raw_down(KBD_RCTRL)));
|
||||
uint8_t m = (uint8_t)(kbd_raw_down(KBD_MUSIC_TOGGLE) &&
|
||||
(kbd_raw_down(KBD_LCTRL) || kbd_raw_down(KBD_RCTRL)));
|
||||
uint8_t nl = (uint8_t)(kbd_raw_down(KBD_CHEAT_NEXTLVL) &&
|
||||
(kbd_raw_down(KBD_LSHIFT) || kbd_raw_down(KBD_RSHIFT)));
|
||||
uint8_t up = kbd_raw_down(KBD_CHEAT_UPSIDE);
|
||||
@@ -401,6 +403,12 @@ void pop_cheat_tick(void) __banked
|
||||
pop_status_msg = (uint8_t)(pop_snd_want ? POP_MSG_SOUND_ON
|
||||
: POP_MSG_SOUND_OFF);
|
||||
}
|
||||
if (m && !music_prev) {
|
||||
pop_mus_want ^= 1; /* не под pop_cheats */
|
||||
pop_settings.music_enabled = pop_mus_want;
|
||||
pop_status_msg = (uint8_t)(pop_mus_want ? POP_MSG_MUSIC_ON
|
||||
: POP_MSG_MUSIC_OFF);
|
||||
}
|
||||
if (pop_cheats && nl && !nlvl_prev && pop_current_level < POP_LAST_LEVEL)
|
||||
pop_next_level = (uint8_t)(pop_current_level + 1);
|
||||
/* U — переворот экрана (seg000:0793 — чит и в оригинале).
|
||||
@@ -417,6 +425,7 @@ void pop_cheat_tick(void) __banked
|
||||
cheat_prev = k;
|
||||
immo_prev = i;
|
||||
sound_prev = s;
|
||||
music_prev = m;
|
||||
nlvl_prev = nl;
|
||||
upsd_prev = up;
|
||||
xdec_prev = xd;
|
||||
@@ -617,12 +626,13 @@ void pop_qsave_restore_room(uint8_t room) __banked
|
||||
/* Обе страницы теперь содержат только свежий фон. Старые габариты
|
||||
* персонажей относятся к кадру до QuickLoad и не должны запускать heal
|
||||
* поверх восстановленной комнаты. */
|
||||
pop_cd[POP_CH_KID].valid[0] = pop_cd[POP_CH_KID].valid[1] = 0;
|
||||
pop_cd[POP_CH_KID].ovalid[0] = pop_cd[POP_CH_KID].ovalid[1] = 0;
|
||||
pop_cd[POP_CH_OPP].valid[0] = pop_cd[POP_CH_OPP].valid[1] = 0;
|
||||
pop_cd[POP_CH_OPP].ovalid[0] = pop_cd[POP_CH_OPP].ovalid[1] = 0;
|
||||
pop_cd_forget();
|
||||
pop_cd_init();
|
||||
pop_hp_invalidate();
|
||||
/* Полоса состояния тоже переписана заново — «QUICKLOAD» обязан лечь
|
||||
* поверх свежего фона на ОБЕИХ страницах, иначе он виден через кадр и
|
||||
* мигает. */
|
||||
pop_status_invalidate();
|
||||
}
|
||||
|
||||
/* start_level (seg003:0055) + do_startpos (seg003:01A3) + set_start_pos
|
||||
@@ -843,10 +853,7 @@ void pop_flip_screen(void) __banked
|
||||
/* Всё, что рисуется поверх фона, надо положить заново: прошлые
|
||||
* прямоугольники персонажей относятся к СТАРОЙ (неперевёрнутой)
|
||||
* картинке, и heal по ним стёр бы не то место. */
|
||||
pop_cd[POP_CH_KID].valid[0] = pop_cd[POP_CH_KID].valid[1] = 0;
|
||||
pop_cd[POP_CH_KID].ovalid[0] = pop_cd[POP_CH_KID].ovalid[1] = 0;
|
||||
pop_cd[POP_CH_OPP].valid[0] = pop_cd[POP_CH_OPP].valid[1] = 0;
|
||||
pop_cd[POP_CH_OPP].ovalid[0] = pop_cd[POP_CH_OPP].ovalid[1] = 0;
|
||||
pop_cd_forget();
|
||||
POP_CD_TOUCH_ALL(); /* фон трогали целиком */
|
||||
pop_hp_invalidate(); /* полосу HP перерисовать */
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
/* Runtime-владельцы нужны только для линковки pop_settings_apply(); codec
|
||||
* их не вызывает. */
|
||||
uint8_t pop_speed_mode, pop_cheats, pop_snd_want;
|
||||
uint8_t pop_speed_mode, pop_cheats, pop_snd_want, pop_mus_want;
|
||||
void pop_sfx_toggle(void) __banked { pop_snd_want = (uint8_t)!pop_snd_want; }
|
||||
|
||||
static void set_all(uint8_t snd, uint8_t spd, uint8_t prof, uint8_t cheats,
|
||||
|
||||
Reference in New Issue
Block a user