SCT — секції мап
Джерело: coreprime/kbot @ 0ab97bf · Редагувати на GitHub
Файл
.sct— це багаторазовий фрагмент рельєфу мапи: дизайнери рівнів Cavedog зшивали десятки таких фрагментів в офіційному редакторі мап (TAE), щоб зібрати готові мапи.tnt. Кожна секція містить власні графіку тайлів, сітку тайлів, висоти для кожної клітинки й мініатюру мінімапи. Секції — це будівельні блоки; TNT — зібраний виріб.
sections/greenworld/coast/b_n_w02_135.sct, відрендерена командою kbot sct image
Вбудована мінімапа (ліворуч) і сітка висот (праворуч).
Tip
Спробуйте самі.
kbot sct info sections/greenworld/coast/b_n_w02_135.sct
kbot sct describe sections/greenworld/coast/b_n_w02_135.sct
kbot sct image sections/greenworld/coast/b_n_w02_135.sct -t out.png
kbot sct minimap sections/greenworld/coast/b_n_w02_135.sct -t mini.png
kbot sct heightmap sections/greenworld/coast/b_n_w02_135.sct -t height.png
Див. довідник CLI kbot sct.
З Go. Скористайтеся пакетом formats/sct:
import "github.com/coreprime/kbot/formats/sct"
f, _ := os.Open("sections/greenworld/coast/b_n_w02_135.sct")
defer f.Close()
s, _ := sct.LoadFromReader(f)
fmt.Printf("%dx%d tiles, %d unique, %d heights\n",
s.Header.Width, s.Header.Height, s.Header.NumTiles, len(s.HeightMap))
Структура на диску
┌─ Header (28 B) ──┐ version, pointers, dimensions
├─ Tile pixels ────┤ NumTiles × 1024 bytes (32×32 palette indices)
├─ Section data ───┤ Width × Height × int16 tile indices
├─ Height grid ────┤ (Width × 2) × (Height × 2) × {4 or 8} bytes
└─ Minimap ────────┘ 128 × 128 palette indices
Поля-вказівники можуть указувати на секції файлу в будь-якому порядку, але інструменти Cavedog виводять їх у показаній вище послідовності, і саме на неї розраховує кожен сторонній читач.
Заголовок (28 байтів)
typedef struct {
uint32 Version; // 2 or 3 (TA's TAE always emits 3)
uint32 PtrMinimap; // → 128×128 minimap
uint32 NumTiles; // unique 32×32 tile count
uint32 PtrTiles; // → tile pixel block
uint32 Width; // section width in tiles
uint32 Height; // section height in tiles
uint32 PtrData; // → tile-index grid + heights
} SCTHeader;
| Поле | Примітки |
|---|---|
Version |
3 для релізних секцій TA. Деякі ранні прототипи використовують 2 — та сама структура, але записи висот мають 8 байтів замість 4. kbot приймає обидві версії. |
Width, Height |
Обидва задано в тайлах (одиницях по 32 px). Щоб секція була сумісна з TAE, обидва мають бути кратні 4 (тобто кратні 128 px). |
NumTiles |
Кількість унікальних тайлів. Сітка індексів у секції даних посилається на цей масив. |
Пікселі тайлів
NumTiles блоків по 32 × 32 = 1024 байти — індекси палітри TA.
Однакові тайли формат автоматично не дедуплікує, тож погано зібрана
секція може марнувати місце; команда image у kbot попередить, якщо
секція має недосяжні тайли.
Дані секції — індекси тайлів + сітка висот
int16 tileMap[Height][Width]; // Index into the tile pixel array
// (signed; negative values are unused
// in retail content but valid)
struct HeightCell_V3 { uint8 h; int16 always_minus_one; uint8 always_zero; };
struct HeightCell_V2 { uint8 h; int16 always_minus_one; uint8 always_zero;
uint32 reserved; };
HeightCell heights[Height * 2][Width * 2];
Сітка висот учетверо щільніша за сітку тайлів: кожен тайл отримує
2 × 2 = 4 значення висоти з роздільністю 16 пікселів — так само, як
працюють клітинки атрибутів у TNT. Поля always_-1 і always_0 — це
службові метадані редактора; під час round-trip можна записати назад
будь-яке значення.
Мінімапа
128 × 128 індексів палітри. Розміри фіксовані незалежно від ширини й
висоти секції в тайлах, і мінімапа розтягується під пропорції
секції, а не вписується з полями.
Порядок читання
header = read_struct(SCTHeader)
assert header.Version in (2, 3)
seek(header.PtrTiles)
tiles = [read(1024) for _ in range(header.NumTiles)]
seek(header.PtrData)
tile_map = read_int16_array(header.Width * header.Height)
entry_size = 4 if header.Version == 3 else 8
attrs_w, attrs_h = header.Width * 2, header.Height * 2
heights = [read(entry_size)[0] for _ in range(attrs_w * attrs_h)]
seek(header.PtrMinimap)
minimap = read(128 * 128)
Розібраний приклад — b_n_w02_135.sct
$ kbot sct describe sections/greenworld/coast/b_n_w02_135.sct
Version: 3
Section: 4 × 4 tiles (128 × 128 px)
Tiles: 64 unique
Heights: 8 × 8 cells (height range 50–62)
Minimap: 128 × 128
Секція 4×4 тайли — найменша, яку прийме TAE (розміри мають бути кратні
128 px). Хоча секція має 4 × 4 = 16 розміщень, вона посилається на 64
унікальні тайли, бо кожна клітинка 32×32 сама обмежена краєвими
переходами, для яких дизайнер хотів мати варіанти. Це нормально: секції
жертвують місцем заради свободи змішування.
Як TNT використовує секції
Пул тайлів мапи .tnt — це об'єднання всіх .sct, які дизайнер рівня
«намалював» на ній, після чого TAE прибирає дублікати. Тож якщо ви
бачите, що два TNT мають спільний тайл, він майже завжди походить з
однієї SCT.
Цей зв'язок існує лише під час створення мапи. Рушій під час гри
ніколи не відкриває секцій; він бачить лише вже зібраний TNT. Секції
лежать на диску в архівах редактора (worlds.hpi) і в архівах світів
кампанії, наприклад sections/greenworld/coast/, sections/lava/hills/
тощо.
Підводні камені
Warning
Щоб TAE завантажив секцію, Width × Height має бути кратним 4.
Сам формат цього не вимагає — можна записати секцію 3×3 тайли, і kbot
охоче її відрендерить, — але редактор Cavedog відхилить її із
загадковою помилкою «section misaligned».
Version == 2має 8-байтові клітинки висот, а не 4-байтові. Перший байт — це, як і раніше, висота; решта 7 байтів — службові дані редактора, які під час запису можна заповнити нулями.- Мінімапа 128×128 розтягується, а не вписується з полями, під пропорції секції. Секція 4×16 на мінімапі виглядатиме жахливо сплюснутою.
- Сигнатури, крім
Version, немає. Щоб перевірити, чи файл може бути SCT, переконайтеся, щоVersion ∈ {2, 3}і що всі чотири поля-вказівники потрапляють у межі файлу. - Діапазони висот цілком на розсуд дизайнера. TAE обмежує їх 0–255, але секція може бути ідеально пласкою (одне значення) або займати весь діапазон. Рендерер TNT нормалізує висоти перед показом; SCT — ні.
Типові розміри
| Ресурс | Діапазон, що трапляється в sections/**/*.sct від Cavedog |
|---|---|
| Розмір файлу | 30 КБ – 2 МБ |
| Розміри секції (у тайлах) | 4×4 – 32×32 |
| Унікальних тайлів у секції | 30–600 |
| Накладні витрати на заголовок | завжди 28 байтів |
| Мінімапа (завжди 128×128, палітрова) | 16384 байти |
Секцій у worlds.hpi |
~5000 (в усіх біомах) |