From f10771194b2400992f5fadeaba9be2a885405049 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Mon, 10 Aug 2026 12:19:42 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A8=D0=B0=D0=B3=20C:=20=D0=B4=D0=B2=D0=BE?= =?UTF-8?q?=D1=80=D1=86=D0=BE=D0=B2=D0=B0=D1=8F=20=D0=BA=D0=BB=D0=B0=D0=B4?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=81=D1=82=D0=B5=D0=BD=20(wall=5Fpattern,=20?= =?UTF-8?q?=D0=BF=D0=B0=D0=BB=D0=B0=D1=81=D0=BD=D0=B0=D1=8F=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=82=D0=BA=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Во дворце тело стены — не кирпичи-спрайты, а шесть СПЛОШНЫХ ЗАЛИВОК плюс пять моно-разделителей поверх (seg008:1946). Порт целиком: - gen_palace_wall_colors (seg000:1942): 3 ряда × 4 подряда × 11 колонок = 132 цвета, сид = номер комнаты, подряды 1/3 из 0x61..0x64, подряды 0/2 из 0x66..0x69, соседние по горизонтали не повторяются. Одиннадцать колонок, а не десять: заливки 3 и 5 берут цвет СЛЕДУЮЩЕЙ колонки. Пересчёт на смене комнаты — там же, где сбрасывается кэш кладки (wall_pattern_reset). Таблица не static: writable-данные банка живут в _DATA/W2. - Геометрия заливок дословно из add_wipetable(layer, left, bottom, height, width): прямоугольник = x..x+width-1, (bottom-height+1)..bottom. - Пять prandom(2) на тайл кэшируются так же, как подземельные решения (wp_a/wp_b переиспользуются — наборы в одной комнате не сосуществуют). Сохранён квирк порядка: при which_part == 0 разыгрывается ОДНО значение, и нижний разделитель берёт ПЕРВОЕ из серии, а не пятое. - Заливки режутся по окну fore-клипа: иначе легли бы поверх областей, которые в этом кадре никто не восстанавливает. Вне fore-прохода — pop_cd_touch, потому что bar идёт мимо pop_blit_b. - wall_fram_bottom / wall_fram_main во дворце НЕ рисуются (seg008:576, 711) — и в горячей половине слоя (pop_bg.c), и в холодной (pop_room.c). - Упаковщик: дворцовые wall-id 3..17 пакуются силуэтом в цвете 6 общей 16-цветной палитры (blitters_46h_mono_6). В подземелье те же id — обычные кирпичи, поэтому mono только у паласного набора. ИЗВЕСТНОЕ РАСХОЖДЕНИЕ: рисунок цветов не совпадает с SDLPoP попиксельно, потому что наш prandom — 16-битный xorshift, а не LCG оригинала (замена сделана раньше по бюджету кадра, prng_alternatives.md). Совпадают геометрия, диапазоны цветов и правило «соседние не повторяются». Проверено в MAME: уровень 4 — песочный мрамор с разделителями, структурно как эталон SDLPoP; уровень 1 не изменился. tests-host 5/5. Остаётся расхождение по двери уровня (мы заполняем проём плетёнкой целиком, оригинал рисует несколько кусков лестницы) — отдельным шагом. Co-Authored-By: Claude Opus 5 --- applications/PoP/poc/res/bg/pal_wall.atl | Bin 5267 -> 5267 bytes applications/PoP/roomtest/pop_bg.c | 134 +++++++++++++++++++++- applications/PoP/roomtest/pop_room.c | 7 +- applications/PoP/toolchain/pop_pack_bg.py | 18 ++- 4 files changed, 152 insertions(+), 7 deletions(-) diff --git a/applications/PoP/poc/res/bg/pal_wall.atl b/applications/PoP/poc/res/bg/pal_wall.atl index 1c82e43a0edf336819d68b072f8a4b43dc15c115..f3a72c27fa694f940a7a2ef14f4b507fec5f12b1 100644 GIT binary patch literal 5267 zcmdT_OKuZE5bY?5Y+{>_lZ{1W%?%KnSw`XpAQl_}$sf%TI06e69EC%$;2>Op1ya-X zo$l%$&)A9M(3YmE-m9vve)I9i_wU}$-SO9-u*KP#bAR394(#*MVg~lpcsv68jqNYC zlV{Zw@UK^7ucu_c&&a-hQB8pV^70n#k5|h}h(FB9{+=%{ApU$^orC?!_RS5&aN>o# zbDMarqZQ+u2w&fiX!dT^SrHRWJXJ)*@Mfb#k{)uHXe>@d4r4{L-DdfL*uu&Q!ZU(l zTY*jZqGxC{nHVN4EPI%2sG$n(8=DgoAXhvN`7Gkz#Hv`)tX3rg-~!PQo>eexD-=!% z>@63aDoRF<2@6XrsgEpFL}BZ8z-_|+?a5uSrwEU_g)r{W2?h5_1&B|uPl*;bgk<}$ z+E(<^0ftM7I2#xuOg0nzLSuvWvMR&g7n)EN?Ag{wUI*;`!p&Tim$>UB(ja^~)sLJ8 zKB-a6E`Mk(p??Y~p>W!Yn-JSp=x017;L}#9pf6rV5S&#A9BedH_kZEojYP<`=>CTj5Ww+13!U# zkj-RI!h!>gnui!_=xUQsANQWej`#@opl_xDJjgAQhje)U!w-?XI!ElGcgK%jU$_Uy zo?-9)BzX`HbcdG$#Rnm=)zE%J^LF2%Zua2*m7CcAgM)i@kJrcO6Riq|A;71{y|0}3 zzPbDOVY?iVb$e=!QAm^%p4uTg49O`eacEtsmtVR|c{X?VJq{ZL3Gs}=`N0{nJNMss_y92l{=TcWgKg@!V(;V%J7}V6)?$%!{T~~J#a6&pV(G+ Ugbl-bh&`Z>PEQ*`bLO`C>(+X2jK!Nked4T zRDZ@ZcH%g6q^YX+s;aBse4LN(-@To=oyj)yj{9(rS_iS;2@#pL69PLlBZ(ebXC!V`o zx3+6#mc`cwU)_yp_HNZ#VTs00We5we*MW%AAxDTN#hJ)qs%SQwEFXvsuAE>zBLq$> zsEJ&{8JdvHj1VcTdnDOd!xhrkniCT!mmP0L=r0grkLQQV;u3GSl`6rW)q6D^z&n(bk= zt@x!wj8-6QHZn{k*+R$*O%2-1vJ87)Xu?&vXIpQ)4%qv-n>v#hxvMA&0em`xZ=41{ zsU{XzKC~$jeiULNz;P>S!faa+KI1WgpSD5;fAKPc;jBVoux6<4-@+?5QX#i8VV7+W zz}|L1t=4EyjeVh3In7n8c(9e5=*$62oH~h55!Sw@rF+*j6hPiThVgW)K{gxs5!{1p zrt~BtIKrek#8|`EH2L&#?|IS@AK)JJ%`|{Vxm9vV$LAmZ2+6B+Bt7)*_`&N7_u$ww z?A@Ow2jNh6d?`qL6p~sE?Kd=U_Z{kH5AI*Nar%33aIfz1`k3%U2Zdn>^!ah`D`&oM z?mm9lE(c`YoLXZNlH|mvc8Cr`a!N`ZT371j7w%G@&E0*E!-8Pa7#)_6=d1@$Ry@}o zK^ZF^PkS!h#Zfg2ir|JpcXH~=o$KB@PHH>W|Jp`IJ?3 diff --git a/applications/PoP/roomtest/pop_bg.c b/applications/PoP/roomtest/pop_bg.c index c6c40a5..7e8f162 100644 --- a/applications/PoP/roomtest/pop_bg.c +++ b/applications/PoP/roomtest/pop_bg.c @@ -120,10 +120,45 @@ static void draw_right_mark(uint16_t dv, int arg1, int row, int col) #define WP_N 40 static uint8_t wp_a[WP_N], wp_b[WP_N], wp_ok[WP_N]; +/* ---- Цвета дворцовой кладки (gen_palace_wall_colors, seg000:1942) ---- * + * Тело стены во дворце — не кирпичи-спрайты, а СПЛОШНЫЕ ЗАЛИВКИ; их цвета + * разыгрываются на КОМНАТУ: сид = номер комнаты, 3 ряда × 4 подряда × 11 + * колонок. Подряды 1 и 3 берут 0x61..0x64, подряды 0 и 2 — 0x66..0x69 + * (это слоты wall-палитры, во дворце они песочные); соседние по горизонтали + * не повторяются. Одиннадцать колонок, а не десять: заливки 3 и 5 берут + * цвет СЛЕДУЮЩЕЙ колонки. + * + * Не static: таблицу читает и холодная половина слоя (pop_room.c, банк 7). + * Writable-данные банка живут в _DATA/W2 и видны отовсюду — в отличие от + * const-таблиц, которые из чужого банка не прочитать. */ +uint8_t pop_pal_wall_col[3 * 44]; + +static void gen_palace_wall_colors(uint8_t room) +{ + pop_rnd_t saved = pop_t_seed; + uint8_t row, sub, col; + uint8_t *p = pop_pal_wall_col; + pop_prandom_set(pop_t_seed, room); + prandom(1); /* сброс, как в оригинале */ + for (row = 0; row < 3; row++) + for (sub = 0; sub < 4; sub++) { + uint8_t base = (uint8_t)((sub & 1) ? 0x61 : 0x66); + uint8_t prev = 0; /* 0 — «цвета ещё не было» */ + for (col = 0; col < 11; col++) { + uint8_t c; + do { c = (uint8_t)(base + prandom(3)); } while (c == prev); + *p++ = c; + prev = c; + } + } + pop_t_seed = saved; +} + static void wall_pattern_reset(void) /* смена комнаты — сид другой */ { uint8_t i; for (i = 0; i < WP_N; i++) wp_ok[i] = 0; + if (pop_palace) gen_palace_wall_colors(pop_t_room); } /* Выходы wall_rnd — в файловых статиках: возвращать девять значений @@ -219,11 +254,97 @@ static const uint8_t WP_PARTS[4] = { WP_RND | WP_MID | WP_BOT | WP_RIGHT | WP_LEFT, /* 3 WWW */ }; +/* ---- Кладка ДВОРЦА (seg008:1946) ------------------------------------ * + * Тело стены — не кирпичи-спрайты, а шесть СПЛОШНЫХ ЗАЛИВОК цветами из + * pop_pal_wall_col, поверх — пять разделителей chtab_7 (mono, цвет 6). + * Геометрия дословно из add_wipetable(layer, left, bottom, height, width): + * прямоугольник = x..x+width-1, (bottom-height+1)..bottom. + * + * Порядок prandom важен так же, как в подземелье: при which_part == 0 + * разыгрывается ОДНО значение, и нижний разделитель берёт ПЕРВОЕ из серии, + * а не пятое. */ +static uint8_t wpp_d[5]; /* пять prandom(2), 0..2 */ + +static void wall_rnd_palace(int row, int col) +{ + int idx = (col >= 0 && col < 10) ? (row + 1) * 10 + col : -1; + pop_rnd_t saved; + uint8_t i; + if (idx >= 0 && idx < WP_N && wp_ok[idx]) { + uint8_t a = wp_a[idx]; + wpp_d[0] = (uint8_t)(a & 3); wpp_d[1] = (uint8_t)((a >> 2) & 3); + wpp_d[2] = (uint8_t)((a >> 4) & 3); wpp_d[3] = (uint8_t)((a >> 6) & 3); + wpp_d[4] = wp_b[idx]; + return; + } + saved = pop_t_seed; + pop_prandom_set(pop_t_seed, pop_t_room + TBL_LINE[row < 0 ? 2 : row] + col); + prandom(1); /* сброс */ + for (i = 0; i < 5; i++) wpp_d[i] = (uint8_t)prandom(2); + pop_t_seed = saved; + if (idx >= 0 && idx < WP_N) { + wp_a[idx] = (uint8_t)(wpp_d[0] | (wpp_d[1] << 2) | + (wpp_d[2] << 4) | (wpp_d[3] << 6)); + wp_b[idx] = wpp_d[4]; + wp_ok[idx] = 1; + } +} + +/* Заливка куска стены. x/ytop — уже ЭКРАННЫЕ (с POP_YOFF). В fore-проходе + * режем по окну клипа: без этого заливка легла бы поверх областей, которые + * в этом кадре никто не восстанавливает. */ +static void wpp_fill(int x, int ytop, int w, int h, uint8_t colour) +{ + int x2 = x + w - 1, y2 = ytop + h - 1; + if (pop_t_fclip_on) { + if (x < pop_t_fclip_x0) x = pop_t_fclip_x0; + if (ytop < pop_t_fclip_y0) ytop = pop_t_fclip_y0; + if (x2 >= pop_t_fclip_x1) x2 = pop_t_fclip_x1 - 1; + if (y2 >= pop_t_fclip_y1) y2 = pop_t_fclip_y1 - 1; + } + if (pop_t_clip_top && ytop < pop_t_clip_top) ytop = pop_t_clip_top; + if (x < 0) x = 0; + if (ytop < 0) ytop = 0; + if (x2 > 319) x2 = 319; + if (y2 > 255) y2 = 255; + if (x2 < x || y2 < ytop) return; + setfillstyle(SOLID_FILL, colour); + bar(x, ytop, x2, y2); + if (!pop_t_fclip_on) pop_cd_touch(x, ytop, x2 - x + 1, y2 - ytop + 1); +} + +static void wall_pattern_palace(int row, int col, int which_part) +{ + int xh = POP_COL_XH[col]; + int dbys = 63 * row + 65; /* база БЕЗ POP_YOFF — для блитов */ + int dmys = dbys - 3; + int dby = dbys + POP_YOFF, dmy = dmys + POP_YOFF; /* экранные — для заливок */ + /* Цвета лежат по рядам 0..2; полоса у потолка (row −1) — это ряд 2 + * комнаты сверху, оригинал и сид для неё берёт по ряду 2. */ + const uint8_t *cl = pop_pal_wall_col + 44 * (row < 0 ? 2 : row) + col; + wall_rnd_palace(row, col); + if (which_part) { + wpp_fill(xh * 8, dmy - 59, 32, 20, cl[0]); + wpp_fill(xh * 8, dmy - 39, 16, 21, cl[11]); + wpp_fill((xh + 2) * 8, dmy - 39, 16, 21, cl[12]); + wpp_fill(xh * 8, dmy - 18, 8, 19, cl[22]); + wpp_fill((xh + 1) * 8, dmy - 18, 24, 19, cl[23]); + pop_wall_b((uint8_t)(wpp_d[0] + 3), (xh + 3) * 8, dmys - 53); + pop_wall_b((uint8_t)(wpp_d[1] + 6), xh * 8, dmys - 34); + pop_wall_b((uint8_t)(wpp_d[2] + 9), xh * 8, dmys - 13); + pop_wall_b((uint8_t)(wpp_d[3] + 12), xh * 8, dmys); + } + wpp_fill(xh * 8, dby - 2, 32, 3, cl[33]); + pop_wall_b((uint8_t)((which_part ? wpp_d[4] : wpp_d[0]) + 15), xh * 8, dbys); +} + static void wall_pattern(int row, int col, int which_part) { int xh = POP_COL_XH[col]; int dby = 63 * row + 65; - uint8_t parts = WP_PARTS[pop_wall_modifier(row, col)]; + uint8_t parts; + if (pop_palace) { wall_pattern_palace(row, col, which_part); return; } + parts = WP_PARTS[pop_wall_modifier(row, col)]; wall_rnd(row, col); if (which_part) { @@ -359,7 +480,10 @@ static void fore_only_tile(int row, int col) if (!FORE_ANY[code]) return; /* ранний выход, как в оригинале */ t = &pop_tile_table[code]; if (code == 20) { - pop_wall_b(POP_WALL_FRAM_MAIN[pop_wall_modifier(row, col)], x, dmy); + /* Во дворце тело стены даёт wall_pattern заливками, а спрайтовая + * грань не рисуется вовсе (seg008:711). */ + if (!pop_palace) + pop_wall_b(POP_WALL_FRAM_MAIN[pop_wall_modifier(row, col)], x, dmy); wall_pattern(row, col, 1); return; } @@ -399,7 +523,8 @@ static void ceil_over_kid_tile(int col) int x = POP_COL_XH[col] * 8; int dby = 63 * (-1) + 65; /* = 2 */ if (code == 20) { /* draw_tile_bottom: стена */ - pop_wall_b(POP_WALL_FRAM_BOTTOM[pop_wall_modifier(-1, col)], x, dby); + if (!pop_palace) /* seg008:576 */ + pop_wall_b(POP_WALL_FRAM_BOTTOM[pop_wall_modifier(-1, col)], x, dby); wall_pattern(-1, col, 0); } else if (t->bottom_id) { pop_env_b(t->bottom_id, x, dby); @@ -535,7 +660,8 @@ static void overlay_mid_tile(int row, int col) } if (code == 20) { /* draw_tile_bottom */ uint8_t wmod = pop_wall_modifier(row, col); - pop_wall_b(POP_WALL_FRAM_BOTTOM[wmod], x, dby); + if (!pop_palace) /* seg008:576 */ + pop_wall_b(POP_WALL_FRAM_BOTTOM[wmod], x, dby); wall_pattern(row, col, 0); } else if (t->bottom_id) { pop_env_b(t->bottom_id, x, dby); diff --git a/applications/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c index cc8e408..29b4006 100644 --- a/applications/PoP/roomtest/pop_room.c +++ b/applications/PoP/roomtest/pop_room.c @@ -363,7 +363,9 @@ static void draw_tile(int row, int col) /* --- draw_tile_bottom (seg008.c:570) --- */ if (code == 20) { wmod = pop_wall_modifier(row, col); - pop_wall_b(POP_WALL_FRAM_BOTTOM[wmod], x, dby); /* сплошная нижняя грань */ + if (!PALACE) /* во дворце тело стены даёт */ + pop_wall_b(POP_WALL_FRAM_BOTTOM[wmod], x, dby); /* wall_pattern */ + /* заливками (seg008:576) */ pop_wall_pattern_b(row, col, 0); } else if (t->bottom_id) { pop_env_b(t->bottom_id, x, dby); @@ -422,7 +424,8 @@ static void draw_tile(int row, int col) /* --- draw_tile_fore (seg008.c:690) --- */ if (code == 20) { wmod = pop_wall_modifier(row, col); - pop_wall_b(POP_WALL_FRAM_MAIN[wmod], x, dmy); /* основная грань */ + if (!PALACE) /* seg008:711 */ + pop_wall_b(POP_WALL_FRAM_MAIN[wmod], x, dmy); /* основная грань */ pop_wall_pattern_b(row, col, 1); } if (code == 0x12) { /* чомпер: передние зубья + кровь. diff --git a/applications/PoP/toolchain/pop_pack_bg.py b/applications/PoP/toolchain/pop_pack_bg.py index 1b203fc..82a0f30 100644 --- a/applications/PoP/toolchain/pop_pack_bg.py +++ b/applications/PoP/toolchain/pop_pack_bg.py @@ -95,6 +95,7 @@ GATE_ANIM_ENV_IDS = {50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 67} GATE_TOP_ENV_IDS = {60, 61, 62, 63, 64, 65, 66, 67} GATE_TOP_MASK_ID = 68 + # НАЖАТАЯ кнопка-closer (get_tile_to_draw seg008:2FE: при таймере связи >1 # tiles_6_closer рисуется как tiles_5_stuck). tile_table[5] = база 41 (уже # есть), правая грань 35, нижняя 36 — оба id встречаются ТОЛЬКО в этом @@ -131,6 +132,16 @@ POT_MASK_COLOR = VGA16_PAL_BASE + 0 # чёрный (blitters_40h_mono без POT_BUBBLE_COLOR = VGA16_PAL_BASE + 12 # ярко-красный (color 12: «лечение») CHOMPER_BLOOD_COLOR = VGA16_PAL_BASE + 12 # тот же mono-цвет 12 (seg008:678) +# Кладка ДВОРЦА (wall_pattern, seg008:1928, ветка palace): тело стены — +# сплошные заливки цветом из palace_wall_colors, а поверх идут ПЯТЬ +# разделителей chtab_7 id 3..17 (prandom(2) + 3/6/9/12/15), и рисуются они +# blitters_46h_mono_6 — mono-блитом ОДНИМ цветом 6 общей 16-цветной палитры +# экрана. В подземелье те же id — обычные спрайты кирпичей (wall_fram_main/ +# bottom), поэтому силуэтом пакуем ТОЛЬКО дворцовый набор. +PALACE_WALL_MONO_IDS = set(range(3, 18)) +PALACE_WALL_MONO_COLOR = VGA16_PAL_BASE + 6 + + # Основание факела (seg008:489 draw_tile_right: при левом соседе-факеле # рисуется chtab_6 id 146). В статический render_room не попадает. TORCH_ENV_IDS = {146} @@ -505,7 +516,12 @@ def main(): print(f" (env стр {pg}: пусто)") # WALL -> idx=id - wall_entries = {id: load_indexed(R.CHTAB_WALL, id) for id in wall_ids} + wall_entries = {id: load_indexed( + R.CHTAB_WALL, id, False, + PALACE_WALL_MONO_COLOR + if (set_name == "palace" and id in PALACE_WALL_MONO_IDS) + else None) + for id in wall_ids} cnt, sz = pack_atlas(os.path.join(OUT_DIR, prefix + "wall.atl"), wall_entries) total_bytes += sz print(f" {prefix}wall.atl: {len(wall_ids)} спрайтов, каталог {cnt}, {sz} Б")