47a4b084c4
Эта сессия (меню + текст в служебных полосах):
* Меню: рамка выделения считается от силуэта текста (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>
74 lines
3.2 KiB
C
74 lines
3.2 KiB
C
/*
|
||
* pop_demo.c — воспроизведение attract-demo SDLPoP, банк 9 (FG7).
|
||
*
|
||
* Это не отдельная физика и не сценарные телепорты. Таблица custom->demo_moves
|
||
* подаёт ровно те же control_* в pop_ctrl_auto_move(), через которые идут
|
||
* игрок и автоконтроль персонажей. Поэтому демо проверяет настоящий маршрут
|
||
* level 0, а не его нарисованную имитацию.
|
||
*/
|
||
#include <stdint.h>
|
||
#include <kbd_raw.h>
|
||
#include "pop_ctrl.h"
|
||
#include "pop_guard.h"
|
||
#include "pop_map.h" /* hitp_curr — смерть demo имеет приоритет над AI */
|
||
#include "pop_demo.h"
|
||
|
||
typedef struct {
|
||
uint8_t time;
|
||
int8_t move;
|
||
} demo_move_t;
|
||
|
||
/* data.h SDLPoP: демо пробегает уровень 0 до комнаты 24. Последний -1
|
||
* означает «больше не менять уже поданный control_*», а не ввод. */
|
||
static const demo_move_t demo_moves[] = {
|
||
{0x00, 0}, {0x01, 1}, {0x0D, 0}, {0x1E, 1}, {0x25, 5},
|
||
{0x2F, 0}, {0x30, 1}, {0x41, 0}, {0x49, 2}, {0x4B, 0},
|
||
{0x63, 2}, {0x64, 0}, {0x73, 5}, {0x80, 6}, {0x88, 3},
|
||
{0x9D, 7}, {0x9E, 0}, {0x9F, 1}, {0xAB, 4}, {0xB1, 0},
|
||
{0xB2, 1}, {0xBC, 0}, {0xC1, 1}, {0xCD, 0}, {0xE9, -1},
|
||
/* do_auto_moves читает moves[demo_index] ещё после сдвига индекса.
|
||
* Это недостижимая метка из data.h, не команда: она держит индекс в
|
||
* границах, пока demo_time доходит до 0xFE. */
|
||
{0xFF, -2}
|
||
};
|
||
|
||
static uint8_t demo_time;
|
||
static uint8_t demo_index;
|
||
|
||
void pop_demo_begin(void) __banked
|
||
{
|
||
demo_time = 0;
|
||
demo_index = 0;
|
||
}
|
||
|
||
uint8_t pop_demo_tick(void) __banked
|
||
{
|
||
uint8_t index;
|
||
int8_t move;
|
||
|
||
/* Урон мечом сводится ПОСЛЕ этого вызова в предыдущем игровом кадре.
|
||
* На следующем тике HP уже ноль, но pop_kid_dead ещё не поднят: обычный
|
||
* pop_ctrl_tick делает это в ctrl_kid_death(), а demo обходит его своим
|
||
* AI. Без этого гарда AI снова управлял мёртвым Кидом, возвращал его в
|
||
* стойку и страж убивал его по кругу. Attract-demo при смерти кончается;
|
||
* текущий главный цикл ещё дорисует один последний кадр и уйдёт в title. */
|
||
if (hitp_curr == 0) return 1;
|
||
|
||
/* do_demo(): в бою запись времени/кнопок стоит, Кидом управляет тот же
|
||
* combat AI, что стражами, но с отдельным skill 10. */
|
||
if (pop_demo_kid_ai()) return 0;
|
||
if (demo_time >= 0xFE) return 1;
|
||
++demo_time;
|
||
index = demo_index;
|
||
if (demo_moves[index].time <= demo_time) ++demo_index;
|
||
else index = (uint8_t)(demo_index - 1);
|
||
move = demo_moves[index].move;
|
||
pop_ctrl_auto_move(move);
|
||
return (uint8_t)(demo_time >= 0xFE);
|
||
}
|
||
|
||
uint8_t pop_demo_user_input(void) __banked
|
||
{
|
||
return kbd_raw_any_down();
|
||
}
|