Files
Sprinter-SDCC/applications/SprPoP/src/_pop_bg.h
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

64 lines
4.6 KiB
C

/*
* _pop_bg.h — ВНУТРЕННИЙ контракт между двумя половинами слоя фона.
* Публичный API слоя — pop_bg.h; общие листья (резидент) — pop_tile.h.
*
* Слой фона разрезан на два банка:
* pop_bg.c (банк 2) — ГОРЯЧАЯ половина: fore-проход поверх персонажей,
* оверлеи, клип. Работает каждый кадр.
* pop_room.c (банк 7) — ХОЛОДНАЯ: полная отрисовка комнаты (draw_tile),
* точечные перерисовки тайлов, падающие плиты,
* загрузка атласов. Раз на комнату/событие.
*
* Резать пришлось потому, что банк 2 упирался в 16 КБ страницы (90.4 % до
* разгрузки), а чомперам и остальным ловушкам уровня 3 место нужно. Граница
* проведена по частоте вызова: холодная половина платит трамплин (~654 такта)
* за каждый вызов сюда, и на входе в комнату таких вызовов ~40 — 26 000
* тактов, шестая часть кадрового бюджета РАЗОВО. Для горячего пути цена
* нулевая: он зовёт свои функции прямо, а общие листья — из резидента.
*
* Что осталось в банке 2 и вынесено сюда обёртками: кладка стены
* (wall_pattern — её зовёт и fore-проход каждый кадр) и решётка ворот
* соседней комнаты (draw_gate_back — её зовёт шовная окклюзия). Обратных
* зависимостей (горячая половина → холодная) нет вовсе.
*/
#ifndef _POP_BG_H
#define _POP_BG_H
#include <stdint.h>
/* Состояние дрожащих плит — живёт в pop_map, читают обе половины. */
extern uint8_t pop_loose_modif[30];
extern uint8_t pop_ceil_modif[10]; /* плита-потолок: loose ряда 2 комнаты сверху */
/* Видимая высота полосы кладки у потолка (room-space y 0..7): её печёт
* холодная половина, а поверх персонажа кладёт горячая. */
#define CEIL_BAND_H 8
/* Тайл ОБЪЕКТА активного персонажа (set_objtile_at_char, seg006:1833) — в
* момент обработки этого тайла спрайт попадает в midtable. Задаёт горячая
* половина (pop_fore_over_char), читают other_overlay_tile (там же) и
* порядок «плита vs Kid» в mob_tick_one (холодная). Писучие данные банка
* лежат в _DATA/W2 и видны из любого банка — см. sdcc_banked_call_rules. */
extern int8_t pop_bg_obj_col, pop_bg_obj_row;
/* ---- Точки входа ГОРЯЧЕЙ половины для холодной (через трамплин) ------- */
void pop_wall_pattern_b(int row, int col, int which_part) __banked;
void pop_wall_pattern_reset_b(void) __banked;
void pop_gate_back_b(uint8_t modl, int xh, int dby, int dmy) __banked;
#endif
/* ---- Слоты «первая запечка была на странице N» ----------------------- *
* bake_copy (pop_room.c) копирует готовый прямоугольник со второй страницы
* дабл-буфера вместо повторного пересчёта. Это верно ТОЛЬКО пока содержимое
* тайла между двумя кадрами не изменилось. У анимированных тайлов (кнопка с
* идущим таймером связи, ворота) пометка ОБНОВЛЯЕТСЯ каждый кадр, и картинка
* при этом другая — такой тайл обязан пересчитываться честно.
*
* Поэтому pop_set_redraw / pop_set_redraw_above гасят слот на ПЕРЕпометке
* (пометка пришла, когда счётчик ещё не отработал). Объявления БЕЗ __banked:
* pop_redraw.c и pop_room.c живут в одном банке, и трамплин тут не нужен. */
void pop_bake_slot_reset(uint8_t tilepos);
void pop_bake_slot_reset_above(uint8_t col);