Modders' workshop · guide · 75 min read
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.
Reviewed on 25 September 2026, update 9.0. Checked against RPFM 5.0.6's code, language file and manual, the game's data and the Assembly Kit on patch 8.1. The 9.0 changes that affect our project are included; the rest has not been rechecked. Corrected on 5 October 2026: facts proven since on the game's 9.0.2, and versions released since (CAIME 1.1.0, RPFM 5.1.1). When in doubt, check, and tell us.
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/), Lua scripts (script/), images (ui/), model definitions (variantmeshes/) and, for a campaign, its start position (campaigns/).
This guide was written on RPFM 5.0.6 (released on 1 August 2026) and tables read on patch 8.1. Since then, 5.1.0 (28 September 2026) and 5.1.1 (4 October 2026) have come out, fixing the MCP sessions' memory leak and the order of the keys written to twad_key_deletes since the game's 9.0: take the latest; the online manual describes 5.1 (155 MCP tools). RPFM's schemas for 9.0 came out on the day of the update (24 September); the tables cited here have not been reread one by one since: when in doubt, check. Every change follows these six steps:
- Find
The closest game row.
View → Toggle Global Search Window (Ctrl+Shift+F)
- Copy
Into your table (
db/), under a key of your own (the same one to modify a game row).<table>_tables/ <file> right-click the pack's root → Create… → Create DB, then Paste as New Row
- Name
The texts shown to the player, in
text/.db/ <file>.loc right-click the pack's root → Create… → Create Loc
- Check
References, keys, empty fields.
Diagnostics panel → Check PackFile
- Try
The pack installed in
<game>\, in game.data\ right-click the pack's root → Install
- 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/ |
| 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 Create…, 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§
From its releases, take the Windows archive (for 5.0.6, rpfm-v5.0.6-x86_64-pc-windows-msvc.zip; take the latest version), unzip it into a folder of your own, outside Program Files, and run rpfm_ui.exe: that is RPFM's window (rpfm_server.exe, next to it, is its server, which the window starts itself). Then, once and for all:
- 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.
- 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).
- 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.
The basic moves§
RPFM 5.0.6's window, area by area:
- 1The menusPackFile, MyMod, View, Game Selected, Tools, About. Settings: PackFile → Settings (Ctrl+,); schemas: About → Check Updates; the cache: Game Selected → Generate Dependencies Cache.
- 2The tabsone per open file.
- 3Pack Contentsthe pack's tree; its first line, the root, bears the pack's name.
- 4The root's right-click menuSave Pack As…, Save Pack For Release…, Install, Uninstall, Change PackFile Type, Optimize PackFile, Build Startpos (and other actions): what concerns the whole pack.
- 5Dependenciesthe files of the game, of parent mods and of the Assembly Kit, read-only; right-click → Import brings one into your pack.
- 6The open tableyour 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.
- 7DiagnosticsCheck PackFile reads the pack through; each line has a severity: Error, Warning or Info. Aim for zero Error.
- 8Global Searchhidden at first: View → Toggle Global Search Window (Ctrl+Shift+F); sources Game Files or Assembly Kit Tables.
- 9The status barshort messages from the last operation.
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>\ and, next to it, the folder <MyMod>\ 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's root → Create… → Create DB; pick the table (land_units_tables) and name the file with your prefix: db/. 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 ! (the details: “Pack rules”).
- Never rename or move a
_tablesfolder (“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§
- In the game's table, select the model row (click its number): Copy… → Copy.
- 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.
- 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.
Writing texts§
- Right-click the pack's root → Create… → Create Loc, for example
text/.db/ atlas.loc - 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.
- The key has the form
<table>_<column>_<row key>. Example: the name of unitatlas_chevaliers(columnonscreen_nameofland_units) has the keyland_units_onscreen_name_atlas_chevaliers. - Right-click the pack's root → 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 removesaaa_…(andzzz_…if it is empty).
Your first mod, in ten minutes§
A short test, which also checks your setup: making a game unit dearer in custom battles. The diagnostics and installing the pack are detailed right after (“Checking, testing, publishing”); why the @ makes your file win, in “Pack rules”.
- MyMod → New MyMod: game Warhammer 3, name
my_first_mod. - View → Toggle Global Search Window (Ctrl+Shift+F): Game Files source, DB type; search for
wh_dlc07_brt_cav_knights_errant_0, then click themain_units_tablesresult: the game's table opens at the right row, read-only. - Click the row's number, then right-click → Copy… → Copy.
- Right-click your pack's root → Create… → Create DB: table
main_units_tables, name@my_main_units. The@makes your file sort before the game's (data__): so yours wins. - 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. - View → Toggle Diagnostics Window → Check PackFile: no Error line.
- 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.
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. Double-clicking a line opens the faulty cell; a false positive is ignored by right-clicking the line (Ignore…: in a file, a column, a folder or the whole pack), and that choice is saved in the pack itself (RPFM manual, Diagnostics page). The figure follows a mod from the diagnostics to the game.
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§
- 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\ - Tick the mod in Creative Assembly's launcher, or in a third-party manager (Kaedrin's Mod Manager, Runcher).
- Same menu → Uninstall: removes the copy.
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_deletestable 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.
Uploading to the Workshop through CA's launcher: the launcher retries every upload 4 times by itself, and it creates an empty item at each attempt for a new item. “error code #25” means a limit reached, on the account's or Steam's side (refused before anything is sent), not a fault in your pack; the launcher does not display it. One upload at a time, then read its log (%APPDATA%\, times in UTC); after an error 25, send and create nothing more: retry hours later, and check the page.
After a game update§
- About → Check Updates, schemas button.
- Game Selected → Generate Dependencies Cache: otherwise RPFM compares your tables with an outdated game.
- On every table flagged Outdated table: right-click → Update Table.
- 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, separately, and with every game or kit update (9.0, then 9.0.2) it rewrites raw_data\; the rows you had added there disappear. Check and restore them before regenerating the dependencies cache and before building anything; after restoring, count each batch against its source.
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.
Example: the game's battle units table is the file db/. In the wiki's sort order (Submodding page), @my_mod sorts before data__ and zmy_mod after it; RPFM's manual, however, lists ztable before data__ (Datacores page). To modify a game row, the safe choice is therefore a name starting with @ or !. A fragment named data__ replaces the whole table: 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
.locfiles 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.locthat 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_deletestable (columnstable_nameandkey) that removes the named rows at launch (the steps are below). - In the
datafolder, 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.
Removing a game row§
- Create
twad_key_deletes_tablesonce in your pack (right-click the root → Create… → Create DB), under a file name with your prefix. - Open the game's table and select the rows to remove.
- Right-click → Add Selection to Key Deletes → your table: RPFM writes the keys into it, even when the key spans several columns. Exception: since the game's 9.0, RPFM 5.0.6 writes the key of
land_units_to_unit_abilites_junctionsthe wrong way round, and the row is not removed; move to 5.1.0 or later, or write it by hand (land_unitthenability), and check in game.
Beware: removing a row still cited by another table crashes the game, and the diagnostics do not check this yet (RPFM manual, Datacores page). 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.
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.
The unit's steps§
- Global Search for the model's key: it appears in 24 tables of the kit; the table below gives the ones you need.
- 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.unitandland_units.keythe same key. - Set the stats, costs, appearance and card, then the recruitment, the custom battle and the texts.
- 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).
- 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/ |
variants | only for a new model: variant_filename | variant_name | → variantmeshes/ |
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 |
What changes, what stays§
- To change: the new key
atlas_chevalierswherever CA's appears (main_units.unit,main_units.land_unit,land_units.key,unit_variants.unitandunit_variants.name, theunitcolumns of the permissions and buildings), and a new number inbuilding_units_allowed.key(right-click → Generate Ids, from a starting number of your own, below 2³¹). The key ofunit_variantsspans two columns (faction+unit):unitis the one that changes;nameis not part of the key, but CA puts the unit's key in it andunit_variants_coloursrefers to it: change it too. - To keep for a first test: CA's
variantandunit_card(wh_dlc07_brt_knights_errant: same model, same card), the military group (wh_main_group_bretonnia), the battle faction (wh_main_brt_bretonnia, withgeneral_unit= 0) and the four stables (wh_main_brt_stables_2towh_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: it only becomes active (Tools menu) once the Enable Unit Editor (EXPERIMENTAL) box is ticked in the settings (PackFile → Settings). 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_groupsandunit_attributes_to_groups_junctions;unit_set_to_unit_junctions;ui_unit_bullet_point_unit_overrides;allied_recruitment_unit_permissions;armed_citizenry_unit_groupsandbuilding_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 ofunits_to_groupings_military_permissionsto be repeated there, Casualfield calls it optional.
The unit's texts§
land_units_onscreen_name_<key>: the name (Casualfield givesunit_description_historical_texts_UNITKEY, but the kit's schema stores the name inland_units.onscreen_name);unit_description_short_texts_text_<short key>andunit_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_permissionsand the name in a.locfile. - In the campaign, as well:
units_to_groupings_military_permissionsandbuilding_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
enabledcolumn ofbuilding_units_allowedonly forbids recruitment when three conditions are met: the building-unit pair exists elsewhere,factionis filled in, the box is unticked. units_custom_battle_permissionsalso 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§
- Diagnostics: no invalid reference, no missing text.
- 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.
- New campaign: the unit can be recruited at every building level listed.
Procedure 2: adding a faction§
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.
The faction's steps§
factions: copy a neighbouring faction (wh_main_brt_bordeleaux) under a new key; setsubculture,name_group,military_group,flags_path=ui\and the colours (or Tools → Faction Painter). Never take over the rows of aflags\ <key> *_ownership_content_pack_junctionstable (for instancefaction_ownership_content_pack_junctions) for a faction of yours: CA locks its DLC there, and the game closes on launch without a message; a DLC lock is done by script.- The flags: a
ui/folder like CA's:flags/ <key>/ 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. 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).- The texts, then the start tables of the table below, in the kit (Dave) or in the pack (RPFM), plus
start_pos_character_to_settlementsfor a garrisoned leader andstart_pos_diplomacy. - The startpos, regenerated (see “What needs a new startpos”), stored in
campaigns/in the pack; then Diagnostics, Install (except for a pack opened from the<campaign>/ startpos.esf datafolder, 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 |
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_bastonnein Warhammer I,wh_main_brt_bastonnein Warhammer III): when porting data, match them on the culture and the end of the key (the porting tutorial). - DLC ownership junctions. No
*_ownership_content_pack_junctionstable (faction, playable area, battle…) in your pack for a key of yours: the game closes within 9 to 13 s, with no report and nomp_log, andbad_mods_report.txtdoes not see the fault (9.0.2). A DLC lock is done by script. - The faction's settlements. A settlement's slot template (
primaryfromstart_pos_region_slot_templates),primary_buildingandport_buildingexist as they are at CA for the owner's subculture; never a*_porttemplate on a region without a port. A faction that CA starts as a horde, without a region, gets no settlement at the start. Otherwise the startpos generates, and loading the campaign crashes (9.0.2).
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 system (regions, armies, diplomacy, effects).
Checking that the faction works§
- Clean diagnostics;
campaigns/is really in the pack, dated from the generation.<campaign>/ startpos.esf - Start tables served through a mod pack:
%APPDATA%\names the faulty record, one at a time. It does not see everything: a DLC ownership junction or a non-emptyThe Creative Assembly\ Warhammer3\ crash_report\ bad_mods_report.txt campaigns.maskmake the game close or crash without a report; then remove one table at a time, on a copy of the pack. - New campaign: the faction is on the map, in its region, with its flag and its name; its leader leads his army.
- 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. A full example follows the three procedures: the Red Duke, further down.
Stage A: the lord in the database§
agent_subtypes: copy a neighbouring lord (wh_dlc07_brt_alberic);auto_generateunticked for a unique character,associated_unit_override= his unit,recruitment_category=legendary_lords,recruitable= 1 andcap=-1(like Alberic). Leavemagic_loreempty: filled in, withlegendary_lords, it can hide the other legendary lords of the recruitment pool (wiki, Agent subtypes tables page).- The appearance:
campaign_character_art_sets→campaign_character_arts(a uniqueid) →agent_uniforms→variants. - The portrait: Warhammer III reads it from
ui/, arranged by art set (for Alberic,portraits/ portholes/ portrait_settings*.bin 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.portraitstays empty. - The skills:
character_skill_node_sets(columnagent_subtype_key),character_skill_node_set_items(set,item),character_skill_nodes(tier= the tree's column,indent= the row),character_skillsthencharacter_skill_level_to_effects_junctions,character_skill_node_linksfor the arrows. faction_agent_permitted_subtypes(the faction,general, the subtype) andnames(newidvalues for the forename and the surname). For custom battles:units_custom_battle_permissionswithgeneral_unit= 1,general_portraitandgeneral_uniform.
| 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 |
The lord's texts§
agent_subtypes_onscreen_name_override_<subtype>: the type's label (“Legendary Lord” for Alberic); his proper name comes fromnames_name_<id>;agent_subtypes_description_text_override_<subtype>;character_skills_localised_name_<key>,character_skills_localised_description_<key>andcharacter_skill_node_sets_enc_title_<key>;frontend_faction_leaders_frontend_description_<record>andfrontend_faction_leaders_localised_description_<record>; according to CA,localised_action_pointsandloading_screen_textare 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)- Parameter order of
cm:spawn_character_to_pool: faction, forename, surname, clan, other name, age, male, type, subtype, immortal, art set. Names are writtennames_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_unitskeys (the same as themain_unitskeys in CA's data). - The script goes into
script/, 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.campaign/ mod/
Stage C: picking him on the “New Campaign” screen§
start_pos_characters: the character (see procedure 2).start_pos_starting_general_options:general(the character) →frontend_faction_leader(his record), plusprecedenceandexclude_other_options, also required. Without this table, no lord can be picked.- A
frontend_faction_leadersrecord, with a free key:character_image(an 85 × 115 portrait, read on CA's at patch 8.1),uniform(the.cscscene),video,loading_screen_image,agent_subtype_record,faction. The kit's schema (reread on 9.0) requires others:voiceover,difficulty,show_full_intro_option,localised_description,frontend_description,startpos_map,override_force_location_xand_y: copy a CA record and change what is yours. start_pos_factions.playable= 1, then the regenerated startpos.
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
tiercolumn stays below 48 (wiki, page oncharacter_skill_nodes). startpos_map: CA mostly putsdefaultor the record's own key there; for a new record, leavedefault. 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_xandoverride_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: this is what the game writes for lords not in a garrison (measured in our workshop on three lords); the description in CA's schema swaps width and height.-1leaves the default position (in CA's data, Eltharion: 0.398 and 0.420).
Checking that the lord works§
- Diagnostics: no undeclared art set or variant, no invalid reference.
- Stage A: in a custom battle, he can be picked as his faction's general, with his portrait and his uniform.
- 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/file in a loaded pack makes the game writeenable_console_logging script_log_*.txtfiles. - 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.
A full example: the Red Duke, from his faction to his victory§
The three procedures, applied to a real lord: the Red Duke, made playable on our Season of Revelation map on 23 September 2026, then given mechanics of his own with update 9.0. The lore, the story and the status of each mechanic are in the Duke's article; here, the technical side, brick by brick: tables and columns, startpos, moves in RPFM, checks and pitfalls. Everything below exists in our pack; the numbers are design choices, to be tuned in game.
Seven bricks, in the order you lay them: the faction, the lord and his record, his units, his chronicle, his events, his mechanics, his victory. For each one: its tables and the columns that matter, what it needs from the startpos, the move in RPFM, the check, the pitfalls.
1. The faction: Mousillon, made playable§
The Duke leads Mousillon (wh_main_vmp_mousillon), a CA faction, minor in Immortal Empires: the factions table already has its row, nothing to create. Everything happens in the start tables (for a new faction, that is procedure 2).
| Table | What matters | Startpos |
|---|---|---|
start_pos_factions | playable = 1, is_major = 1; faction_potential (the help the AI gets) and cai_personality_group (in our workshop, starting from the Immortal Empires values) | yes |
start_pos_diplomacy | starting wars and treaties | yes |
- In RPFM: open
start_pos_factionsin the start tables' pack (they don't ship in the published mod), type the faction's key into the filter at the top of the table, change the boxes, Ctrl+S; then regenerate the startpos. - Check: Mousillon can be picked on the “New Campaign” screen, once the Duke's record is done (brick 2).
- Pitfall, CA systems that list their factions: several CA scripts keep, in Lua tables, per campaign name, the factions they serve; a faction that is minor in Immortal Empires isn't there. We added Mousillon to the vampire lair spawning (first lair at Derrevin Libre) and to the AI's background incomes, in their tables, after CA's files load and before they start.
- Pitfall, the technology that backfires: CA's technology that confederates the Red Duke would, if researched by the played Duke, absorb his own faction; the script locks it for him (
cm:lock_one_technology_node).
2. The legendary lord and his record§
The Duke already exists in CA's database: his subtype (wh_dlc05_vmp_red_duke), his unit, his looks and, with 9.0, his innate trait and two unique skills. Stage A of procedure 3 is therefore done; what remains is his links with the campaign and stage C.
| Table | What matters | Startpos |
|---|---|---|
campaign_to_agent_subtypes | campaign_type = your campaign, agent_subtype = his subtype: this is how CA declares every legendary lord of Immortal Empires (its schema mentions, for example, the Changeling's forms) | no |
start_pos_characters | the Duke: Type = general, subtype, ministerial_position = faction_leader, Name and Surname; garrisoned in Mousillon | yes |
start_pos_starting_general_options | general → the character, frontend_faction_leader → his record, plus precedence and exclude_other_options | yes |
frontend_faction_leaders | his record: CA has none for the Duke, who isn't playable in Immortal Empires; a copy of Mannfred's, with his portrait (character_image), his video (video), his voice (voiceover), agent_subtype_record, faction, and a scene of our own (uniform, built on Vlad's); prelude_battle and loading_screen_intro_video emptied | regenerated in our workshop |
faction_agent_permitted_subtypes | his three 9.0 heroes (Walach Harkon, Krell, Dieter Helsnicht): CA's rows for wh_main_vmp_vampire_counts, copied under Mousillon | no |

- In RPFM: in the game's
frontend_faction_leaderstable, select the model record, Copy… → Copy, then, in your file, Paste as New Row; change the key (free) and the columns of the table above. For the heroes: the same move onfaction_agent_permitted_subtypes, changingfaction. - Check: on the “New Campaign” screen, his record appears with portrait, video and description, his marker is on Mousillon, and the game starts with him.
- Pitfall, allowed heroes: without the
faction_agent_permitted_subtypesrows, the 9.0 technologies that grant Walach Harkon, Krell and Dieter Helsnicht gave the played Duke nothing. - Pitfall, the garrisoned leader: without
override_force_location_x/on his record, his marker stays in the corner of the Map tab (procedure 3, “The lord's pitfalls”).y - His items: his two 9.0 items (the Armour of Blood, the Jewel of Unlife) come to him as he ranks up, through CA's quest system (
set_up_rank_up_listener), hooked up for our map by a script.
3. His units, briefly§
Nothing new: his troops are CA's Vampire Counts ones. Only three things concern him: start_pos_land_units (his starting armies, startpos), faction_to_mercenary_set_junctions (his regiments of renown; the kit's schema flags it for the startpos) and his chronicle's rewards, which grant units by script. For a unit of your own: procedure 1.
4. His chronicle: missions of his own§
Five steps: “Crac de Sang”, “The Blood of Aquitaine”, “Douse the Lighthouse”, “The Sealed Palace”, “The Grail Crusade”. The first: hold Derrevin Libre within 25 turns, for 1,000 gold, a unit and an item. The finale places a Bretonnian army at a lore location, waiting for the Duke. Status: first step being tested; the next ones, tested outside the game.
| Table | What matters | Startpos |
|---|---|---|
missions | one row per step: key, mission_type (the main objective's type, for example CONTROL_N_REGIONS_INCLUDING), ui_image, event_category = Military, sticky_by_default = 1, can_be_manually_cancelled = 0, generate = 0; copied from a CA narrative mission | no |
text/ | missions_localised_title_<key>, missions_localised_description_<key>; for a scripted objective, mission_text_text_<key> | no |
The objectives and rewards are not in the table: the script sets them, with CA's mission_manager. The first step, shortened:
-- first step of the chronicle (shortened)
local mm = mission_manager:new("wh_main_vmp_mousillon", "saison_chronique_duc_1");
mm:set_mission_issuer("CLAN_ELDERS");
mm:add_new_objective("CONTROL_N_REGIONS_INCLUDING");
mm:add_condition("total 1");
mm:add_condition("region wh_dlc05_aquitaine_derrevin_libre");
mm:add_payload("money 1000");
mm:set_turn_limit(25);
mm:trigger();
- In RPFM: open the game's
missions, take a CA narrative mission (ours starts fromwh3_dlc27_mission_sla_narrative_05), Copy… → Copy, Paste as New Row into your table, new key. The image:ui_image=<folder>/, for the pack's file<name> ui/(ours are 800 × 450).eventpics/ <folder>/ <name>.png - Check: on turn 2, when the chronicle starts, the mission appears with its title, image, objectives and time limit.
- Pitfall, scripted objectives: Warhammer III has no
mission_texttable in its packs (RPFM's schema, 9.0);override_text mission_text_text_<key>only reads the text key, to put in your.loc. The kit'smission_texttable only serves for texts. - CA's model: its narrative missions (
wh3_dlc27_dechala_narrative.lua, read on patch 8.1): objectives set bymission_manager, steps chained onMissionSucceeded; in our workshop, a failed step (MissionFailed) also moves on to the next one.
5. His events: incidents and dilemmas§
Nine incidents tell the player what happens to the Duke (the Tomb of Galand, a domain of Aquitaine retaken or lost, the favour won or lost, the Decree of Richemont, a broken oath…); two dilemmas make him choose: “The Blood Tax” (levy the tax or spare the peasants) and “The Blood Oath” (bind a Bretonnian lord by blood, or destroy him).
| Table | What matters | Startpos |
|---|---|---|
incidents | key, localised_title, localised_description, ui_image, override_icon, generate = 0; copied from a CA incident with no target | no |
cdir_events_incident_option_junctions | two rows: GEN_TARGET_NONE, and VAR_CHANCE = 100 | no |
dilemmas | the same columns as incidents; copied from a CA dilemma | no |
cdir_events_dilemma_choice_details | one row per choice: dilemma_key, choice_key (FIRST, SECOND), localised_choice_label, localised_choice_title | no |
cdir_events_dilemma_option_junctions | GEN_TARGET_MODEL, and VAR_CHANCE = 0: the game never draws it by itself, the script launches it | no |



- In RPFM: on a CA incident's or dilemma's key, right-click → Find References shows its rows in the junction tables; copy them, paste them under your key, and give new numbers to the junctions'
idcolumn (right-click → Generate Ids). - The texts:
incidents_localised_title_<key>,dilemmas_localised_title_<key>; for a choice, the row's key is the dilemma followed by the choice, with no separator:cdir_events_dilemma_choice_details_localised_choice_label_<dilemma>FIRST. - Check: the incident or dilemma appears with its image and texts; each choice has the planned outcome.
- Pitfall, script messages: before his incidents, the Duke spoke through script messages using CA image indexes, which showed unrelated pictures (the Beastmen, the Lady of the Lake); an incident under your key picks its own image and texts.
- Pitfall, an incident's image: CA puts a bare name there (
generic); we put<folder>/, as for a mission: how it renders is still to be confirmed in game.<name>
6. His mechanics: effect bundles, resources, scripts§
The Duke's mechanics come down to three things: effect bundles under his keys, CA's vampire resources, and Lua scripts. The rule that follows: the table gives what is displayed (name, icon, target, image, text), the script decides when and how much.
| Mechanic | Trigger | What it does |
|---|---|---|
| Abhorash's Kiss | the first load | CA's Blood Dragon bloodline ritual, performed once (cm:perform_ritual) |
| The lost dukedom | RegionFactionChangeEvent on his three domains of Aquitaine | a bundle depending on the domains held: “The Dispossessed Duke” (none: leadership −4, public order −2), reconquest (one: leadership −2), nothing (two), “The Duke Restored” (all three: leadership +6, income +10%, replenishment +10%) |
| Abhorash's favour | BattleCompleted, CharacterWithdrewFromBattle | a bundle on the Duke's army, in three degrees (melee attack and leadership +6, +8, +10); lost if he withdraws or loses, worn off after 5 turns without battle |
| The blood tax | RegionFactionChangeEvent (a Bretonnian region taken) | a dilemma: Blood (+300) and corpses, or public order; the hunt draws closer or recedes |
| The blood oath | FactionBeginTurnPhaseNormal | a dilemma: a Bretonnian lord reduced to a single holding becomes his vassal (two at most, three once the dukedom is restored) |
| Richemont's hunt | FactionBeginTurnPhaseNormal | from turn 8, every 10 turns while he holds a domain of Aquitaine: an army of Questing Knights, 10 to 18 units depending on the turn |
| The Tomb of Galand | FactionBeginTurnPhaseNormal | a Duke's army that starts its turn on Ceren Field loses 15% of its strength |
| The challenge (coded, not yet seen in game) | FactionBeginTurnPhaseNormal, then MissionSucceeded and MissionFailed | from turn 6, a Bretonnian lord at war with him and close by challenges him: an 8-turn mission, the lord's fall (ELIMINATE_CHARACTER_IN_BATTLE) and a victory by the Duke in person (SCRIPTED objective); taken up: 1,500 gold and Blood (+150); dodged: Abhorash's favour broken, each vassal may break his oath (25%) |
The challenge is planned and coded, but not yet seen in game: a playable version comes first. It will stay only if it proves useful in play; its numbers (time limit, distance, rewards) are design choices, to be tuned in game.


- Effect bundles: one
effect_bundlesrow per bundle (key,localised_title,localised_description,ui_icon,bundle_target: faction, force or region); the effects, CA's, are applied bycm:create_new_custom_effect_bundle,add_effectandcm:apply_custom_effect_bundle_to_faction(or_to_force), as CA does. - Resources: CA's vampire ones, with no new table: Blood (
wh3_dlc29_vmp_power), throughcm:faction_add_pooled_resourceor a dilemma payload; corpses (wh3_dlc29_vmp_corpses), counted per province, throughcm:pooled_resource_factor_transaction. - Scripts: in our campaign's folder, loaded by its start script (for an Immortal Empires mod,
script/); state is kept withcampaign/ mod/ cm:set_saved_valueandcm:get_saved_value, under a prefix of your own. In RPFM, a script is added as a file: right-click the root → Create… → Create Text, or Add File/s. - Pitfall, the event that never comes: with 9.0, on our map,
FactionTurnStartreached no mod listener: three CA listener conditions read systems absent from our map, and their error cancelled the event for all. So our turn starts go throughFactionBeginTurnPhaseNormal, and every listener is protected (pcall), so that an error is named in the log instead of cutting everything off. - Pitfall, the silent mechanic: a bundle whose
effect_bundlesrow is missing from the pack, a text missing from the.locfiles: nothing crashes, nothing shows. Look for your keys in the pack (Global Search, Packfile source). - Pitfall, a shared resource: a CA resource or ritual tied to Immortal Empires, reused as is on another map, crashed a tooltip in our workshop; give them your own keys.
7. His victory§
CA gives the Red Duke no victory list. His follows the 9.0 format: a short and a long victory, rewarded without ending the game; only domination (27 of our 57 settlements, the Immortal Empires proportion) ends it. Status: being tested.
- Short: hold the province of Aquitaine, build the Tomb of the Red Duke there (at Gien, a landmark of our map), use Blood Decrees five times.
- Long: the short one, ten Blood Decrees, two bloodline lords awakened in lairs, Aquitaine and Quenelles destroyed, the sealed palace of Mousillon reopened, and his chronicle's finale.
No new table, no startpos: CA's victory engine (victory_objectives_config.lua) is loaded from its own folder, never copied, and its Immortal Empires list replaced by ours, written with its own objective generators. The short one:
-- the Red Duke's short victory
short = {
objectives = {
generate_CONTROL_N_PROVINCES_INCLUDING_objective({"wh_dlc05_aquitaine"}, 1),
generate_SCRIPTED_MISSION_objective("saison_victoire_duc_tombeau",
"mission_text_text_saison_victoire_duc_tombeau"),
generate_PERFORM_RITUAL_BY_CATEGORY_objective("VAMPIRE_PROVINCE", 5,
"mission_text_text_wh3_dlc29_vmp_use_shyish_actions")
},
payloads = {effect_bundle = "wh3_dlc29_bundle_ie_victory_objective_vmp_lair_builder"}
}- Scripted objectives: ticked by
cm:complete_scripted_mission_objective(the tomb built), or counted bycm:increase_scripted_mission_count(a lair awakened, on CA'sScriptEventVampireLairAwakenedevent); their text,mission_text_text_<key>, in your.locfiles. - Pitfall, the Immortal Empires list: CA's engine builds its own at load, with regions absent from our map; its listener (
InitializeVictoryObjectivesIEConfig) is removed first, and its first-tick callback, which isn't protected, is wrapped in apcall. - Check: in the victory objectives panel, the Duke's two lists; in game, each objective ticking off.
Summary: startpos or not§
In RPFM, the two families don't go to the same place: the start_pos_* tables into the pack used to generate the startpos (removed from the published mod), the rest into the mod itself. New tables in a played pack: a startup test before any announcement. Generation: next section, and the tools guide.
What needs a new startpos§
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,climatesandcampaign_ground_types(campaign_mapsexists on the kit's side, not in Warhammer III's packs, where a map is declared throughcampaigns.map_nameandcampaign_map_playable_areas);- seven mercenary tables:
mercenary_pools,mercenary_unit_groups,mercenary_pool_modifiers,mercenary_pool_to_groups_junctions,mercenary_pool_type_enums,faction_to_mercenary_set_junctionsandprovince_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: our generation reads two packs, and Build Startpos only loads one (the tools guide). RPFM 5.0.6's instructions, however, list Warhammer 3 among the games where Build Startpos works: for a one-pack mod, try it first.
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”).
- Put the
start_pos_*andcampaignstables in the pack: create them, or import them from the Assembly Kit through the Dependencies panel. The instructions RPFM displays also ask fordb/, but not for Warhammer III (RPFM's code; changelog, version 4.3.11).victory_objectives.txt - The open pack must be saved in the game's
datafolder (Save Pack As…, or PackFile → Open From Data): otherwise RPFM refuses. - Right-click the pack's root → Build Startpos; pick the campaign (the list comes from your
campaignstable: a campaign whosecampaignsrow is only in the kit is not listed). Only tick Process HLP and SPD data (the AI's map data,hlp_data.esfandspd_data.esf) for a new map; RPFM itself creates the<game>\folder where the game writes them (by hand, create it before the generation, otherwise no hlp and no spd). The time varies a lot: 16 to 22 s for us (400 × 440 and 560 × 905 hexes, generated through a hand-writtendata\ campaign_maps\ <map>\ user.script.txt, which runs the same processing); tw-modding's campaign map tutorial gives under a minute forspd, about five minutes forhlpon a medium map and well over an hour on a large one; RPFM estimates 10 to 30 minutes. - Click Build Startpos: RPFM writes into
user.script.txtthe open pack'smodline andprocess_campaign_startpos <campaign> <sub-start>;(that second argument stays empty for Warhammer III), then the game starts through Steam (click Play in the launcher if needed), generates and closes by itself. Don't close RPFM meanwhile. - Once the game has closed, click Hit this when the game is closed: RPFM puts
campaigns/(and, if the option was ticked, the hlp and spd files) into the pack, and puts your<campaign>/ startpos.esf user.script.txtback as it was; it leaves a copy next to it,user.script.txt.bak. - 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%\ then names the faulty record.
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.
- If RPFM's window is open, its server is already running: the window starts it itself. Otherwise, run
rpfm_server.exewithout arguments (it has no--helpoption: it starts at once). It listens on127.0.0.1:45127and stops once the last session is closed (a dropped WebSocket connection keeps its session for five minutes; since 5.1.0, an MCP session idle for five minutes is closed). - Point your assistant at
http://(Streamable HTTP). With Claude Code:127.0.0.1:45127/ mcp claude mcp add --transport http rpfm http://. Any Streamable HTTP MCP client works.127.0.0.1:45127/ mcp - 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).
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_selectedfirst. 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_processedon the definition before building a row. For example, a region'sr,g,bcolumns become a singleColourRGBvalue 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_filewith{"DB": ["<file>", "land_units_tables", <version>]}. - Removing a game row:
add_keys_to_key_deleteswrites its keys into atwad_key_deletestable of the pack (see “Pack rules”). build_starpos(that is the tool's spelling; argumentspack_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 callbuild_starpos_postwith the same arguments: it putscampaigns/(and the hlp and spd files) into the pack and restores your<campaign>/ startpos.esf user.script.txt; thensave_packfile. To give up:build_starpos_cleanup. Without one of them,user.script.txtkeepsquit_after_campaign_processing;and the game will close on every launch. With RPFM 5.1 or later, the session is closed after five minutes of inactivity, while a generation with hlp and spd can last 10 to 30 minutes according to RPFM: send a light command every 2 to 3 minutes (list_open_packs) or reopen the pack beforebuild_starpos_post(deduced from the code, not yet tried).- Before
build_starpos: the pack must be opened through its path in<game>\, written with forward slashes (data\ /), otherwise “The Pack needs to be in /data”;build_starpos_get_campaign_idsonly lists a campaign if the pack holds itscampaigns_tablesrow;build_starpos_check_victory_conditionslooks fordb/, which Warhammer III does not need.victory_objectives.txt rpfm_server's memory: in 5.0.6, each MCP connection creates a session that is never freed (2 to 10 GB); in our workshop, after a night of packs built through MCP, it held nearly 70 GB. 5.1.0 fixes this leak. Measure its private memory ((Get-Process rpfm_server).PrivateMemorySize64, or “Committed memory”), not the working set (for us, 91 MB of working set for 9.4 GB of private memory); beyond 8 GB, restart it. This leak was not the cause of our intermittent rendering crash: it came back on a restarted PC, with the server off (the tools guide).- Other useful tools:
list_open_packsreturns the openpack_keys;call_commandcalls a server command that has no named tool yet (manual, MCP page). In our workshop,extract_packed_filesrequiredexport_as_tsv, which its schema calls optional (without it, an empty answer and nothing written), andopen_packfileswith several paths returned a single key (to be confirmed): open one pack at a time. - 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; the online 5.1 manual describes 155.
- 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 (we had 4.2.7): neither 5.0 nor 5.1 ships it (the CAIME guide).
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, then corrected on 5 October 2026 (RPFM 5.1.0 and 5.1.1, game on 9.0.2): check them again after an update (schemas, dependencies cache; and, if the Assembly Kit is used too, the rows added to raw_data\, 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>\); 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).warhammer_3\ <name>.pack - Table file:
db/, never<table>_tables/ <prefix>_<name> data__, no final digit or space. Add: new key. Modify a game row: same key, in a file whose name sorts beforedata__(@,!). Remove a game row:twad_key_deletes(Warhammer III ≥ 6.3; MCP tooladd_keys_to_key_deletes), never a copied table;mod_disabledonly exists in two tables. - Text key:
<table>_<column>_<key>; a mod's.locfiles apply to every language; never a game key. - Unit: same key in
main_units.unitandland_units.key;building_units_allowedat every building level,key= unique integer (Generate Ids). - Faction: unique
start_pos_factions.IDbelow 2³¹,cai_personality_grouprequired; one startpos per campaign; Immortal Empires and Realm of Chaos: Mixer. Never a*_ownership_content_pack_junctionstable for a key of yours (the game closes without a report); settlement slot templates and buildings taken as they are from CA for the owner's subculture; no settlement at the start for a faction that CA starts as a horde. - 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, namesnames_name_<id>. - Playable lord, full example (the Red Duke):
start_pos_factions(playable,is_major),start_pos_characters,start_pos_starting_general_options+frontend_faction_leaders,campaign_to_agent_subtypes,faction_agent_permitted_subtypes;missions,incidents,dilemmas,effect_bundlesunder your keys, whose objectives, payloads and effects are set in Lua; turn starts onFactionBeginTurnPhaseNormal, protected listeners. - Startpos to regenerate: tables with a field flagged
requires_startpos_reprocess(readTWaD_<table>.xml); everything else, database alone. - MCP:
http://(Streamable HTTP), one session per connection, separate from the window; “Initialization guide” resource first;127.0.0.1:45127/ mcp set_game_selected(warhammer_3,rebuild_dependencies: true),is_schema_loaded(otherwiseupdate_schemas); rows followingfields_processed; complex arguments as JSON strings;build_starpos(pack opened from<game>/; “Success” = game launched) → game closes →data/ build_starpos_post→save_packfile; to give up,build_starpos_cleanup; on 5.1+, session closed after 5 min of inactivity (a light command every 2-3 min during a long generation, to be checked); restartrpfm_serverwhen its PRIVATE memory goes over 8 GB (session leak in 5.0.6, fixed in 5.1.0; it was not the cause of our rendering crash). twad_key_deletes: since 9.0, RPFM 5.0.6 writes the key ofland_units_to_unit_abilites_junctionsthe wrong way round; 5.1.0 or later, or the key by hand (land_unitthenability), then check in game.- Ask the human before launching the game, driving the screen or publishing; delete nothing: tidy away, only the human deletes.
- Reading and documenting a file format, as RPFM and CAIME do: yes. Altering CA's programs or working around their limits: no, with one exception the workshop owns, stated plainly in the BOB guide: BOB run under a debugger for a new map's terrain. When a tool gets stuck, a control case, then the community (CAIME's Discord, tw-modding).
Sources and links§
- RPFM manual, especially Datacores, Optimising a mod and MCP; RPFM's code at tag v5.0.6 (menu locations, Build Startpos, server); releases; RPFM 5.0.6 itself (labels, changelog).
- tw-modding wiki: RPFM For Dummies (written for RPFM 2.3.2, old menus), RPFM Post-Setup for Beginners, Localisation, Submodding, Startpos, Lua/First Script, Example Scripts.
- Startpos of Immortal Empires and Realm of Chaos: MIXER – Mixu's Unlocker, by Mixu.
- Units: Creating Custom Units for WH3, by Casualfield (linked from tw-modding), Unit Card Guide, Building units allowed tables, Units custom battle permissions tables.
- Lords: Custom Lords & Heroes (Warhammer II, by Nanu), Agent subtypes tables, Character skill nodes tables.
- Scripts, by Vandy: Campaign Custom Starts, campaign_manager (
cm:create_force_with_general,cm:is_new_game) and episodic_scripting (cm:spawn_character_to_pool,cm:transfer_region_to_faction,cm:add_unit_to_faction_mercenary_pool). - 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. - The Red Duke example: Making the Red Duke playable (lore, story, status of each mechanic); CA's
victory_objectives_config.luaandwh3_dlc27_dechala_narrative.lua, read in the game's files (the first on 9.0, the second on patch 8.1). - Our other guides: the modder's tools, CAIME, Terry, BOB, modding with an AI; and the modders' workshop.
Frequently asked questions
Which RPFM version should I use for Warhammer III?
The latest. This guide was written on 5.0.6 (released on 1 August 2026), whose menus it follows; since then 5.1.0 (28 September) and 5.1.1 (4 October) have come out, fixing the MCP sessions' memory leak and the order of the twad_key_deletes keys since 9.0. The online manual describes 5.1, 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 CA legendary lord playable when he isn't?
Like the Red Duke: his faction playable and major (start_pos_factions), the character in start_pos_characters, a frontend_faction_leaders record (a copy of a neighbouring one) linked through start_pos_starting_general_options, his subtype declared for the campaign (campaign_to_agent_subtypes), his heroes allowed (faction_agent_permitted_subtypes), then a regenerated startpos. His mechanics come next: missions, incidents, dilemmas and effect bundles under your keys, driven by Lua scripts.
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:// to your assistant; with Claude Code: claude mcp add --transport http rpfm http://.
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 probably doesn't take priority over the game's (data__). The safe choice: start your file's name with @ or !, keep the game row's key, 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. Since 9.0, RPFM 5.0.6 writes the key of land_units_to_unit_abilites_junctions the wrong way round: take 5.1.0 or later, or write it by hand, and check in game.
Why does the game close as soon as it starts?
A quit_after_campaign_processing; line was left in user.script.txt (%APPDATA%\): 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. If it closes within seconds with no report and no mp_log: a *_ownership_content_pack_junctions table for a key of yours? Remove it; otherwise, remove one table at a time on a copy of the pack.
This guide as plain text (Markdown), for AIs and offline reading
This is a community guide
A mistake, a pitfall you hit, a tip or a newer tool version: tell us, and every correction will be checked, then added with its source.