Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  
Pages: 1 ... 3 4 [5]

Author Topic: Relationships Utility  (Read 17427 times)

Quietust

  • Bay Watcher
  • Does not suffer fools gladly
    • View Profile
    • QMT Productions
Re: Relationships Utility
« Reply #60 on: August 30, 2012, 07:46:50 am »

I'm pretty sure the pointer is a function pointer, as I disassembled the memory that it points to, and it looks like a function call.  Quite why this is the case I'm not sure, it could just be a callback function as part of the structure, or could be something more exotic like a polymorphic class vtable entry (I know almost nothing about how this is implemented, but from what I gather it's normally part of the object, not just a random pointer floating around).
If you're looking at the various histfig link classes, then what you're seeing is indeed the vtable - if a class has any virtual (i.e. polymorphic) functions, the very first field in the object will be a pointer to said list. DFHack contains definitions for most of those vtable functions within df-structures.
Logged
P.S. If you don't get this note, let me know and I'll write you another.
It's amazing how dwarves can make a stack of bones completely waterproof and magmaproof.
It's amazing how they can make an entire floodgate out of the bones of 2 cats.

Telgin

  • Bay Watcher
  • Professional Programmer
    • View Profile
Re: Relationships Utility
« Reply #61 on: February 02, 2013, 12:27:39 am »

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!

Code: [Select]
-- 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:

Code: [Select]
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:

Code: [Select]
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:

Code: [Select]
-- 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:

Code: [Select]
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.
« Last Edit: February 17, 2013, 08:56:22 pm by Telgin »
Logged
Through pain, I find wisdom.

eharper256

  • Bay Watcher
  • Legendary +5 Nep-Nep Fanboy
    • View Profile
    • Detarame
Re: Relationships Utility
« Reply #62 on: February 17, 2013, 04:22:12 pm »

Interesting. You might want to edit the original post to reflect the fact you've now got a DFHack script though, else people will bypass it automatically (I nearly did).
Logged
"If the world's a stage, and the people actors, then who the f**k has my script!?"
Community Fort "Lakebones"
My Games & Anime Blog, Detarame

Telgin

  • Bay Watcher
  • Professional Programmer
    • View Profile
Re: Relationships Utility
« Reply #63 on: February 17, 2013, 09:05:27 pm »

Quite right, I meant to do that some time ago but as usual forgot to do so.  That's been corrected.

I also edited in the divorce script I promised.  Both the marriage and divorce scripts have been tested and appear to work just fine.  I updated the marriage script, by the way.  The old version could run into problems with worlds of any significant age, but the new one should work for all worlds.
Logged
Through pain, I find wisdom.

Broken

  • Bay Watcher
    • View Profile
Re: Relationships Utility
« Reply #64 on: February 18, 2013, 06:29:42 am »

MUAHAHAHAHA!

My dictatorial powers are now complete. My helpless subjects will have to marry when and whom i so desire.

Thanls.
Logged
Quote
In a hole in the ground there lived a dwarf. Not a nasty, dirty, wet hole, filled with the ends of worms and an oozy smell, nor yet a dry, bare, sandy hole with nothing in it to sit down on or to eat: it was a dwarf fortress, and that means magma.
Dwarf fortress: Tales of terror and inevitability

EmperorJon

  • Bay Watcher
  • Still waiting...
    • View Profile
Re: Relationships Utility
« Reply #65 on: February 22, 2013, 09:45:04 pm »

Ahahah! I literally just came back for some DF-ing and remebered this thread, searched, and BAM, updated less than a month ago WITH PROGRESS.

You my friend are getting somewhere. I assume you intend to continue with some stuff? Will be very useful to set exactly who is who's friend etc. for story driven forts.

EDIT:



And I don't even know LUA... a bit of syntax analysis from what was already there, and a quick look on GitHub at the structures, and I can do... yeah... stuff. All hail the other members of the party, for they are like gods to us. Also there's a dodgy affair going on!
« Last Edit: February 22, 2013, 10:35:09 pm by EmperorJon »
Logged
I think it's the way towns develop now. In the beginning, people move into a town. Then they start producing tables, which results in more and more tables. Soon tables represent a significant portion of the population, they start lobbying for new laws and regulations, putting people to greater and greater disadvantage...
Link for full quote. 'tis mighty funny.

Gaybarowner

  • Bay Watcher
  • [SLOW_LEARNER] [VERMIN_HATEABLE]
    • View Profile
Re: Relationships Utility
« Reply #66 on: February 22, 2013, 10:46:27 pm »

Ahahah! I literally just came back for some DF-ing and remebered this thread, searched, and BAM, updated less than a month ago WITH PROGRESS.

You my friend are getting somewhere. I assume you intend to continue with some stuff? Will be very useful to set exactly who is who's friend etc. for story driven forts.

EDIT:



And I don't even know LUA... a bit of syntax analysis from what was already there, and a quick look on GitHub at the structures, and I can do... yeah... stuff. All hail the other members of the party, for they are like gods to us. Also there's a dodgy affair going on!
ITS HAPPENING!!
Logged

EmperorJon

  • Bay Watcher
  • Still waiting...
    • View Profile
Re: Relationships Utility
« Reply #67 on: February 22, 2013, 11:26:58 pm »

I've found where friendship data is stored...


:D :D :D

I dunno how to hack it apart using lua though... :( :(

I'm just stealing syntactical knowledge from stuff here... what I have so far as a command:

(Note the spelling mistake, this is what the structure is called)

for a,b in pairs(dfhack.gui.getSelectedUnit().status.acquintances) do print(a,b) end

This gives me a nice list. For example for one of my Dwarves printing .status.aquintances gives me 0x24370ba4, and running the above command then gives me:

0 0x1db08c90
1 0x1db08b58
2 0x1db08b28
3 0x1db08ae0
4 0x1db08ab0
5 0x1db08a98

Now... I don't know what I'm doing here. "acquintances" is obviously a pointer, which I have the value of... (or is that the value it's pointing to?) and then I now have another 6 hex values, of which I have no idea whether they're addresses holding the info, addresses pointing to the info, offsets to put on the original pointer, or whatever, nor how I would actually do any of that.

Any help appreciated, hopefully I can assist Telgin in his quest.

EDIT:



Format illogical, order nonsensical, but I'm learning!
« Last Edit: February 22, 2013, 11:54:12 pm by EmperorJon »
Logged
I think it's the way towns develop now. In the beginning, people move into a town. Then they start producing tables, which results in more and more tables. Soon tables represent a significant portion of the population, they start lobbying for new laws and regulations, putting people to greater and greater disadvantage...
Link for full quote. 'tis mighty funny.

Telgin

  • Bay Watcher
  • Professional Programmer
    • View Profile
Re: Relationships Utility
« Reply #68 on: February 23, 2013, 12:36:42 am »

Hehe, yes, it's possible to have a lover and spouse, and the game behaves surprisingly well under these circumstances.  The dwarf will never marry their lover if they've already got a spouse though.  The divorce script should remove both parts of the relationship if you need it to though.

And yep, the friends are stored in the acquintances list.  I considered writing a script to handle that, but haven't yet since it's less of a big deal.  It's easy enough to modify existing ones through the interactive Lua interface by setting the id, strength and friend levels, but adding or removing them is hard to do without a real script to handle it.  Maybe one day soon!
Logged
Through pain, I find wisdom.

EmperorJon

  • Bay Watcher
  • Still waiting...
    • View Profile
Re: Relationships Utility
« Reply #69 on: February 23, 2013, 10:12:43 am »

Yeah, I could write one now that altered them as far as I know, but because I don't understand how they're actually stored and set up I couldn't remove or add them. They're not a nice link like the others, they're an array, or a linked list, or something (I have no idea)... which means you have to know exactly where to stick the next value...
Logged
I think it's the way towns develop now. In the beginning, people move into a town. Then they start producing tables, which results in more and more tables. Soon tables represent a significant portion of the population, they start lobbying for new laws and regulations, putting people to greater and greater disadvantage...
Link for full quote. 'tis mighty funny.

Williham

  • Bay Watcher
    • View Profile
Re: Relationships Utility
« Reply #70 on: March 05, 2013, 03:01:49 pm »

I'm somewhat confident that pregnancy_mystery is a flag relating to wether or not to blend DNA.
Logged

Telgin

  • Bay Watcher
  • Professional Programmer
    • View Profile
Re: Relationships Utility
« Reply #71 on: March 05, 2013, 03:04:34 pm »

Someone mentioned in the DFHack thread that you must set pregnancy_mystery to 0 in order to get creatures with no male caste to give birth without a crash.  Normally you set it to 1.  Anything else results in a crash, so far as I know.

So... maybe that has something to do with blending genes from the parents, but I'm not sure.
Logged
Through pain, I find wisdom.

Williham

  • Bay Watcher
    • View Profile
Re: Relationships Utility
« Reply #72 on: March 05, 2013, 03:09:07 pm »

This script works; on married and unmarried dwarves:

Code: [Select]
-- Impregnates a creature

local args = {...}

function impregnate ()

  local victim = dfhack.gui.getSelectedUnit()
  local genes

  if (victim == nil) then
    return
  end

  genes = victim.appearance.genes:new()

  if (victim.relations.pregnancy_timer > 0) then
    print('Already pregnant!')
    return
  end

  victim.relations.pregnancy_ptr = genes;
  victim.relations.pregnancy_timer = 30;

  if (victim.relations.lover_id == 0) then
    victim.relations.pregnancy_mystery = 0;
  else
    victim.relations.pregnancy_mystery = 1;
  end

end

dfhack.with_suspend(impregnate)

Copy and paste into filename.lua.

(It's not highly tested.)
« Last Edit: March 07, 2013, 01:43:46 pm by Williham »
Logged

Putnam

  • Bay Watcher
  • DAT WIZARD
    • View Profile
Re: Relationships Utility
« Reply #73 on: April 06, 2013, 11:41:32 pm »

Can I take your marry function? I could use it for some things...

Telgin

  • Bay Watcher
  • Professional Programmer
    • View Profile
Re: Relationships Utility
« Reply #74 on: April 07, 2013, 02:16:27 am »

Sure, feel free to take it and make any changes you want to it.  Now you've got me curious.  :)
Logged
Through pain, I find wisdom.
Pages: 1 ... 3 4 [5]