SCT — 맵 섹션
.sct파일은 재사용할 수 있는 맵 지형 조각입니다 — Cavedog의 레벨 디자이너들은 공식 맵 에디터(TAE)에서 이런 조각 수십 개를 이어 붙여 완성된.tnt맵을 만들었습니다. 각 섹션에는 자체 타일 그래픽, 타일 그리드, 칸별 높이, 섬네일 미니맵이 들어 있습니다. 섹션은 구성 요소이고, TNT는 조립된 결과물입니다.
kbot sct image로 렌더링한 sections/greenworld/coast/b_n_w02_135.sct
내장 미니맵(왼쪽)과 고도 그리드(오른쪽).
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 |
정식판 TA 섹션은 3. 일부 초기 프로토타입은 2를 쓰는데, 구조는 같지만 높이 항목이 4바이트가 아니라 8바이트입니다. kbot은 둘 다 받아들입니다. |
Width, Height |
둘 다 타일(32 px 단위)로 나타냅니다. TAE와 호환되는 섹션이라면 둘 다 4의 배수(즉 128 px의 배수)여야 합니다. |
NumTiles |
고유 타일 수. 데이터 섹션의 인덱스 그리드가 이 배열을 가리킵니다. |
타일 픽셀
각 32 × 32 = 1024바이트인 블록 NumTiles개로, TA 팔레트를 가리키는 팔레트
인덱스입니다. 똑같은 타일이라도 형식이 자동으로 중복을 제거하지 않으므로,
잘못 만든 섹션은 공간을 낭비할 수 있습니다. kbot의 image 명령은 섹션에 쓰이지
않는 타일이 있으면 경고합니다.
섹션 데이터 — 타일 인덱스 + 높이 그리드
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];
높이 그리드는 타일 그리드보다 네 배 촘촘합니다 — 타일마다 16픽셀 해상도로
2 × 2 = 4개의 높이 표본이 있으며, 이는 TNT 속성 칸의 동작 방식과 같습니다.
always_-1과 always_0 필드는 에디터가 남긴 임시 메타데이터이므로, 왕복 변환
때 어느 값을 다시 써도 됩니다.
미니맵
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는 높이 칸이 4바이트가 아니라 8바이트입니다. 첫 바이트는 여전히 고도이고, 나머지 7바이트는 쓸 때 0으로 채워도 되는 에디터 임시 값입니다.- 128×128 미니맵은 레터박스 없이 늘어나 섹션의 가로세로 비율에 맞춰집니다. 4×16 섹션은 미니맵에서 끔찍하게 찌그러져 보입니다.
Version말고는 시그니처가 없습니다. SCT 후보를 검증하려면Version ∈ {2, 3}인지, 그리고 네 포인터 필드가 모두 파일 안을 가리키는지 확인하세요.- 높이 범위는 전적으로 디자이너에게 달려 있습니다. TAE는 0–255로 제한하지만, 섹션은 완전히 평평할 수도(값 하나), 전체 범위를 쓸 수도 있습니다. TNT 렌더러는 표시하기 전에 정규화하지만, SCT는 그렇지 않습니다.
일반적인 크기
| 에셋 | Cavedog sections/**/*.sct에서 관찰된 범위 |
|---|---|
| 파일 크기 | 30 KB – 2 MB |
| 섹션 크기(타일) | 4×4 – 32×32 |
| 섹션당 고유 타일 | 30–600 |
| 헤더 오버헤드 | 항상 28바이트 |
| 미니맵(항상 128×128 팔레트) | 16384바이트 |
worlds.hpi의 섹션 수 |
약 5000개(모든 지형 테마 합계) |