Уровень 4: второй тайлсет (дворец) — ассеты и переключение (шаги A и B)

Порт tbl_envir_ki[tbl_level_type[level]] (seg000:1108): оригинал под один и
тот же набор id грузит РАЗНЫЙ .DAT — VDUNGEON или VPALACE.

Упаковщик (toolchain/pop_pack_bg.py):
- аргумент набора: `pop_pack_bg.py dungeon|palace`.  Каскад каталогов —
  сначала свой набор, потом чужой фолбэком (в распакованном data/ res230/
  231/348 есть только в VDUNGEON, два десятка — только в VPALACE).
- ОБА набора пакуются по одному объединению id, поэтому раскладка
  id -> (страница, idx) общая и заголовок один: коду достаточно подменить
  имена файлов.
- PALACE_ENV_IDS: 78/80/82 (doortop_fram_bot), 81/83 (doortop_fram_top),
  84 (полоска стены), 145 (stripe_id) — их рисует только палас, render_room
  про них не знает.
- ENV_SHIFT 5 -> 4: с паласными кусками страница 2 переваливала за 16 КБ
  (16 996).  Цена — 10 страниц EMM на набор вместо 5, при 215 свободных.
- *tile.pal: 32 записи (env 0x50..0x5F + wall 0x60..0x6F) на набор.  Полная
  kid.pal не трогается — Кид, страж, меч и зелья в других слотах.

Движок:
- pop_level_type() (tbl_level_type, SDLPoP data.h:840): дворцовые уровни
  4, 5, 6, 10, 11, 14.  Живёт в pop_level.c, потому что по типу расходятся
  не только атласы, но и ветки отрисовки seg008, кладка стены и модификатор
  пустой клетки от упавшей плиты (remove_loose, seg007:0EB8).
- pop_bg_load(set): no-op при том же наборе, при смене выгружает старый
  (иначе текут 12 EMM-страниц) и правит 32 записи палитры в ОБЕ страницы
  дабл-буфера.  Зовётся на старте и на границе уровня, не в кадре.
- Путь к атласу склеивается на месте (bg_path): двадцать строк-имён в банке
  — лишние полкилобайта.

Проверено в MAME: уровень 1 (подземелье) рисуется как прежде; уровень 4
(-DFIRST_LEVEL=4) — дворцовые арки, окна, пол, решётчатая дверь уровня,
Кид не перекрашен.  Стены пока чёрные: паласный wall_pattern — шаг C.

tests-host: 5/5.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 11:28:45 +03:00
parent c8fe0bd37a
commit 97929b55d3
34 changed files with 250 additions and 65 deletions
+71 -15
View File
@@ -8,6 +8,9 @@
*
* Только DUNGEON-стиль (Фаза 1).
*/
/* Имена файлов атласов обоих тайлсетов нужны только здесь; макрос обязан
* стоять ДО первого включения pop_bg_atlas.h (его тянет pop_tile.h). */
#define POP_BG_ATLAS_NAMES
#include <graphics.h>
#include <gfx.h>
#include <sprite.h>
@@ -22,34 +25,87 @@
#include "pop_level.h" /* pop_doorlink2 — состояние нажатия кнопки */
#include "pop_kid.h" /* Kid.curr_col/curr_row — порядок оверлеев */
#include "pop_cdraw.h" /* pop_cd — габарит кадра Кида (y-порядок плиты) */
#include <fcntl.h>
#include <unistd.h>
static const char *const ENV_ATL[5] = {
/* Ресурсы разложены по каталогам образа (8.3, как в DSS): BG — фон,
* KID — персонаж, GUARD — стражи, LEVELS — уровни. Иначе корень диска
* зарастает десятками .atl. */
"BG\\pop_env0.atl", "BG\\pop_env1.atl", "BG\\pop_env2.atl",
"BG\\pop_env3.atl", "BG\\pop_env4.atl"
};
/* ---- Тайлсет: подземелье / дворец ----------------------------------- *
* Порт tbl_envir_ki[tbl_level_type[level]] (seg000:1108): оригинал под один
* и тот же набор id грузит РАЗНЫЙ .DAT — VDUNGEON или VPALACE. У нас так
* же: раскладка id -> (страница, idx) общая (упаковщик печёт оба набора по
* одному объединению id), меняются только файлы атласов и 32 записи
* палитры — env 0x50..0x5F и wall 0x60..0x6F. Кид, страж, меч и зелья
* живут в других слотах, поэтому общая kid.pal не трогается. */
static uint8_t bg_set = 0xFF; /* какой набор сейчас загружен */
int pop_bg_load(void) __banked
/* Ресурсы разложены по каталогам образа (8.3, как в DSS): BG — фон, KID —
* персонаж, GUARD — стражи, LEVELS — уровни. Иначе корень диска зарастает
* десятками .atl. Путь склеиваем на месте: держать по строке на каждый из
* двадцати атласов — лишние полкилобайта в банке. */
static char bg_path_buf[20];
static const char *bg_path(const char *name)
{
char *d = bg_path_buf;
*d++ = 'B'; *d++ = 'G'; *d++ = '\\';
while (*name) *d++ = *name++;
*d = 0;
return bg_path_buf;
}
/* 32 записи (env, затем wall) в ОБЕ палитры: у страниц дабл-буфера они
* раздельные. Буфер — на стеке: gfx_pal_load отдаёт указатель BIOS ($A4),
* а тот читает только #4000-#BFFF, то есть банковую rodata бы не увидел
* (те же грабли, что в pop_guard_set_palette). */
static int bg_load_tile_pal(const char *name)
{
uint8_t buf[64];
int fd = open(bg_path(name), O_RDONLY);
if (fd < 0) return -1;
if (read(fd, buf, 64) != 64) { close(fd); return -1; }
gfx_pal_load(0, POP_PAL_ENV, 16, buf);
gfx_pal_load(1, POP_PAL_ENV, 16, buf);
if (read(fd, buf, 64) != 64) { close(fd); return -1; }
gfx_pal_load(0, POP_PAL_WALL, 16, buf);
gfx_pal_load(1, POP_PAL_WALL, 16, buf);
close(fd);
return 0;
}
/* Загрузить набор set (0 подземелье, 1 дворец). Повторный вызов с тем же
* набором ничего не делает; со сменой — сначала выгружает старый, иначе
* EMM-страницы утекут (их по 12 на набор). */
int pop_bg_load(uint8_t set) __banked
{
int i;
for (i = 0; i < 5; i++)
if (atlas_load(&pop_env[i], ENV_ATL[i]) != 0) return -1;
if (atlas_load(&pop_wall_at, "BG\\pop_wall.atl") != 0) return -1;
if (atlas_load(&pop_fore_at, "BG\\pop_fore.atl") != 0) return -1;
if (atlas_load(&pop_pot_at, "BG\\pop_pot.atl") != 0) return -1;
return 0;
if (set >= POP_TILESETS) return -1;
if (set == bg_set) return 0;
if (bg_set != 0xFF) { /* смена набора — освободить старый */
for (i = 0; i < POP_ENV_PAGES; i++) atlas_free(&pop_env[i]);
atlas_free(&pop_wall_at);
atlas_free(&pop_fore_at);
}
for (i = 0; i < POP_ENV_PAGES; i++)
if (atlas_load(&pop_env[i], bg_path(pop_env_atl[set][i])) != 0) return -1;
if (atlas_load(&pop_wall_at, bg_path(pop_wall_atl[set])) != 0) return -1;
if (atlas_load(&pop_fore_at, bg_path(pop_fore_atl[set])) != 0) return -1;
if (bg_set == 0xFF && /* chtab_1 от набора не зависит */
atlas_load(&pop_pot_at, bg_path(POP_POT_ATL)) != 0) return -1;
bg_set = set;
/* Палитра — ПОСЛЕ атласов: если атлас не загрузился, цвета остаются от
* прежнего набора и картинка хотя бы не разъезжается. */
return bg_load_tile_pal(pop_tile_pal[set]);
}
void pop_bg_free(void) __banked
{
int i;
for (i = 0; i < 5; i++)
if (bg_set == 0xFF) return;
for (i = 0; i < POP_ENV_PAGES; i++)
atlas_free(&pop_env[i]);
atlas_free(&pop_wall_at);
atlas_free(&pop_fore_at);
atlas_free(&pop_pot_at);
bg_set = 0xFF;
}
static const uint8_t SPIKES_FRAM_RIGHT[10] = {0, 134, 135, 136, 137, 138, 137, 135, 134, 0};