Atlas of Bretonnia

Workshop journal · · 8 min read

The 9 walls of porting a campaign map to Warhammer III

Porting a Warhammer I map to Warhammer III mostly means running into walls. In four days I counted nine big ones. For each: what we saw, what was really going on, how to get past it, and what I took away from it.

A coast of our map seen from high up, stepped like a staircase, with a pale strip of water along the shore
A coast of our map on 24 September, stepped like a staircase and edged with a pale strip of water: water and coasts were one of these walls.

How to read these cards

Each wall follows the format of our error log: the symptom, the cause, the fix, then the lesson, in a box. The causes were found by Claude, Anthropic's AI assistant, often by reading a crash report. The symptoms were usually mine, spotted in game with a Warhammer I screenshot next to it.

Where the nine walls hitCompiling the terrainBOB turns the project into game files2BOB does nothing, silentlyGenerating the startposthe game freezes the starting state1the impossible startposLoading the campaignfactions, settlements, AI3Athel Loren without settlement slots4an AI without a map, a pack that jumps the queuePlaying turn oneup to the first end of turn5the end-of-turn crash9the new game that thinks it is a saveLooking at the mapin game, next to Warhammer I6“I don't recognise the map”7stuff flying everywhere8a sea without waterThe game crashes: the crash report tells you where to look.Nothing crashes: a control case, or Warhammer I next to it, shows thegap.
The nine walls, sorted by when they hit, from the terrain to the in-game picture. In red, those that crash the game; in brown, those that say nothing.

Technical terms are explained along the way. For the full method, in order, see the tutorial.

Wall 1: the impossible startpos

The startpos is the file that freezes a campaign's starting state: factions, characters, settlements, diplomacy. No startpos, no game. The game builds it itself, from the data you give it.

  • Symptom: on the morning of 21 September, startpos generation crashes on every attempt.
  • Cause: two defects in our data, one behind the other. Three of our ports didn't have CA's shape. And the declared minimap didn't exist: it had been fixed inside the pack, which every build wiped out.
  • Fix: export one of CA's maps and look at its port towns: a 19-hex disk, 16 on land and 3 on the sea side, as the port. Then fix the source, the kit's tables, and have the pack build check that every declared file exists. The first startpos came out on the 21st at 12:08 pm.
Port town: 16 + 3 hexes11Port: 3 rim hexes, 2 sea, 1 beach22Main: 16 land hexes33The sprawl covers the 19 hexes
CA's model, read from its maps: a perfect 19-hex disk, whose centre becomes the town's position; 16 land hexes in the main slot, and 3 consecutive rim hexes, sea side, as the port (blue line). Three of our ports did not follow this model.

Wall 2: BOB doing nothing, silently

BOB is the Assembly Kit's builder: it turns the terrain project into files the game reads.

  • Symptom: BOB runs only 6 actions instead of a dozen. No relief, no textures, and no error. We lost an hour trying files at random.
  • Cause: a limit of the tool. For a map the game's database (the one in CA's packs) does not know, BOB runs only those 6 actions, without a message; adding the map to the kit's database or to a mod pack changes nothing.
  • The right way: a control case first, to clear our files: ChaosRobie's project, copied under another name, failed the same way. Then the community: the CAIME Discord, tw-modding. We know of no official way to lift this limit, and our guides rule out taking a CA tool apart or working around it.
Only six actions?How many actions doesbob.log announce?over 6Not the limit12 for us on acomplete run; thencount theFailed to find tile inbob_warnings.log6Run a control casea project known to begood, copied underanother name, then runthrough BOBAnd the control case?over 6Your projectis to blame: searchyour own files6 tooThe kit's limityour project is not toblame: ask thecommunitydon't force the toolThe 6 actions of an unknown mapthe Terry file and five masks; no relief, no textures,no logic map, no shroud; and no message at all.
Six actions instead of a dozen, without a message: a control case tells whether it is your project or the kit's limit.

Wall 3: Athel Loren without settlement slots

  • Symptom: the terrain is ready, and my two tests late on the 21st crash as soon as the campaign loads.
  • Cause: the day before, the 36 rows giving slots to Athel Loren's 18 settlements had been dropped on a false check: “Warhammer III declares none for its elf regions”. Yet the game looks for every settlement's primary slot. Our first fix targeted something else, and the crash came back unchanged.
  • Fix: the rows restored from Immortal Empires' templates, plus a new check comparing our startpos with one from a campaign that loads. That check finds this defect on its own.

Wall 4: an AI without a map, and a pack that jumps the queue

  • Symptom: a crash when the game sets up the human player. Worse, two tests were running on an old startpos, and we thought the fix didn't work.
  • Cause: three causes, one behind the other. The startpos was generated without the AI's map data, the two files it uses to find its way (hlp_data.esf and spd_data.esf). One faction had an empty AI group. And the startpos inside the pack took priority over the copy sitting next to it.
  • Fix: generate with the option that produces the AI data, and ship it; give each faction the AI group CA gives it in Immortal Empires, otherwise its culture's.
When it breaks,what is missing?During generationThe script_log stops right after "Loading Mods", with no dump.→ start_pos_regions: rebel_faction_name andlong_description empty, no "PLACEHOLDER".When the campaign loadsA crash at the very start of loading.→ A primary slot for every settlement(start_pos_region_slot_templates).A crash while loading.→ For every faction, an AI group containing apersonality.A crash while setting up the player (new map).→ hlp_data.esf and spd_data.esf in the pack.On the selection screenNo lord to pick.→ The start_pos_starting_general_optionsrows.On turn 1A character in the map's corner, on cell (1, 1).→ Its start_pos_character_to_settlements link(character placed at (0, 0)).No intro, no missions.→ A startpos generated AFTER the scripts (save counterat 1).First: bad_mods_report.txt names the rejected row, one ata time, in:%APPDATA%\The Creative Assembly\Warhammer3\crash_report\
When it breaks tells you where to look: each symptom we went through, and the row or file that was missing.

With that wall down, the campaign finally loaded.

It works!!!! […] the map looks like nothing, but at least it loads.

Charles, 21 September, 8:31 pm (translated)

Wall 5: the end-of-turn crash nobody saw

  • Symptom: my game on the evening of the 21st stops at the end of turn one. I didn't report it; Claude found it the next day, somewhat by chance, in the crash reports.
  • Cause: a campaign has two terrains. The second one, a battle terrain, is used from the first battle, even between two AIs. Ours had never been shipped.
  • Fix: build that battle terrain the way CA does, compile it with BOB, and have the pack build check that every declared file exists.
One campaign, two terrainsThe campaign playable areacampaign_map_playable_areasterrain_folder = <folder>The campaign terrainterrain/campaigns/<map>/the map's relief, tiles andplaced objectsThe battle terrainterrain/battles/<folder>/a battle tile map; CA's alsocarries battle locations andcatchment mapsWithout that battle terrain: a crash at the first land battle,even between two AIs, so often at the very first end of turn.
A campaign map has two terrains: its own, and a battle terrain, named by the terrain_folder column of its campaign playable area.

Wall 6: “I don't recognise the map”

  • Symptom: 21 September, 10:50 pm, in game: “this is a bit of a mess”, then “I don't recognise the map” (translated).
  • Cause: Warhammer III models in place of Warhammer I's; 5,277 pieces of Chaos set dressing made visible to all by a wrong rule; the winter glade's snow wiped out, mistaken for a defect. Everything had been checked with numbers, nothing by eye.
  • Fix: Warhammer I's own files, which is why the Realm of The Wood Elves DLC is required to play; only natural set dressing visible to all; the snow redone the Warhammer III way.
Warhammer I: Tal Eth Ayr and the Oak of Ages, at the start of the mini-campaignWarhammer I
Warhammer III: our map, Tal Eth Ayr, the Oak of Ages and the King's Glade, Orion's armyWarhammer III
The reference, and the result: Tal Eth Ayr and the Oak of Ages in Warhammer I (left), then our map in Warhammer III on 25 September, with the King's Glade and Orion's army. Every visual now goes through this comparison.

Wall 7: stuff flying everywhere

  • Symptom: 22 September, 10:40 pm, objects floating all over the place. Claude's audits only found 45.
  • Cause: a coordinate frame. In Warhammer III, objects live in hex space, while the terrain is stored in square pixels, over a depth shorter by a factor of √3/2. Our objects had been wrongly converted: squashed 13% towards the south, they floated or sank. The audits, run in the same wrong frame, saw nothing.
  • Fix: never convert object positions, and read the terrain at row z × √3/2. Checked in Terry at 11:23 pm: the waystones were back in the ground.
Two frames, one mapObjects: hex space338.9Terrain: square pixels293.5× √3/2depth, north to southWrong: convert theobjectin the airsunkSquashed 13% south, it floatsor sinks. The audits, in thesame wrong frame, only found45.Right: the objectstays putstanding on the groundIts position stays in hexspace; it is the terrain thatis read at row z × √3/2.The check: Terry's camera parked on precise points.Land, or sea?
Diagram. Objects live in hex space, the terrain in square pixels, over a depth shorter by a factor of √3/2. Never convert an object's position: read the terrain at the right place.

Wall 8: a sea without water

  • Symptom: 23 September, 3:40 am: “by the sea there's no water, just the sandy bottom” (translated). No sea, no rivers, no lakes.
  • Cause: to smooth the coastline, the same surface had been written into both of the terrain's height maps: height, the terrain, and sea_height, the seabed. No depth left: the game drew no water. Terry, on the other hand, showed it.
  • Fix: give each map its meaning back; the water was back at 4:06 am. The same day, a seabed raised near the coasts did it again. Hence the rule, now in the code: as on CA's maps, sea_height stays below 0 everywhere, even under land.
Two height mapsheight: the terrainsea_height: the seabed0: the water levelOur two maps, the same surface0zero depthno sea, no rivers, no lakes:just the sandy bottomAt CA: the seabed below 0, everywhere0the water is drawnthe seabed, even under landTerry, though, showed the water: water is judged in game.
Diagram, in cross-section. The game draws water at level 0, above the seabed (sea_height). Writing the same surface into both maps leaves no depth; on CA's maps the seabed stays below 0 everywhere, even under land.

Wall 9: the new game that thought it was a save

  • Symptom: no cinematic, no “How to play”, no victory mission, and a weird camera.
  • Cause: the startpos predated the campaign scripts. Its save counter (__save_counter) only goes to 1 if the scripts run during generation. Stuck at 0, it made every new game look like a save. The Claude session in charge of scripts tracked it down.
  • Fix: regenerate the startpos after any script change, plus an automatic check that the counter is 1.
The startpos is a snapshot22 Sept, 11:40 pm: the startpos is generatedThe turn-0 snapshot is taken. Our campaign scripts don'texist yet.Its save counter stays at 0.123 Sept, 12:50 am: the scripts arriveThe pack contains them; the snapshot never saw them.23:19 am: Charles's testNo cinematic, no “How to play”, no victory mission. Thescript log says why:Loading value __save_counter [0]Every new game looks like a save.3The fixRegenerate the startpos with the up-to-date pack, copy itinto the project, rebuild the pack.The counter is 1: an automatic check makes sure of it.4
The startpos freezes turn 0 at the moment it is generated: everything it needs on turn one, scripts included, must exist before.

What the nine walls have in common

Read together, these walls boil down to four simple rules.

  • Compare with something that works (walls 1, 2, 3). A known-good control case settles things faster than ten hypotheses.
  • Read what the code was looking for (walls 3, 5). A crash report tells you which data was missing; a difference between two files is only a lead.
  • Judge by eye, in game (walls 6, 7, 8). The numbers looked fine while the map was unrecognisable.
  • Respect the order of things (walls 4, 9). The startpos is a snapshot, the pack wins over loose copies, and each change gets tested on its own.

Each of these mistakes is logged in our error journal, with the rule that prevents it. I explain how we keep it in Working with an AI on a big mod. And to see where all this is heading, the map of all Bretonnia is waiting for you.

Suggest an ideaSee the map