I bet you all thought I forgot about this thread, eh? Not so. I actually just never got around to figuring out how the heck to find the proper vtable pointers, and so gave up on doing it the hard way. In other news, I decided to give DFHack a try and as one might suspect, it makes life so much easier for this. Better yet, it allows allocation of memory so you don't have to hijack an existing relationship to marry two people together.
Here's the Lua script for marrying any two dwarves (or whatevers). I threw this together while teaching myself DFHack's API and Lua at the same time, so there is only minimal error checking. This seems to work fine for me, but there may be problems that could crash your game or corrupt your save. You've been warned! Make a backup first!
-- Marries two specified creatures. Take care when using this on creatures that are already married!
local args = {...}
function marry (unit_id1, unit_id2)
local victim1, historic_victim1
local victim2, historic_victim2
for key, value in pairs(df.global.world.units.all) do
-- The arguments are strings, but the structs contain numbers
if value.id == unit_id1 then
victim1 = value
end
if value.id == unit_id2 then
victim2 = value
end
end
if df.isnull(victim1) then
print('The first unit was not found.')
return
end
if df.isnull(victim2) then
print('The second unit was not found.')
return
end
if victim1.relations.spouse_id ~= -1 then
print('Warning: the first unit is already married. Their marriage will be replaced.')
end
if victim2.relations.spouse_id ~= -1 then
print('Warning: the second unit is already married. Their marriage will be replaced.')
end
print("Marrying " .. victim1.name.nickname .. " and " .. victim2.name.nickname)
for key, value in pairs(df.global.world.history.figures) do
if value.id == victim1.hist_figure_id then
historic_victim1 = value
end
if value.id == victim2.hist_figure_id then
historic_victim2 = value
end
end
if df.isnull(historic_victim1) then
print('The historical figure for the first unit was not found.')
return
end
if df.isnull(historic_victim2) then
print('The historical figure for the second unit was not found.')
return
end
local new_link1 = df.histfig_hf_link_spousest:new()
local new_link2 = df.histfig_hf_link_spousest:new()
-- Not documented, but this is the historical figure id
new_link1.anon_1 = victim2.hist_figure_id
new_link1.link_strength = 100
new_link2.anon_1 = victim1.hist_figure_id
new_link2.link_strength = 100
local link_count1 = #historic_victim1.histfig_links
local link_count2 = #historic_victim2.histfig_links
historic_victim1.histfig_links:resize(link_count1 + 1)
historic_victim1.histfig_links[link_count1] = new_link1
historic_victim2.histfig_links:resize(link_count2 + 1)
historic_victim2.histfig_links[link_count2] = new_link2
victim1.relations.spouse_id = victim2.id
victim2.relations.spouse_id = victim1.id
end
if df.isnull(args[1]) or df.isnull(args[2]) then
print('You must pass in two unit ids.')
return
end
dfhack.with_suspend(marry, tonumber(args[1]), tonumber(args[2]))
Just toss that in a file called marry.lua in your DFHack scripts folder. To use it, you'll need to run this command:
marry unit_id1 unit_id2
So you need to get those unit ids. The fastest way I know of to do that is to use the 'k' look around command and put it over each dwarf, then run this command inside the interactive Lua interpreter:
print(dfhack.gui.getSelectedUnit().id)
Do that for each dwarf, then run the marry command above (only once!) and let the wedding bells ring. There's a lot that could be done to tweak the script, especially error checking and sanity checking. It will most certainly let you try to marry someone to multiple people (of either gender!), which will work for the relationships screen, but in the invisible relations section they're only really married to the last one you run through this command.
Next up is probably a divorce script, which should be pretty simple too.
Note that you can marry two dwarves of the same gender with this, and two females will not get pregnant if married together. You can also marry someone to his or herself, if you're feeling weird. This will probably work with anything that has a historical figure entry, but expect oddness if you try it on anything but whatever species and civ you're playing as.
Edit: Here's the divorce script promised. Just toss this into divorce.lua:
-- Divorces a creature from its spouse and / or lover. Run this per creature in a relationship.
local args = {...}
function divorce (unit_id)
local victim, historic_victim
for key, value in pairs(df.global.world.units.all) do
-- The arguments are strings, but the structs contain numbers
if value.id == unit_id then
victim = value
end
end
if df.isnull(victim) then
print('The unit was not found.')
return
end
if victim.relations.spouse_id == -1 and victim.relations.lover_id == -1 then
print('Warning: the unit has no lover or spouse, nothing to do.')
return
end
print("Divorcing " .. victim.name.nickname)
for key, value in pairs(df.global.world.history.figures) do
if value.id == victim.hist_figure_id then
historic_victim = value
end
end
if df.isnull(historic_victim) then
print('The historical figure for the unit was not found.')
return
end
local link_count = #historic_victim.histfig_links
-- Remove the spouse hf link, if it exists
for key, value in pairs(historic_victim.histfig_links) do
if df.histfig_hf_link_spousest:is_instance(value) then
if key ~= link_count -1 then
-- Moves the spouse entry to the end of the list, then resizes the list to size - 1, erasing it
historic_victim.histfig_links[key] = historic_victim.histfig_links[link_count - 1]
end
historic_victim.histfig_links:resize(link_count - 1)
link_count = link_count - 1
value:delete()
break
end
end
-- Remove the lover hf link, if it exists
for key, value in pairs(historic_victim.histfig_links) do
if df.histfig_hf_link_loverst:is_instance(value) then
if key ~= link_count -1 then
-- Moves the lover entry to the end of the list, then resizes the list to size - 1, erasing it
historic_victim.histfig_links[key] = historic_victim.histfig_links[link_count - 1]
end
historic_victim.histfig_links:resize(link_count - 1)
link_count = link_count - 1
value:delete()
break
end
end
victim.relations.spouse_id = -1
victim.relations.lover_id = -1
end
if df.isnull(args[1]) then
print('You must pass in a unit id.')
return
end
dfhack.with_suspend(divorce, tonumber(args[1]))
To run it, just execute this command from DFHack's top level interface:
divorce unit_id1
The important thing to notice here is that it only takes in one unit id. You'll need to run it for each person you want to divorce. This will allow you to divorce a person from their dead spouse or lover, for example.