Workshop journal · · 12 min read
Making the Red Duke playable: from lore to game tables
In Warhammer I's mini-campaign, only Orion and Durthu were playable. In our port, the Red Duke has been playable since 23 September, and the 9.0 vampire rework let us give him mechanics of his own. Here they are, where they stand, and the technical side: tables, Lua, text, pack and tests.

The starting point: a duke without his dukedom
The Red Duke, le Duc écarlate in French, comes from Games Workshop's lore. Once Duke of Aquitaine, turned vampire, he was defeated at the First Battle of Ceren Field and walled up in a marble tower; set free, he returned for the second, where the tomb of his son Galand put him to flight. Sources: the Vampire Counts army book (7th edition), C. L. Werner's novel The Red Duke, Circle of Blood and Knights of the Grail.
According to Warhammer I's starting data, he is already in the Season, leading Mousillon, an AI faction at war with Bastonne; you couldn't play him. Yet in the lore, his land is Aquitaine: Mousillon is CA's choice, and CA doesn't make that faction playable.
Playable on 23 September, rethought with 9.0
On 23 September at 5:15 am, I asked for four more lords on the selection screen, the Duke among them; by 5:47 am, they were there. His selection card is a copy of Mannfred's, with its columns replaced, and his scene is his own, built on Vlad's (sword and shield, like the Duke), with the Duke's model swapped in.
But playable doesn't mean gripping. That same day, I asked for a Claude session to take him on after 9.0, so “the Red Duke fits in properly with all of it, with his own mechanics, his own quests, his own goals”. The method: sourced lore first, proposals next, and I choose. (My quotes are translated from French.)
He starts at Mousillon with Château Rachard, Martel and Yremy, at war with Bastonne and, since 24 September, with Aquitaine: Richemont of Aquitaine ordered that he be hunted across all of Bretonnia, an order still in force (The Red Duke; Knights of the Grail). With Bastonne, peace is blocked for fifteen turns then reopened, as between Kemmler and Parravon, because the AI signed peace around turn 6 and stopped fighting (coded but not yet seen in game).
Since 9.0, he has the Vampire Counts rework: lairs, bloodlines, corpses and Blood Decrees. His first lair is at Derrevin Libre, in his old dukedom, below the ruins of Crac de Sang (a place from the novel, put on our map by the project). His technologies are those of CA's vampire lords, with corpse thresholds halved, since our map is much smaller than Immortal Empires. And Abhorash's Kiss (The Red Duke): his faction starts with one level of the Blood Dragon bloodline.
All of this groundwork is being tested: coded and seen in short automated test runs (2 to 5 turns). New, coded but not yet seen in game: two more lairs, drawn at random, with the Duke served first.
His mechanics, one by one
At CA, no vampire lord has a mechanic of his own, except Neferata; the Duke's come down to Lua scripts, effect bundles, dilemmas, incidents and missions.
- The chronicle: five steps, from Crac de Sang to The Blood of Aquitaine, then Douse the Lighthouse (Turris Vigilans and Alberic) and The Sealed Palace, up to the finale, The Grail Crusade. First step being tested; the next ones, tested outside the game.
- The victories: the short one, retake the province of Aquitaine, build the Tomb of the Red Duke there, at Gien, and use Blood Decrees five times; the long one, ten Blood Decrees, two bloodline lords awakened in lairs, Aquitaine and Quenelles destroyed, the sealed palace of Mousillon reopened and his finale. They reward you without ending the game: only domination (27 of the 57 settlements) ends it. Being tested.
- Richemont's Decree: Aquitaine wars on him with no peace possible. If he takes a domain of Aquitaine, Questing Knights arrive three turns later, then, from turn 8, every ten turns as long as he holds one, 10 to 18 units depending on the turn. Coded but not yet seen in game.
- The Tomb of Galand: each turn spent on Ceren Field, below Gien (an estimated location, for lack of a source), his units lose 15% of their strength, and an illustrated incident announces it. Coded but not yet seen in game.
- The lost dukedom: an effect bundle depending on the domains of Aquitaine he holds (Chateau d'Epee, Derrevin Libre, Gien): “The Dispossessed Duke” with none, leadership and public order down; “The Duke Restored” with all three, leadership, income and replenishment up. Lore: he takes his dukedom back from those he calls usurpers (The Red Duke). Coded but not yet seen in game.
- Abhorash's favour: CA's Blessing of the Lady, turned inside out. When the Duke wins a battle he leads, especially a heroic victory, his army gains melee attack and leadership, in three degrees; the favour is lost if he withdraws, if he is beaten or on Ceren Field, and wears off without battle. Lore: the Blood Dragons' code of honour (Vampire Counts, 6th edition). Coded but not yet seen in game.
- The blood tax and the blood vassals: two dilemmas. For each Bretonnian region taken, levy the tax (Blood and corpses, but the hunt draws closer) or spare the peasants; a Bretonnian lord reduced to a single holding can become his vassal by blood. Safeguards, so the Duke isn't “overpowered”: two vassals at most (three once the dukedom is restored), never Aquitaine, oaths that can break, less public order per vassal. The tax comes from the lore; the vassals are inspired by it. Coded but not yet seen in game.
- Items and heroes: his two 9.0 CA items, the Armour of Blood and the Jewel of Unlife, as he ranks up; three legendary heroes allowed, Walach Harkon, a Blood Dragon like him, Krell and Dieter Helsnicht. Coded but not yet seen in game.
The numbers are design choices, to be tuned in game: no long game has been played, and balance hasn't been measured.
Tech 1: from Python batches to game tables
Everything the game reads from its database goes, for us, through batches: Python functions that can be replayed, which write into the XML tables of the Assembly Kit, CA's official kit, by copying a CA row under our keys or building a new row on its model. Nothing is rewritten, everything is backed up before writing.
Golden rule: keys of our own, never a CA table replaced, so that campaigns coexist with the mod active; on 23 September, two replaced CA tables crashed a tooltip. Everything that belongs to the Duke starts with saison_duc_. His batches:
- 13:
faction_to_mercenary_set_junctions, his regiments of renown. - 17: starting armies, treasury, second lord (
start_pos_*tables). - 31: the two support factions of the 9.0 vampires and our campaign's unique heroes (
campaign_to_agent_subtypes). - 33:
faction_agent_permitted_subtypes, which allows Walach Harkon, Krell and Dieter Helsnicht for Mousillon; without these rows, the technologies that grant them gave the played Duke nothing. - 34:
effect_bundles, seven effect bundles under our keys; their effects, CA's, are applied by the script. - 35:
dilemmasand its choice and option tables. - 32 and 37:
incidents, the Duke's nine announcements, with no target or payload: the effects stay in the script.
A second file tells the pack which rows to take (kit table, game table, column, prefix):
TABLES_LOT34 = [
('effect_bundles', 'effect_bundles_tables', 'key', 'saison_duc_'),
]These tables are new in a played pack: the rule calls for a startup test before any announcement, since a single table too many once closed the game without a message.
Tech 2: RPFM and the pack
RPFM, Frodo45127's Rusted PackFile Manager, is the community tool for packs; since version 5, its windowless server, rpfm_server, exposes its functions to an AI. Our build_pack.py drives it: it reads our rows from the kit and copies them in the order of the game's schema, which is what counts (missing column: empty value; extra column: dropped). One trap: RPFM wants rows in “processed” format, a colour as a single value.
It also checks each Lua script's syntax, since one error makes the whole campaign load fail, and refuses any file that would take the place of a Warhammer III file.
Tech 3: start tables and the startpos
A lord becomes playable mostly in the startpos, the start position: for the Duke, start_pos_factions (playable and major faction), start_pos_characters, start_pos_starting_general_options (the link to his card) and start_pos_land_units. These tables never ship in the mod: a separate pack is used for generation, and the game itself builds the startpos. The recipe:
- The batch writes into the kit; the start tables go into the generation pack.
- Validation: no cell to fix; pack rebuilt.
- The game generates the startpos.
- Checks: save counter at 1 (otherwise every new game thinks it's a saved one), structure compared with the previous one.
- The startpos goes into the pack; then the test.
Details and traps: the tools guide.
Tech 4: the Lua scripts
The Duke has his own file, saison_duc.lua, loaded by required.lua. On every load, saison_start.lua starts the 9.0 vampire systems in CA's order, then the Duke's, whose Kiss of Abhorash borrows the bloodline ritual. The rest is event listeners:
RegionFactionChangeEvent: a domain of Aquitaine changes hands, or a Bretonnian region falls to the played Duke.DilemmaChoiceMadeEvent: a dilemma's choice.BattleCompletedandCharacterWithdrewFromBattle: the favour, won or broken.FactionBeginTurnPhaseNormal: the tomb, the vassals, the favour wearing off, the hunt.ScriptEventVampireLairAwakened, a CA event: the awakenings for the long victory.
The turn listener, shortened:
core:add_listener(
"saison_duc_tour",
"FactionBeginTurnPhaseNormal",
function(context) return context:faction():name() == "wh_main_vmp_mousillon" end,
function(context)
local faction = context:faction();
saison_sur("tombeau de Galand", tombeau_de_galand, faction);
if faction:is_human() then
saison_sur("vassaux du sang", tour_des_vassaux, faction);
saison_sur("usure de la faveur", user_la_faveur, faction);
end;
end,
true
);The Duke's data goes into CA's Lua tables on load (saison_donnees_ca.lua). For victories, CA's engine is loaded from its own folder, never copied, and its Immortal Empires list replaced by our ten lists, written with its own objective generators; CA gives the Duke no list.
What 9.0 changed:
- The old blood kiss system is gone, and our tuning with it.
FactionTurnStartreached no mod listener on our map; ours moved toFactionBeginTurnPhaseNormal. Cause found and fixed in the automated tests, to be confirmed in a played game: three CA listener conditions, for Mother Ostankya and Yuan Bo, read systems absent from our map, and their error cut the event off.- In the released game, an error in one listener stops the event for all, CA's included: ours are protected (
saison_ecouteur,saison_sur) and the error goes to the log. - CA's technology that confederates the Red Duke would, if researched by the played Duke, absorb his own faction: it is locked for him.
Tech 5: text, in French and English
Our text lives in textes_gameplay.json, with French and English for each key. A game text key is <table>_<column>_<row key>: dilemmas_localised_title_saison_duc_impot_du_sang gives “L'impôt du sang” and “The Blood Tax”.
A check first looks for defects. French goes into the main pack, English into a small pack that loads first, under the same path. Golden rule: never overwrite a game text, which Warhammer III would load whatever the player's language; a key the game knows is removed, and the game supplies it itself, up to date.
Names follow the game's text: in 9.0, CA's French calls the Duke “Duc écarlate” in 21 texts, including his own name, against 3 “Duc Rouge”; we fixed 22 texts. With no voiced line for him, I chose a silent flyover with its text for his prologue (coded but not yet seen in game).
Tests: what they see, what they don't
Any pack that adds a table goes through a startup test. Then come the automated games: a separate test pack launches the game through the interface, like a player, and plays turn after turn.
Their limit: when the AI plays everything, the player's faction doesn't play. The lost dukedom and the tomb also apply to an AI Duke, but the hunt, the favour and the dilemmas only target the played Duke. So, for the most part, the Duke can only be seen by playing him.
A Duke test on 23 September showed that on turn 1 nobody had an enemy, hence the starting wars; a 29-turn game with Drycha saw Grom and the AI Duke wipe out Bordeleaux before turn 20, hence, between AI factions, no war on Bordeleaux before turn 15 (coded but not yet seen in game).
For the chronicle, a test bench runs our scripts in a Lua interpreter, with an imitation of CA's interface: that's “tested outside the game”. The real test will be my own game, with ten things to watch.
Next
Tune these mechanics' timings and strength in game. Other mechanics are in preparation.
The Duke's card and the mechanics are on the home page; the porting method is in the tutorial.