Skip to content
coreprimePublic

About

Toolkit and file-format reference for Total Annihilation and TA: Kingdoms: HPI, 3DO, COB, GAF, TNT and more

Topics

Resources

Stars

9 stars

Watchers

0 watching

Forks

Repository files navigation

KBot - Total Annihilation Toolkit

KBot Logo

Go Reference License: MIT GitHub issues GitHub commit activity

The format reference is published, with every page cross-linked, at creators.coreprime.net.

A toolkit for working with Total Annihilation game assets. KBot provides a unified CLI for managing the various proprietary file formats used by the game, along with a browser-based studio (asset explorer, map editor, unit viewer, and live sandbox). There are also development tools, allowing advanced debugging and processing of various formats, including the COB/BOS scripting language.

Installation

go install github.com/coreprime/kbot/cmd/kbot@latest

Or build from source:

git clone https://github.com/coreprime/kbot.git
cd kbot
task build        # builds the Studio web assets and kbot binary
task install      # installs kbot to $GOPATH/bin

Getting Started

To run the visual explorer for Total Annihilation:

  • Perform the installation steps
  • Launch kbot studio ~/games/totala and open the Files tab to browse, preview, and inspect every asset in the install

To develop/code against the project:

  • Create a flattened version of your Total Annihilation gmae assets with kbot mount flatten ~/games/totala ~/games/totala-flattened
  • Clone .env to .env.local and set your paths
  • task build / task lint will run the code quality checks.

Table of Contents

Commands

kbot ctx — Working-Directory Contexts

Register named Total Annihilation installs (packed or flattened) so the other kbot subcommands that rely on a virtual filesystem can pick them up automatically — no more typing --vfs ~/games/totala on every command.

Contexts persist to ~/.kbot (a small JSON file, conceptually equivalent to ~/.kube/config). One context is marked current at any time; the KBOT_CONTEXT environment variable overrides the persisted current for the running process so you can flip between installs per-shell without editing the file.

# Register a packed TA install and mark it current
kbot ctx add ~/games/totala --alias ta-gog --game totala --version 3.1c

# Register a TA: Kingdoms install alongside
kbot ctx add ~/games/tak --alias kingdoms --game takingdoms

# Register a flattened directory you keep for development
kbot ctx add ~/ta-flattened --alias ta-flat --game totala --version 3.1c-flat

# Adopt the current directory.  If it's already registered, just switch
# to it; otherwise launch a small interactive prompt for alias, game,
# and version.
cd ~/games/totala
kbot ctx here

# Show every registered context (the current one is starred).
# `kbot ctx` with no subcommand does the same; use `kbot ctx --help`
# for the full help text.
kbot ctx
kbot ctx list

# Print the active context's path — handy in shell substitution
cd "$(kbot ctx path)"
ls "$(kbot ctx path)/units"

# Print a specific context's path without switching to it
kbot ctx path --alias kingdoms

# Switch the persisted current context
kbot ctx use kingdoms

# Per-shell override (does not touch ~/.kbot)
KBOT_CONTEXT=ta-flat kbot tnt preview "metal heck.tnt" --target preview.png

# Replace an existing entry (e.g. you moved the game folder)
kbot ctx add ~/new/totala --alias ta-gog --game totala --version 3.1c --replace

# Remove a context
kbot ctx delete kingdoms

--game must be one of totala, takingdoms, or custom. custom is the escape hatch for directories that look like a TA layout but aren't an official release (mod test-beds, partial extractions, etc.).

Where contexts apply. Any subcommand that mounts a VFS will fall back to the active context when its path argument is omitted:

Command Without an arg/flag
kbot mount Mounts the active context.
kbot mount flatten --target … Flattens the active context.
kbot tnt preview <file.tnt> Uses the active context as the VFS root for sprites and the sister .ota.
kbot mcp Registers the active context as the default game-data folder.
kbot cob lint Lints the active context's tree when no path is passed.
kbot cob roundtrip Scans the active context's tree when no path is passed.
kbot gaf roundtrip Scans the active context's tree when no path is passed.

Explicit arguments (a positional path, --vfs, or --game-data) always override the context. Use KBOT_CONTEXT=<alias> to pick a different registered context just for one invocation.

The file format is plain JSON, so you can hand-edit it if you need to:

{
  "current": "ta-gog",
  "contexts": {
    "ta-gog":    { "path": "/Users/me/games/totala", "game": "totala",     "version": "3.1c" },
    "kingdoms":  { "path": "/Users/me/games/tak",    "game": "takingdoms" },
    "ta-flat":   { "path": "/Users/me/ta-flattened", "game": "totala",     "version": "3.1c-flat" }
  }
}

kbot cob — COB/BOS Scripting

Work with compiled unit scripts (COB bytecode) and their BOS source.

# Decompile COB to BOS source
kbot cob decompile unit.cob
kbot cob decompile unit.cob --target unit.bos

# Compile BOS source to COB (TA script unless the source says .version 6)
kbot cob compile unit.bos --target unit.cob
kbot cob compile unit.bos --target unit.cob --strict   # fail on warnings

# Disassemble COB to assembly listing
kbot cob disassemble unit.cob
kbot cob disassemble unit.cob -a          # annotated with flow arrows
kbot cob disassemble unit.cob -s Create   # single script

# Assemble back to COB
kbot cob assemble unit.coba --target unit.cob

# Lint for common issues
kbot cob lint unit.cob
kbot cob lint scripts/                     # lint a whole directory
kbot cob lint scripts/ -q                  # summary only
kbot cob lint scripts/ --ci > cob.sarif    # SARIF 2.1.0 for CI ingest

# Roundtrip validation (byte-perfect decompile→compile and disassemble→assemble)
kbot cob roundtrip scripts/
kbot cob roundtrip scripts/ --detailed

All commands support --stream to read from stdin and --target to write to a file (default: stdout).

compile and assemble write what TA 3.1c runs. A source compiles as a TA script (COB version 4) unless it declares .version 6 (TA: Kingdoms). In a TA script the TA: Kingdoms constructs (play-sound, Mission-Command, __tak_math_*, .sound_name) and % are errors: TA faults on the Kingdoms instructions and has no modulo instruction (0x10037000 is its bitwise XOR). Anything that still compiles but deserves a look (a function defined twice binds every call to the first definition, as the game does) and every finding of the ta-* lint rules below is printed to stderr as a warning; --strict makes warnings fail the command. Listings are disassembled with the game's opcode names (XOR, NOT, XOR_ALT, NAME@0x… for a word with stray low bits); the assembler still reads the older names MOD, BITWISE_XOR, BITWISE_NOT and LOGICAL_XOR as the words they were written for.

Lint rules:

Rule Severity Description
unused-piece ⚠️ warning Piece declared but never used
unused-static ⚠️ warning Global variable never accessed
unused-local ⚠️ warning Local variable allocated but unused
always-true ℹ️ info if(1) / while(1) — always-true condition
dead-code ⚠️ warning if(0) / while(0) — unreachable code
long-function ⚠️ warning Function exceeds 100 instructions
high-complexity ⚠️ warning Cyclomatic complexity > 15
invalid-call ❌ error call-script to non-existent function
speed-zero ⚠️ warning move/turn with speed <0> — never completes
empty-function ℹ️ info Function body is only return 0
duplicate-animation ⚠️ warning Identical sequential animation command
sleep-only-guard ℹ️ info if block contains only a sleep
duplicate-if ℹ️ info Back-to-back identical if conditions
raw-signal ℹ️ info Signal uses raw number (BOS only)
unnamed-global ℹ️ info Static var uses global_N naming (BOS only)
signal-never-signalled ⚠️ warning set-signal-mask watches a signal nobody sends
recursive-call ⚠️ warning call-script forms a cycle
duplicate-function ⚠️ warning Two scripts share a name; the game only calls the first
malformed-cob ❌ error / ⚠️ warning File does not load or a script's code is truncated (error); damaged name tables the reader tolerated (warning)

Every COB that does not declare the TA: Kingdoms version (6) is also checked against what TA 3.1c runs. These rules are errors, and appear in the SARIF output like the others:

Rule Severity Description
ta-kingdoms-opcode ❌ error TA: Kingdoms instruction (PLAY_SOUND, MISSION_COMMAND, TAK_MATH_*); TA faults on it
ta-unknown-opcode ❌ error Opcode word no game runs
ta-push-flags ❌ error PUSH/POP flag the game faults on (PUSH takes 1, 2 or 4; POP 2 or 4)
ta-stack-limit ❌ error More than the game's 32 stack slots
ta-get-arguments ❌ error GET with fewer than 5 pending values; it takes the rest from the function's locals
ta-stack-underflow ❌ error An instruction pops more values than are pending
ta-discard-call ❌ error DISCARD_CALL (0x10063000) with more than 4 arguments

kbot hpi — Archive Files

Manage HPI, UFO, and CCX archive files.

# List archive contents
kbot hpi list archive.hpi
kbot hpi list archive.hpi -v               # verbose (sizes, compression)
kbot hpi list archive.hpi -p "*.wav"       # filter by pattern

# Extract files
kbot hpi extract archive.hpi
kbot hpi extract archive.hpi -t ./output   # target directory
kbot hpi extract archive.hpi -p "sounds/*" # extract matching files

# Pack a directory into an archive (LZ77, key 0xBF, Cavedog trailer)
kbot hpi pack ./content --target archive.hpi
kbot hpi pack ./content --target mod.ufo --method none --key 0   # stored, unencrypted

# Show archive details, including "TA 3.1c would mount: yes/no, reason"
kbot hpi info archive.hpi

All read commands support --stream to read the archive from stdin. extract writes every file inside the target folder: an entry whose stored name has a . or .. segment is skipped with a message, and of several entries whose paths differ only in letter case only the last — the one the game reads — is extracted.


kbot gaf — Sprite Animations

Work with GAF animation files containing sprite sequences.

# List sequences (frame count, whether the game loops it, duration)
kbot gaf list sprites.gaf

# Export a sequence as an animated PNG (default) or GIF
kbot gaf export sprites.gaf
kbot gaf export sprites.gaf --format gif --sequence 3

# Dump all sequences and frames to a folder (PNG by default)
kbot gaf dump sprites.gaf --target ./sprites

# Build a GAF from a dump folder
kbot gaf build ./sprites --target rebuilt.gaf

# Check that decode→encode and dump→build keep what the game reads
kbot gaf roundtrip ./anims

Frames are exported as the game draws them: a raw frame's key pixels and a compressed frame's skipped pixels are transparent, palette index 0 is opaque black, frames show for their duration in 1/30 s ticks, and an animation loops only when the sequence's loop byte is set.

Each sequence folder of a dump holds the frame images, a frames.csv (size, origin, key, duration, storage and the +11 byte of every frame) and a sequence.csv (position, exact name, loop word and +4 word). The build command reads both back, so a dump/build cycle keeps what the game reads. A sequence without a sequence.csv loops, like every stock sequence. Frames without a storage are compressed, except that every frame written to textures/*.gaf or anims/vismasks.gaf is raw, because the game reads those as plain pixel arrays (--storage overrides). Composite (layered) frames are written flattened. A frame with no pixels gets no image; its frames.csv row rebuilds it. A sub-folder with neither CSV file is skipped with a warning; any other sub-folder the build cannot read stops the build rather than leaving that sequence out.


kbot taf — Truecolor Animations

Work with TA: Kingdoms truecolor (16-bit ARGB) animation files. Unlike GAF, a TAF carries its own colour and alpha, so no palette is needed. See docs/formats/taf.md for the format.

# Inspect
kbot taf info anims/manabomb_1555.taf      # per-frame table
kbot taf list anims/manabomb_1555.taf      # one-line summary
kbot taf lint anims/manabomb_1555.taf      # structural validation

# Render
kbot taf render anims/bluefire_4444.taf --frame 4 -o frame4.png
kbot taf sheet  anims/manabomb_1555.taf --cols 5 --bg "#202830" -o sheet.png
kbot taf export anims/fireballa_1555.taf --format apng -o anim.png   # or --format gif

# Decompile / compile round-trip (TAF <-> TSF + PNG layers)
kbot taf decompile anims/manabomb_1555.taf --target ./manabomb
kbot taf compile   ./manabomb/manabomb.tsf --images ./manabomb -o rebuilt.taf
kbot taf diff anims/manabomb_1555.taf rebuilt.taf      # verify identical
kbot taf roundtrip anims/manabomb_1555.taf             # byte-for-byte check

# Import from common formats
kbot taf from-gif   boom.gif  --format argb4444 --name Boom -o boom.taf
kbot taf from-sheet strip.png --frame-width 64 --frame-height 64 --count 8 -o strip.taf

export defaults to APNG (full alpha); --format gif produces a 1-bit-cutout preview. Every command resolves bare filenames through the active kbot context or an explicit --vfs mount.


kbot tsf — Animation Text Scripts

Inspect and validate TSF documents — the human-readable, brace-delimited text form of a TAF that the TA: Kingdoms GUI loader reads directly for menu backgrounds. kbot taf decompile emits TSF and kbot taf compile reads it back.

# Summarise the animation, frames and layer images
kbot tsf info anims/titlescreen.tsf

# Check the document matches the compiler's expectations
kbot tsf lint anims/titlescreen.tsf

lint checks document shape only (one animation, one Filename'd layer per frame, recognised pixel formats); use kbot taf compile to exercise the full image-loading pipeline.


kbot pcx — PCX Images

Inspect and convert PCX image files.

# Describe a PCX file (detailed metadata, then what TA 3.1c will do with it)
kbot pcx describe image.pcx

# Convert to PNG, GIF, or BMP
kbot pcx convert image.pcx --format png
kbot pcx convert image.pcx --format png --target output.png

# One-line info summary
kbot pcx info image.pcx

TA 3.1c reads PCX files differently from image editors: it loads version 5 only, ignores BytesPerLine (padded rows shift), decodes everything as 8-bit single-plane data and takes the palette from the last 768 bytes. kbot pcx describe, the MCP pcx_describe tool and the studio's asset explorer (a badge next to the preview) report every such difference. See docs/formats/pcx.md.


kbot fnt — Bitmap Fonts

Inspect and render Total Annihilation 1-bpp bitmap fonts. Each .fnt file holds up to 256 glyphs sharing a single height; widths vary per glyph.

# One-line summary
kbot fnt info comix.fnt

# Detailed metadata (height, baseline, first character code, glyph
# count, character-code coverage)
kbot fnt describe comix.fnt
kbot fnt describe comix.fnt --list      # also enumerate every defined glyph

# Render a string to PNG, laid out as the game does (text converted to
# Windows-1252 by default; --codepage picks another, raw passes bytes through)
kbot fnt render comix.fnt --text "Hello TA" --target hello.png
kbot fnt render armfont.fnt --text "Commander" --fg "#ffff00" --bg transparent
kbot fnt render comix.fnt --text "Привет" --codepage cp1251 --target ru.png

# Render every defined glyph as a 16-column sprite sheet
kbot fnt sheet comix.fnt --target sheet.png

# Dump one PNG per glyph (named U+00XX.png) into a directory
kbot fnt dump comix.fnt --target ./glyphs

Colors accept #rrggbb, #rrggbbaa, or the literal transparent / none. Rendering follows the game: each character byte selects the glyph with that code, a glyph advances by exactly its width, a character with no glyph adds nothing, and drawing stops at the first newline.


kbot sct — Map Sections

Inspect and render Total Annihilation .SCT map sections — the reusable tile blocks the official map editor stitches together to build .TNT maps.

# One-line summary
kbot sct info hill01.sct

# Detailed metadata (header, tile/attr counts, height stats)
kbot sct describe hill01.sct

# Render the full tile grid (32 px per tile) to PNG
kbot sct image hill01.sct --target hill01.png

# Export the elevation grid (16 px resolution) as a normalised grayscale PNG
kbot sct heightmap hill01.sct --target hill01-h.png

# Export the embedded 128x128 minimap as PNG
kbot sct minimap hill01.sct --target hill01-mini.png

kbot crt — TA: Kingdoms Scenarios

Inspect the .crt scenario sidecar that pairs with a TA: Kingdoms .tnt map. It holds the units placed before play, the per-player rule engine and named trigger regions. Multiplayer maps ship an empty stub; campaign and special maps populate every section. See docs/formats/takmap.md for the binary layout.

# Unit-type breakdown, rule counts and trigger regions
kbot crt describe "king of the hill.crt"

# Add every placement with its position, owner and facing
kbot crt describe --verbose "savannah hunt.crt"

kbot pal — Palettes & Lookup Tables

Inspect and convert Total Annihilation .PAL palettes, plus the related color-index lookup tables built from them: .ALP (65,536 bytes, 256×256 half-and-half blends) and .SHD / .LHT (8,192 bytes, 32 shade or light levels of 256 colors). The game ignores a table of any other size, and so does kbot pal lookup.

# One-line summary (size, unique colors, duplicates, TA-style flag)
kbot pal info palette.pal

# Every entry with hex + RGB
kbot pal describe palette.pal

# 16x16 PNG swatch grid (index 0 hatched with magenta so it stands out;
# the game draws it as ordinary black in terrain and backdrops)
kbot pal swatch palette.pal --target palette.png --cell 16

# Convert to editor-friendly formats
kbot pal convert palette.pal --target palette.gpl              # GIMP Palette
kbot pal convert palette.pal --target palette.txt --format jasc  # JASC-PAL text
kbot pal convert palette.pal --target re-emitted.pal --format pal  # binary TA .PAL

# Render a lookup table as a PNG swatch (.ALP 256×256 cells, .SHD/.LHT
# 256×32 cells) using the embedded palette (or pass --palette, a .pal or
# .pcx; TA: Kingdoms keeps most palettes in PCX files next to their tables)
kbot pal lookup palette.alp --target alp.png
kbot pal lookup palette.lht --palette palette.pal --target lht.png
kbot pal lookup palettes/aramon.alp --palette palettes/aramon.pcx --target aramon-alp.png

kbot tnt — TNT Maps

Inspect, render, unpack and pack Total Annihilation .TNT map terrain.

# Summary on the console
kbot tnt describe "metal heck.tnt"

# Render artefacts
kbot tnt image     "metal heck.tnt" --target map.png         # full RGBA map (1:1, 32 px / tile)
kbot tnt heightmap "metal heck.tnt" --target height.png      # 8-bit grayscale (16 px / cell)
kbot tnt buildmap  "metal heck.tnt" --target build.png       # per-cell buildability classification
kbot tnt voidmap   "metal heck.tnt" --target voids.png       # engine-void mask (Feature == 0xFFFC only)
kbot tnt minimap   "metal heck.tnt" --target mini.png        # embedded minimap

# Same as `image`, but composite feature sprites and draw numbered StartPos
# markers from the sister .ota.  --vfs points at a flattened TA install (or
# any directory containing features/*.tdf and anims/*.gaf).
kbot tnt preview "metal heck.tnt" --vfs ~/ta-flattened --target preview.png

# Pick a different schema's StartPos set: the one the game finds as
# "Schema 2" (default 0; a schema after a gap in the numbering draws none).
kbot tnt preview "metal heck.tnt" --schema 2 --target preview-s2.png

# --vfs may be omitted when a kbot context is active (see `kbot ctx`)
kbot tnt preview "metal heck.tnt" --target preview.png

# Tiny ASCII rendering (dev-joke / quick sanity check)
kbot tnt ascii "metal heck.tnt" --cols 64

# Decompose into editable files
kbot tnt unpack "metal heck.tnt" --target ./metal_heck

# Preserve the original feature table verbatim so the unpack/pack round-trip
# is byte-identical (default unpack drops trailing scratch bytes Cavedog's
# tooling left in feature-name buffers; pack rebuilds the table from the
# unique names referenced in features.csv).
kbot tnt unpack "metal heck.tnt" --target ./metal_heck --lossless

# Reverse the unpack
kbot tnt pack ./metal_heck --target metal_heck.tnt

Unpack layout (--target <dir>):

<dir>/
  map.png            full RGBA render of the tile grid
  heightmap.png      8-bit grayscale, pixel = raw elevation byte
  minimap.png        paletted PNG of the embedded minimap
  tiles/<n>.png      paletted 32×32 PNG per unique tile
  tilemap.csv        2D grid of tile indices (rows = y, cols = x)
  features.csv       feature_index,name,attr_x,attr_y per placement
  metadata.json      header constants + round-trip info (feature table when --lossless)

heightmap.png is unnormalised so the pixel value equals the raw elevation byte — round-trip safe. Pass --normalize to stretch the range for human viewing. minimap.png is paletted so palette indices survive the round-trip.

kbot tnt unpack is lossy by default: the feature name table is omitted from metadata.json and kbot tnt pack rebuilds it from the unique names in features.csv. This loses any trailing scratch bytes the original tooling left in the table. Pass --lossless to record the original feature table (and the raw feature bytes as feature_raw_b64) in metadata.json so the directory packs back to a byte-identical TNT.

Buildmap classification (kbot tnt buildmap):

The PNG is at attribute-cell resolution (one pixel per 16×16 cell, same as heightmap). Each cell is classified, in priority order:

Colour Meaning
Black engine-void — TileAttr.Feature == 0xFFFC
Red a feature is placed in the cell (rocks, trees, geo-vents, ...)
Blue underwater — Height is below sea level
Yellow cliff edge — |Δheight| to a 4-neighbour exceeds 32 game units
Green buildable

By default the .tnt header's SeaLevel field drives the underwater check; pass --sealevel <n> to override (0 disables the check). 0xFFFD / 0xFFFE are not classified as void — see docs/formats/tnt.md for the rationale (Metal Heck and a few other early Cavedog maps mark steam vents this way, and those cells are demonstrably buildable in-engine).

Voidmap (kbot tnt voidmap):

A transparent PNG at attribute-cell resolution. Cells with Feature == 0xFFFC are opaque red; everything else is transparent — overlay it on a kbot tnt image render to highlight just the engine-void areas.

The Studio's Advanced › Export menu offers the same artefacts plus the minimap and the active-schema preview:

Menu item Backend CLI / MCP equivalent
Export Minimap client canvas snapshot (n/a)
Export Full Render tnt.RenderTileMap + tntpreview.ComposeWith (active schema's StartPos markers) kbot tnt preview --schema <ActiveSchema> / tnt_preview
Export Map Image tnt.RenderTileMap (bare tile grid, no overlays) kbot tnt image / tnt_image
Export Buildmap tnt.RenderBuildMapFor (placed features block their cells) kbot tnt buildmap / tnt_buildmap
Export Voidmap tnt.RenderVoidMap kbot tnt voidmap / tnt_voidmap

Linting a map:

kbot tnt lint runs the same checks Studio's Quality Checker dialog applies, plus the tile-pool diagnostics that kbot tnt optimize is built on. No files are modified — the command is read-only and returns exit-1 when any issue is reported (handy for CI).

By default the command mounts the active kbot context (kbot ctx) as a virtual filesystem, so the path argument can be a bare basename, a virtual path inside an HPI / GP3 archive, or an absolute disk path. The sibling .ota and the metal-proximity feature registry are picked up from the same VFS automatically.

# Bare basename — searched against maps/ in the active context VFS.
kbot tnt lint "metal heck.tnt"

# Virtual path — addresses a file inside an HPI archive directly.
kbot tnt lint "maps/the pass.tnt"

# Local file on disk (absolute or relative to cwd) still works.
kbot tnt lint ./my-edit.tnt

# Override the mount root for one invocation.
kbot tnt lint --vfs ~/ta-flattened "metal heck.tnt"

# Skip the quality pass; just report tile-pool savings.
kbot tnt lint --no-quality "metal heck.tnt"

# Override the sister .ota lookup (also accepts virtual paths).
kbot tnt lint --ota ./my-edit.ota "metal heck.tnt"

# Emit SARIF 2.1.0 on stdout for CI ingest (GitHub, GitLab, Harness…).
kbot tnt lint --ci "metal heck.tnt" > tnt-lint.sarif

Relative paths like ./maps/Metal Heck.tnt are resolved against the mounted VFS (lowercased and ./ stripped); any .. segment is rejected so a path argument can't escape the kbot workspace.

Checks (each emits a row, ✅ / ⚠️ / ❌ icon, one-line summary):

Group Rule Catches
Tile pool duplicate-tiles Byte-identical tile graphics
Tile pool similar-tiles Visually-similar graphics sharing a heightmap footprint (configurable via --similarity)
Tile pool unused-tiles Tile graphics no cell references
Map data bad-tile-index Tile map cells naming a tile beyond the tile set (the game reads past its tile set there)
Map data unresolved-feature Cells whose feature word names no entry of the feature table (the game places nothing there)
Map data interchange-bounds A map larger than other map tools accept
Map data minimap No stored minimap, or one too small for the game's radar
Quality dedupTiles Same as duplicate-tiles, surfaced via the Studio quality dialog id
Quality otaFields Lobby-required OTA metadata missing
Quality startsInBounds Start positions outside the map or sitting on a void cell
Quality schemaSlots An advertised numplayers count the schema the game picks for it cannot host (the game reads Network 1..4 schemas from Schema 0 up to the first gap; schemas it never reads are listed)
Quality metalProximity Start without a metal-producing feature within 24 tiles (skipped on metal-rich schemas; feature metal read as the game stores it, so metal=56.8 is 56)
Quality voidIslands Passable cells unreachable from any start (≥ 20 cells)
Quality heightDiscontinuities Cliffs > 32 height units between adjacent cells — likely to block ground pathing

The shared implementation lives in internal/maplint so the CLI, the studio dialog, and the MCP tool all run identical logic against identical thresholds. The MCP tool name is tnt_lint and accepts path, ota_path (optional), similarity (optional, defaults to 1.0), quality (optional, defaults to true), and game_data (optional, picks the VFS used for the metal-proximity check).


kbot zrb — Smacker Video

Work with Smacker (.smk/.zrb) video files.

# Show video information (stored and display size, present audio tracks)
kbot zrb info video.smk

# Convert to MP4 at the height the game shows (640x240 interlaced → 640x480)
kbot zrb to-mp4 video.smk video.mp4
kbot zrb to-mp4 video.smk video.mp4 --line-double   # repeat lines instead of black
kbot zrb to-mp4 video.smk video.mp4 --stored-height # keep 640x240

Requires FFmpeg for conversions. There is no MP4 → Smacker conversion: stock FFmpeg has no Smacker encoder or muxer and kbot has no Smacker writer, so kbot zrb from-mp4 reports that and stops. Make SMK2 movies for TA with RAD Game Tools' Smacker tools.


kbot bik — Bink Video

Work with Bink (.bik) video files — the cutscene format TA: Kingdoms uses in place of the original game's Smacker (.zrb) videos. See docs/formats/bik.md for the format.

# Show video information (native header parse — no ffmpeg needed)
kbot bik info movies/takmission14_ph.bik

# Decode to MP4
kbot bik to-mp4 movies/takmission14_ph.bik intro.mp4

to-mp4 requires FFmpeg (which ships a Bink decoder). Conversion is decode-only: no open-source Bink encoder exists, so there is no from-mp4. Smacker is no different in practice: kbot zrb from-mp4 only reports that no Smacker encoder exists.


kbot mount — Asset Explorer

Browse game files interactively in a terminal.

# Terminal browser
kbot mount ~/ta-content

# Flatten (extract all files to disk)
kbot mount ~/ta-content flatten --target ./flat

# Omit the path to mount the active kbot context (see `kbot ctx`)
kbot mount flatten --target ./flat

Terminal commands: ls, cd, pwd, cat, describe, archives, stats, exit

Total Annihilation installs are mounted in TA 3.1c's archive order (loose files, rev31.gp3, *.ccx, *.ufo, the first ten *.hpi, then the *.hpi past that limit — see HPI load order), so every path resolves to the copy the game reads. archives and stats list the mount order and every archive the game would skip (no Cavedog trailer, a TA: Kingdoms archive, an unreadable file), with the reason.

For a graphical asset explorer, use the Files tab in kbot studio (see below). It browses the VFS with per-folder listings and adds rich per-format previews:

  • View GAF animations with APNG preview, sequence/frame controls, and transparency toggles
  • View PCX/FNT images with format options, and palettes as a swatch grid
  • Play SMK/ZRB/Bink video transcoded to MP4 with an animated poster
  • View TNT/SCT maps with minimap/heightmap/tilemap/buildmap/voidmap renders
  • Inspect COB disassembly/decompilation, TDF/FBI/OTA structure, and raw hex
  • Background cache warming with live websocket progress

kbot pack — Static Asset Packs

Extract a game install into a static asset pack: the same JSON/PNG payloads KBot Studio serves on demand, written once as plain files. Serve the output directory over any static HTTP host and point @coreprime/kbot-game3d's HttpPackProvider at its base URL — the renderer runs with no studio server (the seam a replayer or any embedding site consumes).

# Pack every unit in a TA install
kbot pack ~/games/totala ./ta-pack

# A minimal pack for two units plus one map
kbot pack ~/games/totala ./ta-pack --units armcom,armpw --maps "greenhaven"

# TA:Kingdoms
kbot pack ~/games/kingdoms ./tak-pack --game tak

A pack contains manifest.json (game id, sides, unit list, content hash), unitdb.json (per-unit movement class + motion domain, build/HP/weapon stats, corpse chain, build-pic path and slot-ordered weapon ids; footprint, terrain limits and standing orders are resolved as TA 3.1c resolves them, and the economy keeps metalMake, extractsMetal and makesMetal apart), palette.json, weapons.json (every weapon units can name in the game's ID-indexed weapon table, built from weapons/*.tdf as the game builds it — render data: rendertype, colors — resolved RGB plus raw indices — beam duration, velocity, trajectory and guided-flight fields — turn rate, water weapon, acceleration, flight time — blast diameter, range, sound stems), features.json (every map-feature definition: category, footprint, height, 3DO object link and GAF sprite dims — what the renderer's 3D feature stand-ins size themselves from; the maps' referenced object models pack under models/), and per-asset files under models/ (authored geometry), models-enhanced/ (hidden-face reconstruction, the Enhanced Mesh variant), textures/, cob/, sounds/, weaponbitmaps/, unitpics/, cursors/, groundtiles/ and maps/ (map JSON + tile atlas + authentic minimap PNG). The whole weapon catalogue's projectile meshes, sounds and sprite strips pack regardless of the unit subset, so a replay can draw any weapon a recording names. Output is deterministic — the same install always produces a byte-identical pack, and the manifest's contentHash is the pack's identity. See kbot pack --help for the full layout.

import { createWorld, HttpPackProvider } from '@coreprime/kbot-game3d'
const world = await createWorld(canvas, {
  assets: new HttpPackProvider('https://cdn.example.com/packs/ta-31c'),
})
await world.addUnit('armcom', { x: 0, z: 0, side: 1 })

kbot studio — KBot Studio (Web Workbench)

A browser-based workbench for Total Annihilation and TA: Kingdoms. It mounts a game install (or the active kbot ctx) over a VFS and opens a tabbed single-page app whose welcome screen offers several workflows.

# Open against a packed TA install
kbot studio ~/games/totala

# Or use the active kbot context (see `kbot ctx`)
kbot studio

# Custom port (default 8100)
kbot studio --port 9000

Explorer — the full Files tab / VFS browser: table and icon listings with live thumbnails, search, and a layer/source override, plus rich per-format previews — GAF/PCX/FNT/palette images, TNT/SCT maps, COB/BOS code (decompile, disassembly, call graph, lint), AI profiles, TDF/FBI/OTA structure, SMK/ZRB/BIK video, and audio — with a metadata, hex, and layering tab on every file and a download menu that offers rendered variants (PNG/GIF, MP4) alongside the raw bytes. A background cache warmer renders preview assets on startup and reports progress live over a websocket.

Map Creator — a visual TNT/OTA map editor. Start from a blank canvas or open an existing map, then paint terrain from .sct sections, place and scatter features, sculpt the heightmap, mark engine voids, and set per-schema start positions. Split panes, undo/redo, a ruler, and symmetry tools aid editing; a Quality Checker lints the map (with auto-fixes) before you save. The export menu bundles a downloadable map archive (or loose TNT/OTA) plus full renders, minimaps, heightmaps, buildmaps, and voidmaps. A Total Annihilation map downloads as <map>.ufo: TA 3.1c mounts every .ufo, above every .hpi, while a .hpi competes for the game's ten *.hpi slots. TA: Kingdoms maps download as .hpi.

Workspaces and mod export — the picker's Export mod… dialog packs a workspace's changed files into one archive. For Total Annihilation choose .ufo (the default: no count limit, ranks above every .hpi), .ccx (ranks above every .ufo) or .hpi. Before the download it checks the archive against the base install's mount order: its position, any retail *.hpi it would push past the ten-archive limit, and every file a higher-priority archive or a loose file of the install also provides — the game ignores the mod's copy of those, so they must ship as loose files in the game directory. Every exported archive ends with the Cavedog trailer the game requires.

Saving an opened map edits its .ota in place: only the values you change are rewritten, and everything the editor does not show (victory conditions, pre-placed units and features, fractions, other keys) is kept; an .ota it cannot read is left unchanged. Schemas and start positions are read the way the game reads them, and the saved .tnt carries a minimap laid out as the game's own (with its minimap flag set).

Sandbox Mode — drop units onto a battlefield and test them live. A WASM physics core simulates movement, commands, weapon fire, and damage with team colours and particle effects. Run a local in-browser scene, host an authoritative match, or join a live hosted sandbox for shared multiplayer testing. Units carry the game's resolved data (movement-class terrain limits, standing orders — the commanders start on Hold Position — and weapons from the ID-indexed weapon table); a map loads with the schema a two-player skirmish would use, its SurfaceMetal under every plot and player 1 at StartPos1.

Unit Creator — open a unit to inspect and test it: an orbiting 3D model viewer with per-piece animation and weapon hardpoints, a piece tree, texture and weapon galleries, and a COB/BOS thread debugger that steps scripts with synced source/assembly, locals, and runtime port values.

A Scripting workflow and further tools are previewed on the welcome screen as planned additions.


kbot document — Reference Catalogue Generator

Generate the markdown reference catalogues that live in two standalone sibling repos:

# Total Annihilation — uses the active kbot context for --source
kbot document --target ~/go/src/github.com/coreprime/reference-ta

# TA: Kingdoms
kbot document --game takingdoms \
              --source ~/tak-flat \
              --target ~/go/src/github.com/coreprime/reference-tak

# Explicit source + skip the portrait re-conversion (TA only — TA:K
# has no unitpics/ to convert)
kbot document --source ~/ta-flat --target ./reference --skip-portraits

# Force a fresh PCX → PNG batch (default skips if PNG already exists)
kbot document --target ./reference --force-portraits

The TA catalogue reads the data the way TA 3.1c does. FBIs and TDFs are decoded with kbot-io's codec, so comments are blanked, a value runs to the next ; and numbers are read as the game reads them (BuildCostMetal=40.28 costs 40). Weapons come only from the .tdf files directly in weapons/ (gamedata/weapons.tdf is not a weapon source) and are placed in the game's 256-slot table by ID=; each unit's Weapon1/2/3= is resolved through that table. A unit gets a [CANBUILD] list only with Builder=1, read up to the first gap in the canbuildN numbering, skipping names that match no unit and keeping at most 30; each download/*.tdf adds its first five sections, whatever their names, and a build menu holds at most 31 units. Entries the game leaves out are listed at the end of ta-weapons.md and ta-buildtree.md (retail data has none).

Output layout under --target (TA):

<target>/
├── ta-units.md         278-row unit catalogue
├── ta-weapons.md       198-row weapon catalogue + reverse cross-ref
├── ta-buildtree.md     per-builder 2×3 menu grids + visual reverse index
└── img/ta-units/*.png  282 unit portraits (Objectname-keyed, lowercased)

Output layout under --target (TA: Kingdoms):

<target>/
├── tak-units.md        per-side unit catalogue (Aramon/Taros/Veruna/Zhon/Creon/Mon/Lif/NPC)
├── tak-weapons.md      inline [WEAPONn] sections harvested from every FBI
└── tak-buildtree.md    per-builder canbuild/ listings (linear Priority order)

Implementation lives in internal/documentor; text/template files are embedded under internal/documentor/templates/ (one set per game). The TA:K extractor (extract_tak.go) is separate from the TA extractor because TA:K inlines weapons in FBIs, has 4 sides (5 with Iron Plague), and uses canbuild/<builder>/*.tdf directories rather than gamedata/sidedata.tdf + download/*.tdf.

Convenience targets:

Task What it does
task document TA → ../reference-ta (uses $TA_UNPACKED_PATH)
task document-tak TA:K → ../reference-tak (uses $TAK_UNPACKED_PATH)

Note on output licensing. The kbot document tool itself is MIT-licensed (it ships in this repo). Its output — the rendered markdown tables and the copied/converted unit portraits — is derivative of Cavedog/Humongous game data and is not covered by kbot's MIT licence. The reference repos (coreprime/reference-ta / coreprime/reference-tak) intentionally ship without a LICENSE file for that reason; see their READMEs for the attribution notice.


kbot mcp — Model Context Protocol Server

Expose kbot's TA tooling to AI assistants (Claude Code, Claude Desktop, Cursor, etc.) over MCP. The assistant can then decompile scripts, lint COB, inspect HPI archives, render GAF/PCX, and so on directly against your game install.

# Register a TA install as a virtual filesystem (recommended)
kbot mcp --game-data ~/games/totala

# Name it explicitly and add a TA: Kingdoms install alongside
kbot mcp \
  --game-data totala=~/games/totala \
  --game-data kingdoms=~/games/tak

# Raw mount roots without the VFS layer (back-compat; assistant must pass absolute paths)
kbot mcp --mount ~/games/totala --mount /tmp/kbot-out

# Long-lived HTTP transport for multi-client setups
kbot mcp --http 127.0.0.1:8765 --game-data ~/games/totala

# Without --game-data or --mount, kbot mcp registers the active kbot
# context (see `kbot ctx`) as the default game-data folder
kbot mcp

--game-data NAME=PATH (or just PATH, name derived from the basename) registers a Total Annihilation / TA: Kingdoms install as a named virtual filesystem. The folder is scanned once and its .hpi / .ufo / .ccx / .gp3 archives are layered under its physical files in the order the game mounts them (for Total Annihilation, TA 3.1c's order: rev31.gp3, *.ccx, *.ufo, the first ten *.hpi, then the *.hpi past that limit; see HPI load order). vfs_game_data lists the mount order and any archive the game would skip, with the reason. The first --game-data is the default the assistant uses when a tool call omits game_data. Each game-data base is added implicitly to the path guard, so on-disk paths inside it also resolve.

Once a game-data folder is configured, every tool's path argument accepts:

  • an absolute on-disk path (e.g. /Users/me/games/totala/units/ARMCOM.fbi),
  • a virtual path inside the VFS (e.g. units/ARMCOM.fbi; the game's own units\ARMCOM.fbi spelling works on every host), or
  • a bare filename (e.g. ARMCOM.bos) that the resolver searches for across every archive and physical file. Ambiguous matches return a list so the assistant can pick.

When a hit lives inside an archive, kbot extracts it to a temp file for the duration of the call and cleans up afterwards. Physical files are passed through directly.

--mount is the older, simpler gate: it just restricts paths the assistant passes to lie inside the given root, with no VFS layering. Use it when you want to expose a non-game-data directory (such as an output scratch dir) without the cost of walking and indexing archives. Without any --mount or --game-data, the server runs in permissive mode and accepts any absolute path — fine for local development, unsafe on shared hosts.

TNT render tools

These tools render various artefacts from a .tnt to PNG. Each accepts path (the .tnt argument, resolved through the same VFS rules as every other tool) and output (the destination PNG path):

Tool Purpose
tnt_image Full tile-grid render at 32 px per tile. Used by the studio's Export Full Render menu item.
tnt_heightmap 8-bit grayscale elevation grid (round-trip safe by default; normalize=true for human viewing).
tnt_buildmap Per-cell buildability classification — black/red/blue/yellow/green key (see the CLI section above). Optional sealevel overrides the .tnt header's value (0 disables the underwater check).
tnt_voidmap Engine-void mask — cells with Feature == 0xFFFC painted red, everything else transparent.
tnt_minimap Embedded 252×252 minimap (paletted PNG when paletted=true).
tnt_preview tnt_image plus composited feature sprites and numbered StartPos markers for the schema the game finds as "Schema schema" (defaults to 0). Requires game_data so feature sprites and the sister .ota resolve.

TAF / TSF animation tools

These tools inspect and render TA: Kingdoms truecolor animations (see docs/formats/taf.md). taf_render and taf_sheet return the rendered PNG inline so the assistant can show it directly; taf_export writes an animated file to output.

Tool Purpose
taf_info Per-frame JSON: name, size, origin, pixel format and duration for every frame.
taf_list One-line JSON summary (name, frame count, duration, format).
taf_render Render a single frame and return it inline as PNG (optional output also saves it).
taf_sheet Render every frame into a grid sprite sheet, returned inline as PNG.
taf_export Write an animated GIF (format: gif) or APNG (format: apng/png, default) to output.
taf_lint Structural validation findings as JSON.
tsf_info Summarise a TSF document — animation, frames and referenced layer images.
tsf_lint Shape-check a TSF document against the compiler's expectations.

Bink video tools

These tools inspect and convert TA: Kingdoms Bink cutscenes (see docs/formats/bik.md).

Tool Purpose
bik_info Header JSON: geometry, frame count, frame rate, duration, alpha/grayscale flags and per-track audio. Native parse, no ffmpeg.
bik_to_mp4 Decode a .bik to MP4 (H.264/AAC) via ffmpeg and write it to output. Decode-only — no reverse conversion exists.

Smacker video tools

These tools inspect and convert the original TA Smacker/ZRB cutscenes (see docs/formats/smacker.md).

Tool Purpose
zrb_info Header JSON: signature, stored and display geometry, frame count, frame rate, duration and the present audio tracks (rate, channels, sample size, compression). Native parse, no ffmpeg.
zrb_to_mp4 Decode a .zrb/.smk to MP4 (H.264/AAC) via ffmpeg at the height the game shows (640x240 interlaced → 640x480) and write it to output.
zrb_from_mp4 Reports that no Smacker encoder exists: stock ffmpeg has no Smacker encoder or muxer and kbot has no Smacker writer (only an ffmpeg that lists both is tried).

Scenario tools

Tool Purpose
crt_describe Summarise a TA: Kingdoms .crt scenario: placed-unit breakdown, per-player rule counts and named trigger regions, returned as JSON. Pass units: true for the full per-unit placement list.

Archive tools

Tool Purpose
hpi_list List the files in an HPI/UFO/CCX archive (glob filter), with a game_mount line saying whether TA 3.1c would mount it.
hpi_info Header and content summary; game_mount reports whether TA 3.1c would mount the file and why not (no Cavedog trailer, TA: Kingdoms version 2 archive, unreadable directory), the header key as the game reads it and the trailer year.
hpi_extract_file Extract one archive entry to a file.

VFS introspection tools

These tools let the assistant query the virtual filesystem directly:

Tool Purpose
vfs_game_data List registered game-data folders, their base paths, archive counts and file counts, the archive discovery mode, the mounted archives in lookup order, and every archive that was not mounted with the reason.
vfs_find Locate files by bare name (ARMCOM.bos), basename glob (*.bos) or full-path glob (units/ARM*.fbi). Returns the virtual path and the source archive for every hit.
vfs_list List virtual files and subdirectories under a given directory.
vfs_stat Show every layer that contains a file — useful when a mod overrides a base-game asset.

So the user can ask "where is ARMCOM.bos?" and the assistant calls vfs_find with query: "ARMCOM.bos".

Configuring with Claude Code

Register kbot as a user-scoped MCP server so it loads in every session:

claude mcp add -s user kbot kbot mcp --game-data /path/to/total-annihilation

If the claude CLI is not on your PATH, add the entry directly to ~/.claude.json:

{
  "mcpServers": {
    "kbot": {
      "type": "stdio",
      "command": "kbot",
      "args": [
        "mcp",
        "--game-data",
        "/path/to/total-annihilation"
      ]
    }
  }
}

Restart any open Claude Code sessions; new sessions will pick up the server automatically. Use /mcp inside Claude Code to verify the connection and list the available tools.

For Claude Desktop, add the same mcpServers block to its config file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS) and restart the app.


Shell Completion

kbot supports tab completion for all commands, subcommands, and flags.

Zsh (macOS default)

# Generate and install (uses brew --prefix to find the correct path):
kbot completion zsh > $(brew --prefix)/share/zsh/site-functions/_kbot

# Ensure your .zshrc includes:
autoload -Uz compinit && compinit

Note: On Apple Silicon Macs (M1/M2/M3), Homebrew installs to /opt/homebrew. On Intel Macs, it's /usr/local. Using $(brew --prefix) handles both automatically.

You may need to restart your terminal or run exec zsh for changes to take effect.

Bash (macOS with bash)

# Requires bash-completion (install via Homebrew if needed):
brew install bash-completion@2

# Generate and install:
kbot completion bash > $(brew --prefix)/etc/bash_completion.d/kbot

# Ensure your .bash_profile includes:
[[ -r "$(brew --prefix)/etc/profile.d/bash_completion.sh" ]] && . "$(brew --prefix)/etc/profile.d/bash_completion.sh"

Fish

kbot completion fish > ~/.config/fish/completions/kbot.fish

PowerShell

kbot completion powershell | Out-String | Invoke-Expression

# Or save permanently:
kbot completion powershell > kbot.ps1
# Add `. /path/to/kbot.ps1` to your PowerShell profile

Project Structure

kbot/
├── cmd/kbot/        CLI entry point and subcommands
├── formats/         Public format packages
│   ├── ai/          AI opponent profiles
│   ├── bik/         Bink video (TA:K cutscenes; header parse + ffmpeg decode)
│   ├── crt/         TA:K scenario sidecars (placed units, rules, triggers)
│   ├── fnt/         Bitmap fonts (1bpp, MSB-first)
│   ├── gaf/         Sprite animations + writer
│   ├── hpi/         HPI/UFO/CCX archives
│   ├── pal/         TA .PAL palettes + .ALP/.LHT/.SHD lookup tables
│   ├── pcx/         PCX images
│   ├── scripting/   COB/BOS bytecode
│   │   ├── assembly/    Assembler + disassembler
│   │   ├── compiler/    BOS → COB compiler
│   │   ├── decompiler/  COB → BOS decompiler
│   │   ├── linter/      Static analysis (17 rules)
│   │   └── parser/      Lexer, parser, preprocessor
│   ├── sct/         Map sections
│   ├── smacker/     Smacker video
│   ├── tdf/         Text data files
│   ├── tdo/         3DO models
│   └── tnt/         Map terrain
├── filesystem/      Virtual filesystem (HPI layering)
└── internal/
    ├── assetrender/ Shared VFS render/describe engine (studio Files tab)
    ├── assets/      Embedded TA palette
    ├── cache/       On-disk file cache
    ├── mount/       Terminal asset browser (kbot mount)
    ├── studio/      Web workbench + Files explorer (kbot studio)
    └── testutil/    Asset-path helpers for tests

Development

task              # build + vet + lint + test
task build        # build Studio web assets + kbot binary
task install      # install kbot to $GOPATH/bin
task test         # run all tests
task test-race    # tests with race detector
task lint         # golangci-lint + eslint
task coverage     # generate coverage.html

Prerequisites

  • Go 1.24+
  • Node.js 18+ and npm (for the Studio web assets)
  • Task (go install github.com/go-task/task/v3/cmd/task@latest)
  • golangci-lint (for task lint)
  • FFmpeg (optional, for video conversions)

Game Asset Setup

Many tests require a copy of the Total Annihilation game assets. You need:

  1. Original game installation — from GOG, Steam, or original CD media (version 3.1c recommended)
  2. Flattened (unpacked) assets — extracted from the HPI/UFO/CCX archives

Use kbot mount flatten to extract the packed archives into a flat directory:

# First build kbot
task build

# Flatten the game assets (adjust the source path to your TA install location)
./bin/kbot mount ./path/to/total-annihilation --flatten --target ./ta-flattened

Then create a .env.local file in the project root (this file is gitignored):

# .env.local — local paths to game assets for tests
TA_UNPACKED_PATH=/path/to/ta-flattened
TA_PACKED_PATH=/path/to/total-annihilation
TAK_UNPACKED_PATH=/path/to/ta-kingdoms-flattened
Variable Description Used by
TA_UNPACKED_PATH Flattened TA game assets (scripts, maps, textures, etc.) Most format tests
TA_PACKED_PATH Original packed TA archives (HPI, UFO, CCX, GP3) HPI/VFS tests
TAK_UNPACKED_PATH Flattened TA: Kingdoms assets TA:K format, adapter, and combat tests
ALLOW_SKIP_ASSETS If true, tests skip when assets are missing. If false (default), tests fail. All asset tests

By default, missing game assets cause test failures to ensure developers have a complete test environment. Set ALLOW_SKIP_ASSETS=true in CI or environments where game assets are not available.

Supported Games & Custom Games

kbot supports Total Annihilation and Total Annihilation: Kingdoms. The format packages (formats/…) are game-agnostic — a TNT or COB announces its own dialect through version words and parsing dispatches on what the bytes say. Everything that instead depends on which game an install is lives behind a per-game adapter, registered in two registries:

  • Go — games defines the Game/Adapter interfaces (palette resolution, unit sounds, cursor palettes, tilesets, map terrain groups, FBI→unit-meta building). games/totala and games/takingdoms register themselves from package init; sessions resolve one adapter per mounted install via games.Resolve(id).
  • JS — @coreprime/kbot-game-totala and @coreprime/kbot-game-takingdoms carry the studio's per-game surfaces: weapon-script conventions, COB quick-action catalogues, branding, welcome theming, and the view3d tables injected into the game-agnostic @coreprime/kbot-game3d renderer at boot. The studio resolves the active adapter through ui/common/game-registry.js.

Engine behaviour that varies per unit rather than per game — TA's per-slot AimPrimary/FirePrimary scripts versus TA:K's shared parameterized AimWeapon/FireWeapon set — is resolved from what each COB actually exports (engine/sim/weapon_convention.go), so the simulation never consults a game id and mixed-convention content just works.

To build a custom game on the TA formats:

  1. Go: implement games.Game (usually embedding the totala adapter and overriding what differs), call games.Register from your package init, and blank-import the package where the studio wires adapters (internal/studio/palette.go).
  2. JS: create a package composing over @coreprime/kbot-game-totala the way @coreprime/kbot-game-takingdoms does (spread the baseline game object, override fields), and add it to the registry list in internal/studio/web/ui/common/game-registry.js. The required surface is pinned by the contract tests in packages-js/game-takingdoms/contract.test.js (task test-js).
  3. Context: register the install with kbot ctx add <path> --game custom. Unregistered ids resolve to the Total Annihilation baseline in both registries, so a partially-adapted game degrades to TA behaviour instead of failing.

History

Having played Total Annihilation since its release in 1996 religiously, it was one of the formative games of my youth. Back in 1999, a 14 year old version of me logged onto the PlanetAnnihilation forums. They were dead, quiet. I was annoyed that there was lots of modding and activity going on, but nobody was updating the site, similar to how PlanetQuake/PlanetHalfLife were being maintained well. With poor wording, a young version of me unleashed some harsh words.

Instead of telling me to get stuffed, Frank "DMFrank" Rogan responded. He basically offered me the keys to the site, and said if I wanted it that badly, I can give it a go. I spent the next few years learning how to write editorial content, connecting with mod developers, the basics of software engineering to build new features for the site. A few years of building a community, and promoting the work of other developers was good for the soul.

Then life, or more specifically University hit. That plus the age of the game meant that the rate of new and interesting things slowed down. I went away for a while. I relocated to Australia from the UK. Whilst I never really disappeared or stop reading the forums, tracking projects, I no longer had time to be present/engage with them.

I came back in 2014, having realised I could reverse engineer the network protocols behind the defunct Boneyards.net game service a journey I partially documented over at TAUniverse. However it was an immense amount of work, and between trialling out one or two folks to help, none of whom had the means or ability to really lighten the load - the project again, succumbed to life.

Speaking with one of my colleagues at work, the idea came up to try this again - but with the power of AI to help drive the process. Part of the work for rebuilding Boneyards.net involves lots of work with the TA game formats, such as creating new Galactic maps, packing them into HPI files, being able to reason about the planets/maps being referenced.

This project is an open-source implementation of the various formats. I have archived some of these files in another repository. A longstanding bug-bear of mine was the incomplete or tricky to use tooling, so I've endevoured to make these as robust as I can and will continue to polish them.

License

© Steve Gray

The code is released under the MIT License — see LICENSE for details.

About

Toolkit and file-format reference for Total Annihilation and TA: Kingdoms: HPI, 3DO, COB, GAF, TNT and more

Topics

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages