Меню, статус-строка и оболочка игры: 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:
@@ -595,6 +595,15 @@ static int dist_from_wall_forward(uint8_t tiletype, int8_t tcol)
|
||||
{
|
||||
uint8_t type = wall_type(tiletype);
|
||||
int coll_left, cx;
|
||||
/* ОТКРЫТЫЕ ворота стеной НЕ являются (seg004:0A18: `if (tiletype ==
|
||||
* tiles_4_gate && !can_bump_into_gate()) return -1`). Без этой ветки
|
||||
* get_edge_distance считал расстояние до створки как до стены и отдавал
|
||||
* маленькое d с типом EDGE_WALL: осторожный шаг играл короткую
|
||||
* последовательность (safe_step берёт seq по самому d), и к краю
|
||||
* приходилось шагать ДВАЖДЫ. Возврат −1 уводит разбор на тайл впереди,
|
||||
* где открытые ворота опознаются как обычный пол (d = 11, полный шаг).
|
||||
* Поймано пользователем 2026-08-24 (подход слева направо). */
|
||||
if (tiletype == TILE_GATE && gate_passable(tcol, Char.curr_row)) return -1;
|
||||
if (type == 0) return 127;
|
||||
coll_left = pop_x_bump[tcol + FIRST_ONSCREEN_COLUMN] + TILE_MIDX;
|
||||
cx = char_front_coll();
|
||||
@@ -1367,14 +1376,11 @@ void pop_proc_get_object(void) __banked
|
||||
* лечебных): красный кадр даёт общий механизм «Кид ранен»
|
||||
* (flash_if_hurt, seg003:0AFC) — у нас pop_kid_hurt. Раньше мы
|
||||
* ставили вспышку руками, то есть красили экран дважды.
|
||||
* Спецсобытие уровня зелий: там синие склянки забирают ПОЛОВИНУ
|
||||
* запаса, а не одну единицу. */
|
||||
* Level 15/copy protection отсутствует, поэтому синие склянки
|
||||
* всегда забирают ровно одну единицу здоровья. */
|
||||
pop_sfx_stop(); /* seg006:1893 — stop_sounds */
|
||||
pop_sfx_play(13);
|
||||
if (pop_current_level == POP_POTIONS_LEVEL)
|
||||
hitp_delta = (int8_t)(-((hitp_max + 1) >> 1));
|
||||
else
|
||||
hitp_delta = -1;
|
||||
hitp_delta = -1;
|
||||
break;
|
||||
default: /* 6 «открыть все ворота» — TODO
|
||||
* вместе с его уровнем (13) */
|
||||
@@ -3159,6 +3165,43 @@ static void chomped(uint8_t tilepos, int8_t col)
|
||||
if (Char.charid == CHARID_0_KID) pop_kid_dead = 1;
|
||||
}
|
||||
|
||||
/* check_gate_push (seg004:0833): ОПУСКАЮЩАЯСЯ решётка выталкивает персонажа
|
||||
* из своего тайла. Без неё Кид, зашедший в тайл ворот при поднятой решётке,
|
||||
* так и остаётся в нём после опускания — «в суперпозиции»: створка рисуется
|
||||
* то перед ним, то за ним (слой выбирается по направлению взгляда), и уйти
|
||||
* он может в обе стороны. Поймано пользователем 2026-08-24; то же и для
|
||||
* попытки проползти под опускающейся решёткой в присяде.
|
||||
*
|
||||
* Условия оригинала — только позы, где персонаж стоит на месте и его можно
|
||||
* сдвинуть, не ломая анимацию: РАЗВОРОТ (action 7), СТОЙКА (кадр 15) и
|
||||
* кадры приседа 108..110. Решётка ищется в своём тайле И в тайле СЛЕВА:
|
||||
* створка нарисована у ПРАВОЙ грани тайла, поэтому левый сосед давит так
|
||||
* же. Перекрытие обязано быть обеими гранями и в этом кадре, и в прошлом
|
||||
* (у оригинала `curr_row_coll_flags & prev_coll_flags == 0xFF`) — иначе
|
||||
* толчок срабатывал бы на пролёте мимо. Сам толчок — 5 пикселей: ВЛЕВО,
|
||||
* если решётка в тайле персонажа, ВПРАВО — если в левом соседе. */
|
||||
static void check_gate_push(void)
|
||||
{
|
||||
int8_t orig_col = Char.curr_col, col, row = Char.curr_row;
|
||||
uint8_t frame = Char.frame;
|
||||
|
||||
if (Char.action != ACT_TURN && frame != 15 &&
|
||||
!(frame >= 108 && frame < 111)) return;
|
||||
col = orig_col;
|
||||
if (get_tile(col, row) != TILE_GATE) {
|
||||
col--;
|
||||
if (get_tile(col, row) != TILE_GATE) return;
|
||||
}
|
||||
/* Слот флагов годен только внутри ОКОН обоих кадров: вне их лежат
|
||||
* значения прошлых кадров (та же оговорка, что в check_chomped_flags). */
|
||||
if (col < coll_lo || col > coll_hi ||
|
||||
col < coll_prev_lo || col > coll_prev_hi) return;
|
||||
if ((coll_curr[COLL_IDX(col)] & coll_prev[COLL_IDX(col)]) != 3) return;
|
||||
if (gate_passable(col, row)) return; /* can_bump_into_gate */
|
||||
pop_sfx_play(8); /* bumped_sound (seg004:05F1) */
|
||||
Char.x = (uint8_t)((int)Char.x + (orig_col <= col ? -5 : 5));
|
||||
}
|
||||
|
||||
/* check_chomped_kid (seg004:076B): по колонкам ряда — там, где персонаж
|
||||
* перекрывает тайл ОБЕИМИ гранями (у оригинала curr_row_coll_flags == 0xFF,
|
||||
* у нас оба бита) и этот тайл — СОМКНУТЫЙ чомпер (modif & 0x7F == 2),
|
||||
@@ -3221,6 +3264,7 @@ static void kid_phys(void)
|
||||
pop_dbg_p6(); /* ЗАМЕР: check_collisions целиком */
|
||||
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
|
||||
check_bumped(); /* удержать у стены до check_action (порядок PoP) */
|
||||
check_gate_push(); /* seg004: опустившаяся решётка выталкивает из тайла */
|
||||
check_action();
|
||||
pop_dbg_p7(); /* ЗАМЕР */
|
||||
pop_beam_sample(); /* ТЕМП КАДРА: выборка луча (pop_pace.h) */
|
||||
|
||||
Reference in New Issue
Block a user