/* * pop_bg.h — отрисовка СТАТИЧЕСКОГО фона комнаты PoP композицией слоёв * (порт seg008.c draw_tile/wall_pattern, Шаг 3). Рисует прямо в текущую * страницу (banked GFX_BANK_TRANSPARENT — в видео-ОЗУ + теневую копию, * 0xFF прозрачен), ОДИН раз при входе в комнату. * * Данные: getimage-спрайты в атласах pop_env0..4/pop_wall/pop_fore * (toolchain/pop_pack_bg.py), палитра pop_bg.pal. Прямая адресация * id: env[id>>5] idx id&31, wall idx=id, fore idx=id — ноль remap-таблиц. */ #ifndef POP_BG_H #define POP_BG_H #include #include "pop_char.h" /* pop_char_t — общий проход отрисовки поверх Char */ /* ---- Вертикальный layout (PoP-экран 320×200 в нашем 320×256) --------- * * Игровой экран 200px центрируем по высоте: сдвиг всего рисунка вниз на * POP_YOFF = (256-200)/2 = 28px (чёрные борта сверху/снизу). Внутри 200px * верхние POP_PLAYFIELD_H = 192 — поле тайлов/спрайтов (комната = 3 ряда), * нижние 8 — статус-полоса (жизни/сообщения): тайлы/спрайты туда НЕ лезут. * Сдвиг применяется на границе примитивов (blit/heal) — логика рисования * остаётся в «room-space» y=0..191. */ #define POP_YOFF 28 #define POP_PLAYFIELD_H 192 /* Экранный y нижней границы поля (ниже — статус-полоса + борт → клип). */ #define POP_CLIP_BOTTOM (POP_YOFF + POP_PLAYFIELD_H) /* Экранный верх прямоугольника при ПЕРЕВОРОТЕ (зелье инверсии): поле * отражается внутри себя, борта не трогаются. ОДНА формула на весь порт — * блит тайла, heal точечных перерисовок и чёрные плиты обязаны считать * одинаково, иначе стираем в одном месте, а рисуем в другом (именно так * ломались чомперы и решётка). Зеркалит ВЫЗЫВАЮЩИЙ; у примитивов libbgi * семантика координат не меняется. */ #define POP_FLIP_TOP(top, h) (2 * POP_YOFF + POP_PLAYFIELD_H - (top) - (int)(h)) /* Выбор ядра libbgi (pop_onscreen_cols / pop_heal_fast) переехал в * `_pop_draw.h` — его включают только три рисующих модуля. Из общего * заголовка SDCC размножал мёртвые копии этих inline'ов по всем TU: см. * шапку `_pop_draw.h`. */ /* Загрузить 7 атласов фона. Звать ДО initgraph (как poc.c — atlas_load * трогает W3; проверенный порядок). Палитру pop_bg.pal грузит ПРИЛОЖЕНИЕ * после initgraph (gfx_pal_fload). 0 — OK, -1 — ошибка. */ int pop_bg_load(uint8_t set) __banked; /* 0 подземелье, 1 дворец */ /* Пере-применить 32 записи палитры текущего тайлсета (env 0x50, wall 0x60). * Звать после gfx_pal_fload(kid.pal): та собрана из VDUNGEON и затирает их. */ int pop_bg_pal_apply(void) __banked; /* Освободить EMM-страницы атласов. */ void pop_bg_free(void) __banked; /* Нарисовать комнату: fg[30]/bg[30] — foretable-коды/backtable-модификаторы * (по строкам, row*10+col). lcol_fg/lcol_bg — 3 тайла правого столбца * ЛЕВОЙ соседней комнаты (для границы, напр. решётки ворот), или 0 — * тогда левая кромка = стена. room_num — номер комнаты (зерно стен). */ void pop_room_draw(uint8_t room_num, const uint8_t *fg, const uint8_t *bg, const uint8_t *lcol_fg, const uint8_t *lcol_bg) __banked; /* Задать ряд 0 комнаты СНИЗУ — для draw_tile_topright, когда тайл нижнего * ряда стал empty (после падения loose): «стена вниз» рисуется по коду * room_B(0, col-1). ОДИННАДЦАТЬ байт: [0..9] — колонки комнаты снизу, * [10] — тайл (0,9) комнаты СНИЗУ-СЛЕВА (сосед угла (2,0); см. * load_rowbelow, seg008:368). 0 — нет комнаты снизу. */ /* НЕ __banked: тело в резиденте (pop_tile.c) — из банка зовётся прямым call. */ void pop_room_set_below(const uint8_t *below_row0_fg); /* Задать ряд 2 (fg-коды + модификаторы, 10 шт.) комнаты СВЕРХУ — для полосы * кладки у потолка (seg008 draw_room доп. верхний ряд из room_A). 0 — нет * комнаты сверху (кромка уровня → пол). */ void pop_room_set_above(const uint8_t *above_row2_fg, const uint8_t *above_row2_mod); /* Перерисовать передний слой (fore) и оверлеи тайлов ПОВЕРХ персонажа — * ОДИН проход на всех Char (Кид, страж, скелет, дальше тень/визирь): в * оригинале add_kid_to_objtable и add_guard_to_objtable имеют одинаковое * тело, а redraw_at_char/redraw_at_char2 гейтов по charid не имеют. * Звать ПОСЛЕ отрисовки спрайта, когда окно fore-клипа уже стоит * (pop_fore_set_clip) — перебор тайлов расширяется по нему. * * ch — персонаж (Kid/Guard): из него берутся direction/sword/frame/action и * curr_col/curr_row. obj_x — ЛОГИЧЕСКАЯ X кадра (до ×8/7), obj_y — низ * спрайта, w/h — размер кадра. См. redraw_at_char/set_char_collision. */ void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y, uint16_t w, uint16_t h) __banked; /* Окно fore-слоя: прямоугольник СПРАЙТА (экранные координаты, y уже с * POP_YOFF), поверх которого fore-проход возвращает куски тайлов. Ставит * тот, кто нарисовал спрайт (pop_char_draw) — он один знает * итоговый габарит (у Kid — с учётом клинка и обрезки clip_char). Вне * fore-проходов окно игнорируется: фон рисуется целиком. Мотивация и * замер — в pop_bg.c у fclip_on. */ void pop_fore_set_clip(int x, int y, int w, int h); /* Восстановить фон там, где в прошлых кадрах рисовался ОВЕРЛЕЙ * (draw_other_overlay/floor_overlay/полоса потолка): он выходит за * прямоугольник персонажа, поэтому его heal этого не убирает — на второй * странице дабл-буфера остаётся след и мерцает. Звать рядом с * pop_char_heal. */ void pop_fore_heal(void) __banked; /* Loose floors — динамическая отрисовка (состояние pop_loose_modif в * pop_map). Звать на ТЕКУЩЕЙ (back) странице из pop_loose_tick. * shake_draw — дрожащий кадр (heal+SPRITE); bake_empty — запечь пустоту * после падения (NORMAL, в ОЗУ-копию). */ void pop_loose_shake_draw(int row, int col) __banked; void pop_loose_bake_empty(int row, int col) __banked; /* Плита-ПОТОЛОК (loose ряда 2 комнаты СВЕРХУ, наш ряд −1): дрожащий кадр в * полосе у потолка / «запечь» пустоту (колодец наверх) после провала. * Состояние — pop_ceil_modif[10] в pop_map. */ void pop_ceil_shake_draw(int col) __banked; void pop_ceil_bake_empty(int col) __banked; /* Кусок приземлился (loose_land) — переехал в pop_state.h: переменную * читают по обе стороны банковой границы. */ #include "pop_state.h" /* Пики (spike) — перерисовка тайла (row,col) на back-странице по живому * modif (pop_trob). Звать из pop_process_trobs. См. pop_bg.c. */ void pop_spike_redraw(int row, int col) __banked; /* Чомпер — то же самое для его кадра анимации (все куски в СВОЕЙ ячейке, * поэтому соседа перерисовывать не надо). */ void pop_chomp_redraw(int row, int col) __banked; /* Анимация факела/зелья (динамика каждый кадр; фон запечён без них): * modif — живой room_modif тайла. Для факела col — колонка САМОГО факела * (пламя рисуется в ячейке правого соседа). */ void pop_torch_draw(int row, int col, uint8_t modif) __banked; /* Стереть запечённое пламя факела с текущей draw-страницы (фон покоя). * Нужно перевороту экрана: иначе отражение растаскивает языки огня по * комнате — подробности в pop_room.c. */ void pop_torch_wipe(int row, int col) __banked; void pop_potion_draw(int row, int col, uint8_t modif) __banked; /* Ворота на ЛЕВОМ шве (col0) — перерисовать по живому modif соседа слева. * g_lbg (lcol_bg) должен быть обновлён вызывающим. Звать из основного цикла * на back-странице, когда сосед слева анимируется. См. pop_bg.c. */ void pop_room_redraw_seam_left(uint8_t rows) __banked; /* rows = маска рядов 0..2 */ /* Перерисовать ПОЛ тайла (row,col) + правую грань в соседе на back-странице * (wipe по площади пола, БЕЗ стены ряда ниже). Для смены тайла на месте: * кнопка нажата/отжата, floor → debris от упавшей плиты. */ void pop_floor_bake(int row, int col) __banked; void pop_button_redraw(int row, int col) __banked; /* = pop_floor_bake (кнопка) */ /* Дверь уровня: перерисовать её ПРАВУЮ половину (row, col = правая) с * запечкой — створка анимируется (pop_trob animate_leveldoor). */ void pop_leveldoor_redraw(int row, int col) __banked; /* Ворота ВНУТРИ отрисованной комнаты: перерисовать по живому modif. * (row,col) — координаты САМИХ ворот; бары рисует ячейка правого соседа * (draw_tile, ветка lcode==4), туда и идёт wipe+redraw. Порт draw_trob * (seg007:01E6), которым заканчивается animate_door. */ void pop_gate_redraw(int row, int col) __banked; /* Падающий кусок loose (mob, seg007 move_loose): спавн при падении тайла * (row,col) и покадровое продвижение+отрисовка на back-странице. tick * зовётся каждый кадр (внутри pop_loose_tick); клип низа делает clip. */ void pop_loose_mob_spawn(int row, int col) __banked; /* То же, но в ЯВНО заданной комнате: плита, расшатанная Кидом и брошенная * (он ушёл в соседнюю комнату), доваливается сама — см. animate_loose в * pop_trob.c. Дальше кусок летит обычным путём: mob_tick_one умеет чужую * комнату (mob_tile_at читает уровень, посадку сигналит pop_loose_exit). */ void pop_loose_mob_spawn_at(uint8_t room, int row, int col) __banked; void pop_loose_mob_tick(void) __banked; void pop_loose_mob_reset(void) __banked; /* убрать все куски (СТАРТ УРОВНЯ, mobs_count=0) */ /* Забыть, на какой странице сделана первая запечка тайлов (см. bake_copy в * pop_room.c). Звать при смене комнаты/уровня: номера тайлов повторяются, и * первый же тайл с тем же номером получил бы копию вместо честной запечки. */ void pop_bake_forget(void) __banked; /* Передний слой полосы кладки у потолка (ряд −1) для одного столбца — зовёт * проход по пометкам, ряд −1 в нём последний (порт redraw_needed_above + * порядка redraw_needed_tiles, seg008:1B06). */ void pop_ceil_fore_tile_b(int col) __banked; /* Оверлей тайла (row,col) ПОВЕРХ падающего куска, чей тайл — (oref_row, * oref_col): порт set_redraw2 из draw_mob (seg007:1149). Порядок обхода * проверяется внутри. */ void pop_mob_overlay_tile(int row, int col, int8_t oref_row, int8_t oref_col, int mx, int my, uint8_t mh) __banked; /* Смена комнаты: куски продолжают падать в СВОИХ комнатах (do_mobs крутит * mobs[] независимо от drawn_room), сбрасывается только heal-история. */ void pop_loose_mob_room_changed(void) __banked; /* Кусок НИЖЕ Kid по obj_y → рисуется ПОВЕРХ него (порт sort_curr_objs, * seg008:1561). Звать после отрисовки персонажей, перед fore-слоем. */ /* Отрисовка падающих кусков, идущих ПОД Kid. Звать ПОСЛЕ pop_redraw_needed() * — фон кадра обязан быть готов до любого спрайта. */ /* Передний слой ОДНОГО тайла — зовёт pop_fore_needed (pop_redraw.c). */ void pop_fore_tile_b(int row, int col) __banked; void pop_fore_pass_begin(void) __banked; void pop_fore_pass_end(void) __banked; void pop_loose_mob_draw(void) __banked; void pop_loose_mob_draw_over(void) __banked; /* Позиция куска (колонка, y низа) для check_loose_fall_on_kid. 0 — нет. */ uint8_t pop_loose_mob_pos(int *col, int *y) __banked; /* Детектор выхода спрайта за поле: если спрайт [x,w] с ЭКРАННЫМ верхом * sy_top и высотой h заходит ниже поля (статус-полоса/борт) или выше * верхнего борта — взводит внутренний флаг «борта грязные» на 2 кадра. * Звать СРАЗУ после блита падающего объекта (Kid/кусок). */ void pop_clip_sprite(int x, int w, int sy_top, int h) __banked; /* Почистить борта (верхний + статус-полоса/нижний) — но ТОЛЬКО в кадрах, * где кто-то реально вышел за поле (гейт по флагу от pop_clip_sprite): * даром в покое, O(1) при падении (не зависит от числа спрайтов). Звать * КАЖДЫЙ кадр ПОСЛЕ персонажей и fore на back-странице. */ void pop_room_clip_borders(void) __banked; #endif