From e143ee010af19d117a6a2012dcaadba3bc7eec6f Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Tue, 28 Jul 2026 12:07:29 +0300 Subject: [PATCH] =?UTF-8?q?PoP=20roomtest:=20=D0=BD=D0=B0=D0=B6=D0=B0?= =?UTF-8?q?=D1=82=D0=B0=D1=8F=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B0-close?= =?UTF-8?q?r,=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D0=B0=20=D0=BF=D0=B0?= =?UTF-8?q?=D0=B4=D0=B0=D1=8E=D1=89=D0=B8=D1=85=20=D0=BA=D1=83=D1=81=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2,=20=D1=84=D0=BE=D0=BD-=D0=BF=D0=BE=D0=BA=D0=BE?= =?UTF-8?q?=D0=B9=20=D0=BF=D1=80=D0=B8=20=D0=B7=D0=B0=D0=BF=D0=B5=D1=87?= =?UTF-8?q?=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Кнопка-closer (tiles_6) в нажатом состоянии рисуется как tiles_5_stuck (get_tile_to_draw seg008:2FE), но её грани — env id 35 (правая) и 36 (нижняя) — не попадали в атлас: pop_pack_bg собирает набор спрайтов прогоном render_room по статике уровней, а тайла 5 статически в уровнях нет. Итог — чёрный провал на месте нижней грани. Добавлены явным набором STUCK_ENV_IDS (как loose/пики/ворота). Заодно OPENER_NOFLOOR_ENV_IDS = {148}: левая половина кнопки-opener, когда слева пусто (draw_tile_base seg008:0A8E) — ветка была не реализована ни в pop_bg, ни в render_room. bake_rest: при ЗАПЕЧКЕ статического фона (запись идёт в ОЗУ-копию акселератора, из неё восстанавливает heal) get_loose_frame возвращает кадр ПОКОЯ. Иначе транзиентный кадр тряски соседней плиты консервируется в копии, а так как запечка идёт двумя кадрами (по одному на страницу дабл-буфера) — на страницах застывают РАЗНЫЕ кадры, вечное мерцание. mobs[MOB_MAX] вместо одиночных статиков: две плиты, упавшие подряд, теряли первый кусок (застывал в воздухе) и оставляли щебень только от одной. Плюс отложенная допечка прошлого приземления ПЕРЕД разбором нового и ограничение чёрного бара в pop_loose_bake_empty низом тайла. Co-Authored-By: Claude Opus 5 --- applications/PoP/poc/res/bg/pop_env1.atl | Bin 12449 -> 12969 bytes applications/PoP/poc/res/bg/pop_env4.atl | Bin 8982 -> 9434 bytes applications/PoP/roomtest/pop_bg.c | 177 +++++++++++++++------- applications/PoP/roomtest/pop_map.c | 9 ++ applications/PoP/roomtest/roomtest.c | 4 +- applications/PoP/toolchain/pop_pack_bg.py | 17 ++- 6 files changed, 147 insertions(+), 60 deletions(-) diff --git a/applications/PoP/poc/res/bg/pop_env1.atl b/applications/PoP/poc/res/bg/pop_env1.atl index 0ec6ece77b37a40149b01d711441b7664ab9c506..2f92b8cbbd0e4a803340a22c758dde5cf8d8f7bb 100644 GIT binary patch delta 365 zcmZ3OxH5G@L;V9rDFH@C28I@91!fTaf?2@^MCWiTfW-@VrR+fb1Huq>DWVFzAU=n< z6hDZbBmvR)O+rQ*#D6C(Ee4`pPhjWm*A0`Yr|COa|CW07JIV3>SR zLTd6wb}`1_$p_iRC4>KiKnMzm0rG+;H;9M>)g*vP2QaB1A}R}%0qZ~zAt2=-9smDN z5)qxuC@#VR;sP0BOh777(qyuNxFig-{#RgNW|$l(Dl+*2JI~~aVltCgiK%RUz<5v( E04k+bDgXcg delta 608 zcmZ3Px-fA~8d@mmchJ2B2 ve?ft5@(YlIcv%=@CjV5FW&v6gGnr3GX7U*YuE| 10) return 1; } return m; } @@ -555,6 +567,8 @@ static void draw_tile(int row, int col) uint8_t base_id = t->base_id; if (code == 11) /* loose: левая грань (анимир.) */ base_id = LOOSE_FRAM_LEFT[get_loose_frame(pop_loose_modif[row * 10 + col])]; + else if (code == 0x0F && lcode == 0) /* opener, слева пусто */ + base_id = 148; /* левая половина без пола */ if (base_id) env_b(base_id, x, t->base_y + dmy); } @@ -686,8 +700,10 @@ void pop_ceil_bake_empty(int col) bar(x, POP_YOFF, x + 63, POP_YOFF + CEIL_BAND_H); /* стереть плиту в ОЗУ-копии */ gfx_set_bank(GFX_BANK_TRANSPARENT); g_clip_top = POP_YOFF; + bake_rest = 1; /* фон = покой (см. bake_rest) */ draw_tile(-1, col); /* тайл уже EMPTY → колодец наверх */ if (col + 1 < 10) draw_tile(-1, col + 1); + bake_rest = 0; g_clip_top = 0; gfx_set_bank(GFX_BANK_NORMAL); } @@ -742,8 +758,10 @@ void pop_floor_bake(int row, int col) setfillstyle(SOLID_FILL, BLACK); bar(x, yb + 26, x + 59, yb + 64); gfx_set_bank(GFX_BANK_TRANSPARENT); + bake_rest = 1; /* фон = покой (см. bake_rest) */ draw_tile(row, col); if (col + 1 < 10) draw_tile(row, col + 1); + bake_rest = 0; gfx_set_bank(GFX_BANK_NORMAL); } @@ -764,19 +782,29 @@ void pop_loose_bake_empty(int row, int col) * стирает ЗАПЕЧЁННЫЙ пиксель-в-пиксель loose-пол с обоих тайлов, иначе * heal вернул бы старый пол из ОЗУ-копии. Ширина 40 — кусок пола * заходит на x+32..+40. */ + /* НИЗ строго 63*row+65 = низ собственной графики плиты (bottom_id 43/73/74 + * стоит ровно на dby; base 41/69/70 — до dmy=62; правая грань 42/66/67 — + * до dby-1). Ниже начинается СТЕНА ряда ниже (высокий спрайт), которую + * здесь не перерисовываем: заливка на 9 строк глубже оставляла ЧЁРНЫЙ БОКС + * под соседним столбом, причём в ОЗУ-копии — то есть heal возвращал его + * снова, и на второй странице дабл-буфера его не было → мерцание. + * Тот же урок уже учтён в pop_floor_bake. */ + yb += POP_YOFF; gfx_set_bank(GFX_BANK_NORMAL); setfillstyle(SOLID_FILL, BLACK); - bar(x, yb + POP_YOFF, x + 39, yb + 28 + POP_YOFF); /* сам тайл: +40 (кусок пола заходит вправо; сосед перерисуется) */ + bar(x, yb, x + 39, POP_YOFF + 63 * row + 65); /* сам тайл: +40 (кусок пола заходит вправо; сосед перерисуется) */ if (col + 1 < 10) - bar(xr, yb + POP_YOFF, xr + 31, yb + 28 + POP_YOFF); /* сосед: РОВНО 32 — не залезть в col+2 (его не перерисовываем) */ + bar(xr, yb, xr + 31, POP_YOFF + 63 * row + 65); /* сосед: РОВНО 32 — не залезть в col+2 (его не перерисовываем) */ /* Новый статич. фон: сам тайл (empty → «стена вниз» topright комнаты * снизу) + правый сосед (его левая грань: был loose-край env_b(42), * теперь пусто). TRANSPARENT — 0xFF не забивает чёрный, пишет и в * ОЗУ-копию → heal сохранит. */ gfx_set_bank(GFX_BANK_TRANSPARENT); + bake_rest = 1; /* фон = покой (см. bake_rest) */ draw_tile(row, col); if (col + 1 < 10) draw_tile(row, col + 1); + bake_rest = 0; gfx_set_bank(GFX_BANK_NORMAL); } @@ -787,14 +815,24 @@ void pop_loose_bake_empty(int row, int col) * кадр 0 loose (41/42/43) на текущей y. Гравитация: speed 0→+3/кадр * (cap 29), y+=speed. Двоебуфер: коридор фиксирован → heal чистит любой * прошлый кадр ЭТОЙ страницы. Ниже поля кусок прячет clip_borders. */ -static uint8_t mob_active; /* 0 нет; 1 падает */ -static uint8_t mob_clean; /* кадров дочистки после конца полёта (2 = обе стр.) */ -static uint8_t mob_defer; /* кусок ниже Kid → рисуем ПОСЛЕ него (y-порядок) */ -static int mob_x; /* левый край (COL_XH*8) */ -static int mob_y; /* = curmob.y оригинала (низ куска, dby), растёт вниз */ -static int mob_prev_y[2];/* последняя нарисованная y на КАЖДОЙ странице (heal) */ -static int8_t mob_speed; -static int8_t mob_row, mob_col; /* тайл-позиция куска (для окклюзии соседа) */ +/* Падающие куски — ТАБЛИЦА (порт mobs[14]/mobs_count, seg007:965). Одного + * куска не хватает: в комнате 7 плиты (0,5) и (0,6) соседние, Kid роняет обе + * подряд, и второй спавн затирал состояние первого — его пиксели оставались + * висеть в воздухе (на каждой странице на своей y → мерцание), а щебень + * появлялся только под одной плитой. 4 слота: больше одновременно падающих + * плит в одной комнате практически не бывает (в оригинале 14). */ +#define MOB_MAX 4 +typedef struct { + uint8_t active; /* 0 нет; 1 падает */ + uint8_t clean; /* кадров дочистки после конца полёта (2 = обе стр.) */ + uint8_t defer; /* кусок ниже Kid → рисуем ПОСЛЕ него (y-порядок) */ + int x; /* левый край (COL_XH*8) */ + int y; /* = curmob.y оригинала (низ куска, dby), растёт вниз */ + int prev_y[2]; /* последняя нарисованная y на КАЖДОЙ странице (heal) */ + int8_t speed; + int8_t row, col; /* тайл-позиция куска (для окклюзии соседа) */ +} mob_t; +static mob_t mobs[MOB_MAX]; /* Кусок «приземлился» (порт loose_land): 0 = нет, иначе tilepos+1 тайла, на * который он лёг → приложение/pop_map ставит debris + do_knock. */ @@ -809,13 +847,22 @@ static const int16_t MOB_Y_BOUND[5] = {-1, 62, 125, 188, 25}; void pop_loose_mob_spawn(int row, int col) { - mob_x = COL_XH[col] * 8; - mob_y = MOB_Y_LAND[row + 1]; /* y_loose_land: низ куска в момент отрыва */ - mob_row = (int8_t)row; - mob_col = (int8_t)col; - mob_speed = 0; - mob_active = 1; - mob_clean = 0; + uint8_t i; + mob_t *m = 0; + /* свободный слот = не летит И дочистка завершена (иначе затрём хвост, + * который ещё не стёрт со второй страницы) */ + for (i = 0; i < MOB_MAX; i++) + if (!mobs[i].active && !mobs[i].clean) { m = &mobs[i]; break; } + if (!m) return; /* таблица полна — кусок без спрайта (тайл всё равно пуст) */ + m->x = COL_XH[col] * 8; + m->y = MOB_Y_LAND[row + 1]; /* y_loose_land: низ куска в момент отрыва */ + m->row = (int8_t)row; + m->col = (int8_t)col; + m->speed = 0; + m->active = 1; + m->clean = 0; + m->defer = 0; + m->prev_y[0] = m->prev_y[1] = MOB_Y_NONE; } /* Габарит куска по X: left/bottom (41/43, w=32) с mob_x; right (42, w=26) @@ -833,47 +880,53 @@ void pop_loose_mob_spawn(int row, int col) * читается other_overlay_tile (порядок «кто поверх кого»). */ static int8_t obj_col, obj_row; -static void mob_render(uint8_t pg); +static void mob_render(mob_t *m, uint8_t pg); void pop_loose_mob_reset(void) { - mob_active = 0; mob_clean = 0; mob_defer = 0; - mob_prev_y[0] = mob_prev_y[1] = MOB_Y_NONE; + uint8_t i; + for (i = 0; i < MOB_MAX; i++) { + mobs[i].active = 0; mobs[i].clean = 0; mobs[i].defer = 0; + mobs[i].prev_y[0] = mobs[i].prev_y[1] = MOB_Y_NONE; + } } -void pop_loose_mob_tick(void) +static void mob_tick_one(mob_t *m, uint8_t pg) { - uint8_t pg = gfx_get_draw_page() & 1; - if (!mob_active && !mob_clean) return; + if (!m->active && !m->clean) return; /* Стереть кусок, нарисованный на ЭТОЙ странице в прошлый раз (у каждой * страницы своя копия фона). Раньше коридор был фиксирован под ряд 2; * плита-потолок падает через весь ряд 0, поэтому чистим по фактической * прошлой позиции. */ - if (mob_prev_y[pg] != MOB_Y_NONE) { - heal_off(MOB_X0(mob_x), mob_prev_y[pg] - 27, MOB_W, 32); - mob_prev_y[pg] = MOB_Y_NONE; + if (m->prev_y[pg] != MOB_Y_NONE) { + heal_off(MOB_X0(m->x), m->prev_y[pg] - 27, MOB_W, 32); + m->prev_y[pg] = MOB_Y_NONE; } - if (!mob_active) { mob_clean--; return; } + if (!m->active) { m->clean--; return; } /* гравитация (move_loose): ускорение до 29 */ - if (mob_speed < 29) mob_speed += 3; - mob_y += mob_speed; + if (m->speed < 29) m->speed += 3; + m->y += m->speed; /* move_loose (seg007:1126): низ куска пересёк границу своего ряда — * смотрим тайл этого ряда. Пусто/loose — валится дальше (порт * loose_fall «плита ломает плиту» не нужен: в ряду ниже её нет); * пол — приземление (loose_land: debris + do_knock). */ - if (mob_row < 3 && mob_y < 226 && MOB_Y_BOUND[mob_row + 1] <= mob_y) { - uint8_t t = (uint8_t)(tile_code(mob_row, mob_col) & 0x1F); + if (m->row < 3 && m->y < 226 && MOB_Y_BOUND[m->row + 1] <= m->y) { + uint8_t t = (uint8_t)(tile_code(m->row, m->col) & 0x1F); if (t == 0 || t == 11) { - mob_row++; /* mob_down_a_row */ + m->row++; /* mob_down_a_row */ + } else if (pop_loose_landed) { + /* сигнал прошлого приземления ещё не разобран (один за кадр) — + * подержим кусок на границе и приземлим следующим кадром */ + m->y = MOB_Y_BOUND[m->row + 1]; } else { - mob_y = MOB_Y_BOUND[mob_row + 1]; - pop_loose_landed = (uint8_t)(mob_row * 10 + mob_col + 1); - mob_active = 0; mob_clean = 2; /* кусок исчез — на его месте debris */ + m->y = MOB_Y_BOUND[m->row + 1]; + pop_loose_landed = (uint8_t)(m->row * 10 + m->col + 1); + m->active = 0; m->clean = 2; /* кусок исчез — на его месте debris */ return; } } - if (mob_y - 27 >= POP_PLAYFIELD_H) { /* весь ниже поля (улетел из комнаты) */ - mob_active = 0; mob_clean = 2; + if (m->y - 27 >= POP_PLAYFIELD_H) { /* весь ниже поля (улетел из комнаты) */ + m->active = 0; m->clean = 2; return; } /* Порядок «плита vs Kid». В оригинале объекты рисуются при обходе СВОИХ @@ -884,58 +937,66 @@ void pop_loose_mob_tick(void) * поверх Kid, если его тайл идёт в обходе ПОЗЖЕ тайла Kid, а при * совпадении тайлов — если он ниже по y. */ { - int8_t mrow = (int8_t)(((mob_y + 60) / 63) % 4 - 1); /* y_to_row_mod4 */ + int8_t mrow = (int8_t)(((m->y + 60) / 63) % 4 - 1); /* y_to_row_mod4 */ uint8_t over; if (mrow != obj_row) over = (mrow < obj_row); /* ряды идут 2,1,0 */ - else if (mob_col != obj_col) over = (mob_col > obj_col); - else over = (mob_y > kid_fp_obj_y()); - if (over) { mob_defer = 1; return; } + else if (m->col != obj_col) over = (m->col > obj_col); + else over = (m->y > kid_fp_obj_y()); + if (over) { m->defer = 1; return; } } - mob_render(pg); + mob_render(m, pg); } -static void mob_render(uint8_t pg) +void pop_loose_mob_tick(void) +{ + uint8_t pg = gfx_get_draw_page() & 1, i; + for (i = 0; i < MOB_MAX; i++) + mob_tick_one(&mobs[i], pg); +} + +static void mob_render(mob_t *m, uint8_t pg) { /* Падающий кусок (SDLPoP draw_mobs/add_midtable, case 0x80): ТРИ куска — * left(41) на obj_y-3, bottom(43) на obj_y, right(42) на obj_x+4 (=col+1, * заходит в тайл СПРАВА) на obj_y-1. */ gfx_set_bank(GFX_BANK_SPRITE); - env_b(43, mob_x, mob_y); - env_b(41, mob_x, mob_y - 3); - env_b(42, mob_x + 32, mob_y - 1); - mob_prev_y[pg] = mob_y; + env_b(43, m->x, m->y); + env_b(41, m->x, m->y - 3); + env_b(42, m->x + 32, m->y - 1); + m->prev_y[pg] = m->y; /* Окклюзия правого куска соседним полом (SDLPoP redraw_at_cur_mob: * set_redraw_full(curr_tilepos+1) — сосед перерисовывается ПОВЕРХ mob). * Правый кусок 42 целиком лежит в тайле col+1; пока плита у уровня пола, * её правый край ПРЯЧЕТСЯ за передней гранью пола. Перерисовываем * сосед-тайл В ТОМ ЖЕ КАДРЕ поверх куска (иначе край торчит на полу * до heal'а следующего кадра — виден как хвост во время падения). */ - if (mob_col + 1 < 10) { - if (mob_row < 0) g_clip_top = POP_YOFF; /* полоса у потолка — только срез */ - draw_tile(mob_row, mob_col + 1); + if (m->col + 1 < 10) { + if (m->row < 0) g_clip_top = POP_YOFF; /* полоса у потолка — только срез */ + draw_tile(m->row, m->col + 1); g_clip_top = 0; } gfx_set_bank(GFX_BANK_NORMAL); /* клип СВОЕГО выхода за поле (верхний борт / статус-полоса) — по ВСЕЙ ширине */ - pop_clip_sprite(MOB_X0(mob_x), MOB_W, mob_y - 26 + POP_YOFF, 30); + pop_clip_sprite(MOB_X0(m->x), MOB_W, m->y - 26 + POP_YOFF, 30); } /* Отложенная отрисовка куска ПОВЕРХ Kid (он ниже Kid по obj_y). Звать * после kid_draw, до fore-слоя. */ void pop_loose_mob_draw_over(void) { - if (!mob_defer) return; - mob_defer = 0; - mob_render(gfx_get_draw_page() & 1); + uint8_t pg = gfx_get_draw_page() & 1, i; + for (i = 0; i < MOB_MAX; i++) + if (mobs[i].defer) { mobs[i].defer = 0; mob_render(&mobs[i], pg); } } /* Позиция падающего куска для check_loose_fall_on_kid (pop_map): колонка и * y (низ куска). 0 — куска нет. */ uint8_t pop_loose_mob_pos(int *col, int *y) { - if (!mob_active) return 0; - *col = mob_col; *y = mob_y; - return 1; + uint8_t i; + for (i = 0; i < MOB_MAX; i++) + if (mobs[i].active) { *col = mobs[i].col; *y = mobs[i].y; return 1; } + return 0; } /* Спрайт вышел за поле? → взвести флаг на 2 кадра (обе страницы дабл- @@ -976,6 +1037,7 @@ void pop_room_draw(uint8_t room_num, /* прозрачный блит: 0xFF не пишется (чёрный фон/нижние слои проступают) */ gfx_set_bank(GFX_BANK_TRANSPARENT); + bake_rest = 1; /* фон = покой (см. bake_rest) */ for (row = 2; row >= 0; row--) /* снизу вверх (2,1,0) */ for (col = 0; col < 10; col++) draw_tile(row, col); @@ -987,6 +1049,7 @@ void pop_room_draw(uint8_t room_num, for (col = 0; col < 10; col++) draw_tile(-1, col); g_clip_top = 0; + bake_rest = 0; gfx_set_bank(GFX_BANK_NORMAL); } diff --git a/applications/PoP/roomtest/pop_map.c b/applications/PoP/roomtest/pop_map.c index fa8cc52..053f259 100644 --- a/applications/PoP/roomtest/pop_map.c +++ b/applications/PoP/roomtest/pop_map.c @@ -1095,6 +1095,15 @@ void pop_loose_tick(void) } pop_loose_mob_tick(); /* продвинуть+нарисовать падающий кусок (после тайлов) */ check_loose_fall_on_kid(); /* seg007:1192 — плита падает Киду на голову */ + /* Отложенная допечка ПРОШЛОГО приземления на вторую страницу — ПЕРЕД + * разбором нового: иначе при двух плитах, севших подряд, land_bake + * затирался новым и на одной странице оставался старый пол. */ + if (land_bake && pop_loose_landed) { + uint8_t r = 0, c = land_bake_pos; + while (c >= 10) { c -= 10; r++; } + pop_floor_bake(r, c); + land_bake = 0; + } if (pop_loose_landed) { /* loose_land (seg007:11E8): на месте куска — щебень */ uint8_t pos = (uint8_t)(pop_loose_landed - 1); uint8_t r = 0, c = pos; diff --git a/applications/PoP/roomtest/roomtest.c b/applications/PoP/roomtest/roomtest.c index 0cb4435..fc7cccc 100644 --- a/applications/PoP/roomtest/roomtest.c +++ b/applications/PoP/roomtest/roomtest.c @@ -26,9 +26,9 @@ __sfr __at (0xFE) io_border; /* Тест-старт для отладки пик (Фаза S): комната 6 = тестовая (пики в col3/col4 * row2, шахта col3). Kid на полу row0 col1, идёт вправо → падает в шахту → * пики. TODO: вернуть на pop_level_start_room (комната 1) после отладки. */ -#define START_ROOM 8 /* ОТЛАДКА clip_char: спуск с кнопки (0,6) */ +#define START_ROOM 20 /* ОТЛАДКА: кнопка-closer (1,4) — отрисовка нажатой */ #define START_COL 8 -#define START_ROW 0 +#define START_ROW 1 #define RESPAWN_DELAY 400 /* авто-респавн (фолбэк), если не нажали ↑ */ #define RESPAWN_SETTLE 20 /* тиков до приёма ↑ (труп успел лечь) */ diff --git a/applications/PoP/toolchain/pop_pack_bg.py b/applications/PoP/toolchain/pop_pack_bg.py index e5a101d..3454751 100644 --- a/applications/PoP/toolchain/pop_pack_bg.py +++ b/applications/PoP/toolchain/pop_pack_bg.py @@ -71,6 +71,19 @@ SPIKE_ANIM_ENV_IDS = {128, 129, 130, 131, 132, # left # открытых состояний (54-59, 67) нужны анимации открытия — добавляем явно. GATE_ANIM_ENV_IDS = {50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 67} +# НАЖАТАЯ кнопка-closer (get_tile_to_draw seg008:2FE: при таймере связи >1 +# tiles_6_closer рисуется как tiles_5_stuck). tile_table[5] = база 41 (уже +# есть), правая грань 35, нижняя 36 — оба id встречаются ТОЛЬКО в этом +# динамическом состоянии, статически тайла 5 в уровнях нет, значит в used +# render_room они не попадают. Без них у нажатой кнопки чёрный провал на +# месте нижней грани и потерянная правая грань. +STUCK_ENV_IDS = {35, 36} + +# Левая половина кнопки-opener, когда СЛЕВА пусто (draw_tile_base seg008:0A8E: +# curr_tile==15 && tile_left==0 && level_type==0 → id 148 вместо базы 147). +# render_room этой ветки не знает, поэтому добавляем явно. +OPENER_NOFLOOR_ENV_IDS = {148} + # ---- chtab_1 (flame/sword/potion), база ресурса 150, папка PRINCE ---- # Зелье (seg008 draw_tile_fore/draw_tile_anim): склянка id 12 (малая) / 13 # (большая, типы 2..4), кадры пузырька 16..22 (potion_fram_bubb) и его маска @@ -337,7 +350,9 @@ def main(): | LOOSE_ANIM_ENV_IDS # + дрожащие кадры loose | SPIKE_ANIM_ENV_IDS # + кадры выдвижения пик | GATE_ANIM_ENV_IDS # + кадры открытия ворот - | TORCH_ENV_IDS) # + основание факела + | TORCH_ENV_IDS # + основание факела + | STUCK_ENV_IDS # + нажатая кнопка-closer + | OPENER_NOFLOOR_ENV_IDS) # + opener без пола слева wall_ids = sorted(id for ch, id in used if ch == R.CHTAB_WALL) env_bg = [i for i in env_ids if i not in FORE_ENV_IDS]