SCT — Secções de mapa
Fonte: coreprime/kbot @ 0ab97bf · Editar no GitHub
Um ficheiro
.scté um bloco reutilizável de terreno de mapa: os designers de níveis da Cavedog juntavam dezenas destes no editor de mapas oficial (TAE) para construir os mapas.tntfinais. Cada secção traz os seus próprios gráficos de mosaicos, a grelha de mosaicos, as alturas de cada célula e um minimapa de pré-visualização. As secções são os tijolos; os TNT são o produto montado.
sections/greenworld/coast/b_n_w02_135.sct renderizado por kbot sct image
Minimapa incorporado (à esquerda) e grelha de elevação (à direita).
Tip
Experimente por si.
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
Consulte a referência de kbot sct da linha de comandos.
A partir de Go. Use 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))
Disposição no 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
Os campos de ponteiro podem apontar para as partes do ficheiro por qualquer ordem, mas as ferramentas da Cavedog escrevem-nas pela disposição mostrada acima, e é isso que todos os leitores de terceiros assumem.
Cabeçalho (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 nas secções da versão comercial do TA. Alguns protótipos antigos usam 2: a mesma disposição, mas as entradas de altura têm 8 bytes em vez de 4. O kbot aceita ambas. |
Width, Height |
Ambos expressos em mosaicos (unidades de 32 px). Numa secção compatível com o TAE, ambos têm de ser múltiplos de 4 (ou seja, múltiplos de 128 px). |
NumTiles |
Número de mosaicos únicos. A grelha de índices da parte de dados refere-se a este vetor. |
Píxeis dos mosaicos
NumTiles blocos de 32 × 32 = 1024 bytes cada, com índices da paleta do
TA. O formato não elimina automaticamente os mosaicos idênticos, por
isso uma secção mal construída pode desperdiçar espaço; o comando image
do kbot avisa se uma secção tiver mosaicos inalcançáveis.
Dados da secção — índices de mosaicos + grelha 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];
A grelha de alturas é quatro vezes mais densa do que a grelha de
mosaicos: cada mosaico recebe 2 × 2 = 4 amostras de altura com
resolução de 16 píxeis, tal como funcionam as células de atributos do TNT.
Os campos always_-1 e always_0 são metadados de trabalho do editor;
numa ida e volta, pode voltar a escrever qualquer um dos valores.
Minimapa
128 × 128 índices da paleta. As dimensões são fixas, seja qual for a
largura/altura da secção em mosaicos, e o minimapa é esticado para se
ajustar à proporção da secção, em vez de levar barras (letterbox).
Ordem de leitura
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)
Exemplo comentado — 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
Uma secção de 4×4 mosaicos é a mais pequena que o TAE aceita (as medidas
têm de ser múltiplos de 128 px). Embora a secção tenha 4 × 4 = 16
posições, refere 64 mosaicos únicos, porque cada célula de 32×32 está ela
própria delimitada por transições nas margens, das quais o designer queria
variantes. Isto é normal: as secções trocam espaço de armazenamento por
liberdade de mistura.
Como o TNT usa as secções
O conjunto de mosaicos de um mapa .tnt é a união de todos os .sct que o
designer de níveis "pintou" nele, com os duplicados eliminados depois pelo
TAE. Por isso, quando descobrir que dois TNT partilham um mosaico, é quase
certo que veio do mesmo SCT.
A ligação existe apenas no momento da construção. O motor nunca abre
uma secção durante o jogo; só vê o TNT já combinado. As secções ficam no
disco, nos arquivos do editor (worlds.hpi) e nos arquivos dos mundos da
campanha, por exemplo sections/greenworld/coast/, sections/lava/hills/,
etc.
Armadilhas
Warning
Width × Height tem de ser múltiplo de 4 para o TAE carregar a
secção. O formato em si não o impõe (pode escrever uma secção de 3×3
mosaicos e o kbot renderiza-a sem problemas), mas o editor da Cavedog
rejeita-a com um erro críptico, "section misaligned".
- Com
Version == 2, as células de altura têm 8 bytes, e não 4. O primeiro byte continua a ser a elevação; os restantes 7 bytes são dados de trabalho do editor, que pode pôr a zero ao escrever. - O minimapa de 128×128 é esticado, sem barras, para se ajustar à proporção da secção. Uma secção de 4×16 fica horrivelmente achatada no minimapa.
- Não há assinatura além de
Version. Para validar um SCT candidato, verifique seVersion ∈ {2, 3}e se os quatro campos de ponteiro ficam todos dentro do ficheiro. - Os intervalos de alturas ficam inteiramente ao critério do designer. O TAE limita-os a 0–255, mas uma secção pode ser perfeitamente plana (um único valor) ou usar o intervalo completo. O renderizador de TNT normaliza antes de mostrar; o SCT não.
Tamanhos típicos
| Recurso | Intervalo observado em sections/**/*.sct da Cavedog |
|---|---|
| Tamanho do ficheiro | 30 KB – 2 MB |
| Dimensões da secção (mosaicos) | 4×4 – 32×32 |
| Mosaicos únicos por secção | 30–600 |
| Espaço ocupado pelo cabeçalho | sempre 28 bytes |
| Minimapa (sempre 128×128, com paleta) | 16384 bytes |
Secções em worlds.hpi |
~5000 (em todos os biomas) |