Every skirmish map authors its barracks in the map JSON structures array. Originally, the player_id on each barracks determined the starting position completely: player 1 began at p1_barracks, player 2 at p2_barracks, and so on. The setup screen could change which player occupied a seat, but not which authored base that seat used.
Skirmish setup now separates those two ideas. Each player can choose any available barracks on the map as a starting base, while the game still guarantees exactly one starting base per player.
ui/qml/MapSelect.qml gives every seat in the Order of Battle a Base chip next to Colour, Nation, Commander, and Team. Clicking the chip cycles through bases that no other seat currently claims.
One seat is always armed for direct selection. The UI marks it with a lighter card, brighter border, and â–¸ caret; clicking anywhere on a player card arms that seat.
The map preview in ui/qml/MapPreview.qml provides the spatial half of the same control:
If the selected base is already occupied, the two seats swap bases. Swapping instead of stealing preserves the one-base-per-player invariant and prevents a roster from ending up with a player who has no start.
Base selection is disabled when a map offers only one base, as on Iron Sepulcher Watch. The Play button also rejects any roster in which a seat has no base assignment.
MapPreviewGenerator::base_markers() derives a display name for every starting base.
A barracks with an authored id that already reads like a place keeps that identity in human-readable form. For example:
east_lodge_barracks → East Lodge
IDs such as p<N>_barracks, together with unnamed entries, are treated as bookkeeping rather than authored place names. Those bases receive a name derived from their world-space bearing, such as South-West, North-East, or Centre.
Bearings use world orientation, where north is -z, rather than the rotation of the preview image. This is deliberate. Authored place names and the battlefield itself use world north, so a location called the north toll should not become “South” simply because the minimap happens to be rotated by 225°.
When two bases resolve to the same display name, a numeric suffix disambiguates them. BaseMarkersTest verifies that shipped skirmish maps do not expose duplicate base names.
StructureEntry::id, parsed by MapLoader::read_structures, is the stable key for a base.
Maps that omit an ID fall back to structure_<index>. That fallback is stable for a particular version of a map file, but it changes when the structures array is reordered. A map whose bases are intended to be selected by stable name should therefore author explicit IDs.
Two map helpers expose the available seating information:
Game::Map::collect_base_options() lists every point barracks on the map; anddefault_base_assignments() returns the seating authored by the map itself.The setup screen stores the selected base as baseKey on each entry in the existing player_configs list passed to start_skirmish.
SkirmishLoader::start collects those keys and forwards them to MapTransformer::set_base_assignments(), alongside the team and nation overrides it already handles.
When no base assignments are present—for example, in an observed match, campaign mission, or restored save—the transformation path remains inactive and the map behaves exactly as authored.
When base assignments are present, resolve_base_seating() in map_transformer.cpp resolves them before any entity spawns. Reseating changes three things.
The chosen barracks spawns under its new owner.
Any other barracks originally authored for that player revert to neutral ownership. A reseated player can therefore never begin with two bases.
The abandoned camp remains on the map as a capturable neutral prize, just like any neutral outpost placed by the map author.
The player's authored units, together with any other structures they own, are translated by the vector from the original base to the selected one. A builder authored beside the starting barracks therefore remains beside the barracks after reseating.
Translated spawns still pass through the transformer's existing “nudge off forbidden ground” logic. If moving a camp places a unit in a river or other invalid location, the unit is moved to the nearest valid tile before play begins.
Neutral outposts are often authored with a much smaller max_population than a true starting base. Choosing one as the new start does not reduce the player's intended army capacity: the player carries the troop cap of the base they gave up.
The choice therefore changes strategic position without accidentally changing the starting population budget.
A baseKey that does not match any base on the map is a no-op for that player. The player keeps the barracks authored for them rather than being left without a valid start.
This fallback protects saved or externally supplied configurations from turning an unknown base identifier into an unplayable roster.
The behavior is covered at three levels:
tests/map/base_options_test.cpp validates the option list, authored defaults, marker naming and placement, and verifies that every skirmish map contains at least as many bases as the player slots it advertises.tests/map/map_transformer_test.cpp checks seating rules in isolation, including ownership, the one-base-per-player invariant, troop-cap preservation, translated retinues, unknown keys, and assignment isolation between matches.tests/map/skirmish_base_choice_test.cpp exercises the complete path through SkirmishLoader, including the opening camera framing the selected base.The result is a flexible setup choice that changes where a player begins without weakening the map's ownership, population, spawn-placement, or fallback guarantees.