SCT: secciones de mapa
Fuente: coreprime/kbot @ 0ab97bf · Editar en GitHub
Un archivo
.sctes un trozo reutilizable de terreno de mapa: los diseñadores de niveles de Cavedog unían docenas de ellos en el editor de mapas oficial (TAE) para construir los mapas.tntterminados. Cada sección lleva sus propios gráficos de casillas, su cuadrícula de casillas, las alturas de cada celda y un minimapa en miniatura. Son las piezas de construcción; los TNT son el producto montado.
sections/greenworld/coast/b_n_w02_135.sct renderizada con kbot sct image
Minimapa incrustado (izquierda) y cuadrícula de elevación (derecha).
Tip
Pruébalo tú mismo.
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
Consulta la referencia de kbot sct de la CLI.
Desde Go. Usa 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))
Estructura en disco
┌─ 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
Los campos de puntero pueden apuntar a las secciones en cualquier orden, pero las herramientas de Cavedog las generan en la disposición de arriba, y eso es lo que suponen todos los lectores de terceros.
Cabecera (28 bytes)
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;
| Campo | Notas |
|---|---|
Version |
3 en las secciones de TA de la versión comercial. Algunos prototipos tempranos usan 2: la misma disposición, pero con entradas de altura de 8 bytes en lugar de 4. kbot acepta las dos. |
Width, Height |
Los dos se expresan en casillas (unidades de 32 px). Para que una sección sea compatible con TAE, los dos tienen que ser múltiplos de 4 (es decir, múltiplos de 128 px). |
NumTiles |
Número de casillas únicas. La cuadrícula de índices de la sección de datos hace referencia a este array. |
Píxeles de las casillas
NumTiles bloques de 32 × 32 = 1024 bytes cada uno: índices en la
paleta de TA. El formato no elimina automáticamente las casillas
idénticas, así que una sección mal construida puede desperdiciar
espacio; el comando image de kbot avisa si una sección tiene casillas
inalcanzables.
Datos de la sección: índices de casilla + cuadrícula de alturas
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];
La cuadrícula de alturas es cuatro veces más densa que la de
casillas: cada casilla tiene 2 × 2 = 4 muestras de altura con una
resolución de 16 píxeles, igual que las celdas de atributos de TNT. Los
campos always_-1 y always_0 son metadatos de trabajo del editor; en
el viaje de ida y vuelta puedes volver a escribir cualquiera de los dos
valores.
Minimapa
128 × 128 índices de la paleta. Las dimensiones son fijas sea cual sea
el ancho y el alto en casillas de la sección, y el minimapa se estira
para ajustarse a la proporción de la sección en lugar de añadirle franjas
negras.
Orden de lectura
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)
Ejemplo desarrollado: 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
Una sección de 4×4 casillas es la más pequeña que acepta TAE (tienen que
ser múltiplos de 128 px). Aunque la sección tiene 4 × 4 = 16
posiciones, hace referencia a 64 casillas únicas, porque cada celda de
32×32 está a su vez delimitada por transiciones de borde de las que el
diseñador quería variantes. Es normal: las secciones sacrifican espacio a
cambio de libertad para fundir unas con otras.
Cómo usa TNT las secciones
El pool de casillas de un mapa .tnt es la unión de todos los .sct que
el diseñador de niveles «pintó» en él, sin duplicados, que TAE elimina a
posteriori. Así que, cuando veas que dos TNT comparten una casilla, casi
siempre procede del mismo SCT.
La relación existe solo en tiempo de construcción. El motor nunca
abre una sección durante la partida; solo ve el TNT ya combinado. Las
secciones están en disco en los archivos comprimidos del editor
(worlds.hpi) y en los archivos comprimidos de los mundos de la
campaña, p. ej., sections/greenworld/coast/, sections/lava/hills/,
etc.
Escollos
Warning
Width × Height tiene que ser múltiplo de 4 para que TAE cargue la
sección. El formato en sí no lo impone (puedes escribir una sección de
3×3 casillas y kbot la renderizará sin problemas), pero el editor de
Cavedog la rechazará con un críptico error "section misaligned".
Version == 2tiene celdas de altura de 8 bytes, no de 4. El primer byte sigue siendo la elevación; los 7 bytes restantes son datos de trabajo del editor que puedes poner a cero al escribir.- El minimapa de 128×128 se estira, sin franjas negras, para ajustarse a la proporción de la sección. Una sección de 4×16 se verá horriblemente aplastada en el minimapa.
- No hay firma aparte de
Version. Para validar un posible SCT, comprueba queVersion ∈ {2, 3}y que los cuatro campos de puntero caen todos dentro del archivo. - Los rangos de alturas dependen por completo del diseñador. TAE los limita a 0–255, pero una sección puede ser totalmente plana (un único valor) o abarcar todo el rango. El renderizador de TNT normaliza antes de mostrar; el SCT no.
Tamaños habituales
| Recurso | Rango observado en los sections/**/*.sct de Cavedog |
|---|---|
| Tamaño del archivo | 30 KB – 2 MB |
| Dimensiones de la sección (casillas) | 4×4 – 32×32 |
| Casillas únicas por sección | 30–600 |
| Sobrecarga de la cabecera | siempre 28 bytes |
| Minimapa (siempre 128×128 con paleta) | 16 384 bytes |
Secciones en worlds.hpi |
~5000 (en todos los biomas) |