Todas las páginas de formatos ↓
Mapas y terreno

SCT: secciones de mapa

Fuente: coreprime/kbot @ 0ab97bf · Editar en GitHub

Un archivo .sct es 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 .tnt terminados. 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.

Sección de costa de Greenworld renderizada como casillas
sections/greenworld/coast/b_n_w02_135.sct renderizada con kbot sct image

Minimapa de la sección    Mapa de alturas de la sección
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 == 2 tiene 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 que Version ∈ {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)

Véase también

  • TNT: el formato de mapa montado que consume las secciones.
  • PAL: la paleta con la que se colorean tanto los píxeles de las casillas como el minimapa.
  • Glosario: tile, attribute cell, heightmap.