Files
Sprinter-SDCC/applications/SprPoP/src/pop_demo.c
T
snark13 31b82661eb SprPoP: автономное приложение, выделенное из roomtest
Порт 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>
2026-08-27 12:12:28 +03:00

74 lines
3.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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();
}