Шаг C: дворцовая кладка стен (wall_pattern, паласная ветка)

Во дворце тело стены — не кирпичи-спрайты, а шесть СПЛОШНЫХ ЗАЛИВОК плюс
пять моно-разделителей поверх (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 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 12:19:42 +03:00
parent 0941ef1d90
commit f10771194b
4 changed files with 152 additions and 7 deletions
Binary file not shown.
+127 -1
View File
@@ -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,6 +480,9 @@ static void fore_only_tile(int row, int col)
if (!FORE_ANY[code]) return; /* ранний выход, как в оригинале */
t = &pop_tile_table[code];
if (code == 20) {
/* Во дворце тело стены даёт 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,6 +523,7 @@ 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: стена */
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) {
@@ -535,6 +660,7 @@ static void overlay_mid_tile(int row, int col)
}
if (code == 20) { /* draw_tile_bottom */
uint8_t wmod = pop_wall_modifier(row, col);
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) {
+4 -1
View File
@@ -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,6 +424,7 @@ static void draw_tile(int row, int col)
/* --- draw_tile_fore (seg008.c:690) --- */
if (code == 20) {
wmod = pop_wall_modifier(row, col);
if (!PALACE) /* seg008:711 */
pop_wall_b(POP_WALL_FRAM_MAIN[wmod], x, dmy); /* основная грань */
pop_wall_pattern_b(row, col, 1);
}
+17 -1
View File
@@ -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} Б")