31b82661eb
Порт PoP переехал в applications/SprPoP — приложение, которое собирается само: код, оригинальные данные, конверторы ресурсов и сборка внутри одной папки. Наружу знает единственный путь — корень тулчейна (SPRINTER_ROOT, по умолчанию ../..). applications/PoP/roomtest ЗАМОРОЖЕНА и остаётся архивом закрытых задач, багов и исполненных планов. Скопировано из applications/PoP/roomtest@4b74478. Перенос проверен побайтово: собранный sprpop.exe совпал с roomtest.exe того же коммита, все 39 дисковых ресурсов и все 16 генерируемых заголовков — тоже, host- тесты зелёные (15/15). Раскладка: src/ рукописный C (roomtest.c -> sprpop.c) gen/ генерируемые заголовки, в репозитории assets/orig/ оригинальные данные игры, вне репозитория (копирайт) assets/packed/ то, что ложится на диск, в раскладке диска tools/ конверторы; все пути — в одном tools/paths.py build/ выход: exe, каталоги ресурсов, hdd/, промежуточные atl/ Сборка ресурсов: assets/packed и gen — версионируемые ВХОДЫ, а не то, что пересчитывается каждым make. Автоматика построена на ОТСУТСТВИИ файла, а не на таймстемпах: git не хранит времена, и в свежем клоне сравнение по времени превращалось бы в лотерею. Недостающий ресурс или заголовок чинится сам, рекурсивным вызовом в ветку генерации. Музыка собирается из любого из четырёх наборов записей (make music-mp3, music-mt32, ...); набор входит в имя stamp'а, поэтому смена набора сама делает музыку устаревшей. Длины реплик больше не захардкожены: упаковщик печатает их в gen/pop_music_ticks.h, и шкала сцены выражена через них — иначе mt32 (реплики на 6% длиннее) молча ломал катсцену. Тулчейн: в app.mk два обратносовместимых крючка (SRC_DIR/BUILD_DIR), HDD_IMG стал ?=; команда сборки roomtest не изменилась. Корневой make host-tests переключён на SprPoP. Подгонка тайминга катсцены с принцессой (PV_MAGIC_LEAD): сцена render-bound и идёт ~49 тиков/с вместо 60, из-за чего кода реплики приходила раньше молнии. Это обход, а не лечение; разбор с замерами — docs/BUGS_OPEN.md, записи SND-PACE-DEAD, PV-RENDER-BOUND, MUS-LEFT-TEAR. 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();
|
||
}
|