Map Editors & Terrain Height/Slope Logic (Research)
Status: First research round, 2026-09-04. All sources were retrieved and verified (web extraction). Uncertainties are explicitly marked with β οΈ.
1. TNT attribute bytes: semantics (largely solved)
The best reverse-engineering source is TAUtil (MHeasell, C#), TAUtil/Tnt/TileAttr.cs:
https://raw.githubusercontent.com/MHeasell/TAUtil/master/TAUtil/Tnt/TileAttr.cs
Structure per cell (4 bytes, row-major across all attribute cells, TntReader.EnumerateAttrs):
| Offset | Field | Meaning |
|---|---|---|
| 0 | Height (byte) |
Height at this cell |
| 1β2 | Feature (uint16, little-endian) |
Index into the map’s feature/anim list. Special values: 0xFFFF = no feature; 0xFFFC (-4) = “void” cell; 0xFFFE (-2) appears on early Cavedog maps (Lava Run, AC02) β meaning unknown (MHeasell asks for info). |
| 3 | Pad1 (byte) |
No known function (MHeasell suspects padding). |
Consequence for our generator: The suspected “255/0/1/2” bytes 1+2 are not a
passability or water flag β they are the feature index (0xFFFF = 255,255 = “no feature”).
Values 0/1/2 in bytes 1+2 mean feature index 0β2, i.e. a feature (tree, rock, vent β¦) sits
at that cell. Byte 3 is padding. β When writing maps: bytes 1+2 = FF FF (no feature)
unless we place features; byte 3 = 0.
Relation to the old DFR spec: The classic file-format hack document (Saruman & Bobban / DFR, v0.0.4, 1997, hosted on units.tauniverse.com) interprets the 4 bytes as “AA BBBB CC”: AA = height, BBBB = index of “special item” (0xFFFF = none), CC = unknown. That matches TAUtil (feature index + padding), confirming the same reading. DFR source: https://units.tauniverse.com/tutorials/tadesign/tadesign/ta-tnt-fmt.txt
SCT (tileset sections): version 2 = 8 bytes per attribute (extra padding longword), version 3 = 4 bytes like TNT (same reads, feature set to 0). Also from TileAttr.cs.
2. Height, water, map size (context)
sealevelin the TNT header (offset 0x24): “Every height lower than this is considered under water” (DFR doc). Lava is defined via the same mechanism plus special values in the OTA file. Source: https://units.tauniverse.com/tutorials/tadesign/tadesign/tntdesc.htm- Header
Width/Heightare in attribute cells (2Γ finer than the tilemap): mapdata =(height/2)*(width/2)uint16 entries; 4 attribute cells per graphics tile (2Γ2). Confirmed both in the DFR doc and TAUtil (DataWidth = Width/2, attribute enumeration over WidthΓHeight cells). - Minimap: 252Γ252 (header always says FC); smaller maps are padded with
$DD(DFR). - Empirical from Annihilator practice (Gabbi tileset tutorial): a section’s heightmap is 1/16 of the tile resolution (512-px section β 32Γ32 heightmap), grayscale BMP; dark = low. Indirectly confirms the 2Γ2-cells-per-tile logic. Source: https://files.tauniverse.com/files/ta/resources/tutorials/tileset-tutorial-zerog/browse-online/
3. Walkability: how slope is computed / the MaxSlope scale
TA itself is closed source β walkability logic is documented via the MovementClass
definition in gamedata/moveinfo.tdf and the FBI/moveinfo tags MaxSlope/MaxWaterDepth:
MOVEINFO.TDFdefines classes, e.g.[CLASS0] Name=KBOTSS2; FootprintX=2; FootprintZ=2; MaxWaterDepth=12; MaxSlope=32;and[CLASS1] KBOTSF2 β¦ MaxSlope=11;. Source: https://units.tauniverse.com/tutorials/tadesign/tadesign/tdfgdata.htm- FBI doc: “MaxSlope β What is the maximum slope this unit can go on.” (values 8β36 in vanilla). Source: https://units.tauniverse.com/tutorials/tadesign/tadesign/fbidesc.htm
The best-documented reference implementation β the Spring engine (inherits TA movement classes; SMF format derived directly from TNT):
-
Slope computation (
rts/Map/ReadMap.cpp, aroundCReadMap::UpdateHeightMapSynced): per cell, the 8 surrounding face normals (from the 2 triangles per height cell) are considered;avgslope= mean of the normals’ y components Γ 0.125,maxslope= minimum of all 8 y components, then smoothing:slope = mix(maxslope, avgslope, maxslope/avgslope); stored asslopeMap = 1.0 - slope. The code comment: “smooth it a bit, so small holes don’t block huge tanks”. Sources: https://raw.githubusercontent.com/spring/spring/develop/rts/Map/ReadMap.cpp (lines ~660β697), format loader: https://raw.githubusercontent.com/spring/spring/develop/rts/Map/SMF/SMFReadMap.cpp -
maxSlope semantics (official Spring wiki, author kloot): Spring internally uses
(1.0 - terrainNormal.y)as slope (0 = flat, 1 = vertical). The “can we walk here?” test ismaxSlope > terrainSlope; the conversion is1.0 - cos(DEG2RAD(maxSlope * 1.5)), i.e. practically: maxTerrainAngle(degrees) = maxSlope Γ 1.5 (maxSlope 30 β 45Β°, 36 β 54Β°, >60 = walkable everywhere). Source: https://github-wiki-see.page/m/beyond-all-reason/springrts_engine_wiki_mirror/wiki/Movedefs.lua (mirror of the springrts.com wiki page “Movedefs.lua”). Also confirmed by the BAR movedefs.lua comment: “maxSlope is multiplied by 1.5 at load, so 60 degrees is its actual maximum” β https://github.com/beyond-all-reason/Beyond-All-Reason/blob/aae9279d/gamedata/movedefs.lua
Transfer to TA (β οΈ with caution): TA is closed source; the 1.5 scaling is a Spring decision. But the vanilla movement-class values (tanks 8β15, KBots 11β32) were set in 1997 to behave plausibly as degreesΓ1.5 (β roughly 12Β°β48Β° maximum walkable slope), and Spring adopted the TA classes unchanged. Working rule for the map generator: a cell step (16-elmo grid) that jumps more than ~30 height units over 2 cells blocks all vanilla tanks; steps β€ ~15 per cell remain walkable for most units. The definitive TA-internal formula remains open β calibrate against the cliff sections of the official tilesets and run in-game tests.
Practical consequence for the generator: vanilla TA movement classes sit at MaxSlope
8β36 (tanks low ~8β15, KBots 11β32). A height jump of more than ~15β16 units per
attribute cell (16 elmo) exceeds practically every vanilla tank class and is safe only
for the steepest KBot classes. β οΈ The exact formula slope = f(maxCornerDiff, minCornerDiff)
per path tile is not publicly documented for TA itself β no community source found that
nails down an empirically determined max height jump per cell. Recommendation: our own
empirical tests (build defined step heights into a map, send runner units across) and
calibration against the vanilla movement-class values. No broader community convention
(“max height jump per tile = X”) exists per this research β the cliff sections of the
official tilesets are the implicit reference (see below).
4. Slope/cliff tile practice from the editors
Annihilator 1.5/1.6 (Kinboat) β the community’s de-facto standard
- Tutorial (lister3128, on File Universe):
https://files.tauniverse.com/files/ta/resources/tutorials/annihilator-tutorials/browse-lister-online/
- Maps are composed from ready-made sections (hills, cliffs, coasts); there is no fine height-editing function (only section placement) and no undo. The height logic therefore lives entirely in the pre-made sections/tilesets.
- “Compress Map” removes overdrawn tiles (file size).
- Annihilator can export heightmaps (Map β Export Heightmap as BMP) β used by the FAF import guide for SupCom: https://forum.faforever.com/topic/161/a-method-of-recreating-classic-total-annihilation-maps-in-supcom
- Tileset tutorial (gabbi): heightmap handling when building your own sections (1/16 resolution, grayscale, import BMP + heightmap into Annihilator, export HPI tileset). This is the documented method by which the community creates slope tiles with matching heights.
TAE (official Cavedog Map & Mission Editor)
- On the CC CD under
TAE\; same section logic as Annihilator; official docs: https://www.tauniverse.com/cavedog-mirror/totala/mapeditor.html - β οΈ Height/slope rules are not documented as numbers there either; TAE ships the official section libraries (Green/Desert/β¦), whose cliff/ramp sections are the de-facto convention.
Mappy & SnappyMap (MHeasell) β most modern tools, directly relevant to us
- Mappy (modern Annihilator replacement, C#): “Accurate, Cavedog quality minimaps”, automatic tile dedup, full undo, feature drag. https://github.com/MHeasell/Mappy β β οΈ the readme documents no slope rules; the source code (tile placement / height collision check) would be the next excavation site.
- SnappyMap (procedural terrain generator β exactly our use case): converts heightmap images + tileset + config into TA maps. Documented limitation: “currently only supports land/sea island maps, and can’t do cliffs or hills” β i.e. even this tool has not solved slope-tile composition (cliffs/ramps). Config requires at least one section type each: land, sea, 4Γ coast, 4Γ corner-coast, 4Γ “reflexive coast” (3/4 land). https://github.com/MHeasell/SnappyMap β valuable for our design (section-matching approach confirmed; cliff/ramp auto-matching remains the open research question).
Other tools
- TATOOL (Saruman/DFR, 1997): the first format-hack utility; can extract mapdata/mapattr/tilegfx/tileanims and recompute headers (described in the DFR document).
- TAMEC (tamec.org) was the central tileset/utility distribution (Annihilator 1.5/1.6 downloads, tilesets by C_A_P, Wizard-Kane, ChinaHooks Green Completion etc. β Ravines module with “wider modular slope ways”: https://tapower.tauniverse.com/tilesets.htm). TAMEC/FilePlanet links are mostly dead today (Wayback needed).
- For the tools on our task list (YankSpankers, TATool/MapComp, segen, Crux, TASlider, TADuplicateMapMaker) this round found no hits with height/slope documentation β still open.
5. Ramp/slope tile placement rules (empirical, from editor docs)
Direct rule documentation is missing; what is documented:
- Slope/cliff behaviour lives in the section bitmap + section heightmap, not in separate rules: Annihilator/TAE place sections as a whole; height comes from the section heightmap; neighbouring sections must be height-compatible by eye (which is why TAMEC modules are named “Savannah Cliffs and Ramps”, “Flat and Transitions”, “modular slope ways” β the community created transition sections as a convention, not rules).
- β οΈ No editor with documented auto-matching of ramp tiles was found. SnappyMap (coast/corner-coast system) is the only documented automatic approach β for coast tiles, not cliffs.
6. Open questions / next steps
- Byte 3 (Pad1) and feature = 0xFFFE (Lava Run/AC02): unknown β MHeasell is actively looking for information.
- The exact TA slope formula (walkability from corner heights) β only achievable via CALib/binary RE or our own in-game tests; use the vanilla movement-class MaxSlopes (8β36) as calibration anchors.
- Wayback grabbing for: TAMEC utility descriptions (Annihilator readme, Mappy forum thread), xolon.org / totalannihilation.net format docs, tauniverse.com forum threads on “map editor height” (the old vBulletin is barely indexable β Google site search + Wayback).
- Inspect the Mappy source (GitHub) for height/slope placement checks.
- The local KB (
/home/hermes3/ta-halle/kb/) contained onlydomain-setup.mdbefore this article β no duplication.