# RPFM, the guide: adding a unit, a faction and a lord to Total War: Warhammer III

> RPFM (Rusted PackFile Manager) opens and builds Total War packs. This guide shows step by step how to use it, then walks through three complete procedures for Total War: Warhammer III: adding a unit, a faction, a legendary or playable lord. The pitfalls come from the community and from our own project.

Page: https://bretonia.dev/atelier/en/rpfm/ · Atlas of Bretonnia, modders' workshop · updated on 2026-09-25. Plain-text version, for readers and AIs; the diagrams are described in words. Reviewed on 25 September 2026, update 9.0. Written against its sources, RPFM 5.0.6's code and manual (labels and locations), the game's data and the Assembly Kit on patch 8.1; what 9.0 changed on our project has been added, the rest has not been rechecked point by point.

## What RPFM does, and what a pack is

A Total War mod is a `.pack` file, an archive the game reads from its `data` folder. **RPFM** (Rusted PackFile Manager, by Frodo45127, under the MIT licence) opens the game's packs read-only and builds yours. A pack holds tables (`db/`), texts (`text/db/<file>.loc`), Lua scripts (`script/`), images (`ui/`), model definitions (`variantmeshes/`) and, for a campaign, its start position (`campaigns/<campaign>/startpos.esf`).

*Diagram: Anatomy of a pack.*

In the diagram: Anatomy of a pack · `atlas_chevaliers.pack` · your mod: an archive, placed in the game's data folder · `db/land_units_tables/` · never rename or move it (RPFM) · `atlas_land_units` · yours, with your prefix; @ or ! in front: only to modify a game row · `data__` · the game table's name: never in your pack · `text/db/` · `atlas.loc` · one key per sentence; applies to every language · `script/campaign/mod/` · `atlas_script.lua` · loaded by the game on its own · `ui/units/icons/` · `<card>.png` · the unit card: 60 × 130 · `ui/flags/<faction>/` · the flags · `ui/portraits/portholes/` · `portrait_settings*.bin` · portrait settings, per art set · `variantmeshes/variantmeshdefinitions/` · `*.variantmeshdefinition` · a new model · `campaigns/<campaign>/` · `startpos.esf` · made by the game; one per campaign · `campaign_maps/<map>/` · `hlp_data.esf · spd_data.esf` · made by the game, for a new map · `settings.rpfm_reserved · notes.rpfm_reserved` · RPFM's settings and notes, ignored by the game · Tables and .loc files: merged by key. · Other files: same path, only one wins.

A pack stores each file at its path. The fragments of one table (a _tables folder), like .loc files, are merged by key; for any other file, two files at the same path do not both load: only one wins. In gold, what the game makes; in grey, what only RPFM reads.

This guide follows RPFM **5.0.6**, the latest stable release as of 24 September 2026, and tables read on patch 8.1. For update 9.0, released the same day, RPFM's schemas were published that very day; the tables cited here have not been reread one by one since: when in doubt, check. Every change follows these six steps:

1. **Find**: The closest game row. (View → Toggle Global Search Window (Ctrl+Shift+F))
2. **Copy**: Into your table (`db/<table>_tables/<file>`), under a key of your own (the same one to modify a game row). (right-click the pack → Create… → Create DB, then Paste as New Row)
3. **Name**: The texts shown to the player, in `text/db/<file>.loc`. (right-click the pack → Create… → Create Loc)
4. **Check**: References, keys, empty fields. (Diagnostics panel → Check PackFile)
5. **Try**: The pack installed in `<game>\data\`, in game. (right-click the pack's root → Install)
6. **Publish**: The optimised version. (right-click the pack's root → Save Pack For Release… (on a copy))

## The words of the trade

| Word | Meaning |
|---|---|
| Pack | a mod's archive, placed in the game's `data` folder |
| Table | a table of the game's database, stored in `db/<table>_tables/` |
| Key | what identifies a row, unique in its table (sometimes several columns) |
| Fragment | one table file; the game reads every fragment of a table, its own and the mods' |
| Datacore | a fragment named like the game's (`data__`), which replaces the whole table |
| `.loc` | a text file: one key per displayed sentence |
| Schema | the description of the tables: columns, keys, links (RPFM's, or the kit's: `TWaD_*.xml`) |
| Dependencies cache | the game's data, prepared by RPFM for searching and checking |
| Assembly Kit | Creative Assembly's official kit (Steam → Library → Tools) |
| Subtype | a character's exact type (`agent_subtypes`) |
| Art set | a character's set of appearances (`campaign_character_art_sets`) |
| Startpos | `startpos.esf`, a campaign's turn-0 save |
| Pack root | the first line of the pack tree, bearing the pack's name; its right-click menu holds Install, Uninstall, Save Pack For Release… and Build Startpos |
| Palette | RPFM's search by name: Ctrl+Shift+P for an action, Ctrl+P for a file |
| `%APPDATA%` | your Windows account's settings folder: type `%APPDATA%` into File Explorer's address bar |
| Military group | `military_group`, a faction's recruitment group: a unit allowed to a group is allowed to all its factions (in CA's data, Bretonnia's factions have `wh_main_group_bretonnia`) |
| `twad_key_deletes` | the table that removes game rows at launch (Warhammer III, since patch 6.3): columns `table_name` and `key` |
| Mixer | *MIXER – Mixu's Unlocker*, a mod by Mixu: its startpos makes every faction of Immortal Empires and Realm of Chaos playable; according to the wiki, it is most players' only startpos mod |
| Error / Warning / Info | the three severities of the diagnostics: *Error*, a problem in game; *Warning*, a likely problem; *Info*, worth knowing. Aim for zero *Error* |

## Installing and setting up RPFM 5.0.6

Download RPFM from its [releases](https://github.com/Frodo45127/rpfm/releases), then, once and for all:

1. **PackFile → Settings.** *Game Paths*: the *Game Folder* (the folder of the game's executable) and the *Assembly Kit Folder*. *Extra Paths*: the *MyMod's Folder*, **outside Program Files**, where Windows protects writing. *Default Game*: Warhammer 3.
2. **About → Check Updates**, then, in the Update Manager, the button of the *Schema Updates* line: the schema tells RPFM how to read each table of the current patch (you can also tick *Check Schema Updates on Start* in the settings).
3. **Game Selected → Generate Dependencies Cache**, a few minutes: RPFM prepares the game's data for its searches and checks. Without the Assembly Kit set up, it warns that its diagnostics will give false positives.

*Diagram: Setting up RPFM: three moves.*

In the diagram: Setting up RPFM: three moves · PackFile → Settings · Settings · Game Paths · Warhammer 3 · `Game Folder` · `<game>` · `Assembly Kit Folder` · `<Assembly Kit>` · Extra Paths · `MyMod's Folder` · `<MyMod>` · not in Program Files: Windows protects writing there · `Default Game` · `Warhammer 3` · About → Check Updates · Update Manager · `Program Updates:` · `Schema Updates:` · `Lua Autogen Updates:` · `Old AK Updates:` · also, in Settings: tick Check Schema Updates on Start · Game Selected → Generate Dependencies Cache · the game's data · the cache · a few minutes; for its searches and checks · Without the Assembly Kit set up, RPFM warns: false positives in the diagnostics. · After a game update: do 2 and 3 again.

Schematic, a common layout. Three settings to make once, in order: the folders (1), the table schemas (2), the dependencies cache (3); after a game update, do 2 and 3 again.

> **The manual, the window and older tutorials**: The [RPFM manual](https://frodo45127.github.io/rpfm/manual/) describes 5.0.6, but not always with the window's labels: it writes "Pack" for the **PackFile** menu and, on one page, "Edit → Preferences" for **PackFile → Settings**. Older tutorials (RPFM 2 to 4) write "PackFile → Preferences" and "Special Stuff → …": that menu is gone; its actions (Build Startpos, Optimize PackFile…) are on the **pack root's right-click menu**. Some of RPFM's own messages still mention "Special Stuff" for the cache: it is **Game Selected → Generate Dependencies Cache**. When in doubt, the **palette** (Ctrl+Shift+P) finds any action by name.

## The basic moves

RPFM 5.0.6's window, area by area:

*Diagram: RPFM 5.0.6's window and its nine areas.*

1. **The menus**: PackFile, MyMod, View, Game Selected, Tools, About. Settings: PackFile → Settings (Ctrl+,); schemas: About → Check Updates; the cache: Game Selected → Generate Dependencies Cache.
2. **The tabs**: one per open file.
3. **Pack Contents**: the pack's tree; its first line, the root, bears the pack's name.
4. **The root's right-click menu**: Save Pack As…, Save Pack For Release…, Install, Uninstall, Change PackFile Type, Optimize PackFile, Build Startpos (and other actions): what concerns the whole pack.
5. **Dependencies**: the files of the game, of parent mods and of the Assembly Kit, read-only; right-click → Import brings one into your pack.
6. **The open table**: your row, highlighted; in red, an invalid reference: no land_units row bears this key yet. The atlas_main_units file has no @: it adds a new key.
7. **Diagnostics**: Check PackFile reads the pack through; each line has a severity: Error, Warning or Info. Aim for zero Error.
8. **Global Search**: hidden at first: View → Toggle Global Search Window (Ctrl+Shift+F); sources Game Files or Assembly Kit Tables.
9. **The status bar**: short messages from the last operation.

Schematic, usual layout. Settings, schemas and the cache are in the menus (1); what concerns the whole pack is on its root's right-click menu (4). Any action can also be found by name in the palette: Ctrl+Shift+P. The Special Stuff and Edit → Preferences menus of older tutorials do not exist in 5.0.6.

### Creating your mod

**MyMod → New MyMod.** Pick the game (Warhammer 3) and a name with underscores, no spaces and no `.pack` (`author_description`). RPFM creates the pack `<MyMod>\warhammer_3\<name>.pack` and, next to it, the folder `<MyMod>\warhammer_3\<name>\` for your working files (plus a Git repository or a VSCode project if you tick them), then opens the pack. The MyMod folder must be set first, or RPFM refuses.

### Finding game data

**View → Toggle Global Search Window** (Ctrl+Shift+F): *Game Files* source (or *Assembly Kit Tables*), *DB* type (*Loc* for texts), then the key, underscores included; a click opens the table at the right row, read-only. Other ways: **PackFile → Load All CA Packs** (read-only; greyed out when the dependencies cache is loaded), or **View → Toggle Dependencies Window**, right-click → *Import*, which brings a game file into your pack: rename a table brought in this way at once (F2), as under the name `data__` it would replace the game's.

### Creating a table

Right-click the pack → **Create… → Create DB**; pick the table (`land_units_tables`) and name the file with your prefix: `db/land_units_tables/atlas_land_units`. The name matters: when your file and the game's (`data__`) give the same key, the one whose name sorts first wins. To **add** rows (new keys), any name of your own will do; to **modify** a game row (same key), start the name with `@` or `!`.

- **Never rename or move a `_tables` folder** ("NEVER RENAME/MOVE THE TABLE FOLDERS", RPFM writes): the folder is what tells the game which table this is.
- **No final digit and no space in a table file name**: according to RPFM, a final digit makes the mod crash for everyone except its author, and a space sometimes keeps the table from loading.

### Starting from a game row

1. In the game's table, select the model row (click its number): **Copy… → Copy**.
2. In your table: **Paste as New Row** (Ctrl+Shift+V). To **add** a row, **change the key first**: otherwise your row adds nothing, it competes with the game's row.
3. To duplicate one of your rows: **Clone… → Clone and Append**.

### Following a key

Right-click a cell → **Go To… → Go To Definition** (the row cited), **Go To Loc Entry** (its text) or **Find References** (everything that cites this key); **Rename References** renames a key everywhere (the manual calls it *Cascade Edition*). This is how you take a unit or a lord apart, table after table.

*Diagram: Following a key.*

In the diagram: Following a key · `atlas_chevaliers` · right-click a cell · `Go To…` · `Find References` · `Rename References` · `Go To Definition` · `Go To Loc Entry` · `building_units_allowed · unit` · `units_custom_battle_permissions · unit` · `…` · `Find References` · what cites it · `main_units` · `unit` · `atlas_chevaliers` · `land_unit` · `atlas_chevaliers` · `Go To Definition` · the row it cites · `land_units` · `key` · `atlas_chevaliers` · `Go To Loc Entry` · its text · `text/db/atlas.loc` · `land_units_onscreen_name_atlas_chevaliers` · `text` · the name shown · Rename References: renames the key everywhere · the manual calls it Cascade Edition

Schematic: right-clicking a cell gives four moves that follow a key from table to table: the row it cites (1), its text (2), everything that cites it (3); Rename References renames it everywhere (4). Each cell bears the number of the move that starts from it.

### Writing texts

1. Right-click the pack → **Create… → Create Loc**, for example `text/db/atlas.loc`.
2. Three columns: *key* (the key), *text* (the displayed text) and *tooltip*, whose role is unknown according to RPFM's manual: do as CA's rows do.
3. The key has the form `<table>_<column>_<row key>`. Example: the name of unit `atlas_chevaliers` (column `onscreen_name` of `land_units`) has the key `land_units_onscreen_name_atlas_chevaliers`.
4. Right-click the pack → **Generate Loc Data** produces two files. `zzz_…`: the keys your tables are missing; rename it or copy its lines into your `.loc`, then fill them in. `aaa_…`: the game texts your tables cite; keep only what you change. Publish neither under its original name: RPFM rewrites them at every generation, and the optimiser removes `aaa_…` (and `zzz_…` if it is empty).

*Diagram: A text key, and Generate Loc Data.*

In the diagram: A text key · `land_units` · the table · `+` · `_onscreen_name_` · the column · `+` · `atlas_chevaliers` · the row's key · `land_units_onscreen_name_atlas_chevaliers` · in your .loc: the unit's name · Right-click the pack → Generate Loc Data · `zzz_…` · your keys with no text: rename it, or copy its rows into your .loc, then fill them in · `aaa_…` · game texts your tables cite: keep only what you change · Neither one under its original name: RPFM rewrites them at every generation; the optimiser removes aaa_… (and zzz_… if it is empty).

A text key follows the form <table>_<column>_<row key>. Generate Loc Data makes two files: zzz_… for your keys with no text, aaa_… for the game texts your tables cite.

## Checking, testing, publishing

### The diagnostics

**View → Toggle Diagnostics Window → Check PackFile**, before every test: a fault caught here saves a pointless game launch. Each line has a severity: *Error* (a problem in game), *Warning* (a likely problem), *Info* (worth knowing); aim for zero *Error*. The figure follows a mod from the diagnostics to the game.

*Diagram: Checking a mod, from diagnostics to the game.*

In the diagram: From diagnostics to the game · Check PackFile · in the Diagnostics panel · Error, Warning, Info: aim for zero Error. · Ctrl+S · saves the open packs. · Right-click the root → Install · RPFM copies the pack into the game's data folder. · Tick the mod · in the launcher, Kaedrin's Mod Manager or Runcher. · Missing from the list: right-click the root → Change · PackFile Type → Mod, and Index Includes Timestamp unticked. · Try it in game · a unit: custom battle; a faction, a lord: new campaign. · The game crashes at launch: back to 1 · `Invalid reference, Value Cannot be Empty,` · `Banned Table` · A script does nothing: read its log · `script/enable_console_logging →` · `script_log_*.txt` · The game closes at every launch: a leftover line · `user.script.txt:` · `quit_after_campaign_processing;`

Five steps, in order: every test in game starts with Check PackFile. If the game crashes, a script stays silent or the game keeps closing, the red exit tells you where to look.

RPFM checks:

- **tables**: outdated version; invalid reference, one of the leading causes of crashes on launch according to RPFM; empty or duplicate key; empty row; empty mandatory field, which may crash the game (RPFM), often without an error message (Casualfield); file name ending in a digit or containing a space; table overwriting the game's ("datacoring"); banned table (*Banned Table*), which the game checks and which crashes it if altered;
- **files**: path cited but not found, missing text;
- **portraits**: undeclared art sets and variants;
- **configuration**: dependencies cache missing or outdated.

### Trying it in game

1. Ctrl+S (saves the open packs), then **right-click the pack's root** (the first line of the tree, bearing its name) **→ Install**: RPFM copies the pack into `<game>\data\`.
2. Tick the mod in Creative Assembly's launcher, or in a third-party manager (Kaedrin's Mod Manager, Runcher).
3. Same menu **→ Uninstall**: removes the copy.

> **Our project**: You don't need this for an ordinary mod: the launcher is enough. To play, you tick the mod in the launcher; to generate a startpos by hand, one `mod <pack>;` line per pack in `user.script.txt` (`%APPDATA%\The Creative Assembly\Warhammer3\scripts\user.script.txt`). That is how our project generates its startpos from two packs: the mod, and a pack kept for the start tables, whose IDs start at 2,140,790,001. A `quit_after_campaign_processing;` line left in that file after a generation closes the game on every launch. The recipe: [the tools guide](https://bretonia.dev/atelier/en/tools/).

### Publishing

Keep your working pack: copy it first (or *Save Pack As…* under another name), because **Save Pack For Release…** saves the optimised version in place of the open pack. Right-click the pack's root → **Save Pack For Release…**: the optimiser opens, then the pack is saved; **Optimize PackFile**, in the same menu, does the same work without saving, so you can review first. The optimiser only does what you tick:

- remove files and rows identical to the game, duplicates, empty files;
- compress the pack;
- remove unused art sets and variants from portraits (unticked by default);
- for Warhammer III, *Import datacores into twad_key_deletes*: it writes into a `twad_key_deletes` table the rows a copied game table removed. Check that table, rename it (RPFM would rewrite it on the next run), then delete the copied table yourself.

*Diagram: Publishing without breaking anything.*

In the diagram: Publishing without breaking anything · A copy first · Working pack · kept as it is · Its copy · the one you publish · Save Pack As…, under another name, also makes a copy. · In the copy: Save Pack For Release…, on the root's right-click menu · `atlas_chevaliers.pack` · `Save Pack As…` · `Save Pack For Release…` · `Optimize PackFile` · Optimize PackFile, same menu: the same work without saving, to review first. · It saves the optimised version in place of the open pack. · The optimiser only does what is ticked · Identical to the game, duplicates, empty files · ticked by default · Compress the pack · ticked by default · Unused art sets and variants of portraits · unticked by default · `Import datacores into twad_key_deletes` · unticked by default; tick it if your pack holds a copied table (Warhammer III) · After the datacores option · `twad_key_deletes` · `table_name · key` · check it, then rename it: RPFM would rewrite it · `db/land_units_tables/data__` · the copied game table: delete it yourself

Schematic. Save Pack For Release… replaces the open pack with its optimised version: work on a copy. The optimiser only does what is ticked (here, its default settings); its datacores option writes the removed rows into twad_key_deletes, and deleting the copied table afterwards is up to you.

### After a game update

1. **About → Check Updates**, schemas button.
2. **Game Selected → Generate Dependencies Cache**: otherwise RPFM compares your tables with an outdated game.
3. On every table flagged *Outdated table*: right-click → **Update Table**.
4. Read through the new columns: RPFM puts default values in them, not necessarily the right ones for your rows.

With 9.0, RPFM's schemas came out on the day of the update. **If you also write to the Assembly Kit**: Steam updates it after the game (for us, an hour later) and rewrites `raw_data\db`; the rows you had added there disappear. Check and restore them before regenerating the dependencies cache and before building anything.

## Pack rules: who wins over whom

The game reads **every fragment** of a table, its own and every mod's: as long as the keys differ, the rows add up. When two fragments give **the same key**, the one whose name sorts first wins; and a fragment carrying the exact path of a game file replaces it entirely.

The figure below shows who wins when several fragments of the same table give the same key.

*Diagram: Who wins when two fragments give the same key.*

In the diagram: The fragments of a table · `db/land_units_tables/` · `! # $ % & ' ( ) + , - ; = @ 0-9 a-z [ ] ^ _ ` { } ~` · read in name order, first to last (wiki, Submodding page) · `@my_mod` · `wh_dlc07_brt_cav_knights_errant_0 · value A` · your mod, sorted before the game · `data__` · `wh_dlc07_brt_cav_knights_errant_0 · value B` · the game's table · `zmy_mod` · loses · `wh_dlc07_brt_cav_knights_errant_0 · value C` · your mod, sorted after the game: the game's row wins, your value is ignored · The game keeps value A: the first name in sort order wins. · `data__ · in your mod` · Same path as the game's file, so it replaces it entirely: a datacore. CA's updates to this table no longer get through; two datacores of the same table exclude each other. · Removing a game row · `twad_key_deletes:` · `table_name = land_units · key = <key>` · right-click the game's rows · → Add Selection to Key Deletes · Warhammer III, since patch 6.3

Three fragments of one table give the same key: the game keeps the one whose name sorts first; the others are ignored. A z name is for new rows that other mods should be able to correct (RPFM manual, Datacores page). A fragment named like the game's file replaces it entirely; to remove a game row, a twad_key_deletes table.

Example: the game's battle units table is the file `db/land_units_tables/data__`. According to RPFM's manual, `@my_mod` comes before it, `zmy_mod` after it, and a fragment named `data__` replaces it entirely: that is a "datacore", which the diagnostics flag ("Leaving them named data__ will overwrite the base game files", Casualfield warns).

### Four rules that prevent the worst surprises

- **Names and keys of your own**: your rows add up without replacing anything, and other campaigns and other mods stay untouched.
- **Don't redefine a game text.** The game loads a mod's `.loc` files whatever the player's language: a repeated game key replaces the official text in every language. Only add new keys. For a second language, a second pack: it holds only a `.loc` that repeats your keys, under a name starting with `!` so it takes priority over yours (wiki, *Submodding* page).
- **To remove a game row, don't copy its table** (a datacore): a fragment removes nothing from the others. Since patch 6.3, Warhammer III reads a `twad_key_deletes` table (columns `table_name` and `key`) that removes the named rows at launch. In RPFM: create `twad_key_deletes_tables` once in your pack (Create DB), open the game's table, select the rows, right-click → **Add Selection to Key Deletes** → your table. Beware: removing a row still cited by another table crashes the game, and the diagnostics do not check this yet. Only two tables also have a `mod_disabled` box (`character_skill_node_set_items`, `faction_agent_permitted_subtypes`), which CA's schema presents as the means given to modders since they could not remove rows.
- **In the `data` folder, a pack wins over loose files** (seen in our workshop): a regenerated file (a startpos, for example) only counts once it is back in the pack.

*Diagram: Your texts, in every language.*

In the diagram: Your texts, in every language · The game loads your .loc files whatever the player's language. · A new key · `land_units_onscreen_name_atlas_chevaliers` · your text, in every language · A game key reused · `factions_screen_name_wh_main_brt_bordeleaux` · replaces the official text, in every language · For a second language: a second pack · in text/db/, read in name order: `!atlas_de.loc` · example: the translation pack, your keys translated; sorted first, it wins · `atlas.loc` · your mod

A mod's .loc applies to every language: put only new keys in it. For a second language, a second pack takes up your keys in a .loc whose name starts with ! (wiki, Submodding page).

## Your first mod, in ten minutes

Before the three procedures, a short test that also checks your setup: making a game unit dearer in custom battles. The wiki's *RPFM For Dummies* tutorial starts the same way, with a table edit found through the global search.

*Diagram: A first mod in ten minutes.*

In the diagram: A first mod in ten minutes · MyMod → New MyMod · Warhammer 3; RPFM creates the pack, then opens it. · `my_first_mod.pack` · View → Toggle Global Search Window · Ctrl+Shift+F; Game Files, DB, then the key: `wh_dlc07_brt_cav_knights_errant_0` · `main_units_tables` · the game's table, read-only · Copy… → Copy · click the row's number, then right-click · Create… → Create DB · right-click your pack: table main_units_tables, name · @my_main_units · both files, read in name order: `@my_main_units` · yours, sorted first: it wins · `data__` · the game's · Paste as New Row · Ctrl+Shift+V · same key: you modify the game's unit · `multiplayer_cost` · `700` · `999` · its price in custom battles; 700 in CA's data · Diagnostics panel → Check PackFile · no Error line · Ctrl+S, then right-click the root → Install · tick the mod in the launcher · In a custom battle, Bretonnia's Knights Errant cost · 999.

Seven moves, from the game's table to the test in game: your @my_main_units file sorts before the game's (data__), so its row wins, and the Knights Errant go from 700 to 999 in custom battles. To add a unit instead of modifying one, you change the key: that is procedure 1.

1. **MyMod → New MyMod**: game Warhammer 3, name `my_first_mod`.
2. **View → Toggle Global Search Window** (Ctrl+Shift+F): *Game Files* source, *DB* type; search for `wh_dlc07_brt_cav_knights_errant_0`, then click the `main_units_tables` result: the game's table opens at the right row, read-only.
3. Click the row's number, then right-click → **Copy… → Copy**.
4. Right-click your pack → **Create… → Create DB**: table `main_units_tables`, name `@my_main_units`. The `@` makes your file sort before the game's (`data__`): so yours wins.
5. Right-click in the empty table → **Paste as New Row** (Ctrl+Shift+V). Keep the key: here, you **modify** the game's unit. Set `multiplayer_cost` (its price in custom battles; 700 in CA's data) to 999.
6. Diagnostics panel → **Check PackFile**: no *Error* line.
7. Ctrl+S, then right-click the pack's root → **Install**; tick the mod in the launcher. In a custom battle, Bretonnia's Knights Errant cost 999.

To **add** a unit instead of modifying one, you change the key: that is procedure 1.

## Procedure 1: adding a unit

The safe way: copy a game unit that looks like yours, then change what must change. Example followed: Bretonnia's Knights Errant, `wh_dlc07_brt_cav_knights_errant_0`.

### Before you start: the model unit

- A model unit **with the same skeleton and the same animations** as yours: otherwise, it renders deformed in game.
- A **key prefix** of your own, so you never compete with the game or another mod.

The next figure shows a unit's tables and the columns that link them.

*Diagram: A unit's tables and their links.*

In the diagram: A unit · `main_units` · key: unit · `land_unit → land_units` · `costs, caste, ui_unit_group_land` · minimum · `land_units` · key: key (same as unit at CA) · stats, weapons · `man_entity → battle_entities` · `primary_melee_weapon → melee_weapons` · minimum · `unit_variants` · key: faction + unit · `unit → land_units` · `variant → variants` · `unit_card → ui/units/icons` · minimum · `ui/units/icons/<card>.png` · a 60 × 130 PNG image · minimum · `variants` · key: variant_name · only for a new model · `→ .variantmeshdefinition` · optional · `unit_description_short_texts` · `· unit_description_historical_texts` · a text of the unit's own · optional · `units_to_groupings_military_permissions` · keys: unit, military_group · `military_group = factions.military_group` · `(wh_main_group_bretonnia)` · campaign · `building_units_allowed` · key: key (a unique number) · `building → building_levels` · stables 2 to 5: one row each · campaign · `units_custom_battle_permissions` · key: unit + faction + general_unit · custom battle · `general_unit = 0` · minimum · `text/db/<mod>.loc` · `land_units_onscreen_name_<key>` · minimum

A unit's tables, as a tree: each card gives its key and links. Minimum: what custom battle needs; campaign: on top of that, to recruit it in the campaign; optional: depending on the unit. The unit card is an image (a 60 × 130 PNG), not a table.

### The unit's steps

1. **Global Search** for the model's key: it appears in 24 tables of the kit; the table below gives the ones you need.
2. For each: **Create DB** (a file with your prefix), **Paste as New Row**, then change what makes the unit and keep the rest (see "What changes, what stays"). Like CA, give `main_units.unit` and `land_units.key` the same key.
3. Set the stats, costs, appearance and card, then the recruitment, the custom battle and the texts.
4. Check: right-click your key → **Find References**. RPFM's manual advises this check after adding a unit: it shows whether the unit is wired up everywhere it should be (costs, recruitment, factions).
5. **Diagnostics**, **Install**, custom battle test.

| Table | What to set | Key | Links |
|---|---|---|---|
| `main_units` | the copy; costs, `caste`, `ui_unit_group_land` | `unit` | `land_unit` → `land_units.key` |
| `land_units` | the copy; stats, weapons | `key` | `man_entity` → `battle_entities`; `primary_melee_weapon` → `melee_weapons`; `short_description_text` → `unit_description_short_texts`; `historical_description_text` → `unit_description_historical_texts` |
| `unit_variants` | `name` = your key (as CA does), `faction` empty (= every faction), `variant`, `unit_card` | `faction` + `unit` | `unit` → `land_units.key`; `variant` → `variants.variant_name`; the card: `ui/units/icons/<unit_card>.png` |
| `variants` | only for a new model: `variant_filename` | `variant_name` | → `variantmeshes/variantmeshdefinitions/<name>.variantmeshdefinition` |
| `units_to_groupings_military_permissions` | the unit and the military group | both columns | `military_group` = `factions.military_group` (for example `wh_main_group_bretonnia`) |
| `building_units_allowed` | `building`, `unit`, `key` (a unique number), `faction` empty | `key` | `building` → `building_levels.level_name`; `unit` → `main_units` |
| `units_custom_battle_permissions` | the battle faction (CA uses `wh_main_brt_bretonnia`), `general_unit` = 0 | `unit` + `faction` + `general_unit` | → `main_units`, `factions` |
| `unit_description_short_texts`, `unit_description_historical_texts` | only for a text specific to the unit | `key` | cited by `land_units` |

*Diagram: Copying a unit: what changes, what stays.*

In the diagram: Copying a CA unit · CA's key · `wh_dlc07_brt_cav_knights_errant_0` · your key · `atlas_chevaliers` · In green, what changes; “kept”, CA's value. · Your files: `db/<table>_tables/atlas_…` · no @: you are adding · `main_units` · `unit` · CA's key · `→` · `atlas_chevaliers` · `land_unit` · CA's key · `→` · `atlas_chevaliers` · `caste` · `melee_cavalry` · kept · `land_units` · `key` · CA's key · `→` · `atlas_chevaliers` · `melee_attack` · `28` · kept · `unit_variants` · `name` · CA's key · `→` · `atlas_chevaliers` · `faction` · `(empty)` · kept · `unit` · CA's key · `→` · `atlas_chevaliers` · `variant` · `wh_dlc07_brt_knights_errant` · kept · `unit_card` · `wh_dlc07_brt_knights_errant` · kept · name: not in the key; CA puts the unit's key there, and unit_variants_colours cites it. · `units_to_groupings_military_permissions` · `unit` · CA's key · `→` · `atlas_chevaliers` · `military_group` · `wh_main_group_bretonnia` · kept · `building_units_allowed` · `key` · `→` · `4 new numbers` · `unit` · CA's key · `→` · `atlas_chevaliers` · `building` · `wh_main_brt_stables_2 … _5` · kept · at CA: 60317, 60343, 63497, 63640, one row per stable; and two Copher ports. · the numbers: right-click → Generate Ids. · `units_custom_battle_permissions` · `unit` · CA's key · `→` · `atlas_chevaliers` · `faction` · `wh_main_brt_bretonnia` · kept · `general_unit` · `0` · kept · `text/db/atlas.loc` · a file of yours · `key` · `land_units_onscreen_name_atlas_chevaliers` · `text` · the name shown · A CA key left behind: your row competes with CA's.

The guide's example, filled in with CA's Knights Errant rows (Assembly Kit, patch 8.1): your key wherever CA's appears (unit_variants' name included) and new numbers in building_units_allowed, in green; the rest kept for a first try, in grey. Check: right-click your key → Find References.

### What changes, what stays

- **To change**: the new key `atlas_chevaliers` wherever CA's appears (`main_units.unit`, `main_units.land_unit`, `land_units.key`, `unit_variants.unit` and `unit_variants.name`, the `unit` columns of the permissions and buildings), and a **new number** in `building_units_allowed.key` (right-click → **Generate Ids**, from a starting number of your own, below 2³¹). The key of `unit_variants` spans two columns (`faction` + `unit`): `unit` is the one that changes; `name` is not part of the key, but CA puts the unit's key in it and `unit_variants_colours` refers to it: change it too.
- **To keep for a first test**: CA's `variant` and `unit_card` (`wh_dlc07_brt_knights_errant`: same model, same card), the military group (`wh_main_group_bretonnia`), the battle faction (`wh_main_brt_bretonnia`, with `general_unit` = 0) and the four stables (`wh_main_brt_stables_2` to `wh_main_brt_stables_5`).
- **A CA key forgotten** in one of your rows: that row competes with CA's instead of adding up.

RPFM also has a **Unit Editor**, experimental according to its manual; this guide follows the tables route.

### The unit's optional tables

According to Casualfield's guide, depending on what the unit must do:

- `land_units_to_unit_abilites_junctions`, for its abilities (the misspelling is CA's);
- `unit_attributes_groups` and `unit_attributes_to_groups_junctions`;
- `unit_set_to_unit_junctions`;
- `ui_unit_bullet_point_unit_overrides`;
- `allied_recruitment_unit_permissions`;
- `armed_citizenry_unit_groups` and `building_level_armed_citizenry_junctions`, for garrisons;
- `cdir_military_generator_unit_qualities`, for the AI;
- `units_to_exclusive_faction_permissions`;
- `faction_rebellion_units_junctions`, for rebel armies: CA's schema asks for the unit of `units_to_groupings_military_permissions` to be repeated there, Casualfield calls it optional.

### The unit's texts

- `land_units_onscreen_name_<key>`: the name (Casualfield gives `unit_description_historical_texts_UNITKEY`, but the kit's schema stores the name in `land_units.onscreen_name`);
- `unit_description_short_texts_text_<short key>` and `unit_description_historical_texts_text_<long key>`, if the unit has its own descriptions;
- `land_units_concealed_name_<key>`, optional.

### The unit's minimum viable set

- **In custom battles**: `main_units`, `land_units`, `unit_variants`, the card (a 60 × 130 PNG according to the wiki's *Unit Card Guide*; not interlaced like CA's, read on patch 8.1), `units_custom_battle_permissions` and the name in a `.loc` file.
- **In the campaign**, as well: `units_to_groupings_military_permissions` and `building_units_allowed`.

### The unit's pitfalls

- **Every building level.** CA lists the unit at every level that allows it, not just the one that unlocks it: its Knights Errant appear on the level 2, 3, 4 and 5 stables.
- **The `enabled` column of `building_units_allowed`** only forbids recruitment when three conditions are met: the building-unit pair exists elsewhere, `faction` is filled in, the box is unticked.
- **`units_custom_battle_permissions` also acts in the campaign** (wiki, *Units custom battle permissions tables* page): a hero's card in the building browser comes from it, and the preview of the units affected by an effect takes it into account.
- **An empty mandatory field, a file named `data__`**: see the diagnostics and the pack rules.

### What the unit needs from the startpos

Nothing, except to exist from turn 0 (`start_pos_land_units`, `start_pos_settlement_garrisons`, mercenary pools); even then, a script spares you the startpos: `cm:create_force_with_general` places an army, `cm:add_unit_to_faction_mercenary_pool` stocks a pool.

### Checking that the unit works

1. Diagnostics: no invalid reference, no missing text.
2. Custom battle with the chosen battle faction: the unit is in the list, with its card and its name; in battle, the model is whole.
3. New campaign: the unit can be recruited at every building level listed.

## Procedure 2: adding a faction

> **First of all: which campaign?**: Each campaign has only one start file, and a mod cannot add to it: it replaces it entirely. Two mods that touch the same campaign's startpos therefore do not combine ("there can be only one startpos", says the [modding wiki](https://tw-modding.com/wiki/Startpos)). **Immortal Empires or Realm of Chaos: don't write your own startpos.** Most players already use the one from [MIXER – Mixu's Unlocker](https://steamcommunity.com/sharedfiles/filedetails/?id=2859968660), which makes every faction of those two campaigns playable and allows no other startpos mod; the wiki advises asking Mixu to add your faction, then doing the rest by script (for example with [Campaign Custom Starts](https://chadvandy.github.io/tw_modding_resources/WH3/campaign/custom_starts.html)). A campaign of your own, like ours: follow this procedure, accepting the incompatibility with any other startpos mod for that campaign.

A faction fits in a few tables, but it only exists on the map through the startpos.

### Before you start: what to gather

A target campaign, an existing culture and subculture, the flags, and time to generate a startpos.

The figure shows a faction's tables, and the path from the start tables to the startpos.

*Diagram: A faction's tables and their links.*

In the diagram: A faction · `factions` · key: key · `subculture → cultures_subcultures` · `name_group → names_groups` · `military_group → groupings_military` · `flags_path = ui\flags\<key>` · `ui/flags/<key>/` · `mon_24.png · mon_64.png · mon_256.png…` · the flags: images to supply · `faction_agent_permitted_subtypes` · key: faction + agent + subtype · `subtype → agent_subtypes` · the lords and heroes allowed · `text/db/<mod>.loc` · `factions_screen_name_<key>` · `factions_screen_adjective_<key>` · `factions_screen_name_when_rebels_<key>` · `factions_attack_desc_<key>` · `factions_defend_desc_<key>` · `start_pos_factions` · key: ID (a new integer) · `faction, campaign, playable, is_major` · `cai_personality_group` · required · `start_pos_regions` · key: region + campaign · `owning_faction → start_pos_factions.ID` · `start_pos_characters` · key: ID · `faction → start_pos_factions.ID` · `Type = general` · `ministerial_position = faction_leader` · `start_pos_land_units` · key: id · `general → start_pos_characters.ID` · `unit_type → main_units` · Then the startpos · `start_pos_*` · your tables · the game builds it · `startpos.esf` · in the pack · One startpos per campaign; Immortal Empires and Realm of · Chaos: Mixer.

A faction: its factions row, its flags, its allowed lords, its texts and its start_pos_* tables. The game builds the startpos from them, only one per campaign, which your mod replaces entirely: for Immortal Empires and Realm of Chaos, the community goes through Mixer.

### The faction's steps

1. **`factions`**: copy a neighbouring faction (`wh_main_brt_bordeleaux`) under a new key; set `subculture`, `name_group`, `military_group`, `flags_path` = `ui\flags\<key>` and the colours (or **Tools → Faction Painter**).
2. **The flags**: a `ui/flags/<key>/` folder like CA's: `mon_24.png`, `mon_64.png`, `mon_256.png` (24, 64 and 256 pixels), `mon_64_glow.png`, `mon_icon.png`, `mon_rotated.png`, `mon_banner.dds` (CA's own file names, read on patch 8.1). Which ones are essential is not confirmed: supply the full set.
3. **`faction_agent_permitted_subtypes`**: the model's rows, which say which lords and heroes the faction can have (Bordeleaux has 12: general, colonel, minister, champion, wizard).
4. **The texts**, then **the start tables** of the table below, in the kit (Dave) or in the pack (RPFM), plus `start_pos_character_to_settlements` for a garrisoned leader and `start_pos_diplomacy`.
5. **The startpos**, regenerated (see "What needs a new startpos"), stored in `campaigns/<campaign>/startpos.esf` in the pack; then **Diagnostics**, **Install** (except for a pack opened from the `data` folder, which Install refuses), new campaign.

| Table | What to set | Key | Links |
|---|---|---|---|
| `factions` | the faction | `key` | `subculture` → `cultures_subcultures`; `name_group` → `names_groups`; `military_group` → `groupings_military` |
| `faction_agent_permitted_subtypes` | the lords and heroes allowed | `faction` + `agent` + `subtype` | → `agent_subtypes` |
| `start_pos_factions` | `ID` (a new integer), `faction`, `campaign`, `playable`, `is_major`, `cai_personality_group` | `ID` | → `factions`, `campaigns` |
| `start_pos_regions` | `owning_faction` = that `ID`, `faction_capital` | `region` + `campaign` | → `start_pos_factions.ID` |
| `start_pos_characters` | the leader: `Type` = general, `subtype`, `ministerial_position` = faction_leader, `portrait_id`, `startx` and `starty`, `Name` and `Surname` | `ID` | `faction` → `start_pos_factions.ID`; `Name` → `names.id`; `portrait_id` → `campaign_character_art_sets` |
| `start_pos_land_units` | the army: `unit_type`, `general`, `soldiers` | `id` | → `main_units`; → `start_pos_characters.ID` |

*Diagram: A faction's flags.*

In the diagram: A faction's flags · `factions` · `flags_path` · `ui\flags\<key>` · `ui/flags/<key>/` · `mon_24.png` · 24 px · `mon_64.png` · 64 px · `mon_256.png` · 256 px · Four more files: `mon_64_glow.png` · 100 × 100 · `mon_icon.png` · 256 × 256 · `mon_rotated.png` · 112 × 90 · `mon_banner.dds` · 256 × 256 (DXT5) · “mon” is CA's own file name; names and sizes read from the game's files (patch 8.1) · ? · Which ones are essential: not confirmed. · Supply the full set.

A faction's ui/flags/<key>/ folder, like CA's: three sizes (24, 64 and 256 pixels) and four more files, names and sizes read from the game's files (patch 8.1), “mon” included. Which ones are essential is not confirmed: supply the full set.

### The faction's texts

`factions_screen_name_<key>`, `factions_screen_adjective_<key>`, `factions_screen_name_when_rebels_<key>`, `factions_attack_desc_<key>` and `factions_defend_desc_<key>`; the leader's name comes from `names_name_<id>`.

### The faction's minimum viable set

`factions`, the flags, `faction_agent_permitted_subtypes`, the texts, `start_pos_factions`, a region or a leader with his army, and the regenerated startpos.

### The faction's pitfalls

- **The AI group.** Every faction must have an AI group (`cai_personality_group`) containing a personality, or the game crashes. In our workshop: the group the faction has in Immortal Empires, failing that the one of its culture's minor factions.
- **The start tables' IDs**: unique integers, below 2³¹ (the limit of a signed 32-bit integer), that no CA row already uses (CA's are scattered up to nearly 2³¹, patch 8.1).
- **Keys change from one game to another** (`wh_dlc05_brt_mini_bastonne` in Warhammer I, `wh_main_brt_bastonne` in Warhammer III): when porting data, match them on the culture and the end of the key ([the porting tutorial](https://bretonia.dev/blog/en/tutorial-porting-a-warhammer-1-map-to-warhammer-3.html)).

### What the faction needs from the startpos

Everything that makes it exist: the wiki lists playable factions, like those that appear during a game, among what needs a startpos. Once the faction is in the startpos, scripts take over: `cm:transfer_region_to_faction(region, faction)`, `cm:create_force_with_general(...)`, or CA's [Campaign Custom Starts](https://chadvandy.github.io/tw_modding_resources/WH3/campaign/custom_starts.html) system (regions, armies, diplomacy, effects).

### Checking that the faction works

1. Clean diagnostics; `campaigns/<campaign>/startpos.esf` is really in the pack, dated from the generation.
2. Start tables served through a mod pack: `%APPDATA%\The Creative Assembly\Warhammer3\crash_report\bad_mods_report.txt` names the faulty record, one at a time.
3. New campaign: the faction is on the map, in its region, with its flag and its name; its leader leads his army.
4. If playable, it can be picked on the selection screen once procedure 3 is done for its leader.

## Procedure 3: adding a legendary or playable lord

Three stages: **A**, the lord exists in the database; **B**, a script makes him appear in an existing faction; **C**, he can be picked on the "New Campaign" screen, and only this stage needs a startpos. Prerequisites: his battle unit (procedure 1), his model, his portrait.

*Diagram: A lord in three stages.*

In the diagram: A lord in three stages · No startpos · A · The database · `agent_subtypes` · `units_custom_battle_permissions` · and the look, the skills, the texts; general_unit = 1: for custom battles · He can be picked in a custom battle. · B · A script · ? · `cm:spawn_character_to_pool` · for a faction that already exists · to be tested: unproven in Warhammer III · New game: among the lords to recruit. · With a regenerated startpos · C · The “New Campaign” screen · `start_pos_characters` · `start_pos_starting_general_options` · `frontend_faction_leaders` · `startpos.esf` · and playable = 1 in start_pos_factions · His record shows on the selection screen.

Three stages: A, the lord exists in the database; B, a script makes him appear in a faction that already exists (to be tested); C, he can be picked on the “New Campaign” screen. Only stage C needs a regenerated startpos.

### Stage A: the lord in the database

1. **`agent_subtypes`**: copy a neighbouring lord (`wh_dlc07_brt_alberic`); `auto_generate` unticked for a unique character, `associated_unit_override` = his unit, `recruitment_category` = `legendary_lords`, `recruitable` = 1 and `cap` = `-1` (like Alberic). Leave `magic_lore` empty: filled in, with `legendary_lords`, it can hide the other legendary lords of the recruitment pool (wiki, *Agent subtypes tables* page).
2. **The appearance**: `campaign_character_art_sets` → `campaign_character_arts` (a unique `id`) → `agent_uniforms` → `variants`.
3. **The portrait**: Warhammer III reads it from `ui/portraits/portholes/portrait_settings*.bin`, arranged by art set (for Alberic, `portrait_settings__3.bin`, a 300 × 164 thumbnail: read on CA's files, patch 8.1). In RPFM: **Create… → Create Portrait Settings**, then its editor. In CA's rows, `campaign_character_arts.portrait` stays empty.
4. **The skills**: `character_skill_node_sets` (column `agent_subtype_key`), `character_skill_node_set_items` (`set`, `item`), `character_skill_nodes` (`tier` = the tree's column, `indent` = the row), `character_skills` then `character_skill_level_to_effects_junctions`, `character_skill_node_links` for the arrows.
5. **`faction_agent_permitted_subtypes`** (the faction, `general`, the subtype) and **`names`** (new `id` values for the forename and the surname). For custom battles: `units_custom_battle_permissions` with `general_unit` = 1, `general_portrait` and `general_uniform`.

*Diagram: Stage A: a lord's tables and their links.*

In the diagram: Stage A: the database · `agent_subtypes` · key: key · `associated_unit_override → main_units` · `recruitment_category = legendary_lords` · auto_generate unticked: a unique character · `campaign_character_art_sets` · key: art_set_id · `agent_subtype → agent_subtypes` · `campaign_character_arts` · key: id · `uniform → agent_uniforms` · `agent_uniforms` · key: uniform_name · `filename, battle_filename → variants` · `ui/portraits/portholes/portrait_settings*.bin` · portrait settings, per art set · `character_skill_node_sets` · key: key · `agent_subtype_key → agent_subtypes` · `character_skill_node_set_items` · key: set + item · `item → character_skill_nodes` · `character_skill_nodes` · key: key · tier: the column (below 48); indent: the row · `character_skill_key → character_skills` · `character_skills` · its effects: `character_skill_level_to_effects_junctions` · `faction_agent_permitted_subtypes` · key: faction + agent + subtype · `agent = general` · the faction may have him · `names` · key: id · new ids for the first and last name; their texts: `names_name_<id>`

Stage A: the lord's tables as a tree, from his subtype to his look, his portrait, his skill tree, his faction and his name.

| Table | What to set | Key | Links |
|---|---|---|---|
| `agent_subtypes` | the subtype | `key` | `associated_unit_override` → `main_units` |
| `campaign_character_art_sets` | the art set | `art_set_id` | `agent_subtype` → `agent_subtypes` |
| `campaign_character_arts` | the appearance, per level | `id` | `uniform` → `agent_uniforms` |
| `agent_uniforms` | the campaign map and battle models | `uniform_name` | `filename`, `battle_filename` → `variants` |
| `character_skill_node_sets` | the skill tree | `key` | `agent_subtype_key` → `agent_subtypes` |
| `character_skill_node_set_items` | the tree's nodes | `set` + `item` | → `character_skill_nodes` |
| `character_skill_nodes` | a skill's position | `key` | `character_skill_key` → `character_skills` |
| `frontend_faction_leaders` | the selection screen record | `key` (free) | `agent_subtype_record`, `faction` |
| `start_pos_starting_general_options` | the link from the character to his record | `id` | `general` → `start_pos_characters.ID` |

*Diagram: The skill tree: a grid.*

In the diagram: The skill tree: a grid · `tier` · → the column · `0` · `1` · `2` · `3` · `4` · `indent` · the row ↓ · `0` · `1` · `2` · This node: `tier = 2, indent = 1` · the arrows: `character_skill_node_links` · the lord's tree: `character_skill_node_set_items` · tier: below 48 (wiki); beyond that, the node does not work

In character_skill_nodes, tier gives a node's column and indent its row (Assembly Kit schema); tier stays below 48 (wiki). The arrows come from character_skill_node_links.

### The lord's texts

- `agent_subtypes_onscreen_name_override_<subtype>`: the type's label ("Legendary Lord" for Alberic); his proper name comes from `names_name_<id>`;
- `agent_subtypes_description_text_override_<subtype>`;
- `character_skills_localised_name_<key>`, `character_skills_localised_description_<key>` and `character_skill_node_sets_enc_title_<key>`;
- `frontend_faction_leaders_frontend_description_<record>` and `frontend_faction_leaders_localised_description_<record>`; according to CA, `localised_action_points` and `loading_screen_text` are no longer used.

### The lord's minimum viable set

`agent_subtypes`, the battle unit, the art set, arts, uniform and variant, `faction_agent_permitted_subtypes`, `names`, the texts, and a script call.

### Stage B: spawning him by script

Without a startpos, a script can add the lord to a faction **that already exists**. This skeleton follows the signatures in CA's scripts, **to be tested**:

```
-- skeleton, to be tested: a lord in an existing faction's pool
cm:add_first_tick_callback(function()
  if cm:is_new_game() then
    cm:spawn_character_to_pool("wh_main_brt_bordeleaux", "names_name_<id>", "names_name_<id>",
      "", "", 30, true, "general", "<subtype>", true, "<art set>")
  end
end)
```

*Diagram: One call, piece by piece: cm:spawn_character_to_pool.*

In the diagram: One call, piece by piece · `cm:spawn_character_to_pool(` · `"wh_main_brt_bordeleaux",` · The faction (it exists) · `"names_name_<id>",` · First name (names) · `"names_name_<id>",` · Surname (names) · `"",` · Clan: empty · `"",` · Other name: unused, empty · `30,` · Age · `true,` · Male · `"general",` · Agent type · `"<subtype>",` · Your subtype · `true,` · Immortal · `"<art set>"` · Its art set · `)` · ? · To be tested: skeleton unproven in Warhammer III. · cm:is_new_game(): the call only happens at the start of a new game

Stage B's call, argument by argument, in the order of CA's scripts: the faction, first name and surname (names_name_<id> keys), clan, other name, age, male, type, subtype, immortal, art set. To be tested.

- Parameter order of `cm:spawn_character_to_pool`: faction, forename, surname, clan, other name, age, male, type, subtype, immortal, art set. Names are written `names_name_<id>`.
- `cm:is_new_game()`: the call only happens at the start of a new game, not at every load. According to CA's documentation, a game is "new" if it has been saved only once, during startpos generation: if a script failed during that generation, a new game may pass for a saved one.
- With an army: `cm:create_force_with_general(faction, units, region, x, y, "general", subtype, forename, clan, surname, other name, faction_leader, callback)`, whose units are, according to CA's documentation, `land_units` keys (the same as the `main_units` keys in CA's data).
- The script goes into `script/campaign/mod/`, which the game loads by itself (tw-modding, *Lua/First Script*), under a unique, prefixed file name: two files at the same path in two packs do not both load.

### Stage C: picking him on the "New Campaign" screen

1. `start_pos_characters`: the character (see procedure 2).
2. `start_pos_starting_general_options`: `general` (the character) → `frontend_faction_leader` (his record). Without this table, no lord can be picked.
3. A `frontend_faction_leaders` record, with a free key: `character_image` (an 85 × 115 portrait, read on CA's at patch 8.1), `uniform` (the `.csc` scene), `video`, `loading_screen_image`, `agent_subtype_record`, `faction`.
4. `start_pos_factions.playable` = 1, then the regenerated startpos.

*Diagram: Stage C: from the startpos character to his record.*

In the diagram: Stage C: the selection screen · `start_pos_factions` · key: ID · `playable = 1` · `start_pos_characters` · key: ID · the character: see procedure 2 · `start_pos_starting_general_options` · key: id · without this table, no lord can be picked · `frontend_faction_leaders` · key: key (free) · `startpos_map = default` · `agent_subtype_record, faction` · `character_image (85 × 115)` · `loading_screen_image` · `override_force_location_x/y` · a fraction of the map; −1 = default · `faction` · his faction · `general` · the character · `frontend_faction_leader` · his record · Then a regenerated startpos: only this stage needs one.

Stage C: start_pos_starting_general_options links the startpos character to his frontend_faction_leaders record (startpos_map = default), then the startpos is regenerated.

### The lord's pitfalls

- **Warhammer II tutorials.** "Custom Lords & Heroes", on tw-modding, targets Warhammer II: check every table against Warhammer III's schema, where the link between a node and its tree goes through `character_skill_node_set_items`.
- **The `tier` column** stays below 48 (wiki, page on `character_skill_nodes`).
- **`startpos_map`**: CA mostly puts `default` or the record's own key there; for a new record, leave `default`. In our workshop, a new key in that field crashed the loading.
- **A garrisoned lord**: left as is, his marker stays in the corner of the selection screen's Map tab; fill in his record's `override_force_location_x` and `override_force_location_y` (wiki, *Startpos* page). They are fractions: the character's position in hexes, divided by the map's width for x and by its height for y, following the example in CA's schema; `-1` leaves the default position (in CA's data, Eltharion: 0.398 and 0.420).

*Diagram: The selection screen's marker.*

In the diagram: The selection screen's marker · the campaign map, in hexes · `0` · ↔ width · ↕ height · `x` · `y` · `override_force_location_x = x / width` · `override_force_location_y = y / height` · at CA, Eltharion: 0.398 and 0.420 · −1: the default position; a lord in a garrison, without this setting, stays in the corner of the Map tab.

override_force_location_x and _y place a lord's marker on the selection screen's map: his position in hexes, divided by the map's width for x and by its height for y; −1 keeps the default position.

### Checking that the lord works

1. Diagnostics: no undeclared art set or variant, no invalid reference.
2. Stage A: in a custom battle, he can be picked as his faction's general, with his portrait and his uniform.
3. Stage B: in a new game with the faction, he is among the lords to recruit, or at the head of his army. If not, read the script log: a non-empty `script/enable_console_logging` file in a loaded pack makes the game write `script_log_*.txt` files.
4. Stage C: on the "New Campaign" screen, his record appears with his portrait and his description, his marker is in place on the Map tab, and the game starts with him.

## What needs a new startpos

*Diagram: Startpos or script?.*

In the diagram: Startpos or script? · One startpos per campaign; Immortal Empires and Realm of · Chaos: Mixer. · Needs the startpos · Playable factions, or ones that appear during the game · A culture's agent types · Landmark slots · Faction potential · Non-aggression and military access · A fixed ID (CQI) for the leader: for a quest battle · Names offered for recruitment · Script or database · Regiments of renown · `cm:add_unit_to_faction_mercenary_pool` · Region, leader, army, items: Campaign Custom Starts · `cm:create_force_with_general` · `cm:transfer_region_to_faction` · The selection screen's marker · `override_force_location_x/y` · Tech tree: nodes with a faction column, plus a script · A faction's features, horde or not (feature forest) · `cm:add_or_remove_faction_features` · Database only: units, subtypes, looks, skills, texts, the factions table.

The startpos records turn 0: what it holds only changes if it is regenerated, and a mod replaces it entirely. From the wiki's Startpos page: on the left, what requires it; on the right, what a script or the database does without it.

The startpos is the turn-0 save: what it records only changes if you regenerate it. The Assembly Kit's schema marks with a flag, `requires_startpos_reprocess`, the columns that go into it: read it in `TWaD_<table>.xml`. On patch 8.1, 42 tables carry one:

- 25 of the 29 `start_pos_*` tables: factions, characters, traits, items, units, regions, settlements, garrisons, diplomacy, lord options, technologies, victory conditions…;
- `campaigns`, `campaign_maps`, `campaign_map_playable_areas`, `campaign_map_regions`, `campaign_map_attritions`, `campaign_map_masks_to_excluded_regions`, `regions`, `region_to_province_junctions`, `climates` and `campaign_ground_types`;
- seven mercenary tables: `mercenary_pools`, `mercenary_unit_groups`, `mercenary_pool_modifiers`, `mercenary_pool_to_groups_junctions`, `mercenary_pool_type_enums`, `faction_to_mercenary_set_junctions` and `province_to_mercenary_set_junctions`.

Units, subtypes, appearances, skills, texts and the `factions` table change in the database alone. Three ways produce a startpos: RPFM's *Build Startpos*, BOB's *Process start pos* action, or a `user.script.txt` written by hand. In our workshop, only writing it by hand worked: [the tools guide](https://bretonia.dev/atelier/en/tools/).

### Build Startpos, in RPFM

Right-click the pack's root **→ Build Startpos** has the game itself generate the startpos (palette, Ctrl+Shift+P: "Build Startpos"). Version 5.0.6 no longer has a *Special Stuff* menu: the "Special Stuff → …" paths of older tutorials and of the wiki apply to version 4.

*Diagram: Build Startpos, step by step.*

In the diagram: Build Startpos · The start tables in the pack · create them, or import them from the Assembly Kit through the Dependencies panel. · `start_pos_*` · `campaigns` · The pack opened from the game's data folder · Save Pack As… into data, or PackFile → Open From Data; otherwise RPFM refuses. · `<game>\data\<mod>.pack` · Right-click the root → Build Startpos · pick the campaign; Process HLP and SPD data: for a new map only. · With this box, the time varies a lot: RPFM announces 10 to 30 minutes, tw-modding well over an hour for a large map. · Build Startpos button: the game starts, builds, closes · Steam running; Play in the launcher if needed. · RPFM first wrote your user.script.txt (the old one kept as .bak); the third line only with Process HLP and SPD data: `mod <mod>.pack;` · `process_campaign_startpos <campaign>;` · `process_campaign_ai_map_data;` · `quit_after_campaign_processing;` · Don't close RPFM here. · Hit this when the game is closed · RPFM puts startpos.esf (and the hlp and spd files) into the pack and restores your user.script.txt. · `campaigns/<campaign>/startpos.esf` · Ctrl+S, then a new campaign · the pack is already in data. · Before publishing: remove the start_pos_* tables from the pack.

Build Startpos has the game itself build the startpos, from the start tables of the pack opened from data. Don't close RPFM before the click of step 5: it puts startpos.esf into the pack and restores your user.script.txt, otherwise the game would close at every launch.

1. Put the `start_pos_*` and `campaigns` tables in the pack: create them, or import them from the Assembly Kit through the Dependencies panel. The instructions RPFM displays also ask for `db/victory_objectives.txt`, but not for Warhammer III (RPFM's code; changelog, version 4.3.11).
2. The open pack must be saved in the game's `data` folder (*Save Pack As…*, or **PackFile → Open From Data**): otherwise RPFM refuses.
3. Right-click the pack's root → **Build Startpos**; pick the campaign (the list comes from your `campaigns` table). Only tick *Process HLP and SPD data* (the AI's map data, `hlp_data.esf` and `spd_data.esf`) for a new map. The time varies a lot: 16 s for us (400 × 440 hexes); tw-modding's campaign map tutorial gives under a minute for `spd`, about five minutes for `hlp` on a medium map and well over an hour on a large one; RPFM announces 10 to 30 minutes.
4. Click **Build Startpos**: the game starts (Steam open; click *Play* in the launcher if needed), generates, then closes by itself. Don't close RPFM meanwhile.
5. Once the game has closed, click **Hit this when the game is closed**: RPFM puts `campaigns/<campaign>/startpos.esf` (and, if the option was ticked, the hlp and spd files) into the pack, and puts your `user.script.txt` back as it was.
6. Save the pack (Ctrl+S), then start a new campaign.

One clarification: you write the `start_pos_*` tables in the kit (Dave) or in RPFM, but the game does not read the kit's XML files; for generation, they must therefore be in a loaded pack (for Build Startpos, the very pack it generates). Once the startpos is produced, remove them from the published pack: they are no longer used. In our workshop, the game validates a mod pack's `start_pos_*` tables at every launch, and `%APPDATA%\The Creative Assembly\Warhammer3\crash_report\bad_mods_report.txt` then names the faulty record.

> **In our workshop, the generation script is written by hand**: RPFM writes the generation's `user.script.txt` itself, but puts only one `mod` line in it: the open pack's. Our generation reads two packs (the mod and the start tables' pack), so we write the script by hand, with one `mod` line per pack, `process_campaign_startpos <campaign>;`, if needed `process_campaign_ai_map_data;`, then `quit_after_campaign_processing;`, and we put the file back as it was afterwards. An `add_working_directory` line we had tried made the game exit within nine seconds without producing anything (RPFM 5.0.6 only writes it for Attila and Thrones of Britannia). The full recipe, and what a startpos requires not to crash: [the tools guide](https://bretonia.dev/atelier/en/tools/).

## RPFM for an AI: the MCP server

Since version 5, RPFM has a server, `rpfm_server.exe`. It speaks MCP (Model Context Protocol), the standard plug for AI assistants: an AI connected to it opens packs, reads and writes tables, searches everywhere and runs diagnostics, in its own session, separate from RPFM's window.

1. If RPFM's window is open, its server is already running: the window starts it itself. Otherwise, run `rpfm_server.exe` without arguments (it has no `--help` option: it starts at once). It listens on `127.0.0.1:45127` and stops when no session is left open.
2. Point your assistant at `http://127.0.0.1:45127/mcp` (Streamable HTTP). With Claude Code: `claude mcp add --transport http rpfm http://127.0.0.1:45127/mcp`. Any Streamable HTTP MCP client works.
3. Ask it to read the server's "Initialization guide" resource first: it describes the order of calls (select the game, check the schema, open the pack).

*Diagram: An AI plugged into RPFM.*

In the diagram: An AI plugged into RPFM · One session per connection · the AI does not see your open packs. · RPFM's window · your open packs · `rpfm_server` · `127.0.0.1:45127` · The assistant · your AI · `/ws` · `/mcp` · Streamable HTTP · For the startpos · Ask the human before launching the game. · `build_starpos` · "Success" = game launched · wait for the game to close · `build_starpos_post` · stores startpos.esf, restores user.script.txt · `save_packfile` · to give up: build_starpos_cleanup

RPFM's window starts rpfm_server itself; the assistant plugs in through /mcp and gets its own session: it does not see the packs open in the window. For the startpos, build_starpos only launches the game; build_starpos_post stores the file.

```
set_game_selected   (warhammer_3, rebuild_dependencies: true)
→ is_schema_loaded            (otherwise update_schemas)
→ open_packfiles              (returns the pack_key)
→ open_pack_info              (the pack tree)
→ decode_packed_file          (e.g. db/land_units_tables/<file>)
→ fields_processed            (the rows' shape)
→ save_packed_file_from_view
→ diagnostics_check
→ save_packfile
```

- **One session = one state** (selected game, open packs): do everything in the same session, `set_game_selected` first. Each connection has its own session: the AI does not see the packs open in your window. To be safe, don't edit the same pack from both sides.
- **Rows are written in "processed" format**: call `fields_processed` on the definition before building a row. For example, a region's `r`, `g`, `b` columns become a single `ColourRGB` value at the end of the row; otherwise saving fails ("expected a row with 6 fields, but we got a row with 8").
- Complex arguments (`Definition`, `GlobalSearch`, `NewFile`) are passed as **JSON strings**. A new table: `new_packed_file` with `{"DB": ["<file>", "land_units_tables", <version>]}`.
- Removing a game row: `add_keys_to_key_deletes` writes its keys into a `twad_key_deletes` table of the pack (see "Pack rules").
- **`build_starpos`** (that is the tool's spelling; arguments `pack_key`, `campaign_id`, `process_hlp_spd`) answers "Success" in zero seconds: it means "game launched", not "startpos built". Wait for the game to close, then call **`build_starpos_post`** with the same arguments: it puts `campaigns/<campaign>/startpos.esf` (and the hlp and spd files) into the pack and restores your `user.script.txt`; then `save_packfile`. To give up: `build_starpos_cleanup`. Without one of them, `user.script.txt` keeps `quit_after_campaign_processing;` and the game will close on every launch.
- As of 20 September 2026, the RPFM 5.0.6 server exposed about 150 tools, 11 prompts and 11 resources: packs, files, tables, dependencies and schemas, search, diagnostics, optimiser, translations, startpos.
- If CAIME (Campaign AI Map Editor), the Campaign Map Toolkit's editor, reads its database through RPFM, it needs `rpfm_cli.exe`, the one from a version 4 of RPFM (in our workshop, 4.2.7): version 5 no longer ships it ([the CAIME guide](https://bretonia.dev/atelier/en/caime/)).

## For an AI reading this page

For an AI helping to mod Total War: Warhammer III with RPFM, the essentials as checkable facts, taken on RPFM 5.0.6 and patch 8.1 of the game (September 2026), reviewed for update 9.0: check them again after an update (schemas, dependencies cache; and, if the Assembly Kit is used too, the rows added to `raw_data\db`, which Steam rewrites).

- RPFM 5.0.6: menus PackFile, MyMod, View, Game Selected, Tools, About (no more *Special Stuff*). PackFile → Settings; About → Check Updates (schemas button); Game Selected → Generate Dependencies Cache; MyMod → New MyMod (creates `<MyMod>\warhammer_3\<name>.pack`); Ctrl+Shift+F global search; Diagnostics panel → Check PackFile. **Right-click the pack's root**: Install, Uninstall, Save Pack For Release…, Optimize PackFile, Build Startpos. Palette: Ctrl+Shift+P (actions), Ctrl+P (files).
- Table file: `db/<table>_tables/<prefix>_<name>`, never `data__`, no final digit or space. Add: new key. Modify a game row: same key, in a file whose name sorts before `data__` (`@`, `!`). Remove a game row: `twad_key_deletes` (Warhammer III ≥ 6.3; MCP tool `add_keys_to_key_deletes`), never a copied table; `mod_disabled` only exists in two tables.
- Text key: `<table>_<column>_<key>`; a mod's `.loc` files apply to every language; never a game key.
- Unit: same key in `main_units.unit` and `land_units.key`; `building_units_allowed` at every building level, `key` = unique integer (Generate Ids).
- Faction: unique `start_pos_factions.ID` below 2³¹, `cai_personality_group` required; one startpos per campaign; Immortal Empires and Realm of Chaos: Mixer.
- Selectable lord: `start_pos_starting_general_options` + `frontend_faction_leaders` (`startpos_map` = `default`) + `playable` = 1 + startpos; by script (to be tested): `cm:spawn_character_to_pool`, names `names_name_<id>`.
- Startpos to regenerate: tables with a field flagged `requires_startpos_reprocess` (read `TWaD_<table>.xml`); everything else, database alone.
- MCP: `http://127.0.0.1:45127/mcp` (Streamable HTTP), one session per connection, separate from the window; "Initialization guide" resource first; `set_game_selected` (`warhammer_3`, `rebuild_dependencies: true`), `is_schema_loaded` (otherwise `update_schemas`); rows following `fields_processed`; complex arguments as JSON strings; `build_starpos` ("Success" = game launched) → game closes → `build_starpos_post` → `save_packfile`; to give up, `build_starpos_cleanup`.
- Ask the human before launching the game, driving the screen or publishing; delete nothing: tidy away, only the human deletes.
- No reverse engineering and no workaround of a CA tool: when a tool gets stuck, a control case, then the community (CAIME's Discord, tw-modding).

## Sources and links

- [RPFM manual](https://frodo45127.github.io/rpfm/manual/), especially [Datacores](https://frodo45127.github.io/rpfm/manual/tutorials/datacores.html), [Optimising a mod](https://frodo45127.github.io/rpfm/manual/tutorials/optimising-a-mod.html) and [MCP](https://frodo45127.github.io/rpfm/manual/server/mcp.html); [RPFM's code at tag v5.0.6](https://github.com/Frodo45127/rpfm/tree/v5.0.6) (menu locations, Build Startpos, server); [releases](https://github.com/Frodo45127/rpfm/releases); RPFM 5.0.6 itself (labels, changelog).
- tw-modding wiki: [RPFM For Dummies](https://tw-modding.com/wiki/Tutorial:RPFM_For_Dummies) (written for RPFM 2.3.2, old menus), [RPFM Post-Setup for Beginners](https://tw-modding.com/wiki/Tutorial:RPFM_Post-Setup_for_Beginners), [Localisation](https://tw-modding.com/wiki/Localisation), [Submodding](https://tw-modding.com/wiki/Tutorial:Submodding), [Startpos](https://tw-modding.com/wiki/Startpos), [Lua/First Script](https://tw-modding.com/wiki/Lua/First_Script), [Example Scripts](https://tw-modding.com/wiki/Example_Scripts).
- Startpos of Immortal Empires and Realm of Chaos: [MIXER – Mixu's Unlocker](https://steamcommunity.com/sharedfiles/filedetails/?id=2859968660), by Mixu.
- Units: [Creating Custom Units for WH3](https://steamcommunity.com/sharedfiles/filedetails/?id=3335345328), by Casualfield (linked from [tw-modding](https://tw-modding.com/wiki/Tutorial:Custom_Units_for_WH3)), [Unit Card Guide](https://tw-modding.com/wiki/Tutorial:Unit_Card_Guide), [Building units allowed tables](https://tw-modding.com/wiki/Building_units_allowed_tables), [Units custom battle permissions tables](https://tw-modding.com/wiki/Units_custom_battle_permissions_tables).
- Lords: [Custom Lords & Heroes](https://tw-modding.com/wiki/Tutorial:Custom_Lords_%26_Heroes) (Warhammer II, by Nanu), [Agent subtypes tables](https://tw-modding.com/wiki/Agent_subtypes_tables), [Character skill nodes tables](https://tw-modding.com/wiki/Character_skill_nodes_tables).
- Scripts, by Vandy: [Campaign Custom Starts](https://chadvandy.github.io/tw_modding_resources/WH3/campaign/custom_starts.html) and [campaign_manager](https://chadvandy.github.io/tw_modding_resources/WH3/campaign/campaign_manager.html).
- Game data read on patch 8.1: the Assembly Kit's schema (`TWaD_*.xml`, `TExc_LocalisableFields.xml`), the CA rows used as models (Knights Errant, Alberic, Bordeleaux), `lib_campaign_manager.lua`.
- Our other guides: [the modder's tools](https://bretonia.dev/atelier/en/tools/), [CAIME](https://bretonia.dev/atelier/en/caime/), [Terry](https://bretonia.dev/atelier/en/terry/), [BOB](https://bretonia.dev/atelier/en/bob/), [modding with an AI](https://bretonia.dev/atelier/en/ai/); and [the modders' workshop](https://bretonia.dev/atelier/en/).

## Frequently asked questions

### Which RPFM version should I use for Warhammer III?

5.0.6, the latest stable release as of 24 September 2026, whose menus this guide follows. The online manual describes that same version, sometimes with labels that differ from the window: the palette (Ctrl+Shift+P) finds the action by name.

### My mod crashes the game on launch: what should I check?

RPFM's diagnostics (Check PackFile). An invalid reference is one of the leading causes of crashes on launch, and an empty mandatory field may crash the game, often without an error message.

### Do I need a new startpos to add a unit?

No. Only starting armies and garrisons and mercenary pools require one, and a script can handle them (`cm:create_force_with_general`, `cm:add_unit_to_faction_mercenary_pool`).

### Can I add a faction without touching the startpos?

No: everything that makes it exist goes through the startpos, of which each campaign has only one, which a mod replaces entirely. Your mod will therefore be incompatible with any other startpos mod for that campaign. For Immortal Empires and Realm of Chaos, ask instead for your faction to be added to Mixer, most players' startpos mod, then do the rest by script.

### Why doesn't my unit show up in custom battles?

It needs `main_units`, `land_units`, `unit_variants`, its card (a 60 × 130 PNG), its name in a `.loc` file and a `units_custom_battle_permissions` row for the battle faction, with `general_unit` set to 0.

### How do I make a lord selectable on the new campaign screen?

The character in `start_pos_characters`, a `start_pos_starting_general_options` row linking him to a `frontend_faction_leaders` record (`startpos_map` set to `default`), `start_pos_factions.playable` set to 1, then a regenerated startpos.

### Why do my texts replace the game's in every language?

The game loads a mod's `.loc` files whatever the player's language. Only add new keys, and make one pack per language.

### How do I connect an AI to RPFM?

If RPFM's window is open, its server is already running; otherwise, run `rpfm_server.exe` (RPFM 5). Then add the MCP server `http://127.0.0.1:45127/mcp` to your assistant; with Claude Code: `claude mcp add --transport http rpfm http://127.0.0.1:45127/mcp`.

### Why doesn't my mod show up in the launcher?

Check that it is installed in the game's `data` folder and that its type is Mod: right-click the pack's root, Change PackFile Type, Mod. In the same submenu, the Index Includes Timestamp box must be unticked: according to RPFM, a pack with it ticked does not show up in the official launcher.

### Why does my change to a game unit do nothing?

Your table file sorts after the game's (`data__`): so the game's row wins. Start your file's name with `@` or `!`, then install the pack again.

### Some tables are flagged Outdated after a game update: what should I do?

Update the schemas (About, Check Updates), then the cache (Game Selected, Generate Dependencies Cache). Then right-click each flagged table, Update Table, and read through the new columns: RPFM puts default values in them.

### How do I remove a game row?

With the `twad_key_deletes` table (Warhammer III, since patch 6.3), never by copying the game's table: create `twad_key_deletes_tables` once in your pack, select the rows in the game's table, then right-click, Add Selection to Key Deletes. Don't remove a row still cited by another table: the game would crash.

### Why does the game close as soon as it starts?

A `quit_after_campaign_processing;` line was left in `user.script.txt` (`%APPDATA%\The Creative Assembly\Warhammer3\scripts`): a startpos generation was interrupted before its end. Remove the line or put back the previous file. With Build Startpos, always click Hit this when the game is closed, which puts it back.
