maplint

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package maplint runs the studio's quality checks against a parsed TNT map + OTA metadata, returning a list of diagnostics. It's the shared implementation behind both `kbot studio`'s Quality Checker dialog and the `kbot tnt lint` CLI / MCP entry points.

The package is deliberately framework-agnostic: callers build an Input value (tnt.Map plus a small set of neutral structs) and get back []Diagnostic. No HTTP, no JSON, no studio-specific types.

The constants at the top tune the heuristics; the studio's UI references the same values when wrapping diagnostics with auto-fix metadata.

Index

Constants

View Source
const (
	// VoidFeature — the canonical sentinel TA writes into
	// TileAttr.Feature for impassable / void cells.  0xFFFF means "no
	// feature, passable"; 0xFFFE / 0xFFFD also appear in the wild on a
	// handful of early Cavedog maps (Metal Heck has 724 of them on
	// what are clearly buildable steam-vent cells, and Lava Run has a
	// similar pattern) — their semantics are not "void" in the engine
	// and we treat them as "no feature" defensively.
	VoidFeature = uint16(0xFFFC)

	// MetalProximityTiles — a start position is flagged when its
	// nearest metal-producing feature is further than this many
	// tiles (1 tile = 32 game-pixels, 1 attribute cell = 16 px).
	// 24 tiles ≈ 1.5× commander beam reach, generous early-game
	// expansion range.
	MetalProximityTiles = 24

	// HeightDiscontinuityThreshold — adjacent attribute cells with
	// |Δheight| beyond this read as cliffs that walking units
	// can't traverse.  TA's units can step ~16 height per attr
	// cell; 32 is double that and a reliable "this looks broken"
	// floor.
	HeightDiscontinuityThreshold = 32

	// MetalRichSurfaceThreshold — when a schema's SurfaceMetal is
	// at or above this value the map is considered metal-rich
	// (Metal Heck uses 255).  In that mode the engine extracts
	// metal from open ground via mexes anywhere, so the
	// metal-proximity check skips that schema's starts.
	MetalRichSurfaceThreshold = 8

	// VoidIslandsTolerance — flood-fill from starts will routinely
	// strand a few cells in tight corners or behind feature
	// footprints.  Anything under this count is treated as
	// acceptable noise and the check stays green.
	VoidIslandsTolerance = 20
)

Tunable thresholds shared across all callers. Documented here so a future maintainer can change behaviour in one place.

Variables

This section is empty.

Functions

func ParsePlayerCounts

func ParsePlayerCounts(s string) []int

ParsePlayerCounts splits a numplayers string like "2, 3, 4" into its individual integer entries.

Types

type Diagnostic

type Diagnostic struct {
	ID       string
	Label    string
	Severity Severity
	Message  string
}

Diagnostic is one result row. ID is a stable, machine-readable identifier for the rule; Label is a short human title; Message is the per-run description ("12 cells stranded" etc.).

func CheckDuplicateTiles

func CheckDuplicateTiles(in Input) Diagnostic

CheckDuplicateTiles flags any byte-identical entries in the TNT tile pool. When the "compressTiles" fix has already been applied the pool is dedup-by-construction, so we shortcut to OK without re-hashing.

func CheckHeightDiscontinuities

func CheckHeightDiscontinuities(in Input) Diagnostic

CheckHeightDiscontinuities counts adjacent attribute cell pairs whose height delta exceeds HeightDiscontinuityThreshold.

func CheckMetalProximity

func CheckMetalProximity(in Input) Diagnostic

CheckMetalProximity walks each start and reports any whose nearest metal-producing feature exceeds MetalProximityTiles. Schemas configured as metal-rich (SurfaceMetal ≥ threshold) skip the check.

func CheckMissingOTAFields

func CheckMissingOTAFields(in Input) Diagnostic

CheckMissingOTAFields walks the lobby-displayed metadata and flags blank required fields.

func CheckSchemaSlotsVsPlayers

func CheckSchemaSlotsVsPlayers(in Input) Diagnostic

CheckSchemaSlotsVsPlayers verifies every declared player count can be hosted by at least one schema (i.e. its StartPos array has ≥ that many spawns).

func CheckStartPositionsInBounds

func CheckStartPositionsInBounds(in Input) Diagnostic

CheckStartPositionsInBounds confirms every schema's start positions land on a passable attribute cell that's inside the map.

func CheckVoidIslands

func CheckVoidIslands(in Input) Diagnostic

CheckVoidIslands flood-fills from every start over the passable attribute grid and counts cells the fill never reached.

func Run

func Run(in Input) []Diagnostic

Run executes every check against the supplied Input and returns diagnostics in a stable order (the studio's UI relies on it).

type FeaturePlacement

type FeaturePlacement struct {
	Name string
	AX   int
	AY   int
}

FeaturePlacement records where a named feature sits on the map in attribute-cell coordinates (16 game pixels per cell).

type Input

type Input struct {
	Map             *tnt.Map
	OTA             *OTAInfo
	Features        []FeaturePlacement
	FeatureRegistry map[string]int // lowercased feature name → metal yield (0 = non-metal)
	AppliedFixes    []string
}

Input bundles everything the lint needs. Map is required; the remaining fields are optional but unlock the corresponding checks.

  • OTA nil → schema, start-position, metal-proximity, metadata checks all skip with an "ok" message.
  • FeatureRegistry nil → metal-proximity check skips with a "feature library unavailable" note.

AppliedFixes lets the caller short-circuit individual rules once their fix has been applied (e.g. compressTiles disables the duplicate-tiles report).

type OTAInfo

type OTAInfo struct {
	MissionName        string
	MissionDescription string
	Planet             string
	NumPlayers         string
	Size               string
	SeaLevel           int
	Schemas            []SchemaInfo
}

OTAInfo is the subset of an .ota file the lint inspects.

func ParseOTA

func ParseOTA(content string) (*OTAInfo, error)

ParseOTA reads a TA .ota file's text and extracts the fields the lint cares about. Schemas live in nested [Schema N] sections, each with its own [specials] block carrying StartPos1..N entries. Returns nil when the file has no [GlobalHeader] (probably not an OTA at all).

type SchemaInfo

type SchemaInfo struct {
	Name         string
	Type         string
	SurfaceMetal int
	StartPos     []StartPos
}

SchemaInfo carries the schema fields the checks read. The studio adapter pulls these from its saveRequest's nested otaSchema; the CLI builds them by parsing an .ota file.

type Severity

type Severity string

Severity is a coarse traffic light used by both the studio dialog and the CLI output.

const (
	SeverityOK      Severity = "ok"
	SeverityWarning Severity = "warning"
	SeverityError   Severity = "error"
)

type StartPos

type StartPos struct {
	Number int
	X, Z   int
}

StartPos mirrors TA's per-schema spawn point — game-pixel coordinates (not attribute cells).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL