/* * pop_tile.c — общие ЛИСТЬЯ слоя фона: блит куска атласа, чтение тайла * комнаты, таблица тайлов. РЕЗИДЕНТ W1 (_CODE). * * Мотивация раскладки (почему резидент, а не банк) — в шапке pop_tile.h: * коротко, эти функции и таблицы нужны ОБЕИМ половинам слоя фона (горячей — * fore-проход каждый кадр, холодной — полная отрисовка комнаты), а код и * rodata банка видны только пока банк замаплен. W1 замаплено всегда. */ #include #include #include #include "pop_bg.h" /* POP_YOFF */ #include "pop_cdraw.h" /* POP_CD_TOUCH — фон тронут, персонажей перерисовать */ #include "pop_tile.h" #include "pop_level.h" /* pop_doorlink2 — подстановка нажатой кнопки */ #include "_pop_draw.h" /* pop_heal_fast */ /* ---- Состояние (писучее, _DATA/W2 — видно из любого банка) ---------- */ atlas_t pop_env[POP_ENV_PAGES], pop_wall_at, pop_fore_at, pop_pot_at; /* Загруженный тайлсет: 0 подземелье, 1 дворец (порт tbl_level_type в той * части, что касается ОТРИСОВКИ). Ставит pop_bg_load; резидент — потому * что по нему расходятся ветки и в банке 7 (полная отрисовка тайла), и в * банке 2 (fore-проход), и в банке 3 (модификатор пустой клетки от упавшей * плиты, remove_loose seg007:0EB8). Читать напрямую, без трамплина. */ uint8_t pop_palace; const uint8_t *pop_t_fg, *pop_t_bg, *pop_t_lfg, *pop_t_lbg; const uint8_t *pop_t_below, *pop_t_above, *pop_t_abovemod; uint8_t pop_t_room; pop_rnd_t pop_t_seed; int pop_t_clip_top; int pop_t_fclip_x0, pop_t_fclip_y0, pop_t_fclip_x1, pop_t_fclip_y1; uint8_t pop_t_fclip_on; uint8_t pop_t_bake_rest; /* Позиция склянки внутри тайла зелья — поля fore_x/fore_y записи 0x0A той же * таблицы. Вынесены МАКРОСАМИ (одно место правки, дублирования данных нет): * пока таблица была `static const`, SDCC сворачивала `tile_table[10].fore_x` * в константу, но глобальную (её читают оба банка) уже не сворачивает — * чтение полей в рантайме стоило +150 Б и IX-кадр на 13 локалов. */ #define POT_FORE_X 2 #define POT_FORE_Y (-3) /* ---- Таблица тайлов (piece, seg008.c:27) --------------------------- */ const piece pop_tile_table[31] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 00 empty */ { 41, 1, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0}, /* 01 floor */ {127, 1, 0, 133, 1, 2, 145, 0, 43, 0, 0, 0}, /* 02 spike */ { 92, 1, 0, 93, 1, 2, 0, 94, 43, 95, 1, 0}, /* 03 pillar */ { 46, 1, 0, 47, 1, 2, 0, 48, 43, 49, 3, 0}, /* 04 gate */ { 41, 1, 1, 35, 1, 3, 145, 0, 36, 0, 0, 0}, /* 05 stuck floor */ { 41, 1, 0, 42, 1, 2, 145, 0, 96, 0, 0, 0}, /* 06 close button */ { 46, 1, 0, 0, 0, 2, 0, 0, 43, 49, 3, 0}, /* 07 door top w/floor */ { 86, 1, 0, 87, 1, 2, 0, 0, 43, 88, 1, 0}, /* 08 big pillar bottom */ { 0, 0, 0, 89, 0, 3, 0, 90, 0, 91, 1, 3}, /* 09 big pillar top */ { 41, 1, 0, 42, 1, 2, 145, 0, 43, 12, POT_FORE_X, POT_FORE_Y}, /* 0A potion */ { 0, 1, 0, 0, 0, 0, 145, 0, 0, 0, 0, 0}, /* 0B loose floor */ { 0, 0, 0, 0, 0, 2, 0, 0, 85, 49, 3, 0}, /* 0C door top */ { 75, 1, 0, 42, 1, 2, 0, 0, 43, 77, 0, 0}, /* 0D mirror */ { 97, 1, 0, 98, 1, 2, 145, 0, 43,100, 0, 0}, /* 0E debris */ {147, 1, 0, 42, 1, 1, 145, 0,149, 0, 0, 0}, /* 0F open button */ { 41, 1, 0, 37, 0, 0, 0, 38, 43, 0, 0, 0}, /* 10 leveldoor left */ { 0, 0, 0, 39, 1, 2, 0, 40, 43, 0, 0, 0}, /* 11 leveldoor right */ { 0, 0, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0}, /* 12 chomper */ { 41, 1, 0, 42, 1, 2, 0, 0, 43, 0, 0, 0}, /* 13 torch */ { 0, 0, 0, 1, 1, 2, 0, 2, 0, 0, 0, 0}, /* 14 wall */ { 30, 1, 0, 31, 1, 2, 0, 0, 43, 0, 0, 0}, /* 15 skeleton */ { 41, 1, 0, 42, 1, 2, 145, 0, 43, 0, 0, 0}, /* 16 sword */ { 41, 1, 0, 10, 0, 0, 0, 11, 43, 0, 0, 0}, /* 17 balcony left */ { 0, 0, 0, 12, 1, 2, 0, 13, 43, 0, 0, 0}, /* 18 balcony right */ { 92, 1, 0, 42, 1, 2, 145, 0, 43, 95, 1, 0}, /* 19 lattice pillar */ { 1, 0, 0, 0, 0, 0, 0, 0, 2, 9, 0,-53}, /* 1A lattice down */ { 3, 0,-10, 0, 0, 0, 0, 0, 0, 9, 0,-53}, /* 1B lattice small */ { 4, 0,-10, 0, 0, 0, 0, 0, 0, 9, 0,-53}, /* 1C lattice left */ { 5, 0,-10, 0, 0, 0, 0, 0, 0, 9, 0,-53}, /* 1D lattice right */ { 97, 1, 0, 98, 1, 2, 0, 0, 43,100, 0, 0}, /* 1E debris w/torch */ }; /* Деление координаты на ширину тайла ТАБЛИЦЕЙ (tile_div_tbl/tile_mod_tbl, * seg006:702/23A6) — как в оригинале. Живут в резиденте, потому что нужны * и карте (банк 3, колонка персонажа), и слою фона (банк 2, футпринт), а * const-таблицу чужого банка не прочитать. * * Почему не деление: SDCC разворачивает / и % в __divsint + __modsint, * причём __modsint внутри снова зовёт __divsint — замер в MAME дал 5 400 * тактов на один вызов при 13+ вызовах за кадр. Индекс — координата как * есть (0..255), смещение -58 уже внутри значений: POP_TILE_DIV[x] = * floor((x - 58) / 14), POP_TILE_MOD[x] — остаток. Сверено с SDLPoP * побайтно. */ const int8_t POP_TILE_DIV[256] = { -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, }; const uint8_t POP_TILE_MOD[256] = { 12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11, 12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11, 12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13, 0, 1, }; const uint8_t POP_COL_XH[10] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36}; const uint8_t POP_WALL_FRAM_BOTTOM[4] = {7, 9, 5, 3}; const uint8_t POP_WALL_FRAM_MAIN[4] = {8, 10, 6, 4}; const uint8_t POP_SPIKES_FRAM_LEFT[10] = {0, 128, 129, 130, 131, 132, 131, 129, 128, 0}; const uint8_t POP_LOOSE_FRAM_LEFT[12] = {41,69,41,70,70,41,41,41,70,70,70,0}; const uint8_t POP_LOOSE_FRAM_BOTTOM[12] = {43,73,43,74,74,43,43,43,74,74,74,0}; /* ---- Чомпер: номер позы и передние куски (seg008 chomper_fram1/for) --- * Резидент, а не банк: позу считает и полная отрисовка тайла (холодная * половина, pop_room.c), и fore-проход поверх персонажа (горячая, * pop_bg.c) — а const-таблица банка из чужого банка не читается вовсе. */ static const uint8_t CHOMP_FRAM1[7] = {3, 2, 0, 1, 4, 3, 3}; const uint8_t POP_CHOMP_FRAM_FOR[5] = {106, 107, 108, 109, 110}; uint8_t pop_chomp_pose(uint8_t modif) { uint8_t m = (uint8_t)(modif & 0x7F); if (m > 6) m = 6; return CHOMP_FRAM1[m]; } /* get_loose_frame (seg008:761): индекс дрожащего кадра по модификатору. * В режиме ЗАПЕЧКИ фона отдаёт кадр ПОКОЯ — транзиентный кадр нельзя * консервировать в ОЗУ-копию (см. pop_t_bake_rest в pop_bg.c). */ uint8_t pop_loose_frame(uint8_t m) { if (pop_t_bake_rest) return 0; if (m & 0x80) { m &= 0x7F; if (m > 10) return 1; } return m; } /* ---- «Фон трогали ВОТ ЗДЕСЬ» (DRAW-COST; контракт — pop_cdraw.h) ----- * * * Метка — БИТОВАЯ МАСКА ТАЙЛОВ, по слову на ряд и по набору на страницу. * Раньше здесь лежал ОДИН union-прямоугольник на страницу, и это убивало * пропуск перерисовки персонажа: три факела комнаты трогают по пятнышку * 16x18 в колонках 1, 6 и 8, а их объединение — полоса x 40..280 на всю * комнату. Кид, стоящий где угодно между крайними факелами, попадал в неё * и перерисовывался каждый кадр со всем fore-проходом (замер: циан 231 829 * тактов против 20 071, период 4 растровых кадра против 3). * * Гранулярность тайла (32 x 63) вместо точного прямоугольника — осознанное * огрубление: факел помечает всю свою колонку по высоте ряда. Персонаж и * так занимает бОльшую часть высоты ряда, зато касания перестают * склеиваться. См. docs/impl_diff.md. */ uint8_t pop_cd_dirty; uint16_t pop_cd_dmask[2][3]; /* CD_LOW[n] = n младших единиц: маска пробега колонок c0..c1 считается как * CD_LOW[c1+1] & ~CD_LOW[c0] — два чтения таблицы вместо цикла сдвигов. */ static const uint16_t CD_LOW[11] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF }; /* Экранный y -> ряд комнаты 0..2 с клампом (полоса кладки у потолка и низ * стены ложатся на крайние ряды). Цепочкой сравнений, а не делением на 63: * у SDCC z80 одно деление ~5 400 тактов (memory sdcc_z80_division_hoisting). */ static uint8_t cd_row_of(int y) { y -= POP_YOFF; if (y < 63) return 0; if (y < 126) return 1; return 2; } /* Колонки прямоугольника [x..x1] (включительно) -> битовая маска. Тайл ровно * 32 px и начинается с x=0, поэтому колонка — просто сдвиг. */ static uint16_t cd_cols_of(int x, int x1) { uint8_t c0, c1; if (x < 0) x = 0; if (x1 > 319) x1 = 319; if (x1 < x) return 0; /* весь прямоугольник вне экрана */ c0 = (uint8_t)(x >> 5); c1 = (uint8_t)(x1 >> 5); return (uint16_t)(CD_LOW[c1 + 1] & ~CD_LOW[c0]); } /* Обе страницы помечаются сразу (персонаж чинится на каждой в свой кадр), * поэтому цикл развёрнут: индекс-переменная заставляла SDCC считать адрес * каждой ячейки сложением. Зовётся с КАЖДОГО блита фона. */ void pop_cd_touch(int x, int y, int w, int h) { uint16_t cols = cd_cols_of(x, x + w - 1); uint8_t r0, r1; if (!cols) return; r0 = cd_row_of(y); r1 = cd_row_of(y + h - 1); pop_cd_dmask[0][r0] |= cols; pop_cd_dmask[1][r0] |= cols; if (r1 != r0) { pop_cd_dmask[0][r1] |= cols; pop_cd_dmask[1][r1] |= cols; if (r1 - r0 > 1) { /* прямоугольник накрыл все три ряда */ pop_cd_dmask[0][1] |= cols; pop_cd_dmask[1][1] |= cols; } } pop_cd_dirty = 3; } /* Задел ли прямоугольник (ЭКРАННЫЕ координаты, x1/y1 ВКЛЮЧИТЕЛЬНО) то, что * трогали на странице p. Резидент: зовёт pop_cdraw.c из банка 4 прямым * вызовом, без трамплина. */ uint8_t pop_cd_hit(uint8_t p, int x0, int y0, int x1, int y1) { uint16_t cols; uint8_t r0, r1; if (!(pop_cd_dirty & (1 << p))) return 0; cols = cd_cols_of(x0, x1); if (!cols) return 0; r0 = cd_row_of(y0); r1 = cd_row_of(y1); if (pop_cd_dmask[p][r0] & cols) return 1; if (r1 != r0) { if (pop_cd_dmask[p][r1] & cols) return 1; if (r1 - r0 > 1 && (pop_cd_dmask[p][1] & cols)) return 1; } return 0; } /* Страница приведена в порядок — снять с неё метку целиком. */ void pop_cd_clear(uint8_t p) { pop_cd_dmask[p][0] = pop_cd_dmask[p][1] = pop_cd_dmask[p][2] = 0; pop_cd_dirty = (uint8_t)(pop_cd_dirty & ~(1 << p)); } /* ---- Блит куска атласа ---------------------------------------------- */ /* Клипованный путь — ОТДЕЛЬНОЙ функцией. У него девять 16-битных локалей, * и SDCC заводит под них кадр IX; пока он жил в общем теле, за этот кадр * платили и блиты БЕЗ клипа (`ld -n(ix)` = 19 тактов номинала, у нас ~46). * А блитов без клипа большинство: весь фон вне fore-прохода — факелы, зелья, * перерисовка тайлов — идёт с pop_t_fclip_on == 0. */ static void blit_b_clip(const uint8_t *img, int x, int top, int w, int h) { int dx = x, dy = top, dw = w, dh = h, sx = 0, sy = 0; if (pop_t_clip_top && dy < pop_t_clip_top) { /* полоса у потолка */ sy = pop_t_clip_top - dy; dy = pop_t_clip_top; dh -= sy; } if (pop_t_fclip_on) { /* окно fore-слоя */ if (dx < pop_t_fclip_x0) { int d = pop_t_fclip_x0 - dx; sx += d; dx += d; dw -= d; } if (dy < pop_t_fclip_y0) { int d = pop_t_fclip_y0 - dy; sy += d; dy += d; dh -= d; } if (dx + dw > pop_t_fclip_x1) dw = pop_t_fclip_x1 - dx; if (dy + dh > pop_t_fclip_y1) dh = pop_t_fclip_y1 - dy; } if (dw <= 0 || dh <= 0) return; if (dx >= 0 && dy >= 0 && dw < 256 && dh < 256 && dx + dw <= 320 && dy + dh <= 256 && w < 256) { if (sx == 0 && sy == 0 && dw == w && dh == h) gfx_blit_noclip(dx, dy, img); else gfx_blit_part_noclip(dx, dy, img, (uint8_t)sx, (uint8_t)sy, (uint8_t)dw, (uint8_t)dh); } else { gfx_blit_part(dx, dy, img, sx, sy, dw, dh); /* край экрана */ } /* Кусок ФОНА лёг туда, где может лежать уже нарисованный персонаж — * пометить область (DRAW-COST, pop_cdraw.h). Признак «это не fore-проход * поверх персонажа» — окно клипа: его ставит только он. */ if (!pop_t_fclip_on) pop_cd_touch(dx, dy, dw, dh); } void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom) { const uint8_t *img; uint16_t w, h; int top; if (idx >= a->count) return; /* Грубый отсев ДО маппинга страницы: реальные w/h лежат в заголовке * ленты, то есть в EMM-странице, и чтобы их прочитать, надо сделать * gfx_w0_map. Берём заведомо больший габарит (кадры фона не крупнее * 64×64) и, если он не задевает окно, уходим не заплатив ни за * atlas_image, ни за map/unmap. */ if (pop_t_fclip_on && (x >= pop_t_fclip_x1 || x + 64 <= pop_t_fclip_x0 || ybottom + POP_YOFF + 1 <= pop_t_fclip_y0 || ybottom + POP_YOFF - 64 >= pop_t_fclip_y1)) return; img = (const uint8_t *)atlas_image(a, idx); gfx_w0_map(a->page); w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8)); h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8)); top = ybottom - (int)h + 1 + POP_YOFF; /* +YOFF: центрирование */ if (w && h) { if (!pop_t_fclip_on && !pop_t_clip_top) { /* БЫСТРЫЙ путь: клипа нет вовсе. Так идут все блиты фона вне * fore-прохода. Линейное ядро без клипа — ~2.9× быстрее общего * (замер: 4617 против 13288 тактов на спрайт 32×3; gfx_blit * платит за клип/16-бит/split, а не за пиксели). W3-скобку * ставит сама libbgi: из кода с --w3 её вызывать нельзя. */ if (x >= 0 && top >= 0 && w < 256 && h < 256 && x + (int)w <= 320 && top + (int)h <= 256) gfx_blit_noclip(x, top, img); else gfx_blit_part(x, top, img, 0, 0, (int)w, (int)h); pop_cd_touch(x, top, (int)w, (int)h); } else { blit_b_clip(img, x, top, (int)w, (int)h); } } gfx_w0_unmap(); } void pop_env_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_env[id >> POP_ENV_SHIFT], (uint8_t)(id & POP_ENV_MASK), x, yb); } void pop_wall_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_wall_at, id, x, yb); } void pop_fore_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_fore_at, id, x, yb); } void pop_pot_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_pot_at, id, x, yb); } void pop_heal_off(int x, int y, int w, int h) { pop_heal_fast(x, y + POP_YOFF, w, h); } /* ---- Чтение тайла отрисованной комнаты ------------------------------ */ uint8_t pop_tile_code(int row, int col) { if (col < 0) { if (pop_t_lfg && row >= 0 && row < 3) return pop_t_lfg[row]; return 20; /* уровневая кромка = стена */ } if (row < 0) { /* верхняя полоса: ряд 2 комнаты сверху */ if (row == -1 && pop_t_above && col < 10) return pop_t_above[col]; return 1; /* нет комнаты сверху → пол (кромка) */ } if (row > 2 || col > 9) return 20; return pop_t_fg[row * 10 + col]; } uint8_t pop_tile_mod(int row, int col) { if (col < 0) { if (pop_t_lbg && row >= 0 && row < 3) return pop_t_lbg[row]; return 0; } if (row == -1 && pop_t_abovemod && col < 10) return pop_t_abovemod[col]; if (row < 0 || row > 2 || col > 9) return 0; return pop_t_bg[row * 10 + col]; } /* Порт подстановки нажатой кнопки из get_tile_to_draw (seg008:240): opener → * floor, closer → stuck, когда таймер связи > 1. Любой слой, который * выбирает ветку отрисовки по типу тайла, обязан смотреть на ПОДСТАВЛЕННЫЙ * тип (иначе кнопка, на которой стоит Kid, ведёт себя как «не пол» — напр. * climb-оверлей уходил в draw_other_overlay и закрашивал Kid целым тайлом * вместо узкой кромки floor_left_overlay). */ uint8_t pop_tile_code_drawn(int row, int col) { uint8_t code = pop_tile_code(row, col); if (code == 0x0F) { if ((pop_doorlink2(pop_tile_mod(row, col)) & 0x1F) > 1) code = 1; } else if (code == 0x06) { if ((pop_doorlink2(pop_tile_mod(row, col)) & 0x1F) > 1) code = 5; } return code; } uint8_t pop_wall_modifier(int row, int col) { uint8_t lw = (col > 0) ? (pop_tile_code(row, col - 1) == 20) : 1; uint8_t rw = (col < 9) ? (pop_tile_code(row, col + 1) == 20) : 1; if (lw && rw) return 3; /* WWW */ if (lw) return 2; /* WWS */ if (rw) return 1; /* SWW */ return 0; /* SWS */ } /* ---- Зелье: склянка (tiles_10_potion, seg008 draw_tile_fore) --------- * * Тип = (modif & 0xF8) >> 3: 0 = пусто (ничего не рисуем), 2..4 — «большая» * склянка (id 13), остальные — маленькая (id 12). Спрайты из chtab_1 (атлас * pop_pot), поэтому НЕ из env: раньше рисовали chtab_6 id 12 (res212), * которого в VDUNGEON нет — оттуда и «мусорный объект». Позиция оригинала: * xh = fore_x + draw_xh, xl = 6, ybottom = fore_y + draw_main_y. * * Здесь, а не в банке: склянку рисует и полная отрисовка тайла (холодная * половина), и fore-проход поверх персонажа (горячая). */ void pop_potion_flask(int row, int col, uint8_t modif) { uint8_t type = (uint8_t)((modif & 0xF8) >> 3); uint8_t id = 12; if (type == 0) return; if (type >= 2 && type < 5) id = 13; /* большая склянка */ if (pop_palace) id += 2; /* seg008:747: во дворце склянки другие */ pop_pot_b(id, (POT_FORE_X + POP_COL_XH[col]) * 8 + 6, POT_FORE_Y + 63 * row + 62); } /* Окно fore-клипа: ставит тот, кто нарисовал персонажа (pop_cdraw, банк 4), * читает blit_b. Резидент — чтобы вызов из банка шёл без трамплина. */ void pop_fore_set_clip(int x, int y, int w, int h) { pop_t_fclip_x0 = x; pop_t_fclip_y0 = y; pop_t_fclip_x1 = x + w; pop_t_fclip_y1 = y + h; } /* Ряд 0 комнаты СНИЗУ (fg-коды) — нужен draw_tile_topright/floorright, когда * тайл нижнего ряда стал видимым (empty после падения loose): «стена вниз» = * топрайт тайла room_B(0, col-1). 0 — нет комнаты снизу (кромка = стена). */ void pop_room_set_below(const uint8_t *below_row0_fg) { pop_t_below = below_row0_fg; } /* Ряд 2 (fg-коды + модификаторы) комнаты СВЕРХУ — для полосы кладки у * потолка (seg008 draw_room: доп. верхний ряд из room_A, draw_tile_aboveroom * при draw_main_y=-1). 0 — нет комнаты сверху (кромка уровня → пол). */ void pop_room_set_above(const uint8_t *above_row2_fg, const uint8_t *above_row2_mod) { pop_t_above = above_row2_fg; pop_t_abovemod = above_row2_mod; }