Таблица рекордов: оба показа оригинала, ввод имени, переходы полосами
pop_hof.c был написан, но никуда не вызывался: после финала автомат уходил мимо него в title, а на титрах экран получался чёрным. Теперь это хвост end_sequence и шаг show_title между credits и attract-demo, как в оригинале. Два корня пустого экрана: - story.pal — 256 записей, и запись 0x3F (цвет глифов шрифта) в ней чёрная: текст рисовался, но был не виден. Генератор кладёт туда золотой 0xB7; - полосовой переход копирует страницу акселератором, а тот читает ОЗУ-копию, куда прозрачный блит текста не пишет. Строки рисуются после перехода, прямо в видимую страницу. Попутно: - s5 в архиве PV — фон таблицы (рамка story + логотип на y=24, HOF_POP); - отдельный индекс палитры под фон текстовой рамки: финал красит его в #800000, титры оставляют #100060 (load_title_images(bgcolor)). Прежний ремап в индекс 9 подменить было нельзя — им нарисована сама титульная картинка; - второй набор глифов в font.atl цветом 0x3E: у SDLPoP шрифт маска и show_hof_text рисует текст дважды разным цветом, у нас цвет запечён в пиксели. Каталог SPA1 держит счётчик в байте, поэтому набор обрезан по '_'; - общий pop_screen_present_ltr(): им теперь пользуются story-переход интро, титры, таблица и титульная картинка финала. Заодно вернулся пропущенный шаг show_title — экран с логотипом и Jordan Mechner между «свадьбой» и титрами (BUGS_CLOSED#cutscene-ltr). Формат POP.HOF — 6 записей, имя до 15 символов, версия 2. Проверено в MAME на сборке LEVEL=14: HAIL → титульная картинка → таблица с полосой ввода и временем справа → ввод имени → титульная картинка; на следующем запуске таблица показывается между credits и demo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3571,3 +3571,25 @@ FIRST_LEVEL`. Рестарт ТОГО ЖЕ уровня (смерть, чекп
|
||||
|
||||
**Проверено в MAME:** после провала на 6-м `pop_current_level` = 7, Kid.room
|
||||
= 16, то есть спецсобытие отработало (старт в 17, переход вниз).
|
||||
|
||||
---
|
||||
|
||||
<a id="cutscene-ltr"></a>
|
||||
## CUTSCENE-LTR. Переходы story должны проявляться слева направо — **ЗАКРЫТ 2026-08-26**
|
||||
|
||||
**Было.** Полосовой переход (`transition_ltr` оригинала) стоял только на
|
||||
«In the absence…»; «свадьба» → титры возникали мгновенно, а промежуточный
|
||||
титульный экран отсутствовал вовсе.
|
||||
|
||||
**Стало.** Переход выделен в общий `pop_screen_present_ltr()` (pop_ui.c) —
|
||||
им пользуются story-переход интро, титры, таблица рекордов и титульная
|
||||
картинка финала. Восстановлен и пропущенный шаг `show_title()`
|
||||
(seg000:2045): между «свадьбой» и титрами показывается композиция с
|
||||
логотипом и Jordan Mechner, тоже полосами, и лишь через 0x78 тиков идут
|
||||
титры.
|
||||
|
||||
**Грабля, которая стоила отладки.** Переход копирует страницу
|
||||
акселератором, а тот читает ОЗУ-копию — прозрачный (спрайтовый) блит туда не
|
||||
пишет. Всё, что нарисовано текстом, обязано рисоваться ПОСЛЕ перехода, иначе
|
||||
переезжает только фон (так вышла пустая таблица рекордов, см.
|
||||
[HOF-ENTRY](TASKS_CLOSED.md#hof-entry)).
|
||||
|
||||
@@ -162,17 +162,47 @@ Rx-overrun не срабатывала. Гашение бита отладчи
|
||||
---
|
||||
|
||||
<a id="cutscene-ltr"></a>
|
||||
## CUTSCENE-LTR. Переходы story через экран TITLE должны проявляться слева направо
|
||||
## CUTSCENE-LTR. Переходы story через экран TITLE — ЗАКРЫТ 2026-08-26
|
||||
|
||||
**Наблюдение пользователя (2026-08-24).** В SDLPoP переходы `Marry Jaffar` →
|
||||
экран с надписью *Prince of Persia* → credits выполнены тем же приёмом, что и
|
||||
`In the absence…`: следующая композиция появляется вертикальными полосами
|
||||
слева направо. Сейчас это последовательность статичных сцен без этого
|
||||
перехода.
|
||||
Разбор и что именно добавилось — [`BUGS_CLOSED.md`](BUGS_CLOSED.md#cutscene-ltr).
|
||||
|
||||
**Очередь.** Не прерывать ради него текущую работу над палитрой и анимацией
|
||||
Princess/Jaffar. Когда будет взят, выделить общий helper полосового перехода
|
||||
и проверить все три story-маршрута, включая пропуск клавишей.
|
||||
---
|
||||
|
||||
<a id="pv-music-stall"></a>
|
||||
## PV-MUSIC-STALL. В сцене с принцессой звучит только первая реплика
|
||||
|
||||
**Наблюдение пользователя (2026-08-26).** «Музыка после открытия двери для
|
||||
входа Джафара пропала» — и дальше сцена идёт в тишине до самого конца.
|
||||
|
||||
**Что измерено** (MAME, чтение переменных `pop_music.c` мостом; адреса
|
||||
считаны от `_pop_mus_page` по `.sym`):
|
||||
|
||||
| момент | `ld_busy` | `mus_ready` | `slot_id[]` |
|
||||
|---|---|---|---|
|
||||
| m50 играет, тик 60 — старт загрузки m53 | 1 | 0 | {55, 50} |
|
||||
| через 28 с | 1 | 0 | {55, 50} |
|
||||
| конец сцены | 0 | 0 | {55, 50} |
|
||||
|
||||
То есть **m53 и m52 вообще не доходят до слотов**: постраничная загрузка
|
||||
(`pop_music_load_begin` + `pop_music_load_step`) за 17 секунд сцены (1040
|
||||
тиков) не успевает прочитать 11 страниц, `mus_ready` так и остаётся нулём, и
|
||||
`pop_music_play()` на своём тике молча ничего не делает. Блок при этом
|
||||
выделяется (`slot_blk[0] != 0`), то есть память и `open` ни при чём —
|
||||
не хватает именно ШАГОВ чтения.
|
||||
|
||||
Начатую загрузку m53 затем отменяет заявка на m52 (`load_begin` начинается с
|
||||
`if (ld_busy) load_abort()`), поэтому не звучит и она.
|
||||
|
||||
**Где искать.** `intro_pv_animated()` (pop_intro.c): `pop_music_load_step()`
|
||||
вызывается ТОЛЬКО внутри цикла ожидания тика насоса, а тот выполняется лишь
|
||||
пока кадр «в плюсе» по времени; после каждой подкачки сцена уходит в долг
|
||||
(«ДОЛГ НЕ ДОГОНЯЕМ»), и следующий шаг случается через несколько кадров.
|
||||
Замер 33 мс на страницу делался на железе — в MAME чтение заметно дороже.
|
||||
|
||||
**Варианты, которые надо взвесить, а не хвататься за первый.** Читать по
|
||||
несколько страниц за ожидание; начинать загрузку раньше (m53 — ещё до сцены,
|
||||
вместе с m50); либо признать, что реплика длиной 16 с не обязана грузиться
|
||||
по странице за кадр, и читать её целиком под чёрным экраном.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ TITLE_DATA := $(TITLE_DIR)/title.pal $(TITLE_ARC)
|
||||
# intro есть ещё чистый фон `b0_*` и шесть <=16K actor-атласов: они остаются
|
||||
# в EMM на время сцены, а не перечитывают полный 320x200 кадр с HDD.
|
||||
PV_DIR := $(CURDIR)/../poc/res/pv
|
||||
PV_STORY := 0 1 2 3 4
|
||||
PV_STORY := 0 1 2 3 4 5
|
||||
PV_SCENES := 0 1 2 3 4 5 6 7
|
||||
PV_PARTS := 0 1 2 3 4
|
||||
PV_ANIM := $(foreach p,$(PV_PARTS),$(PV_DIR)/b0_$(p).atl) \
|
||||
|
||||
@@ -1320,3 +1320,56 @@ make-бита для кода `0x6C`. Скратч-байты — хвост `o
|
||||
**Остаток на следующую сессию:** влетая в комнату, Кид должен уметь
|
||||
зацепиться за край пола, мимо которого пролетает — этой механики у нас,
|
||||
похоже, нет (см. `NEXT_SESSION.md`, п. 1).
|
||||
|
||||
---
|
||||
|
||||
<a id="hof-entry"></a>
|
||||
## HOF-ENTRY. Таблица рекордов — **СДЕЛАНО И ПРОВЕРЕНО В MAME 2026-08-26**
|
||||
|
||||
Порт двух показов оригинала. `pop_hof.c` был написан раньше, но никуда не
|
||||
вызывался: после финала автомат уходил мимо него в title, а на титрах экран
|
||||
получался ЧЁРНЫМ.
|
||||
|
||||
**Где это в оригинале.** `show_title()` (seg000:2060) показывает
|
||||
сохранённую таблицу между credits и attract-demo — 240 тиков, переходом
|
||||
слева направо, без fade. `end_sequence()` (seg001:5F1) после HAIL проявляет
|
||||
титульную картинку (240 тиков), при подходящем результате гасит её, показывает
|
||||
таблицу с золотой полосой ввода, принимает имя, ждёт 120 тиков, снова
|
||||
проявляет титульную картинку и лишь потом дослушивает победную тему
|
||||
(`while (check_sound_playing() && !key_test_quit())`, seg001:637).
|
||||
|
||||
**Два корня, из-за которых экран был пуст.**
|
||||
|
||||
1. `story.pal` — 256 записей, и запись 0x3F (цвет глифов шрифта,
|
||||
`POP_FONT_COLOR`) в ней ЧЁРНАЯ. Текст рисовался, но был не виден.
|
||||
Теперь генератор кладёт в 0x3F золотой 0xB7 оригинала.
|
||||
2. Полосовой переход копирует страницу АКСЕЛЕРАТОРОМ, а тот читает ОЗУ-копию
|
||||
(`gfx_copy_page`: «копируется чистый фон без спрайтов»), куда прозрачный
|
||||
блит текста не пишет. Поэтому на титрах фон переезжал, а строки — нет.
|
||||
Строки теперь рисуются ПОСЛЕ перехода, прямо в видимую страницу.
|
||||
|
||||
**Что появилось попутно.**
|
||||
|
||||
- `s5` в архиве PV — фон таблицы: рамка story + логотип PRINCE OF PERSIA на
|
||||
y=24 (`HOF_POP` оригинала — тот же спрайт res54, что и в титрах).
|
||||
- Отдельный индекс палитры под фон текстовой рамки (`POP_PAL_STORY_BG`, 16).
|
||||
`load_title_images(bgcolor)` красит его в #100060 на титрах и в #800000 в
|
||||
финале; раньше фон был ремаплен в индекс 9, а тот занят самой титульной
|
||||
картинкой (8265 пикселей), и подменить его было нельзя.
|
||||
- Второй набор глифов в `font.atl` цветом `POP_FONT_DARK_COLOR` (0x3E).
|
||||
У SDLPoP шрифт — маска, и `show_hof_text` рисует текст дважды разным
|
||||
цветом; у нас цвет запечён в пиксели, поэтому «другой цвет» = другой набор.
|
||||
Каталог SPA1 держит счётчик картинок в одном байте, поэтому тёмный набор
|
||||
обрезан по '_' (32..95): 190 + 64 = 254 из 255 возможных.
|
||||
- Общий `pop_screen_present_ltr()` в `pop_ui.c` — им теперь пользуются и
|
||||
story-переход интро, и таблица, и титульная картинка финала
|
||||
([CUTSCENE-LTR](BUGS_CLOSED.md#cutscene-ltr)).
|
||||
|
||||
**Формат `POP.HOF`** — 6 записей (как `MAX_HOF_COUNT`), имя до 15 символов,
|
||||
XOR+инверсия в хвосте; версия 2. Проверено на образе: после ввода имени файл
|
||||
128 Б, `PHOF`, запись читается обратно и показывается на титрах.
|
||||
|
||||
**Что проверено в MAME** (сборка `LEVEL=14`, вход в комнату 5 читом обхода
|
||||
комнат): цепочка HAIL → титульная картинка → таблица с полосой ввода и
|
||||
временем справа → ввод имени → титульная картинка; на следующем запуске
|
||||
таблица показывается между credits и demo.
|
||||
|
||||
@@ -1292,15 +1292,14 @@ N навык M» — отдельная надобность, если пона
|
||||
нужны в разное время: `kid.pal` — каждому уровню, заставочные — только
|
||||
заставке.
|
||||
|
||||
### <a id="hof-entry"></a>HOF-ENTRY. Ввод имени в таблицу рекордов
|
||||
### <a id="hof-entry"></a>HOF-ENTRY. Таблица рекордов — ЗАКРЫТА 2026-08-26
|
||||
|
||||
У оригинала после титров победы игрок вписывает своё имя в Hall of Fame
|
||||
(`input_str` в `end_sequence`, seg001:614), и победная тема играет всё это
|
||||
время — она и рассчитана на то, что человек сидит и печатает.
|
||||
|
||||
У нас показ таблицы есть, ввода нет. Из-за этого тема сейчас
|
||||
дослушивается в конце `pop_ending_show`; когда ввод появится, ожидание
|
||||
надо перенести ЗА таблицу — как в оригинале.
|
||||
> **Сделано.** Оба показа оригинала: между credits и attract-demo и хвостом
|
||||
> `end_sequence` (с вводом имени, паузой 120 тиков и возвратом на титульную
|
||||
> картинку). Экран — рамка story с логотипом, время в формате `59:43`,
|
||||
> строки с тенью, мигающий курсор. Ожидание конца победной темы переехало
|
||||
> за таблицу, как в оригинале. Протокол и грабли —
|
||||
> [`TASKS_CLOSED.md`](TASKS_CLOSED.md#hof-entry).
|
||||
|
||||
### <a id="mus-won"></a>MUS-WON. Финальная тема — кольцевой стриминг — ЗАКРЫТА 2026-08-26
|
||||
|
||||
|
||||
@@ -107,13 +107,10 @@ uint8_t pop_app_dispatch(pop_app_event_t event) __banked
|
||||
|
||||
case POP_APP_ENDING:
|
||||
if (event == POP_APP_EV_ENDING_FINISHED) {
|
||||
/* ВРЕМЕННО в обход Hall of Fame: экран HOF написан (pop_hof.c),
|
||||
* но после финала показывает чёрное вместо таблицы, и упереться
|
||||
* в него сразу после победы хуже, чем не показать вовсе.
|
||||
* Решение пользователя 2026-08-24: пока уходим прямо в начало,
|
||||
* HOF чинится отдельно (BUGS_OPEN.md#hof-after-ending).
|
||||
* Из title таблица по-прежнему доступна (pop_hof_show_title). */
|
||||
next = POP_APP_TITLE; accepted = 1;
|
||||
/* Таблица рекордов — ХВОСТ end_sequence, а не отдельный экран
|
||||
* по желанию: она же дослушивает победную тему и гасит экран
|
||||
* перед возвратом в начало (seg001:5F1). */
|
||||
next = POP_APP_HALL_OF_FAME; accepted = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define POP_FONT_COLOR 0x3F
|
||||
#define POP_FONT_DARK_COLOR 0x3E
|
||||
#define POP_FONT_FIRST 32
|
||||
#define POP_FONT_LAST 126
|
||||
#define POP_FONT_SMALL_BASE 0
|
||||
@@ -17,7 +18,12 @@
|
||||
#define POP_FONT_BIG_BASE 95
|
||||
#define POP_FONT_BIG_ASCENT 7
|
||||
#define POP_FONT_BIG_SPACE 1
|
||||
/* Тот же КРУПНЫЙ шрифт цветом POP_FONT_DARK_COLOR: тень и текст на светлом.
|
||||
* Набор обрезан по '_' — им пишется только таблица рекордов. */
|
||||
#define POP_FONT_DARK_BASE 190
|
||||
#define POP_FONT_DARK_FIRST 32
|
||||
#define POP_FONT_DARK_LAST 95
|
||||
#define POP_FONT_SNAPSHOT_OFF 0x3C00
|
||||
#define POP_FONT_ATLAS_SIZE 7667
|
||||
#define POP_FONT_ATLAS_SIZE 10841
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
/*
|
||||
* pop_hof.c — persistent Hall of Fame, банк 9 (FG12).
|
||||
*
|
||||
* Порт двух показов оригинала: между credits и attract-demo (seg000:2060)
|
||||
* и после победы, с вводом имени (end_sequence, seg001:5F1). Экран тот
|
||||
* же самый — рамка story с логотипом PRINCE OF PERSIA (STORY_FRAME +
|
||||
* HOF_POP), поэтому фон приходит готовой композицией из архива PV, а
|
||||
* палитру выбирает вызывающая сцена: обе фазы идут на story.pal, и
|
||||
* отличаются только цветом фона рамки (см. pop_pal_story_load).
|
||||
*
|
||||
* POP.HOF — небольшой самодостаточный формат: magic, version, fixed records
|
||||
* и XOR+инверсия. Поэтому обрыв/мусор на HDD не может поломать старт игры:
|
||||
* decode просто начинает с пустой таблицы. Ввод — raw-клавиатура, как весь
|
||||
@@ -19,19 +26,44 @@
|
||||
#include "pop_timer.h"
|
||||
#include "pop_pal.h"
|
||||
#include "pop_ui.h"
|
||||
#include "pop_cutscene.h" /* POP_T60 — тики оригинала в кадры луча */
|
||||
#include "pop_intro.h" /* фон экрана: рамка story + логотип */
|
||||
#include "pop_title.h" /* титульная картинка после ввода имени */
|
||||
#include "pop_music.h" /* подкачка потоковой темы победы */
|
||||
#include "pop_hof.h"
|
||||
|
||||
#define HOF_COUNT 5
|
||||
#define HOF_NAME_SIZE 10 /* девять букв + '\0' */
|
||||
#define HOF_REC_SIZE 14
|
||||
/* MAX_HOF_COUNT оригинала — шесть строк. */
|
||||
#define HOF_COUNT 6
|
||||
#define HOF_NAME_SIZE 16 /* пятнадцать букв + '\0' */
|
||||
#define HOF_REC_SIZE 20
|
||||
#define HOF_HEADER_SIZE 6
|
||||
#define HOF_SIZE (HOF_HEADER_SIZE + HOF_COUNT * HOF_REC_SIZE + 2)
|
||||
#define HOF_BASE 0x100
|
||||
#define HOF_VERSION 1
|
||||
#define HOF_FADE 4
|
||||
#define HOF_VERSION 2
|
||||
#define HOF_FADE POP_FADE_FRAMES
|
||||
#define HOF_NO_ROW 0xFF
|
||||
|
||||
/* Геометрия hof_rects[] (seg001:0D92) в координатах 320x200: строка i
|
||||
* занимает y = 84 + 14*i .. +12, имя от x=72, время по правому краю 248.
|
||||
* Полоса ввода — та же строка, сдвинутая offset4_rect_add(-4,-1,-40,-1). */
|
||||
#define HOF_ROW_TOP 84
|
||||
#define HOF_ROW_STEP 14
|
||||
#define HOF_ROW_H 12
|
||||
#define HOF_LEFT 72
|
||||
#define HOF_RIGHT 248
|
||||
#define HOF_EDIT_LEFT (HOF_LEFT - 4)
|
||||
#define HOF_EDIT_RIGHT (HOF_RIGHT - 40)
|
||||
/* Крупный шрифт: глиф 7 строк, строка 12 — текст центрируется по вертикали.
|
||||
* baseline = верх строки + 2 + POP_FONT_BIG_ASCENT. */
|
||||
#define HOF_BASELINE(i) (POP_YOFF + HOF_ROW_TOP + (int)(i) * HOF_ROW_STEP + 9)
|
||||
/* Золотой цвет полосы ввода: 0xB7 оригинала = седьмой цвет палитры title,
|
||||
* а у нас story.pal сделана из неё же. */
|
||||
#define HOF_GOLD 7
|
||||
/* Паузы оригинала в тиках 60 Гц: показ таблицы на титрах и пауза после
|
||||
* ввода имени (seg000:2065, seg001:628). */
|
||||
#define HOF_TITLE_TICKS POP_T60(0xF0)
|
||||
#define HOF_AFTER_TICKS POP_T60(120)
|
||||
|
||||
typedef struct {
|
||||
char name[HOF_NAME_SIZE];
|
||||
uint8_t minutes;
|
||||
@@ -136,7 +168,7 @@ static void hof_load(void)
|
||||
for (i = 0; i < HOF_SIZE; i++)
|
||||
hof_buf[i] = ((const uint8_t *)HOF_BASE)[i];
|
||||
gfx_w0_unmap();
|
||||
if (!hof_decode(hof_buf, HOF_SIZE)) hof_clear();
|
||||
if (!hof_decode(hof_buf, (uint8_t)HOF_SIZE)) hof_clear();
|
||||
}
|
||||
mem_free_block(blk);
|
||||
}
|
||||
@@ -209,66 +241,158 @@ static void hof_dec(uint8_t value, char *out)
|
||||
*out = 0;
|
||||
}
|
||||
|
||||
/* «59:43» — те же min-1 : tick/12, что печатает show_hof (seg001:0E94). */
|
||||
static void hof_result_text(const hof_entry_t *entry, char *out)
|
||||
{
|
||||
uint8_t seconds;
|
||||
|
||||
if (!entry->limited) {
|
||||
strcpy(out, "UNLIMITED");
|
||||
return;
|
||||
}
|
||||
hof_dec(entry->minutes, out);
|
||||
/* min == 0 у оригинала означает истёкшее время и печатается как 0:00. */
|
||||
hof_dec(entry->minutes ? (uint8_t)(entry->minutes - 1) : 0, out);
|
||||
while (*out) out++;
|
||||
*out++ = ' ';
|
||||
*out++ = 'M';
|
||||
*out++ = 'I';
|
||||
*out++ = 'N';
|
||||
seconds = entry->minutes ? (uint8_t)(entry->ticks / 12) : 0;
|
||||
*out++ = ':';
|
||||
*out++ = (char)('0' + seconds / 10);
|
||||
*out++ = (char)('0' + seconds % 10);
|
||||
*out = 0;
|
||||
}
|
||||
|
||||
static void hof_draw(uint8_t edit_row)
|
||||
/* show_hof_text(): сначала тень со сдвигом (1,1), затем сам текст. У нас
|
||||
* цвет запечён в глифы, поэтому «другой цвет» — это второй набор
|
||||
* (POP_TEXT_BIG_DARK), а не параметр рисования. */
|
||||
static void hof_text_left(int x, int baseline, const char *s)
|
||||
{
|
||||
uint8_t page, i;
|
||||
|
||||
page = pop_screen_begin();
|
||||
pop_screen_fill(BLACK);
|
||||
gfx_set_bank(GFX_BANK_SPRITE);
|
||||
pop_text_map();
|
||||
pop_text_center_mapped(POP_TEXT_BIG, 160, 58, "HALL OF FAME");
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, 64, 84, "NAME");
|
||||
pop_text_right_mapped(POP_TEXT_SMALL, 255, 84, "TIME");
|
||||
for (i = 0; i < HOF_COUNT; i++) {
|
||||
char rank[3], value[12];
|
||||
const hof_entry_t *entry = &hof_table[i];
|
||||
int y = 106 + (int)i * 20;
|
||||
|
||||
rank[0] = (char)('1' + i); rank[1] = '.'; rank[2] = 0;
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, 46, y, rank);
|
||||
if (entry->name[0])
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, 72, y, entry->name);
|
||||
else if (i == edit_row)
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, 72, y, "_");
|
||||
else
|
||||
pop_text_draw_mapped(POP_TEXT_SMALL, 72, y, "---");
|
||||
if (entry->name[0]) {
|
||||
hof_result_text(entry, value);
|
||||
pop_text_right_mapped(POP_TEXT_SMALL, 255, y, value);
|
||||
}
|
||||
}
|
||||
if (edit_row != HOF_NO_ROW)
|
||||
pop_text_center_mapped(POP_TEXT_SMALL, 160, 228, "TYPE NAME - ENTER");
|
||||
else
|
||||
pop_text_center_mapped(POP_TEXT_SMALL, 160, 228, "PRESS ANY KEY");
|
||||
pop_text_unmap();
|
||||
pop_screen_present(page);
|
||||
pop_text_draw_mapped(POP_TEXT_BIG_DARK, x + 1, baseline + 1, s);
|
||||
pop_text_draw_mapped(POP_TEXT_BIG, x, baseline, s);
|
||||
}
|
||||
|
||||
/* Ожидания на экране рекордов держат ПОТОКОВУЮ музыку: победная тема
|
||||
* (1,2 МБ) играет кольцом страниц, и дочитывать её надо всё время, пока
|
||||
* игрок смотрит таблицу — иначе насос догонит загрузчик и пойдёт по
|
||||
* кругу старыми данными. */
|
||||
static void hof_wait_release(void)
|
||||
static void hof_text_right(int right, int baseline, const char *s)
|
||||
{
|
||||
while (kbd_raw_any_down()) {
|
||||
kbd_raw_sync();
|
||||
pop_text_right_mapped(POP_TEXT_BIG_DARK, right + 1, baseline + 1, s);
|
||||
pop_text_right_mapped(POP_TEXT_BIG, right, baseline, s);
|
||||
}
|
||||
|
||||
/* Золотая полоса под ввод — draw_rect(&rect, bgcolor) оригинала. */
|
||||
static void hof_edit_bar(uint8_t row)
|
||||
{
|
||||
int top = HOF_BASELINE(row) - 10;
|
||||
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
setfillstyle(SOLID_FILL, HOF_GOLD);
|
||||
bar(HOF_EDIT_LEFT, top, HOF_EDIT_RIGHT, top + HOF_ROW_H - 1);
|
||||
}
|
||||
|
||||
/* Строки таблицы в ТЕКУЩУЮ draw-страницу. Отделены от фона потому, что
|
||||
* полосовой переход НЕ ПЕРЕНОСИТ их: gfx_copy_page работает акселератором,
|
||||
* а тот читает ОЗУ-копию страницы, куда прозрачный (спрайтовый) блит не
|
||||
* пишет. Поэтому на титрах строки рисуются уже после перехода, прямо в
|
||||
* видимую страницу. */
|
||||
static void hof_draw_rows(uint8_t edit_row)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
gfx_set_bank(GFX_BANK_SPRITE);
|
||||
pop_text_map();
|
||||
for (i = 0; i < HOF_COUNT; i++) {
|
||||
char value[12];
|
||||
const hof_entry_t *entry = &hof_table[i];
|
||||
int baseline = HOF_BASELINE(i);
|
||||
|
||||
/* Строка, которую сейчас вписывают, уже содержит результат: имени
|
||||
* ещё нет, а время справа оригинал показывает сразу (show_hof
|
||||
* рисует все hof_count строк, включая только что вставленную). */
|
||||
if (!entry->name[0] && i != edit_row) continue;
|
||||
if (entry->name[0]) hof_text_left(HOF_LEFT, baseline, entry->name);
|
||||
hof_result_text(entry, value);
|
||||
hof_text_right(HOF_RIGHT, baseline, value);
|
||||
}
|
||||
pop_text_unmap();
|
||||
}
|
||||
|
||||
/* Готовая страница: фон из архива, полоса ввода и строки. -1 — фон не
|
||||
* прочитан, и тогда таблица просто не показывается: ни игра, ни title от
|
||||
* этого не зависят. */
|
||||
static int hof_page_build(uint8_t page, uint8_t edit_row)
|
||||
{
|
||||
if (pop_intro_hof_bg(page) != 0) return -1;
|
||||
gfx_set_draw_page(page);
|
||||
if (edit_row != HOF_NO_ROW) hof_edit_bar(edit_row);
|
||||
hof_draw_rows(edit_row);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Имя пишется ПРЯМО НА ЭКРАН, как input_str оригинала
|
||||
* (current_target_surface = onscreen_surface_): страницы при вводе не
|
||||
* меняются местами, поэтому дорисовывать фон второй страницы не нужно. */
|
||||
static void hof_edit_draw(uint8_t row, const char *name, uint8_t cursor)
|
||||
{
|
||||
int x = HOF_LEFT, baseline = HOF_BASELINE(row);
|
||||
|
||||
gfx_set_draw_page(gfx_get_visible_page());
|
||||
hof_edit_bar(row);
|
||||
gfx_set_bank(GFX_BANK_SPRITE);
|
||||
pop_text_map();
|
||||
/* Тёмный набор: на золотой полосе оригинал пишет цветом 0xBE. */
|
||||
if (name[0]) {
|
||||
pop_text_draw_mapped(POP_TEXT_BIG_DARK, x, baseline, name);
|
||||
x += pop_text_width_mapped(POP_TEXT_BIG_DARK, name) + 1;
|
||||
}
|
||||
/* draw_text_cursor(): подчерк той же гарнитуры сразу за текстом,
|
||||
* мигающий сменой цвета на фоновый — у нас проще перерисовать. */
|
||||
if (cursor) pop_text_draw_mapped(POP_TEXT_BIG_DARK, x, baseline, "_");
|
||||
pop_text_unmap();
|
||||
}
|
||||
|
||||
/* ---- ожидания -------------------------------------------------------- *
|
||||
*
|
||||
* Любое ожидание этого экрана держит ПОТОКОВУЮ музыку: победная тема
|
||||
* (1,2 МБ) играет кольцом страниц, и дочитывать её надо всё время, пока
|
||||
* игрок смотрит таблицу и печатает имя — иначе насос догонит загрузчика и
|
||||
* пойдёт по кругу старыми данными.
|
||||
*
|
||||
* Прерывание — по НОВОМУ нажатию (детектор фронта, как в сценах intro):
|
||||
* клавиша, которой закончили предыдущий экран, в момент входа ещё зажата,
|
||||
* и считать её за «пропустить» нельзя. */
|
||||
typedef struct { uint8_t armed; } hof_skip_t;
|
||||
|
||||
static void hof_skip_begin(hof_skip_t *sk)
|
||||
{
|
||||
kbd_raw_sync();
|
||||
sk->armed = (uint8_t)!kbd_raw_any_down();
|
||||
}
|
||||
|
||||
static uint8_t hof_skip_requested(hof_skip_t *sk)
|
||||
{
|
||||
kbd_raw_sync();
|
||||
if (!kbd_raw_any_down()) { sk->armed = 1; return 0; }
|
||||
return sk->armed;
|
||||
}
|
||||
|
||||
/* Возврат 1 — ожидание прервано нажатием. */
|
||||
static uint8_t hof_wait_frames(uint16_t frames)
|
||||
{
|
||||
hof_skip_t skip;
|
||||
|
||||
hof_skip_begin(&skip);
|
||||
while (frames--) {
|
||||
if (hof_skip_requested(&skip)) return 1;
|
||||
pop_music_service();
|
||||
gfx_wait_vsync();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* `while (check_sound_playing() && !key_test_quit())` (seg001:637): финал
|
||||
* дослушивает победную тему и прерывается любой клавишей. */
|
||||
static void hof_wait_music(void)
|
||||
{
|
||||
hof_skip_t skip;
|
||||
|
||||
hof_skip_begin(&skip);
|
||||
while (pop_music_busy() && !hof_skip_requested(&skip)) {
|
||||
pop_music_service();
|
||||
gfx_wait_vsync();
|
||||
}
|
||||
@@ -283,8 +407,10 @@ static const uint8_t hof_key_code[30] = {
|
||||
KBD_SPACE, 0x66, KBD_ENTER, KBD_ESC
|
||||
};
|
||||
|
||||
/* Возвращает 1, когда ввод закончен (Enter/Esc); *changed — надо ли
|
||||
* перерисовать строку. */
|
||||
static uint8_t hof_name_tick(hof_entry_t *entry, uint8_t *length,
|
||||
uint8_t *previous)
|
||||
uint8_t *previous, uint8_t *changed)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
@@ -294,12 +420,16 @@ static uint8_t hof_name_tick(hof_entry_t *entry, uint8_t *length,
|
||||
if (i < 26 && *length < HOF_NAME_SIZE - 1) {
|
||||
entry->name[(*length)++] = (char)('A' + i);
|
||||
entry->name[*length] = 0;
|
||||
*changed = 1;
|
||||
} else if (i == 26 && *length && *length < HOF_NAME_SIZE - 1) {
|
||||
entry->name[(*length)++] = ' ';
|
||||
entry->name[*length] = 0;
|
||||
*changed = 1;
|
||||
} else if (i == 27 && *length) {
|
||||
entry->name[--(*length)] = 0;
|
||||
*changed = 1;
|
||||
} else if (i == 28 || i == 29) {
|
||||
previous[i] = down;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -308,22 +438,42 @@ static uint8_t hof_name_tick(hof_entry_t *entry, uint8_t *length,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ввод имени в строку row. Пустое имя оригинал не отвергает, но пустая
|
||||
* строка в таблице выглядела бы как свободное место — подставляем PLAYER. */
|
||||
/* Курсор мигает каждые шесть тиков 60 Гц (start_timer(timer_0, 6) в
|
||||
* input_str) — в кадрах луча это HOF_BLINK. */
|
||||
#define HOF_BLINK POP_T60(6)
|
||||
|
||||
static void hof_edit(uint8_t row)
|
||||
{
|
||||
uint8_t length = 0, previous[30], i;
|
||||
uint8_t length = 0, previous[30], i, changed = 1;
|
||||
uint8_t cursor = 1, phase = 0;
|
||||
hof_entry_t *entry = &hof_table[row];
|
||||
|
||||
for (i = 0; i < sizeof(previous); i++) previous[i] = 0;
|
||||
hof_wait_release();
|
||||
/* Стартовое состояние клавиш: зажатое с прошлого экрана не считается
|
||||
* нажатием, поэтому первый sync только заполняет previous[]. */
|
||||
kbd_raw_sync();
|
||||
for (i = 0; i < sizeof(previous); i++)
|
||||
previous[i] = kbd_raw_down(hof_key_code[i]);
|
||||
for (;;) {
|
||||
kbd_raw_sync();
|
||||
if (hof_name_tick(entry, &length, previous)) {
|
||||
if (hof_name_tick(entry, &length, previous, &changed)) {
|
||||
if (!length) strcpy(entry->name, "PLAYER");
|
||||
hof_save();
|
||||
hof_wait_release();
|
||||
return;
|
||||
}
|
||||
hof_draw(row);
|
||||
if (changed) { cursor = 1; phase = 0; }
|
||||
if (++phase >= HOF_BLINK) {
|
||||
phase = 0;
|
||||
cursor = (uint8_t)!cursor;
|
||||
changed = 1;
|
||||
}
|
||||
if (changed) {
|
||||
hof_edit_draw(row, entry->name, cursor);
|
||||
changed = 0;
|
||||
}
|
||||
pop_music_service();
|
||||
gfx_wait_vsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,48 +481,49 @@ void pop_hof_show(void) __banked
|
||||
{
|
||||
uint8_t row;
|
||||
|
||||
pop_sfx_pause();
|
||||
hof_load();
|
||||
row = hof_insert_current();
|
||||
|
||||
pop_pal_story_load();
|
||||
pop_pal_apply(POP_PAL_DIM_BLACK);
|
||||
hof_draw(row);
|
||||
pop_pal_fade_in(HOF_FADE);
|
||||
|
||||
if (row != HOF_NO_ROW) {
|
||||
hof_edit(row);
|
||||
} else {
|
||||
hof_wait_release();
|
||||
while (!kbd_raw_any_down()) {
|
||||
kbd_raw_sync();
|
||||
pop_music_service();
|
||||
gfx_wait_vsync();
|
||||
uint8_t page;
|
||||
|
||||
/* fade_out_2 гасит титульную картинку перед таблицей (seg001:601).
|
||||
* Палитру не меняем: story.pal с красным фоном рамки уже стоит от
|
||||
* финального HAIL, и снимок для fade сделан ею же. */
|
||||
pop_pal_fade_out(HOF_FADE);
|
||||
page = pop_screen_begin();
|
||||
if (hof_page_build(page, row) == 0) {
|
||||
pop_screen_present(page);
|
||||
pop_pal_fade_in(HOF_FADE);
|
||||
hof_edit(row);
|
||||
hof_save();
|
||||
/* Имя вписано: строку показываем уже обычным золотым текстом,
|
||||
* без полосы (restore_peel + show_hof_text оригинала). */
|
||||
page = pop_screen_begin();
|
||||
if (hof_page_build(page, HOF_NO_ROW) == 0)
|
||||
pop_screen_present(page);
|
||||
hof_wait_frames(HOF_AFTER_TICKS);
|
||||
(void)pop_title_main_ltr();
|
||||
}
|
||||
hof_wait_release();
|
||||
}
|
||||
hof_wait_music();
|
||||
pop_pal_fade_out(HOF_FADE);
|
||||
pop_music_free();
|
||||
pop_sfx_pause();
|
||||
}
|
||||
|
||||
void pop_hof_show_title(void) __banked
|
||||
uint8_t pop_hof_show_title(void) __banked
|
||||
{
|
||||
/* Один keypress должен пропускать всю цепочку title/intro, а не внезапно
|
||||
* останавливаться на уже известной таблице. */
|
||||
if (kbd_raw_any_down()) return;
|
||||
uint8_t page;
|
||||
|
||||
hof_load();
|
||||
if (!hof_has_entries()) return;
|
||||
pop_sfx_pause();
|
||||
pop_pal_story_load();
|
||||
pop_pal_apply(POP_PAL_DIM_BLACK);
|
||||
hof_draw(HOF_NO_ROW);
|
||||
pop_pal_fade_in(HOF_FADE);
|
||||
hof_wait_release();
|
||||
while (!kbd_raw_any_down()) {
|
||||
kbd_raw_sync();
|
||||
pop_music_service();
|
||||
gfx_wait_vsync();
|
||||
}
|
||||
hof_wait_release();
|
||||
pop_pal_fade_out(HOF_FADE);
|
||||
if (!hof_has_entries()) return 0;
|
||||
page = pop_screen_begin();
|
||||
/* Ни fade, ни смены палитры: экран проявляется полосами прямо поверх
|
||||
* credits, на той же story.pal (seg000:2063). Переход несёт ТОЛЬКО
|
||||
* фон — строки дорисовываются следом в видимую страницу. */
|
||||
if (pop_intro_hof_bg(page) != 0) return 0;
|
||||
pop_screen_present_ltr();
|
||||
gfx_set_draw_page(gfx_get_visible_page());
|
||||
hof_draw_rows(HOF_NO_ROW);
|
||||
return hof_wait_frames(HOF_TITLE_TICKS);
|
||||
}
|
||||
|
||||
@@ -2,14 +2,20 @@
|
||||
#ifndef POP_HOF_H
|
||||
#define POP_HOF_H
|
||||
|
||||
/* Показать Hall of Fame, при подходящем результате запросить имя и сохранить
|
||||
* POP.HOF. Ошибка или повреждение файла означают пустую таблицу; gameplay и
|
||||
* последующий title от этого не зависят. */
|
||||
#include <stdint.h>
|
||||
|
||||
/* Хвост end_sequence: при подходящем результате — запрос имени, сохранение
|
||||
* POP.HOF и возврат к титульной картинке; затем в любом случае
|
||||
* дослушивается победная тема и гаснет экран. Ошибка или повреждение
|
||||
* файла означают пустую таблицу; gameplay и последующий title от этого не
|
||||
* зависят. Победная тема на входе ещё звучит — освобождает её эта
|
||||
* функция. */
|
||||
void pop_hof_show(void) __banked;
|
||||
|
||||
/* Показать уже существующую непустую таблицу между credits и demo. Новая
|
||||
* запись здесь не создаётся; удержанная клавиша title/intro пропускает экран
|
||||
* целиком. */
|
||||
void pop_hof_show_title(void) __banked;
|
||||
/* Показать уже существующую непустую таблицу между credits и demo
|
||||
* (seg000:2060): полосовой переход, 240 тиков, ни fade, ни смены палитры.
|
||||
* Возврат 1 — игрок нажал клавишу, то есть всю цепочку title/intro надо
|
||||
* прервать. */
|
||||
uint8_t pop_hof_show_title(void) __banked;
|
||||
|
||||
#endif /* POP_HOF_H */
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include "pv_arc.h"
|
||||
#include "pop_cutscene.h"
|
||||
#include "pop_intro.h"
|
||||
#include "pop_hof.h" /* таблица рекордов между credits и demo */
|
||||
#include "pop_title.h" /* титульная картинка в финале */
|
||||
#include "pop_pace.h" /* pop_wait_edge */
|
||||
#include "pop_pal.h"
|
||||
#include "pop_timer.h"
|
||||
@@ -78,6 +80,7 @@ enum {
|
||||
INTRO_STORY_CREDITS,
|
||||
INTRO_STORY_HAIL,
|
||||
INTRO_STORY_FRAME,
|
||||
INTRO_STORY_HOF, /* рамка + логотип: фон таблицы рекордов */
|
||||
INTRO_PV_WAITING,
|
||||
INTRO_PV_JAFFAR,
|
||||
INTRO_PV_MAGIC,
|
||||
@@ -90,7 +93,10 @@ enum {
|
||||
|
||||
enum {
|
||||
INTRO_PAL_STORY = 0,
|
||||
INTRO_PAL_PV = 1
|
||||
INTRO_PAL_PV = 1,
|
||||
/* Та же story.pal, но фон текстовой рамки красный:
|
||||
* load_title_images(0) в end_sequence (seg001:586). */
|
||||
INTRO_PAL_STORY_END = 2
|
||||
};
|
||||
|
||||
/* Экраны PV лежат в одном архиве по пять полос подряд, ровно в том же
|
||||
@@ -98,10 +104,10 @@ enum {
|
||||
* res41 + тексты 42/43/45/44 и чистая рамка), затем восемь стадий комнаты
|
||||
* принцессы (res951 + персонажи) и чистый фон b0. Поэтому имён здесь нет
|
||||
* — индекс первой полосы экрана считается умножением. */
|
||||
#if (ARC_PV_P0_0 - ARC_PV_S0_0) != 5 * INTRO_PARTS
|
||||
#if (ARC_PV_P0_0 - ARC_PV_S0_0) != 6 * INTRO_PARTS
|
||||
#error "экраны PV в архиве лежат не по пять полос подряд"
|
||||
#endif
|
||||
#define INTRO_SCENES 13
|
||||
#define INTRO_SCENES 14
|
||||
|
||||
/* Две текстовые половины show_title(). Между ними PV теперь идёт отдельным
|
||||
* покадровым renderer-ом, а не четырьмя полными статичными композициями. */
|
||||
@@ -151,10 +157,9 @@ static const pop_cs_cmd_t intro_marry[] = {
|
||||
{ POP_CS_END, 0 }
|
||||
};
|
||||
|
||||
static const pop_cs_cmd_t intro_credits[] = {
|
||||
{ POP_CS_WAIT, POP_T60(0x78) },
|
||||
{ POP_CS_SHOW, INTRO_STORY_CREDITS },
|
||||
{ POP_CS_WAIT, POP_T60(0x168) },
|
||||
/* Гашение — отдельным шагом: между credits и ним оригинал показывает
|
||||
* таблицу рекордов, если файл с ней есть (seg000:2060). */
|
||||
static const pop_cs_cmd_t intro_tail[] = {
|
||||
{ POP_CS_FADE_OUT, 0 },
|
||||
{ POP_CS_END, 0 }
|
||||
};
|
||||
@@ -201,16 +206,19 @@ static const pop_cs_cmd_t time_expired_script[] = {
|
||||
/* end_sequence_anim() — объятие/мышь — здесь сведён к отдельной PV-стадии;
|
||||
* затем end_sequence() SDLPoP показывает HAIL ровно 900 тиков. */
|
||||
/* Хвост финала после живой сцены встречи: только исходный story-экран. */
|
||||
/* FADE_OUT в конце НЕТ намеренно: оригинал не гасит HAIL, а проявляет
|
||||
* поверх него титульную картинку полосами (draw_full_image(TITLE_MAIN) +
|
||||
* transition_ltr, seg001:590). Гасит экран уже таблица рекордов. */
|
||||
static const pop_cs_cmd_t ending_hail_script[] = {
|
||||
{ POP_CS_PALETTE, INTRO_PAL_STORY }, { POP_CS_SHOW, INTRO_STORY_HAIL },
|
||||
{ POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 900 }, { POP_CS_FADE_OUT, 0 },
|
||||
{ POP_CS_PALETTE, INTRO_PAL_STORY_END }, { POP_CS_SHOW, INTRO_STORY_HAIL },
|
||||
{ POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 900 },
|
||||
{ POP_CS_END, 0 }
|
||||
};
|
||||
static const pop_cs_cmd_t ending_script[] = {
|
||||
{ POP_CS_PALETTE, INTRO_PAL_PV }, { POP_CS_SHOW, INTRO_PV_MOUSE },
|
||||
{ POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 54 }, { POP_CS_FADE_OUT, 0 },
|
||||
{ POP_CS_PALETTE, INTRO_PAL_STORY }, { POP_CS_SHOW, INTRO_STORY_HAIL },
|
||||
{ POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 900 }, { POP_CS_FADE_OUT, 0 },
|
||||
{ POP_CS_PALETTE, INTRO_PAL_STORY_END }, { POP_CS_SHOW, INTRO_STORY_HAIL },
|
||||
{ POP_CS_FADE_IN, 0 }, { POP_CS_WAIT, 900 },
|
||||
{ POP_CS_END, 0 }
|
||||
};
|
||||
|
||||
@@ -342,7 +350,7 @@ static void intro_prepare_palette(uint8_t pal)
|
||||
static void intro_commit_palette(void)
|
||||
{
|
||||
if (intro_pending_pal == INTRO_PAL_PV) pop_pal_pv_load();
|
||||
else pop_pal_story_load();
|
||||
else pop_pal_story_load((uint8_t)(intro_pending_pal == INTRO_PAL_STORY_END));
|
||||
pop_pal_apply(POP_PAL_DIM_BLACK);
|
||||
intro_pal_pending = 0;
|
||||
}
|
||||
@@ -1012,29 +1020,8 @@ static int intro_story_prepare(void)
|
||||
return intro_draw_page(INTRO_STORY_ABSENCE, target);
|
||||
}
|
||||
|
||||
/* Полосовой показ подготовленной страницы (transition_ltr оригинала). */
|
||||
static int intro_story_transition(void)
|
||||
{
|
||||
gfx_rect_t stripe;
|
||||
uint8_t visible = gfx_get_visible_page();
|
||||
int x;
|
||||
|
||||
gfx_set_draw_page(visible); /* source gfx_copy_page = target */
|
||||
stripe.y = 0;
|
||||
stripe.h = GFX_HEIGHT;
|
||||
for (x = 0; x < GFX_WIDTH; x += 4) {
|
||||
if (kbd_raw_any_down()) {
|
||||
stripe.x = 0; stripe.w = GFX_WIDTH;
|
||||
gfx_copy_page(&stripe, GFX_COPY_DIRECT);
|
||||
break;
|
||||
}
|
||||
stripe.x = x;
|
||||
stripe.w = (uint16_t)((GFX_WIDTH - x < 4) ? GFX_WIDTH - x : 4);
|
||||
gfx_copy_page(&stripe, GFX_COPY_DIRECT);
|
||||
gfx_wait_vsync();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* Полосовой показ подготовленной страницы живёт в pop_ui: тем же приёмом
|
||||
* появляются титульная картинка финала и таблица рекордов. */
|
||||
|
||||
static void intro_flash(uint16_t ticks)
|
||||
{
|
||||
@@ -1511,6 +1498,35 @@ static uint8_t intro_run(const pop_cs_cmd_t *script)
|
||||
return aborted;
|
||||
}
|
||||
|
||||
|
||||
/* Ожидание кадров у шагов, которые показываются НЕ сценарием, а напрямую.
|
||||
* Оно не глушит поток: победная тема играет кольцом страниц, и дочитывать
|
||||
* её надо каждый кадр, иначе насос догонит загрузчика. Прерывается новым
|
||||
* нажатием (детектор фронта); 1 — прервано. */
|
||||
static uint8_t intro_wait_frames(uint16_t frames)
|
||||
{
|
||||
intro_skip_t skip;
|
||||
|
||||
intro_skip_begin(&skip);
|
||||
while (frames--) {
|
||||
if (intro_skip_requested(&skip)) return 1;
|
||||
pop_music_service();
|
||||
pop_wait_edge();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Титры проявляются полосами, как и всё остальное в show_title()
|
||||
* (draw_full_image(STORY_CREDITS) + transition_ltr, seg000:2055). */
|
||||
static uint8_t intro_credits_ltr(void)
|
||||
{
|
||||
uint8_t page = pop_screen_begin();
|
||||
|
||||
if (intro_draw_page(INTRO_STORY_CREDITS, page) == 0)
|
||||
pop_screen_present_ltr();
|
||||
return intro_wait_frames(POP_T60(0x168));
|
||||
}
|
||||
|
||||
uint8_t pop_intro_show(void) __banked
|
||||
{
|
||||
int pv_result;
|
||||
@@ -1543,7 +1559,7 @@ uint8_t pop_intro_show(void) __banked
|
||||
}
|
||||
(void)pop_sfx_start(); /* идемпотентно: титры его уже открыли */
|
||||
pop_music_play();
|
||||
(void)intro_story_transition();
|
||||
pop_screen_present_ltr();
|
||||
}
|
||||
if (intro_run(intro_before_pv)) {
|
||||
/* Пропуск по клавише. ГЛУШИМ ВЫВОД: сразу за возвратом пойдёт
|
||||
@@ -1599,7 +1615,27 @@ uint8_t pop_intro_show(void) __banked
|
||||
}
|
||||
pop_music_free();
|
||||
pop_sfx_pause();
|
||||
if (intro_run(intro_credits)) {
|
||||
/* ТИТУЛЬНЫЙ ЭКРАН МЕЖДУ «СВАДЬБОЙ» И ТИТРАМИ. show_title() рисует
|
||||
* TITLE_MAIN + логотип + Jordan Mechner и проявляет их полосами
|
||||
* (seg000:2045), и только через 0x78 тиков показывает credits — тоже
|
||||
* полосами. Раньше обе картинки возникали мгновенно. */
|
||||
if (pop_title_logo_ltr() == 0 && intro_wait_frames(POP_T60(0x78))) {
|
||||
intro_restore_game_palette();
|
||||
return 1;
|
||||
}
|
||||
if (intro_credits_ltr()) {
|
||||
intro_restore_game_palette();
|
||||
return 1;
|
||||
}
|
||||
/* ТАБЛИЦА РЕКОРДОВ между credits и attract-demo — ровно там же, где её
|
||||
* показывает show_title(): та же story-палитра, тот же полосовой
|
||||
* переход, никакого fade (seg000:2060). Пустой таблицы это не
|
||||
* касается: она просто не показывается. */
|
||||
if (pop_hof_show_title()) {
|
||||
intro_restore_game_palette();
|
||||
return 1;
|
||||
}
|
||||
if (intro_run(intro_tail)) {
|
||||
intro_restore_game_palette();
|
||||
return 1;
|
||||
}
|
||||
@@ -1696,6 +1732,14 @@ void pop_pre_cutscene_show(pop_pre_cutscene_t scene) __banked
|
||||
pre_cut_finish(intro_run(script));
|
||||
}
|
||||
|
||||
/* Фон таблицы рекордов — та же рамка story плюс логотип PRINCE OF PERSIA
|
||||
* (STORY_FRAME + HOF_POP оригинала). Рисуем в ЗАДАННУЮ страницу и её не
|
||||
* показываем: палитрой, строками и переходом распоряжается pop_hof. */
|
||||
int pop_intro_hof_bg(uint8_t page) __banked
|
||||
{
|
||||
return intro_draw_page(INTRO_STORY_HOF, page);
|
||||
}
|
||||
|
||||
void pop_time_expired_show(void) __banked
|
||||
{
|
||||
pop_sfx_pause();
|
||||
@@ -1724,6 +1768,7 @@ void pop_time_expired_show(void) __banked
|
||||
intro_restore_game_palette();
|
||||
}
|
||||
|
||||
|
||||
void pop_ending_show(void) __banked
|
||||
{
|
||||
pop_sfx_pause();
|
||||
@@ -1743,27 +1788,14 @@ void pop_ending_show(void) __banked
|
||||
(void)pop_sfx_start();
|
||||
(void)pop_music_ring_start(POP_MUS_WON);
|
||||
intro_run(ending_hail_script);
|
||||
/* ДОСЛУШАТЬ. Оригинал не обрывает победную тему переходом в меню:
|
||||
* `while (check_sound_playing() && !key_test_quit())` (seg001:637),
|
||||
* и лишь потом гасит экран. Клавишей прервать можно.
|
||||
*
|
||||
* У ОРИГИНАЛА между титрами и этим ожиданием стоит таблица рекордов
|
||||
* с вводом имени, и тема играет сквозь неё. Наша таблица пока без
|
||||
* ввода, поэтому ждём здесь; когда ввод появится — ожидание
|
||||
* переедет за него (задача HOF-ENTRY). */
|
||||
{
|
||||
intro_skip_t tail;
|
||||
|
||||
intro_skip_begin(&tail);
|
||||
while (pop_music_busy() && !intro_skip_requested(&tail)) {
|
||||
pop_music_service(); /* поток дочитывается по ходу */
|
||||
pop_wait_edge();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
intro_run(ending_script);
|
||||
}
|
||||
pop_music_free();
|
||||
pop_sfx_pause();
|
||||
intro_restore_game_palette();
|
||||
/* ТИТУЛЬНАЯ КАРТИНКА ПОВЕРХ ТЕКСТА. end_sequence заводит таймер на 240
|
||||
* тиков, проявляет TITLE_MAIN полосами и ждёт его (seg001:590). Экран
|
||||
* при этом не гаснет: рамка HAIL просто переползает в рисунок дворца.
|
||||
* Дальше — состояние HALL_OF_FAME; тема продолжает звучать, и
|
||||
* освобождает её уже таблица рекордов, как в оригинале. */
|
||||
if (pop_title_main_ltr() == 0)
|
||||
(void)intro_wait_frames(POP_T60(240));
|
||||
}
|
||||
|
||||
@@ -17,7 +17,13 @@ void pop_pre_cutscene_show(pop_pre_cutscene_t scene) __banked;
|
||||
* возвращается в TITLE. Любой raw-ввод сокращает ожидание. */
|
||||
void pop_time_expired_show(void) __banked;
|
||||
|
||||
/* Финал level 14: Princess room, затем исходный story-screen HAIL. */
|
||||
/* Финал level 14: Princess room, story-screen HAIL и титульная картинка.
|
||||
* Победная тема при выходе ЕЩЁ ЗВУЧИТ: её дослушивает таблица рекордов,
|
||||
* как в end_sequence оригинала. */
|
||||
void pop_ending_show(void) __banked;
|
||||
|
||||
/* Фон таблицы рекордов (рамка story + логотип) в заданную СКРЫТУЮ
|
||||
* страницу; -1 — ресурс не прочитан. Ни палитры, ни показа. */
|
||||
int pop_intro_hof_bg(uint8_t page) __banked;
|
||||
|
||||
#endif /* POP_INTRO_H */
|
||||
|
||||
@@ -103,9 +103,18 @@ void pop_pal_title_load(void) __banked
|
||||
pal_file_load("TITLE\\title.pal", "a:\\title.pal");
|
||||
}
|
||||
|
||||
void pop_pal_story_load(void) __banked
|
||||
/* ЕДИНСТВЕННОЕ ОТЛИЧИЕ ФИНАЛА ОТ ИНТРО — цвет фона текстовой рамки.
|
||||
* load_title_images(bgcolor) SDLPoP красит его в #100060 на титрах и в
|
||||
* #800000 в end_sequence (seg001:586); у нас фон запечён отдельным
|
||||
* индексом POP_PAL_STORY_BG именно ради этой подмены. Она делается ПОСЛЕ
|
||||
* файла и до нового снимка — как тайлсет/Тень в pop_pal_game_load. */
|
||||
void pop_pal_story_load(uint8_t ending) __banked
|
||||
{
|
||||
pal_file_load("PV\\story.pal", "a:\\story.pal");
|
||||
if (!ending) return;
|
||||
gfx_pal_set(0, POP_PAL_STORY_BG, 0x80, 0, 0);
|
||||
gfx_pal_set(1, POP_PAL_STORY_BG, 0x80, 0, 0);
|
||||
pop_ui_palette_snapshot();
|
||||
}
|
||||
|
||||
void pop_pal_pv_load(void) __banked
|
||||
|
||||
@@ -19,7 +19,10 @@ void pop_pal_black(void) __banked;
|
||||
void pop_pal_game_load(void) __banked;
|
||||
void pop_pal_level_load(uint8_t full) __banked;
|
||||
void pop_pal_title_load(void) __banked;
|
||||
void pop_pal_story_load(void) __banked;
|
||||
/* Фон текстовой рамки story лежит в отдельном индексе палитры: ending=1
|
||||
* красит его тёмно-красным (финал), 0 оставляет файловый тёмно-синий. */
|
||||
#define POP_PAL_STORY_BG 16
|
||||
void pop_pal_story_load(uint8_t ending) __banked;
|
||||
void pop_pal_pv_load(void) __banked;
|
||||
|
||||
/* Показ текущего снимка: 0 — исходная яркость, 4 — чёрный. */
|
||||
|
||||
@@ -92,6 +92,26 @@ static int title_draw(uint8_t scene, uint8_t show)
|
||||
return page;
|
||||
}
|
||||
|
||||
/* draw_full_image(TITLE_MAIN) + transition_ltr() финала (seg001:590 и
|
||||
* :629): титульный рисунок проявляется полосами поверх текущего экрана.
|
||||
* Палитру не трогаем — story.pal и title.pal это одни и те же цвета
|
||||
* (обе сделаны из res51). -1 — архив не прочитан. */
|
||||
int pop_title_main_ltr(void) __banked
|
||||
{
|
||||
if (title_draw(TITLE_MAIN, 0) < 0) return -1;
|
||||
pop_screen_present_ltr();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Тот же приём для композиции с логотипом и подписью Jordan Mechner: её
|
||||
* show_title() показывает МЕЖДУ «свадьбой» и титрами (seg000:2045). */
|
||||
int pop_title_logo_ltr(void) __banked
|
||||
{
|
||||
if (title_draw(TITLE_LOGO, 0) < 0) return -1;
|
||||
pop_screen_present_ltr();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t title_wait(uint16_t ticks)
|
||||
{
|
||||
while (ticks--) {
|
||||
|
||||
@@ -14,4 +14,14 @@
|
||||
* напрямую; 0 — последовательность завершилась штатно, продолжать intro. */
|
||||
uint8_t pop_title_show(void) __banked;
|
||||
|
||||
/* Одна титульная картинка (TITLE_MAIN) полосами слева направо — так финал
|
||||
* возвращается к ней после HAIL и после ввода имени в таблицу рекордов.
|
||||
* Палитру не меняет: story.pal и title.pal сделаны из одного res51.
|
||||
* -1 — архив титров не прочитан. */
|
||||
int pop_title_main_ltr(void) __banked;
|
||||
|
||||
/* Композиция с логотипом PRINCE OF PERSIA и Jordan Mechner тем же
|
||||
* переходом — шаг show_title() между «свадьбой» и титрами. */
|
||||
int pop_title_logo_ltr(void) __banked;
|
||||
|
||||
#endif /* POP_TITLE_H */
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <kbd_raw.h>
|
||||
#include <graphics.h>
|
||||
#include <gfx.h>
|
||||
#include <sprite.h>
|
||||
@@ -20,11 +21,19 @@ typedef struct {
|
||||
uint8_t base;
|
||||
uint8_t ascent;
|
||||
uint8_t space;
|
||||
uint8_t first; /* первый код, который есть в наборе */
|
||||
uint8_t last;
|
||||
} pop_font_desc_t;
|
||||
|
||||
/* Тёмный набор — тот же мелкий шрифт другим цветом; глифов в нём меньше
|
||||
* (см. pop_font.h), поэтому диапазон кодов хранится у каждого набора. */
|
||||
static const pop_font_desc_t font_desc[] = {
|
||||
{ POP_FONT_SMALL_BASE, POP_FONT_SMALL_ASCENT, POP_FONT_SMALL_SPACE },
|
||||
{ POP_FONT_BIG_BASE, POP_FONT_BIG_ASCENT, POP_FONT_BIG_SPACE }
|
||||
{ POP_FONT_SMALL_BASE, POP_FONT_SMALL_ASCENT, POP_FONT_SMALL_SPACE,
|
||||
POP_FONT_FIRST, POP_FONT_LAST },
|
||||
{ POP_FONT_BIG_BASE, POP_FONT_BIG_ASCENT, POP_FONT_BIG_SPACE,
|
||||
POP_FONT_FIRST, POP_FONT_LAST },
|
||||
{ POP_FONT_DARK_BASE, POP_FONT_BIG_ASCENT, POP_FONT_BIG_SPACE,
|
||||
POP_FONT_DARK_FIRST, POP_FONT_DARK_LAST }
|
||||
};
|
||||
|
||||
static atlas_t font_at;
|
||||
@@ -38,8 +47,8 @@ __at (0x4000) uint8_t pop_pal_buf[256];
|
||||
|
||||
static uint8_t text_index(const pop_font_desc_t *font, uint8_t ch)
|
||||
{
|
||||
if (ch < POP_FONT_FIRST || ch > POP_FONT_LAST) ch = '?';
|
||||
return (uint8_t)(font->base + ch - POP_FONT_FIRST);
|
||||
if (ch < font->first || ch > font->last) ch = '?';
|
||||
return (uint8_t)(font->base + ch - font->first);
|
||||
}
|
||||
|
||||
int pop_ui_init(void) __banked
|
||||
@@ -339,6 +348,37 @@ void pop_screen_present(uint8_t page) __banked
|
||||
gfx_set_visible_page(page);
|
||||
}
|
||||
|
||||
/* transition_ltr() SDLPoP: подготовленная СКРЫТАЯ страница переползает на
|
||||
* видимую узкими вертикальными полосами слева направо. У оригинала
|
||||
* 2px/120 FPS; здесь 4px/60 FPS даёт ту же длительность (~1,3 с), не
|
||||
* удваивая число полных копий 320x256.
|
||||
*
|
||||
* Страницы при этом НЕ меняются местами: копируем в видимую, как оригинал
|
||||
* копирует offscreen в onscreen. Поэтому у вызывающего после перехода на
|
||||
* ЭКРАНЕ и в скрытой странице лежит одно и то же. */
|
||||
void pop_screen_present_ltr(void) __banked
|
||||
{
|
||||
gfx_rect_t stripe;
|
||||
uint8_t visible = gfx_get_visible_page();
|
||||
int x;
|
||||
|
||||
gfx_set_draw_page(visible); /* source gfx_copy_page = target */
|
||||
stripe.y = 0;
|
||||
stripe.h = GFX_HEIGHT;
|
||||
for (x = 0; x < GFX_WIDTH; x += 4) {
|
||||
if (kbd_raw_any_down()) {
|
||||
/* Прервали — дорисовать остаток разом, а не бросить полукадр. */
|
||||
stripe.x = 0; stripe.w = GFX_WIDTH;
|
||||
gfx_copy_page(&stripe, GFX_COPY_DIRECT);
|
||||
return;
|
||||
}
|
||||
stripe.x = x;
|
||||
stripe.w = (uint16_t)((GFX_WIDTH - x < 4) ? GFX_WIDTH - x : 4);
|
||||
gfx_copy_page(&stripe, GFX_COPY_DIRECT);
|
||||
gfx_wait_vsync();
|
||||
}
|
||||
}
|
||||
|
||||
/* Нарисовать одну полосу и отпустить её страницу. Общая часть обоих
|
||||
* загрузчиков ниже: они отличаются только тем, откуда взялся атлас. */
|
||||
static int strip_blit(atlas_t *at, int x, int *y)
|
||||
|
||||
@@ -12,7 +12,12 @@
|
||||
|
||||
typedef enum {
|
||||
POP_TEXT_SMALL = 0,
|
||||
POP_TEXT_BIG = 1
|
||||
POP_TEXT_BIG = 1,
|
||||
/* Крупный шрифт вторым цветом (POP_FONT_DARK_COLOR): тень строки и
|
||||
* текст на светлом фоне — так show_hof_text() SDLPoP рисует таблицу
|
||||
* рекордов дважды (тень со сдвигом (1,1), затем сам текст). Набор
|
||||
* обрезан по 'Z', см. pop_font.h. */
|
||||
POP_TEXT_BIG_DARK = 2
|
||||
} pop_text_font_t;
|
||||
|
||||
/* Жизненный цикл единственного FONT\FONT.ATL. Загрузка — в холодном boot,
|
||||
@@ -86,6 +91,11 @@ void pop_screen_fill_game(void) __banked;
|
||||
* входа в комнату. */
|
||||
void pop_screen_fill_field(void) __banked;
|
||||
void pop_screen_present(uint8_t page) __banked;
|
||||
/* Показать подготовленную скрытую страницу полосами слева направо
|
||||
* (transition_ltr оригинала). Любая зажатая клавиша достраивает переход
|
||||
* мгновенно. Страницы местами не меняются: содержимое копируется в
|
||||
* видимую, поэтому после возврата обе страницы одинаковы. */
|
||||
void pop_screen_present_ltr(void) __banked;
|
||||
|
||||
/* Нарисовать в уже выбранную draw-страницу набор вертикальных полос SPA1.
|
||||
* Полоса загружается, блитится и тут же освобождается; функция не вызывает
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#ifndef PV_ARC_H
|
||||
#define PV_ARC_H
|
||||
|
||||
#define ARC_PV_COUNT 78
|
||||
#define ARC_PV_COUNT 83
|
||||
|
||||
#define ARC_PV_S0_0 0
|
||||
#define ARC_PV_S0_1 1
|
||||
@@ -30,58 +30,63 @@
|
||||
#define ARC_PV_S4_2 22
|
||||
#define ARC_PV_S4_3 23
|
||||
#define ARC_PV_S4_4 24
|
||||
#define ARC_PV_P0_0 25
|
||||
#define ARC_PV_P0_1 26
|
||||
#define ARC_PV_P0_2 27
|
||||
#define ARC_PV_P0_3 28
|
||||
#define ARC_PV_P0_4 29
|
||||
#define ARC_PV_P1_0 30
|
||||
#define ARC_PV_P1_1 31
|
||||
#define ARC_PV_P1_2 32
|
||||
#define ARC_PV_P1_3 33
|
||||
#define ARC_PV_P1_4 34
|
||||
#define ARC_PV_P2_0 35
|
||||
#define ARC_PV_P2_1 36
|
||||
#define ARC_PV_P2_2 37
|
||||
#define ARC_PV_P2_3 38
|
||||
#define ARC_PV_P2_4 39
|
||||
#define ARC_PV_P3_0 40
|
||||
#define ARC_PV_P3_1 41
|
||||
#define ARC_PV_P3_2 42
|
||||
#define ARC_PV_P3_3 43
|
||||
#define ARC_PV_P3_4 44
|
||||
#define ARC_PV_P4_0 45
|
||||
#define ARC_PV_P4_1 46
|
||||
#define ARC_PV_P4_2 47
|
||||
#define ARC_PV_P4_3 48
|
||||
#define ARC_PV_P4_4 49
|
||||
#define ARC_PV_P5_0 50
|
||||
#define ARC_PV_P5_1 51
|
||||
#define ARC_PV_P5_2 52
|
||||
#define ARC_PV_P5_3 53
|
||||
#define ARC_PV_P5_4 54
|
||||
#define ARC_PV_P6_0 55
|
||||
#define ARC_PV_P6_1 56
|
||||
#define ARC_PV_P6_2 57
|
||||
#define ARC_PV_P6_3 58
|
||||
#define ARC_PV_P6_4 59
|
||||
#define ARC_PV_P7_0 60
|
||||
#define ARC_PV_P7_1 61
|
||||
#define ARC_PV_P7_2 62
|
||||
#define ARC_PV_P7_3 63
|
||||
#define ARC_PV_P7_4 64
|
||||
#define ARC_PV_B0_0 65
|
||||
#define ARC_PV_B0_1 66
|
||||
#define ARC_PV_B0_2 67
|
||||
#define ARC_PV_B0_3 68
|
||||
#define ARC_PV_B0_4 69
|
||||
#define ARC_PV_A0 70
|
||||
#define ARC_PV_A1 71
|
||||
#define ARC_PV_A2 72
|
||||
#define ARC_PV_J0 73
|
||||
#define ARC_PV_J1 74
|
||||
#define ARC_PV_J2 75
|
||||
#define ARC_PV_H0 76
|
||||
#define ARC_PV_T0 77
|
||||
#define ARC_PV_S5_0 25
|
||||
#define ARC_PV_S5_1 26
|
||||
#define ARC_PV_S5_2 27
|
||||
#define ARC_PV_S5_3 28
|
||||
#define ARC_PV_S5_4 29
|
||||
#define ARC_PV_P0_0 30
|
||||
#define ARC_PV_P0_1 31
|
||||
#define ARC_PV_P0_2 32
|
||||
#define ARC_PV_P0_3 33
|
||||
#define ARC_PV_P0_4 34
|
||||
#define ARC_PV_P1_0 35
|
||||
#define ARC_PV_P1_1 36
|
||||
#define ARC_PV_P1_2 37
|
||||
#define ARC_PV_P1_3 38
|
||||
#define ARC_PV_P1_4 39
|
||||
#define ARC_PV_P2_0 40
|
||||
#define ARC_PV_P2_1 41
|
||||
#define ARC_PV_P2_2 42
|
||||
#define ARC_PV_P2_3 43
|
||||
#define ARC_PV_P2_4 44
|
||||
#define ARC_PV_P3_0 45
|
||||
#define ARC_PV_P3_1 46
|
||||
#define ARC_PV_P3_2 47
|
||||
#define ARC_PV_P3_3 48
|
||||
#define ARC_PV_P3_4 49
|
||||
#define ARC_PV_P4_0 50
|
||||
#define ARC_PV_P4_1 51
|
||||
#define ARC_PV_P4_2 52
|
||||
#define ARC_PV_P4_3 53
|
||||
#define ARC_PV_P4_4 54
|
||||
#define ARC_PV_P5_0 55
|
||||
#define ARC_PV_P5_1 56
|
||||
#define ARC_PV_P5_2 57
|
||||
#define ARC_PV_P5_3 58
|
||||
#define ARC_PV_P5_4 59
|
||||
#define ARC_PV_P6_0 60
|
||||
#define ARC_PV_P6_1 61
|
||||
#define ARC_PV_P6_2 62
|
||||
#define ARC_PV_P6_3 63
|
||||
#define ARC_PV_P6_4 64
|
||||
#define ARC_PV_P7_0 65
|
||||
#define ARC_PV_P7_1 66
|
||||
#define ARC_PV_P7_2 67
|
||||
#define ARC_PV_P7_3 68
|
||||
#define ARC_PV_P7_4 69
|
||||
#define ARC_PV_B0_0 70
|
||||
#define ARC_PV_B0_1 71
|
||||
#define ARC_PV_B0_2 72
|
||||
#define ARC_PV_B0_3 73
|
||||
#define ARC_PV_B0_4 74
|
||||
#define ARC_PV_A0 75
|
||||
#define ARC_PV_A1 76
|
||||
#define ARC_PV_A2 77
|
||||
#define ARC_PV_J0 78
|
||||
#define ARC_PV_J1 79
|
||||
#define ARC_PV_J2 80
|
||||
#define ARC_PV_H0 81
|
||||
#define ARC_PV_T0 82
|
||||
|
||||
#endif
|
||||
|
||||
@@ -294,15 +294,11 @@ int main(void)
|
||||
}
|
||||
}
|
||||
if (pop_app_state == POP_APP_INTRO) {
|
||||
/* Таблица рекордов между credits и demo показывается ВНУТРИ
|
||||
* pop_intro_show — там же, где её показывает show_title(). */
|
||||
uint8_t skipped = pop_intro_show();
|
||||
pop_app_event_t ev;
|
||||
|
||||
if (!skipped) {
|
||||
/* show_title() SDLPoP помещает сохранённую таблицу после credits
|
||||
* и до attract-demo. При skip таблица тоже пропускается. */
|
||||
pop_hof_show_title();
|
||||
}
|
||||
ev = skipped ? POP_APP_EV_NEW_GAME : POP_APP_EV_CUTSCENE_FINISHED;
|
||||
pop_app_event_t ev = skipped ? POP_APP_EV_NEW_GAME
|
||||
: POP_APP_EV_CUTSCENE_FINISHED;
|
||||
if (!pop_app_dispatch(ev)) {
|
||||
failed = 1;
|
||||
break;
|
||||
|
||||
@@ -100,16 +100,16 @@ TC_TEST(restart_game_returns_to_intro)
|
||||
TC_EQ(pop_app_state, POP_APP_TITLE);
|
||||
}
|
||||
|
||||
/* Финал ВРЕМЕННО ведёт мимо Hall of Fame прямо в title: экран HOF после
|
||||
* победы показывает чёрное, и застревать в нём хуже, чем не показывать
|
||||
* (решение пользователя 2026-08-24, BUGS_OPEN.md#hof-after-ending). Сам
|
||||
* маршрут HOF -> TITLE остаётся живым: в него ходит title-экран. */
|
||||
/* Финал уходит в таблицу рекордов: она же дослушивает победную тему и
|
||||
* гасит экран, как хвост end_sequence (seg001:5F1). */
|
||||
TC_TEST(ending_to_title_route)
|
||||
{
|
||||
pop_app_state = POP_APP_PLAYING;
|
||||
TC_TRUE(pop_app_dispatch(POP_APP_EV_ENDING_STARTED));
|
||||
TC_EQ(pop_app_state, POP_APP_ENDING);
|
||||
TC_TRUE(pop_app_dispatch(POP_APP_EV_ENDING_FINISHED));
|
||||
TC_EQ(pop_app_state, POP_APP_HALL_OF_FAME);
|
||||
TC_TRUE(pop_app_dispatch(POP_APP_EV_HOF_FINISHED));
|
||||
TC_EQ(pop_app_state, POP_APP_TITLE);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user