DOMINATION

Map authoring by blueprint image + schematic inspection

A hands-on alternative to writing per-map Python generators. You paint a top-down blueprint (or generate one as art); the toolkit converts it to a playable, validated map and lets you see the layout instantly without a 2-minute Unity capture.

The three tools (unity/tools/maptools/)

Tool What it does Speed
map_schematic.py <map.json> Renders any map's terrain grid as a colour PNG with spawn/resource/prop markers — the fast inspection loop <1s, no Unity
image_to_map.py <blueprint.png> --id … --name … --biome … Converts a painted blueprint → validated map JSON (BFS connectivity, symmetry, in-bounds) <1s
terrain_tiers.py <map.json> Builds a coherent elevation heightfield — lower plains → rolling hills → high ground → towering mountains, biased toward the rim, with graded slopes. BFS-proven walkable network <1s
terrain_surface.py <map.json> Paints realistic surface materials onto that elevation — grass plains → dirt/scree hills → rock cliffs → biome caps (snow / scorched / bare rock). Connectivity-safe by construction <1s
dress_map.py <map.json> Scatters biome-appropriate props/decor (trees, rocks, tufts, wrecks) with symmetry <1s
stamps.py Pre-QA'd region stamps (forest, settlement) placed by a single seed pixel
map_palette.py The shared terrain↔colour key (below)

Blueprint colour key (paint terrain with these)

Paint 1 pixel = 1 map cell. Nearest-colour match on import, so approximate hues are fine.

Paint Terrain Notes
green #568C3E grass
tan #D6C08A sand desert base
brown #966E42 dirt
dark grey #5A544E rock IMPASSABLE — chokepoint walls
blue #305C9E water IMPASSABLE deep water
light blue #6EA0C8 shallow ford passable water crossing
plank brown #967846 bridge over water
dark asphalt #46464A paved_road
mid grey #78746C road dirt track
charcoal #3C3630 scorched battle scar
pale cyan #C8E0EC ice snow maps

Pin dots (drop these bright colours as markers)

Reserved saturated hues NOT used by terrain — drop a small dot; the cell under it keeps the base terrain. Multi-pixel dots are auto-clustered to one pin (centroid).

Pin Meaning
pure red #FF0000 player spawn (team = paint order)
magenta #FF00FF ore node
orange #FF8000 oil derrick (contested economy)
yellow #FFFF00 strategic point (tech)
cyan #00FFFF strategic point (radar)
bright green #00FF00 forest region stamp seed
violet #8000FF settlement region stamp seed

Workflow

# 1. paint/generate blueprint.png (left half only if using --mirror)
# 2. convert -> validated map
python3 unity/tools/maptools/image_to_map.py blueprint.png \
    --id my_map --name "My Map" --biome desert --mirror
# 3. inspect the layout instantly
python3 unity/tools/maptools/map_schematic.py \
    unity/Assets/StreamingAssets/data/maps/my_map.json
# 4. redraw blueprint, repeat — or capture in-engine when the layout reads right

--mirror paints only the left half and generates the right by 180° rotation, so the map is guaranteed competitively symmetric — the painter can't accidentally make it unfair.

Terrain relief & realism pipeline (mountains · hills · plains · snow · lava)

A flat blueprint gives you the layout; this pass gives it realistic 3D landscape. Run the two elevation tools after the layout reads right — they are deterministic (seeded from the map id), symmetry-aware, and each ends with a BFS reachability gate that hard-fails rather than ship a map with a severed spawn.

M=unity/Assets/StreamingAssets/data/maps/my_map.json
# 1. RELIEF — raise coherent plains→hills→mountains (rim-biased so bases sit in the valley).
#    --clamp 3.2 keeps ridges high-but-passable for open / many-spawn maps (steppe, 32-base basins).
python3 unity/tools/maptools/terrain_tiers.py   "$M"            # add --clamp 3.2 for open maps
# 2. SURFACE — repaint the ground by elevation+slope so mountains stop rendering as green grass.
#    Biome is auto-read from the map's `biome` field (snow→snow caps, volcanic→scorched, else rock).
python3 unity/tools/maptools/terrain_surface.py "$M"            # or --biome snow|volcanic|desert
# 3. DECOR — scatter trees/rocks/tufts on the new terrain, then inspect.
python3 unity/tools/maptools/dress_map.py       "$M" --mirror auto
python3 unity/tools/maptools/map_schematic.py   "$M"

Why two passes. terrain_tiers.py only moves the ground up; it never re-textures it, so a peak it raised still renders as green grass. Across the shipped set this left tens of thousands of impassable mountain cells painted grass (a glacier map with green peaks, a volcano with green slopes). terrain_surface.py closes that gap by banding the surface:

Elevation Temperate Snow biome Volcanic
plains (low) (left as authored) (left as authored) (left as authored)
rolling hills / high walkable grass + dirt scree on steep swells same same
mountain cliffs (> 3.5 walk cap) rock rock on steep faces rock
summits snow on tall peaks only (≥ 6.5) snow blankets the high ground scorched charcoal

Connectivity is provably preserved: an impassable char (rock) is only ever written to a cell whose elevation is already above the 3.5 walk cap, and every walkable cell only receives passable ground. Roads / water / rail / bridges / concrete / ore are never touched. The pass prints a SURFACE {…} line with the change histogram and an errors list that must be empty.

Why this beats a blind generator

  • You author with your eyes. Layout is a picture you paint, not terrain math you can't see.
  • The schematic is the inner loop. A dark wadi, a broken lane, a resource on water — all visible in <1s, not hidden behind a far 3D overview.
  • Validation is kept as a gate. BFS connectivity, symmetry, in-bounds, determinism still run on every import — the fairness/lockstep guarantees the generators gave, unchanged.
  • Stamps compose from verified pieces. A forest/settlement is designed + QA'd once, then reused by dropping a seed pixel.

Proven end-to-end: a painted blueprint → validated JSON → loads in-engine (connectivity 0 warnings, deterministic checksum match).