Files
Sprinter-SDCC/applications/PoP/roomtest/pop_tune.h
T
snark13 5b6664ffce Зеркальные кадры персонажей — готовыми файлами, а не разворотом в рантайме
Замеры в MAME (такты Z80, кадр = 430 000) решили вопрос однозначно:
  загрузка 28 атласов Кида с HDD  — 27 160 092 такта (1.26 с), 970 003 на страницу;
  разворот ОДНОЙ страницы на месте — 12 645 582 такта (0.59 с), т.е. 771 такт/байт.
Даже идеальный вариант (копия accel'ом ~0.25 кадра + asm-реверс ~2.2 кадра)
дал бы ~1.05 млн на страницу — то есть в лучшем случае сравнялся бы с чтением
готового файла.  На 34 страницы: 1.5 с загрузки против 20 с разворота.

* Упаковщики пишут второй набор: vflip_cols/vflip_page в pop_pack_kid.py ->
  kid0_v.atl…kid27_v.atl, sword_v.atl; pop_pack_guard.py -> g0_v.atl…g4_v.atl
  ТОЛЬКО для обычного стража (на уровне 9 tbl_guard_type = 0; тень рисуется
  атласами Кида, скелет/визирь с переворотом не встречаются).
* pop_vflip.c схлопнулся до таблицы «страница -> зеркальная пара»; весь
  побайтовый разворот и аллокация EMM удалены.
* pop_vflip_load_all (банк 8) грузит набор ОДНИМ вызовом и откуда угодно —
  задел под загрузку ресурсов во время интро.  Зовётся при старте и при
  смене уровня, если это POP_UPSIDE_LEVEL; чит U на других уровнях поднимает
  набор лениво (страховка в главном цикле).
* Стартовый уровень стал параметром сборки: make LEVEL=N (-DFIRST_LEVEL).

Проверено в MAME на уровне 9: переворот мгновенный, pop_flip_screen = 897 263
такта (2.1 кадра) — это сама копия экрана, загрузки в кадре больше нет.
tests-host 6/6.
2026-08-12 23:17:22 +03:00

130 lines
10 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_tune.h — НАСТРАИВАЕМЫЕ КОНСТАНТЫ движка (аналог custom_options
* SDLPoP) в одном месте.
*
* Зачем отдельный заголовок. У оригинала «особые события» уровней зашиты
* магическими числами прямо в код (`current_level == 3 && Char.room == 7`),
* и SDLPoP вынес их в структуру `custom_options_type` (types.h) с
* загрузкой из SDLPoP.ini и правкой через меню Settings/Mods. Нам нужно
* то же самое по двум причинам:
*
* 1. ОТЛАДКА. Некоторые сцены руками почти не проверить (успеть в
* закрывающуюся решётку), и единственный вменяемый способ — временно
* растянуть тайминг. Такие крутилки обязаны жить в одном месте, а не
* прятаться числами по трём модулям.
* 2. МОДЫ. Тот, кто захочет свой набор уровней, меняет ровно эти числа.
*
* Пока это только `#define` — ни INI, ни меню нет (см. TASKS_OPEN.md, запись
* TUNE-1). Но правило уже действует: НОВОЕ «особое событие» или тайминг
* заводится ЗДЕСЬ, а не константой по месту. Имена — как у SDLPoP
* (custom->имя), чтобы сверка с оригиналом оставалась механической.
*/
#ifndef POP_TUNE_H
#define POP_TUNE_H
/* ---- Чекпойнт уровня 3 (SDLPoP: checkpoint_*) ----------------------- *
* Оригинал: level3_set_chkp (seg002:0665) взводит флаг, когда Кид уходит
* ВЛЕВО ИЗ комнаты 7 (вызов стоит до goto_other_room, поэтому Char.room там
* ещё старая); do_startpos (seg003:0141) подменяет по
* нему стартовую позицию и убирает loose-плиту, через которую туда
* попадают (иначе после респавна дорога назад была бы уже провалена). */
#define POP_CHKP_LEVEL 3 /* checkpoint_level */
#define POP_CHKP_ROOM 7 /* комната, ИЗ КОТОРОЙ уходят влево (у
* SDLPoP хардкод; это НЕ комната
* назначения — см. roomtest.c) */
#define POP_CHKP_RESPAWN_ROOM 2 /* checkpoint_respawn_room */
#define POP_CHKP_RESPAWN_TILEPOS 6 /* checkpoint_respawn_tilepos */
#define POP_CHKP_RESPAWN_DIR (-1) /* checkpoint_respawn_dir: влево */
#define POP_CHKP_CLEAR_ROOM 7 /* checkpoint_clear_tile_room */
#define POP_CHKP_CLEAR_COL 4 /* checkpoint_clear_tile_col */
#define POP_CHKP_CLEAR_ROW 0 /* checkpoint_clear_tile_row */
/* ---- Отладочные крутилки (в оригинале и в SDLPoP их НЕТ) ------------ *
* POP_DBG_GATE_HOLD — сколько кадров решётка держится поднятой после
* нажатия кнопки, ПРЕЖДЕ чем пойдёт вниз. В оригинале это таймер связи
* дверей: trigger_button ставит 5, animate_button вычитает по единице, и
* решётка стоит, пока таймер > 1 (то есть ~4 кадра после того, как Кид
* сошёл с кнопки). Проверять руками проход в закрывающуюся решётку при
* таком окне почти невозможно, поэтому значение вынесено сюда: поставь
* 30 — и успеешь дойти спокойно. ОБЯЗАН быть 5 в игровой сборке.
*
* ПОТОЛОК 30. Таймер связи — пять бит модификатора кнопки (маска 0x1F), а
* значение 31 у оригинала означает «событие заклинено» (`link_timer != 0x1F`
* в trigger_button). Больше 30 ставить нельзя — кнопка перестанет
* срабатывать вовсе. */
#define POP_DBG_GATE_HOLD 5
/* ---- Улучшения SDLPoP (в ОРИГИНАЛЕ их нет) -------------------------- *
* POP_ENABLE_JUMP_GRAB — зацепиться за кромку можно ПРЯМО В ПРЫЖКЕ, если
* Shift зажат заранее (SDLPoP `enable_jump_grab`, seg006:1228). Ваниль
* разрешает зацеп только в начале падения (`check_grab`, кадры 102..105),
* то есть Shift приходится нажимать уже в полёте — это и раздражало на
* приёмке. 0 — как в оригинале, 1 — как в SDLPoP с включёнными
* enhancements. */
#define POP_ENABLE_JUMP_GRAB 1
/* ---- Чомперы (SDLPoP: custom->chomper_speed) ------------------------ *
* Период смыкания челюстей в кадрах: фаза крутится 1..N и сбрасывается в 1.
* В оригинале 15; SDLPoP выносит это в настройку, потому что на разных
* уровнях моды любят делать чомперы быстрее. Меньше — злее. */
#define POP_CHOMPER_SPEED 15
/* ---- Отладка сцены «тень крадёт зелье» (уровень 5) ------------------ *
* Тень появляется в комнате 24 сразу при входе, но СТОИТ, пока не откроют
* ворота (кол 1, ряд 0, openness >= 80) — так в оригинале
* (autocontrol_shadow_level5, seg002:1157). Чтобы посмотреть саму сцену
* (подход, питьё, уход), не проходя уровень до кнопки, поставь 1: тень
* стартует запись ходов сразу. ОБЯЗАН быть 0 в игровой сборке. */
#ifndef POP_DBG_SHADOW_NOWAIT
#define POP_DBG_SHADOW_NOWAIT 0
#endif
/* ---- Зелье МЕДЛЕННОГО ПАДЕНИЯ (SDLPoP: FEATHER_FALL_LENGTH) ---------- *
* В оригинале длительность эффекта задана НЕ таймером, а звуком: эффект
* держится, пока играет sound_39_low_weight, и снимается принудительно на
* 225-м тике (do_timers, seg003:0517). Звука у нас нет, поэтому берём тот
* же порог напрямую — 225 ИГРОВЫХ КАДРОВ (у оригинала тик do_timers = кадр
* игрового цикла; 225 кадров при его 12 fps и есть FEATHER_FALL_LENGTH =
* 18,75 с из фикса SDLPoP). По ВРЕМЕНИ у нас выйдет короче ровно настолько,
* насколько наша игра быстрее оригинала — это общий вопрос темпа
* (TASKS_OPEN.md, L1-SPEED), а не свойство зелья. */
#define POP_FEATHER_FRAMES 225
/* ---- Спецсобытие «МЫШЬ» (уровень 8, комната 16) ---------------------- *
* Кнопка (0,7) комнаты 16 открывает решётку (0,3) — ту, за которой лежит
* путь через комнату 4 (два чомпера) к кнопке выхода в комнате 3. Сам Кид
* до кнопки с левой площадки не дотягивается (провал в колонках 4..6),
* поэтому, когда дверь уровня уже открыта, а он всё ещё в комнате 16,
* оригинал через 150 тиков присылает мышь: она пробегает справа налево по
* верхнему ряду, наступает на кнопку и убегает обратно (do_timers
* seg003:0545, do_mouse seg003:0ABA, autocontrol_mouse seg002:07EB).
*
* Числа — custom-константы SDLPoP (data.h:821): уровень/комната/задержка/
* стартовая X. Пороги разворота (166) и исчезновения (200) — литералы
* autocontrol_mouse. Задержка в ИГРОВЫХ КАДРАХ, как POP_FEATHER_FRAMES. */
#define POP_MOUSE_LEVEL 8
#define POP_MOUSE_ROOM 16
#define POP_MOUSE_DELAY 150
#define POP_MOUSE_START_X 200
#define POP_MOUSE_TURN_X 166 /* x < этого — развернуться и уйти */
#define POP_MOUSE_LEAVE_X 200 /* x >= этого в стойке — исчезнуть */
/* ---- Уровень зелий (SDLPoP: «potions level», seg006:1894) ------------ *
* На нём синие склянки «вреда» забирают ПОЛОВИНУ запаса HP, а не единицу.
* В оригинале это условие `current_level == 15` прямо в proc_get_object. */
#define POP_POTIONS_LEVEL 15
/* С какого уровня начинается игра (-DFIRST_LEVEL=N при сборке). Живёт
* здесь, а не в roomtest.c: старт уровня уехал в холодную половину цикла
* (roomtest_cold.c, банк 8), и константа нужна обеим. */
#ifndef FIRST_LEVEL
#define FIRST_LEVEL 1
#endif
/* Уровень с зельем инверсии (переворот экрана). По нему делается
* ПРЕДзагрузка зеркального набора спрайтов персонажей — чтобы пауза на
* чтение 34 страниц пришлась на загрузку уровня, а не на глоток зелья. */
#define POP_UPSIDE_LEVEL 9
#endif /* POP_TUNE_H */