Меню, статус-строка и оболочка игры: title/intro/cutscene/HoF, POP.CFG, палитры
Эта сессия (меню + текст в служебных полосах):
* Меню: рамка выделения считается от силуэта текста (SEL_PAD сверху и
снизу), а не «на глаз»; все экраны центрируются в игровых 200 строках
(UI_CENTER_TOP/UI_CENTER_FIELD) и в них помещаются; Enter и Space —
равноправные клавиши действия (ui_action_down).
* Settings: убраны SHOW SPRINTER SCREEN и BACK, добавлены DEBUG BAR и
ABOUT. About показывает тот же текст, что стартовый Sprinter screen,
минус строка про клавиши — общий about_text(), чтобы экраны не
разъехались. CONTROLS собирается таблицей и центрируется по
фактическому числу строк.
* GAME PAUSED переехала в нижнюю статус-строку, как в оригинале
(SDLPoP rect_bottom_text = {193,70,202,250}): это состояние программы,
а не пункт меню. POP_HP_Y вынесен в pop_cdraw.h — полосу делят два
модуля.
* pop_status.c (банк 9) — текст в обеих служебных полосах. Нижняя:
порт display_text_bottom + таймера (QUICKSAVE/QUICKLOAD/SOUND ON/OFF,
24 тика). Верхняя отладочная переведена с палочек на текст
«Level ##, Room ##, Speed: …, Sound: …, Immortal #» малым шрифтом, с
своим форматированием чисел (без printf и без деления).
Заявка сообщения — запись одного байта pop_status_msg: резидент W1/W2
не растёт, весь рендер в банке. Бюджет после правок не изменился
(_CODE 23981, куча 267 Б).
* Цена вывода: блит глифа ~4,6 тыс. тактов независимо от размера, поэтому
всё change-driven, отладочная строка перерисовывается ПО ПОЛЯМ, пробелы
не блитятся вовсе, а вход в комнату заливает только игровое поле
(pop_screen_fill_field) — борта от комнаты к комнате не меняются.
* Интро: в PV-сцене зазвучали пропавшие эффекты оригинала — закрытие
ворот (4) и открытие двери покоев (51), из которой входит Джафар.
* docs/status_line_text.md — полная инвентаризация ВСЕХ текстов SDLPoP в
статус-строке: геометрия, семантика text_time_total как идентификатора
сообщения, мигание, рестарт по истечении 36/288.
Вместе с этим выкладывается накопленная работа по оболочке полной игры:
автомат состояний (pop_app), title, intro/PV и cutscene, attract-demo,
Hall of Fame, глобальный таймер, настройки и POP.CFG, модуль палитр и
fade, звуковой набор, host-тесты на новые швы.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,8 @@
|
||||
#include "_pop_kdraw.h"
|
||||
#include "pop_vflip.h" /* зеркальные страницы атласов (зелье инверсии) */ /* меч + блит по id — сосед по банку 4 */ /* атласы/кадр Кида, pop_sword_draw, окна Char */
|
||||
#include "pop_cdraw.h"
|
||||
#include "pop_status.h"
|
||||
#include "pop_ui.h" /* POP_COL_OUTSIDE — фон вне игрового поля */
|
||||
#include "pop_shadow.h" /* запечённый набор Тени */
|
||||
#include "pop_pace.h" /* pop_beam_sample — счёт кадров по лучу */
|
||||
#include "pop_state.h" /* ВРЕМЕННО: зонды pop_dbg_b* */
|
||||
@@ -34,6 +36,25 @@
|
||||
#include "kid_data.h"
|
||||
#include "pop_guard_pal.h" /* таблица палитр стражей (сгенерена упаковщиком) */
|
||||
|
||||
/* Последний curr_guard_color обычного стража (0 — цвет ещё не выбирали).
|
||||
* Нужен pop_guard_pal_apply, чтобы восстановить слоты после kid.pal. */
|
||||
static uint8_t guard_pal_cur;
|
||||
|
||||
/* 16 слотов POP_GUARD_PAL_BASE в ОБЕ палитры дабл-буфера.
|
||||
* Таблицы — rodata ЭТОГО модуля, то есть страница банка (0xC000+), а
|
||||
* gfx_pal_load отдаёт указатель в BIOS ($A4 через rst #0x08). BIOS читает
|
||||
* только #4000-#BFFF (см. корневой CLAUDE.md), поэтому копируем запись в
|
||||
* локальный буфер — стек гарантированно в W2. Без копии BIOS забирал
|
||||
* мусор с чужой страницы и страж становился невидимым. */
|
||||
static void pal_load16(const uint8_t *src)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
uint8_t i;
|
||||
for (i = 0; i < 64; i++) buf[i] = src[i];
|
||||
gfx_pal_load(0, POP_GUARD_PAL_BASE, 16, buf);
|
||||
gfx_pal_load(1, POP_GUARD_PAL_BASE, 16, buf);
|
||||
}
|
||||
|
||||
/* Цвет стража (set_chtab_palette, seg003:257). Оригинал держит ОДИН набор
|
||||
* спрайтов и подменяет 16 цветов палитры chtab_5 по level.guards_color —
|
||||
* так на уровне 2 стражи комнат 4/7/11/15/24 разного цвета (1/3/1/1/6).
|
||||
@@ -43,19 +64,27 @@
|
||||
* Грузим В ОБЕ палитры: у страниц дабл-буфера они раздельные. */
|
||||
void pop_guard_set_palette(uint8_t color) __banked
|
||||
{
|
||||
/* Таблица — rodata ЭТОГО модуля, то есть страница банка (0xC000+), а
|
||||
* gfx_pal_load отдаёт указатель в BIOS ($A4 через rst #0x08). BIOS
|
||||
* читает только #4000-#BFFF (см. корневой CLAUDE.md), поэтому копируем
|
||||
* запись в локальный буфер — стек гарантированно в W2. Без копии BIOS
|
||||
* забирал мусор с чужой страницы и страж становился невидимым. */
|
||||
uint8_t buf[64];
|
||||
const uint8_t *p;
|
||||
uint8_t i;
|
||||
if (color == 0 || color > POP_GUARD_NPAL) return;
|
||||
p = pop_guard_pal[color - 1];
|
||||
for (i = 0; i < 64; i++) buf[i] = p[i];
|
||||
gfx_pal_load(0, POP_GUARD_PAL_BASE, 16, buf);
|
||||
gfx_pal_load(1, POP_GUARD_PAL_BASE, 16, buf);
|
||||
guard_pal_cur = color; /* curr_guard_color, для re-apply */
|
||||
pal_load16(pop_guard_pal[color - 1]);
|
||||
}
|
||||
|
||||
/* Пере-применить палитру ТЕКУЩЕГО набора соперника — пара к
|
||||
* pop_bg_pal_apply/pop_shadow_pal_apply. Слоты 0x90..0x9F динамические:
|
||||
* общий kid.pal содержит все 256 записей и затирает их палитрой обычного
|
||||
* стража (цвет 2, тот что запёк упаковщик). Поэтому КАЖДЫЙ file-load
|
||||
* логической палитры обязан доплатить этот диапазон, иначе скелет (и
|
||||
* Джафар) остаются в цвете стража: у скелета все пиксели индекса 3, а это
|
||||
* слот 0x93 = розовый цвет стража — ровно то, что было видно на уровне 3.
|
||||
* Звать из pop_pal_* ДО snapshot. */
|
||||
void pop_guard_pal_apply(void) __banked
|
||||
{
|
||||
int8_t type = pop_level_guard_type(pop_current_level);
|
||||
if (type == 2 || type == 3) { /* свой цвет — из файла набора */
|
||||
pal_load16((type == 2) ? pop_skel_pal : pop_vizier_pal);
|
||||
} else if (type == 0 && guard_pal_cur != 0) {
|
||||
pal_load16(pop_guard_pal[guard_pal_cur - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Атласы стража (chtab_5, база 750): g[id>>3], idx id&7 ---------- */
|
||||
@@ -105,13 +134,7 @@ int pop_guard_load(void) __banked
|
||||
/* Цвет: у скелета он СВОЙ (палитра из его набора), у стража подменяется
|
||||
* из res10.bin по guards_color — это делает pop_guard_set_palette при
|
||||
* входе в комнату (seg002:186: у не-guard curr_guard_color = 0). */
|
||||
if (type == 2 || type == 3) {
|
||||
const uint8_t *src = (type == 2) ? pop_skel_pal : pop_vizier_pal;
|
||||
uint8_t buf[64], k;
|
||||
for (k = 0; k < 64; k++) buf[k] = src[k]; /* BIOS: буфер в W2 */
|
||||
gfx_pal_load(0, POP_GUARD_PAL_BASE, 16, buf);
|
||||
gfx_pal_load(1, POP_GUARD_PAL_BASE, 16, buf);
|
||||
}
|
||||
pop_guard_pal_apply();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -925,7 +948,7 @@ void pop_mirror_draw(int clip_top) __banked
|
||||
*
|
||||
* Спрайты Кида (216/217) лежат в его же атласах, поэтому блитим через
|
||||
* pop_kid_img_blit; спрайт стража — image 0 его chtab (наша страница 0). */
|
||||
#define HP_Y 194
|
||||
#define HP_Y POP_HP_Y /* полосу делит UI: см. pop_cdraw.h */
|
||||
#define HP_STEP 7
|
||||
#define HP_KID_FULL 216
|
||||
#define HP_KID_EMPTY 217
|
||||
@@ -962,10 +985,21 @@ void pop_hp_draw(void) __banked
|
||||
if (hp_todo == 0) return;
|
||||
hp_todo--;
|
||||
|
||||
/* Стереть прошлую полосу: фон под ней — чёрный борт. */
|
||||
/* Стереть прошлую полосу. Фон под ней — НЕ цвет 0, а POP_COL_OUTSIDE
|
||||
* (в палитре тоже чёрный): вспышки подменяют нулевой цвет, и полоса HP
|
||||
* не должна мигать вместе с игровым полем (pop_ui.h). */
|
||||
gfx_set_bank(GFX_BANK_NORMAL);
|
||||
setfillstyle(SOLID_FILL, BLACK);
|
||||
bar(0, HP_Y + POP_YOFF, 319, HP_Y + POP_YOFF + 6);
|
||||
setfillstyle(SOLID_FILL, POP_COL_OUTSIDE);
|
||||
/* Полосу мы делим со статус-строкой (pop_status.h): пока в ней висит
|
||||
* текст, его зону не трогаем — иначе он мигал бы при каждом изменении
|
||||
* жизней. Деления Кида левее POP_STATUS_L, стража — правее POP_STATUS_R,
|
||||
* так что чистить края по отдельности достаточно. */
|
||||
if (pop_status_ticks) {
|
||||
bar(0, HP_Y + POP_YOFF, POP_STATUS_L - 1, HP_Y + POP_YOFF + 6);
|
||||
bar(POP_STATUS_R + 1, HP_Y + POP_YOFF, 319, HP_Y + POP_YOFF + 6);
|
||||
} else {
|
||||
bar(0, HP_Y + POP_YOFF, 319, HP_Y + POP_YOFF + 6);
|
||||
}
|
||||
|
||||
/* Кид: сначала пустые деления, поверх — полные (порядок оригинала). */
|
||||
n = (uint8_t)(hitp_max > HP_MAXDRAW ? HP_MAXDRAW : hitp_max);
|
||||
|
||||
Reference in New Issue
Block a user