Files
Sprinter-SDCC/applications/PoP/roomtest/pop_bg.c
T
snark13 cbae48dbc4 applications/PoP/roomtest: пол-оверлей при подъёме (draw_floor_overlay)
Проблема из динамики: при подъёме [1,2]→[0,3] нижняя часть Kid, которая
физически за полом назначения, просвечивала.

Порт SDLPoP draw_floor_overlay (seg008:1457): на кадрах подъёма 137..144,
если тайл-назначения floor-подобный (floor/pillar/stuck/torch) И тайл СЛЕВА
пуст (кромка), передний край пола (floor_left_overlay[frame-137] =
{32,151,151,150,150,151,32,32}) + низ пола рисуются ПОВЕРХ нижней части Kid.

- pop_bg: climb_overlay_tile + доп-проход в pop_fore_over_kid (новый параметр
  frame) на кадрах 137..144.
- pop_pack_bg.py: CLIMB_OVERLAY_ENV_IDS={32,150,151} явно добавлены в env-фон
  (не попадают в used render_room — рантайм-анимация); env4 count 16→24.
- roomtest: передача Kid.frame в pop_fore_over_kid.

Проверено в MAME (кадр 137): при подтягивании видна только голова/плечи Kid
над кромкой [0,3], нижняя часть скрыта за полом.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 11:37:56 +03:00

479 lines
20 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_bg.c — статический фон комнаты PoP композицией слоёв (порт
* applications/PoP/toolchain/render_room.py -> seg008.c draw_tile/
* wall_pattern). Подробности алгоритма — в render_room.py и
* memory/pop_background_strategy.
*
* Только DUNGEON-стиль (Фаза 1). Анимация (пламя факелов, шипы, дверь-
* плита, сама решётка ворот) НЕ рисуется — отдельными sprite_t поверх
* (Фаза 2). Решётка ворот из ЛЕВОЙ комнаты рисуется статичным кадром.
*/
#include <graphics.h>
#include <gfx.h>
#include <sprite.h>
#include <fcntl.h>
#include <unistd.h>
#include "pop_bg.h"
/* ---- Атласы (прямая адресация) ------------------------------------- */
static atlas_t env[5];
static atlas_t wall_at;
static atlas_t fore_at;
static const char *const ENV_ATL[5] = {
"pop_env0.atl", "pop_env1.atl", "pop_env2.atl",
"pop_env3.atl", "pop_env4.atl"
};
int pop_bg_load(void)
{
int i;
for (i = 0; i < 5; i++)
if (atlas_load(&env[i], ENV_ATL[i]) != 0) return -1;
if (atlas_load(&wall_at, "pop_wall.atl") != 0) return -1;
if (atlas_load(&fore_at, "pop_fore.atl") != 0) return -1;
return 0;
}
void pop_bg_free(void)
{
int i;
for (i = 0; i < 5; i++)
atlas_free(&env[i]);
atlas_free(&wall_at);
atlas_free(&fore_at);
}
/* Блит ленты idx атласа a: НИЗ спрайта на ybottom, левый край x,
* прозрачно (0xFF). w/h читаются из getimage-заголовка ленты (в W0). */
static void blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
{
const uint8_t *img;
uint16_t w, h;
if (idx >= a->count)
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));
if (w && h)
gfx_blit(x, ybottom - (int)h + 1, img);
gfx_w0_unmap();
}
static void env_b(uint8_t id, int x, int yb) { blit_b(&env[id >> 5], (uint8_t)(id & 31), x, yb); }
static void wall_b(uint8_t id, int x, int yb) { blit_b(&wall_at, id, x, yb); }
static void fore_b(uint8_t id, int x, int yb) { blit_b(&fore_at, id, x, yb); }
/* ---- Таблица тайлов (piece, seg008.c:27) --------------------------- */
typedef struct {
uint8_t base_id, floor_left; int8_t base_y;
uint8_t right_id, floor_right; int8_t right_y;
uint8_t stripe_id, topright_id, bottom_id, fore_id, fore_x; int8_t fore_y;
} piece;
static const piece 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, 2, -3}, /* 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 */
};
/* ---- Константные таблицы (render_room.py) -------------------------- */
static const uint8_t COL_XH[10] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36};
static const uint8_t TBL_LINE[3] = {0, 10, 20};
static const uint8_t WALL_FRAM_BOTTOM[4] = {7, 9, 5, 3};
static const uint8_t WALL_FRAM_MAIN[4] = {8, 10, 6, 4};
static const uint8_t DOOR_FRAM_SLICE[9] = {67, 59, 58, 57, 56, 55, 54, 53, 52};
static const uint8_t SPIKES_FRAM_RIGHT[10] = {0, 134, 135, 136, 137, 138, 137, 135, 134, 0};
static const uint8_t BLUELINE_FRAM1[4] = {0, 124, 125, 126};
static const int8_t BLUELINE_FRAM_Y[4] = {0, -20, -20, 0};
static const uint8_t BLUELINE_FRAM3[4] = {44, 44, 45, 45};
static const uint8_t LPOS[5] = {58, 41, 37, 20, 16};
static const uint8_t RPOS[4] = {52, 42, 31, 21};
/* Wall (chtab_7) id: делители/блок/марки. */
#define RES_WALL_RNDBLOCK 13
#define RES_WALL_DIVIDER1 11
#define RES_WALL_MARK_TL 14
#define RES_WALL_MARK_BL 15
#define RES_WALL_MARK_TR 16
#define RES_WALL_MARK_BR 17
/* ---- Текущая комната (globals для tile_code/mod) ------------------- */
static const uint8_t *g_fg, *g_bg, *g_lfg, *g_lbg;
static uint8_t g_room;
static uint8_t tile_code(int row, int col)
{
if (col < 0) {
if (g_lfg && row >= 0 && row < 3) return g_lfg[row];
return 20; /* уровневая кромка = стена */
}
if (row < 0) return 1; /* верхняя кромка = пол */
if (row > 2 || col > 9) return 20;
return g_fg[row * 10 + col];
}
static uint8_t tile_mod(int row, int col)
{
if (col < 0) {
if (g_lbg && row >= 0 && row < 3) return g_lbg[row];
return 0;
}
if (row < 0 || row > 2 || col > 9) return 0;
return g_bg[row * 10 + col];
}
/* seg008.c:1255 (без FAKE_TILES): сосед — стена, если его тип == 20.
* На кромке col0 сосед по умолчанию = стена (как render_room). */
static uint8_t wall_modifier(int row, int col)
{
uint8_t lw = (col > 0) ? (tile_code(row, col - 1) == 20) : 1;
uint8_t rw = (col < 9) ? (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 */
}
/* ---- PRNG (seg009.c:321, 32-бит LCG) — только на ВХОД в комнату ---- */
static unsigned long rnd_seed;
static uint16_t prandom(uint16_t maxv)
{
rnd_seed = rnd_seed * 214013UL + 2531011UL;
return (uint16_t)((uint16_t)(rnd_seed >> 16) % (uint16_t)(maxv + 1));
}
/* ---- Декали-марки стен (seg008.c:2041/2056) ------------------------ */
static void draw_left_mark(uint16_t dv, int arg2, int arg1, int row, int col)
{
uint8_t image_id = (dv & 1) ? RES_WALL_MARK_BL : RES_WALL_MARK_TL;
int lv2 = 0;
int xh = COL_XH[col];
int dby = 63 * row + 65;
if (dv > 3) lv2 = arg1 + 6;
else if (dv > 1) lv2 = arg2 + 6;
wall_b(image_id, (xh + ((dv == 2 || dv == 3) ? 1 : 0)) * 8 + lv2, dby - LPOS[dv]);
}
static void draw_right_mark(uint16_t dv, int arg1, int row, int col)
{
uint8_t image_id = (dv & 1) ? RES_WALL_MARK_BR : RES_WALL_MARK_TR;
int a = (dv < 2) ? 24 : arg1 - 3;
int xh = COL_XH[col];
int dby = 63 * row + 65;
wall_b(image_id, (xh + ((dv > 1) ? 1 : 0)) * 8 + a, dby - RPOS[dv]);
}
/* ---- wall_pattern (seg008.c:1928, dungeon-ветка) ------------------- */
static void wall_pattern(int row, int col, int which_part)
{
int xh = COL_XH[col];
int dby = 63 * row + 65;
unsigned long saved = rnd_seed;
uint16_t mid_div, mid_off, bot_div, bot_off;
uint8_t bg;
rnd_seed = (unsigned long)(g_room + TBL_LINE[row] + col);
prandom(1); /* сброс */
mid_div = prandom(1);
mid_off = prandom(4);
bot_div = prandom(1);
bot_off = prandom(4);
bg = wall_modifier(row, col);
if (bg == 3) { /* WWW */
if (which_part) {
if (prandom(4) == 0)
wall_b(RES_WALL_RNDBLOCK, xh * 8, dby - 42);
wall_b((uint8_t)(RES_WALL_DIVIDER1 + mid_div), (xh + 1) * 8 + mid_off, dby - 21);
}
wall_b((uint8_t)(RES_WALL_DIVIDER1 + bot_div), xh * 8 + bot_off, dby);
if (which_part) {
if (prandom(4) == 0)
draw_right_mark(prandom(3), mid_off, row, col);
if (prandom(4) == 0)
draw_left_mark(prandom(4), mid_off - mid_div, bot_off - bot_div, row, col);
}
} else if (bg == 0) { /* SWS */
if (which_part) {
if (prandom(6) == 0)
draw_left_mark(prandom(1), mid_off - mid_div, bot_off - bot_div, row, col);
}
} else if (bg == 1) { /* SWW */
if (which_part) {
if (prandom(4) == 0)
wall_b(RES_WALL_RNDBLOCK, xh * 8, dby - 42);
wall_b((uint8_t)(RES_WALL_DIVIDER1 + mid_div), (xh + 1) * 8 + mid_off, dby - 21);
if (prandom(4) == 0)
draw_right_mark(prandom(3), mid_off, row, col);
if (prandom(4) == 0)
draw_left_mark(prandom(3), mid_off - mid_div, bot_off - bot_div, row, col);
}
} else { /* WWS (bg==2) */
if (which_part)
wall_b((uint8_t)(RES_WALL_DIVIDER1 + mid_div), (xh + 1) * 8 + mid_off, dby - 21);
wall_b((uint8_t)(RES_WALL_DIVIDER1 + bot_div), xh * 8 + bot_off, dby);
if (which_part) {
if (prandom(4) == 0)
draw_right_mark((uint16_t)(prandom(1) + 2), mid_off, row, col);
if (prandom(4) == 0)
draw_left_mark(prandom(4), mid_off - mid_div, bot_off - bot_div, row, col);
}
}
rnd_seed = saved;
}
/* topright тайла снизу-слева (seg008.c:410). */
static void draw_topright(uint8_t tt, int x, int dby)
{
if (tt == 7 || tt == 12) return;
if (tt == 20) { wall_b(2, x, dby); return; }
if (tile_table[tt].topright_id)
env_b(tile_table[tt].topright_id, x, dby);
}
/* Решётка ворот (portcullis) из ЛЕВОЙ комнаты (seg008.c:1110). */
static void draw_gate_back(uint8_t modl, int xh, int dby, int dmy)
{
int x = xh * 8;
int gate_top_y = dby - 62;
int openness = (((modl > 188) ? 188 : modl) >> 2) + 1;
int gate_bot_y = dmy - openness;
int yb, gate_frame;
if (gate_bot_y + 12 < dmy)
env_b(50, x, gate_bot_y);
else
env_b(51, x, gate_bot_y - 2);
yb = gate_bot_y - 12;
if (yb < 192) {
while (yb >= 0 && yb > 7 && (yb - 7) > gate_top_y) {
env_b(52, x, yb);
yb -= 8;
}
}
gate_frame = yb - gate_top_y + 1;
if (gate_frame > 0 && gate_frame < 9)
env_b(DOOR_FRAM_SLICE[gate_frame], x, yb);
}
/* ---- draw_tile (seg008.c:155) — одна клетка ------------------------ */
static void draw_tile(int row, int col)
{
uint8_t code = tile_code(row, col);
uint8_t lcode = tile_code(row, col - 1);
uint8_t lmod = tile_mod(row, col - 1);
int xh = COL_XH[col];
int x = xh * 8;
int dby = 63 * row + 65;
int dmy = dby - 3;
const piece *t = &tile_table[code];
const piece *lt = &tile_table[lcode];
uint8_t rbl_code;
uint8_t wmod;
/* тайл снизу-слева (для topright) */
if (row + 1 > 2) rbl_code = 1; /* ниже нижнего ряда = пол */
else rbl_code = tile_code(row + 1, col - 1);
/* --- floorright (seg008.c:392) — только can_see_bottomleft --- */
if (code == 0 || code == 9 || code == 12 || code == 26) {
draw_topright(rbl_code, x, dby);
/* floor-right тень (blitters_9_black) на ЧЁРНОМ фоне = no-op
* (чёрный силуэт по чёрному) — не рисуем. Если tile_left имеет
* floor_right: lt->floor_right. */
}
/* --- draw_tile_right (seg008.c:456) --- */
if (code != 20) {
if (lcode == 20) {
wall_b(1, x, tile_table[20].right_y + dmy); /* грань стены */
} else if (lcode == 0) {
if (lmod <= 3 && BLUELINE_FRAM1[lmod])
env_b(BLUELINE_FRAM1[lmod], x, BLUELINE_FRAM_Y[lmod] + dmy);
} else if (lcode == 1) {
uint8_t num;
env_b(42, x, tile_table[1].right_y + dmy); /* правый треуг. пола */
num = (lmod <= 3) ? lmod : 0;
if (num != 0) /* dungeon: level_type=0 */
env_b(BLUELINE_FRAM3[num], x, dmy - 20); /* силуэт кладки */
} else {
if (lt->right_id)
env_b(lt->right_id, x, lt->right_y + dmy);
/* stripe — только палас, dungeon пропускает */
}
}
/* --- draw_tile_anim_right (seg008.c:530) — статичные части --- */
if (lcode == 4) { /* gate: решётка */
draw_gate_back(lmod, xh, dby, dmy);
} else if (lcode == 11) { /* loose: правый край */
env_b(42, x, dby - 1);
} else if (lcode == 2) { /* spike: статич. кадр */
uint8_t sf = (lmod & 0x80) ? 5 : lmod;
if (sf < 10 && SPIKES_FRAM_RIGHT[sf])
env_b(SPIKES_FRAM_RIGHT[sf], x, dmy - 7);
}
/* --- draw_tile_bottom (seg008.c:570) --- */
if (code == 20) {
wmod = wall_modifier(row, col);
wall_b(WALL_FRAM_BOTTOM[wmod], x, dby); /* сплошная нижняя грань */
wall_pattern(row, col, 0);
} else if (t->bottom_id) {
env_b(t->bottom_id, x, dby);
}
/* --- draw_loose (seg008.c:600) — статич. кадр --- */
if (code == 11)
env_b(43, x, dby); /* loose_fram_bottom[0] */
/* --- draw_tile_base (seg008.c:611) --- */
{
uint8_t base_id = t->base_id;
if (code == 11) base_id = 41; /* loose_fram_left[0] */
if (base_id)
env_b(base_id, x, t->base_y + dmy);
}
/* --- draw_tile_fore (seg008.c:690) --- */
if (code == 20) {
wmod = wall_modifier(row, col);
wall_b(WALL_FRAM_MAIN[wmod], x, dmy); /* основная грань */
wall_pattern(row, col, 1);
}
if (t->fore_id)
fore_b(t->fore_id, t->fore_x * 8 + x, t->fore_y + dmy);
}
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)
{
int row, col;
g_room = room_num;
g_fg = fg; g_bg = bg;
g_lfg = lcol_fg; g_lbg = lcol_bg;
/* прозрачный блит: 0xFF не пишется (чёрный фон/нижние слои проступают) */
gfx_set_bank(GFX_BANK_TRANSPARENT);
for (row = 2; row >= 0; row--) /* снизу вверх (2,1,0) */
for (col = 0; col < 10; col++)
draw_tile(row, col);
gfx_set_bank(GFX_BANK_NORMAL);
}
/* ---- fore-over-Kid (порт seg003 redraw_at_char + seg008 set_char_collision) --
* Передний слой (fore_id = foretable-кусок) тайлов ФУТПРИНТА спрайта Kid
* — рядов top..bottom × колонок left..right (<= 2×2 = 4 тайла) — рисуется
* ПОВЕРХ Kid: то, что по изометрии перед ним (передние грани колонн/ворот/
* большой колонны/щебня). Стены и факелы имеют fore_id=0 → остаются сзади.
* Рисуем в GFX_BANK_SPRITE (видео-ОЗУ, 0xFF прозрачен); kid_heal восстановит
* из теневого фона (fore в нём уже запечён pop_room_draw). obj_x — ЛОГИЧЕСКАЯ
* X кадра Kid (до ×8/7), obj_y — низ спрайта, w/h — размер, dir — направление. */
static int8_t col_from_x(int xpos) /* get_tile_div_mod: колонка (58/14) */
{
int x = xpos - 58;
int xh = x / 14;
if (x % 14 < 0) xh--; /* округление вниз для отрицательных */
return (int8_t)xh;
}
static int8_t y_to_row(int y) /* y_to_row_mod4 (TILE_SIZEY=63) */
{
return (int8_t)((y + 60) / 63 % 4 - 1);
}
static void fore_tile(int row, int col) /* передний слой одного тайла */
{
uint8_t code = tile_code(row, col);
const piece *t = &tile_table[code];
int x = COL_XH[col] * 8;
int dmy = 63 * row + 62;
if (code == 20) { /* стена: основная грань (foretable, */
uint8_t wmod = wall_modifier(row, col); /* draw_tile_fore seg008:690) — */
wall_b(WALL_FRAM_MAIN[wmod], x, dmy); /* перекрывает Kid как передний */
wall_pattern(row, col, 1);
} else if (t->fore_id) {
fore_b(t->fore_id, t->fore_x * 8 + x, t->fore_y + dmy);
}
}
/* Пол-оверлей при подъёме (draw_floor_overlay, seg008:1457): когда Kid
* подтягивается на floor-подобный тайл назначения (floor/pillar/stuck/torch),
* а тайл СЛЕВА пуст (т.е. кромка), передний край пола перекрывает нижнюю
* часть Kid — иначе видна часть, что должна быть за полом. Кадр подъёма
* 137..144 индексирует floor_left_overlay. */
static const uint8_t FLOOR_LEFT_OVERLAY[8] = {32, 151, 151, 150, 150, 151, 32, 32};
static void climb_overlay_tile(int row, int col, uint8_t fidx)
{
uint8_t code = tile_code(row, col);
int x, dmy, dby;
const piece *t;
if (code != 1 && code != 3 && code != 5 && code != 19) return; /* не floor-тайл */
if (tile_code(row, col - 1) != 0) return; /* слева не пусто → не кромка */
x = COL_XH[col] * 8;
dmy = 63 * row + 62;
dby = dmy + 3;
t = &tile_table[code];
env_b(FLOOR_LEFT_OVERLAY[fidx], x, dmy + (code == 5 ? 1 : 0));
if (t->bottom_id) env_b(t->bottom_id, x, dby); /* draw_tile_bottom */
}
void pop_fore_over_kid(int obj_x, int obj_y, uint16_t w, uint16_t h,
int8_t dir, uint8_t frame)
{
int wh = (int)((w + 1) >> 1); /* char_width_half */
int xl = obj_x / 2 + 58; /* char_x_left (58-база) */
int xr;
int8_t cL, cR, rT, rB, r, c;
if (dir >= 0) xl -= wh; /* лицом вправо: левый край внутрь */
xr = xl + wh;
cL = col_from_x(xl); if (cL < 0) cL = 0; else if (cL > 9) cL = 9;
cR = col_from_x(xr); if (cR < 0) cR = 0; else if (cR > 9) cR = 9;
rB = y_to_row(obj_y); if (rB < 0) rB = 0; else if (rB > 2) rB = 2;
rT = y_to_row(obj_y - (int)h + 1); if (rT < 0) rT = 0; else if (rT > 2) rT = 2;
gfx_set_bank(GFX_BANK_SPRITE); /* видео-ОЗУ, heal восстановит */
for (r = rT; r <= rB; r++)
for (c = cL; c <= cR; c++)
fore_tile(r, c);
if (frame >= 137 && frame <= 144) { /* подъём: пол назначения поверх Kid */
uint8_t fidx = (uint8_t)(frame - 137);
for (r = rT; r <= rB; r++)
for (c = cL; c <= cR; c++)
climb_overlay_tile(r, c, fidx);
}
gfx_set_bank(GFX_BANK_NORMAL);
}