Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  

Author Topic: DFHack lua script - Modifying Skills after embark  (Read 1475 times)


  • Bay Watcher
    • View Profile
DFHack lua script - Modifying Skills after embark
« on: December 08, 2017, 08:10:49 pm »

So I've begun writing some of my own scripts. I intend to make a lua script that will distribute skills among my dwarf population based on some table values, randomly generating the values for the skills (again with table values).

I'm getting hung up on the skills array, I finally found it but it is completely different than the skills array in dwarf_info from 'embark-skills.lua'. So now I have no idea what skills are what, because from embark-skills they are all accessed by name using strings which made modifying and expanding that script rather easy. However I'm no longer in embark mode and I want to go back and re-distribute skills more efficiently.

Is there a way for me to convert the skills:id field to a name? I don't fully grasp the API, so I'm hoping there is a function in there that can perform the necessary task.

Something similar to this is what I was hoping for (but the skills in souls[0] has more data in it than I expected):
Code: [Select]
for _, unit in ipairs( do
 if dfhack.units.isCitizen(unit) then
  for skill, level in pairs(unit.status.souls[0].skills) do
   unit.status.souls[0].skills[skill] = level + 1

I am going to need to loop through the skills array, but I need to check each skill against the skills listed in my list of worker types. This way I can give workers the specific skills they need.


  • Bay Watcher
    • View Profile
Re: DFHack lua script - Modifying Skills after embark
« Reply #1 on: December 09, 2017, 04:57:16 am »

You should prefer current_soul to souls[0]--not that they'll be any different without someone else messing with them, it's just convention.

unit_skills do indeed have more info than just level. You'll want to get the level from the unit skill to change it.

df.job_name[] will get you the string for that skill. This can be seen in the XML:

Code: [Select]
<enum base-type='int16_t' name="id" type-name='job_skill'/>


  • Bay Watcher
    • View Profile
Re: DFHack lua script - Modifying Skills after embark
« Reply #2 on: December 09, 2017, 02:32:00 pm »

Thank you for pointing me in the right direction. You got the identifier wrong, but I found it after I did a print on all df entries with the substring 'job'.
Code: [Select]
function DisplayTable(t,query)
    for i,k in pairs(t) do
        if query ~= nil then
            if string.find(i, query) then

It was `df.job_skill[id]`
Code: [Select]
local ok,f,t,k = pcall(pairs,dwf.status.current_soul.skills)
if ok then
    for k,v in f,t,k do
        v.rating = 0

I wouldn't have found it if not for you, thanks!
« Last Edit: December 10, 2017, 01:02:09 am by sagenth »


  • Bay Watcher
    • View Profile
Re: DFHack lua script - Modifying Skills after embark
« Reply #3 on: December 09, 2017, 03:48:59 pm »

yeah, sorry, it was 2 am, i was running on fumes

you can just check if the unit has current_soul instead of using pcall, that's basically the only situation where that would error


  • Bay Watcher
    • View Profile
Re: DFHack lua script - Modifying Skills after embark
« Reply #4 on: December 10, 2017, 01:01:04 am »

Oh. I was only using it because I thought I needed to.
Originally I found that I couldn't write to skill.rating. Upon looking at one of the basic scripts, I found code using that form of a for loop. When I used it I found my write issue disappeared.

Today I rewrote a bunch of the code, so that for loop was removed anyway. Oddly it works just fine now. Not sure what I fubared the first time, but I obviously fixed it.

Now I just need a nice way to define my dorf jobs. Check it out:
Code: [Select]
dorf_jobs = {
    miner = {
        stats = {
            current = 0,
            total = 4,
            busy = 80,
            strength = 4800,
            intellect = 1000
        mining = {
            min = 7,
            max = 12
    architect = {
        stats = {
            current = 0,
            total = 3,
            busy = 65,
            strength = 3500,
            intellect = 2400
        designbuilding = {

It goes on for several hundred lines. It is extremely painful to tweak, and I want it to match the df.professions table more closely.


  • Bay Watcher
    • View Profile
Re: DFHack lua script - Modifying Skills after embark
« Reply #5 on: December 10, 2017, 01:07:32 am »

I don't know of a way of automatically coverting but here's a table I found somewhere.  It's accurate for 43.x I don't know if anything's changed in 44

Code: [Select]
0 'Mining' MINING Type: Normal
1 'Wood Cutting' WOODCUTTING Type: Normal
2 'Carpentry' CARPENTRY Type: Normal
3 'Engraving' DETAILSTONE Type: Normal
4 'Masonry' MASONRY Type: Normal
5 'Animal Training' ANIMALTRAIN Type: Normal
6 'Animal Caretaking' ANIMALCARE Type: Normal
7 'Fish Dissection' DISSECT_FISH Type: Normal
8 'Animal Dissection' DISSECT_VERMIN Type: Normal
9 'Fish Cleaning' PROCESSFISH Type: Normal
10 'Butchery' BUTCHER Type: Normal
11 'Trapping' TRAPPING Type: Normal
12 'Tanning' TANNER Type: Normal
13 'Weaving' WEAVING Type: Normal
14 'Brewing' BREWING Type: Normal
15 'Alchemy' ALCHEMY Type: Normal
16 'Clothes Making' CLOTHESMAKING Type: Normal
17 'Milling' MILLING Type: Normal
18 'Threshing' PROCESSPLANTS Type: Normal
19 'Cheese Making' CHEESEMAKING Type: Normal
20 'Milking' MILK Type: Normal
21 'Cooking' COOK Type: Normal
22 'Growing' PLANT Type: Normal
23 'Herbalism' HERBALISM Type: Normal
24 'Fishing' FISH Type: Normal
25 'Furnace Operation' SMELT Type: Normal
26 'Strand Extraction' EXTRACT_STRAND Type: Normal
27 'Weaponsmithing' FORGE_WEAPON Type: Normal
28 'Armorsmithing' FORGE_ARMOR Type: Normal
29 'Metalsmithing' FORGE_FURNITURE Type: Normal
30 'Gem Cutting' CUTGEM Type: Normal
31 'Gem Setting' ENCRUSTGEM Type: Normal
32 'Wood Crafting' WOODCRAFT Type: Normal
33 'Stone Crafting' STONECRAFT Type: Normal
34 'Metal Crafting' METALCRAFT Type: Normal
35 'Glassmaking' GLASSMAKER Type: Normal
36 'Leatherworkering' LEATHERWORK Type: Normal
37 'Bone Carving' BONECARVE Type: Normal
38 'Axe' AXE Type: MilitaryWeapon
39 'Sword' SWORD Type: MilitaryWeapon
40 'Knife' DAGGER Type: MilitaryWeapon
41 'Mace' MACE Type: MilitaryWeapon
42 'Hammer' HAMMER Type: MilitaryWeapon
43 'Spear' SPEAR Type: MilitaryWeapon
44 'Crossbow' CROSSBOW Type: MilitaryWeapon
45 'Shield' SHIELD Type: MilitaryDefense
46 'Armor' ARMOR Type: MilitaryDefense
47 'Siege Engineering' SIEGECRAFT Type: Normal
48 'Siege Operation' SIEGEOPERATE Type: Normal
49 'Bowmaking' BOWYER Type: Normal
50 'Pike' PIKE Type: MilitaryWeapon
51 'Lash' WHIP Type: MilitaryWeapon
52 'Bow' BOW Type: MilitaryWeapon
53 'Blowgun' BLOWGUN Type: MilitaryWeapon
54 'Throwing' THROW Type: MilitaryAttack
55 'Machinery' MECHANICS Type: Normal
56 'Nature' MAGIC_NATURE Type: Normal
57 'Ambush' SNEAK Type: Normal
58 'Building Design' DESIGNBUILDING Type: Normal
59 'Wound Dressing' DRESS_WOUNDS Type: Medical
60 'Diagnostics' DIAGNOSE Type: Medical
61 'Surgery' SURGERY Type: Medical
62 'Bone Setting' SET_BONE Type: Medical
63 'Suturing' SUTURE Type: Medical
64 'Crutch-walking' CRUTCH_WALK Type: Personal
65 'Wood Burning' WOOD_BURNING Type: Normal
66 'Lye Making' LYE_MAKING Type: Normal
67 'Soap Making' SOAP_MAKING Type: Normal
68 'Potash Making' POTASH_MAKING Type: Normal
69 'Dyeing' DYER Type: Normal
70 'Pump Operation' OPERATE_PUMP Type: Normal
71 'Swimming' SWIMMING Type: Personal
72 'Persuasion' PERSUASION Type: Social
73 'Negotiation' NEGOTIATION Type: Social
74 'Judging Intent' JUDGING_INTENT Type: Social
75 'Appraisal' APPRAISAL Type: Normal
76 'Organization' ORGANIZATION Type: Normal
77 'Record Keeping' RECORD_KEEPING Type: Normal
78 'Lying' LYING Type: Social
79 'Intimidation' INTIMIDATION Type: Social
80 'Conversation' CONVERSATION Type: Social
81 'Comedy' COMEDY Type: Social
82 'Flattery' FLATTERY Type: Social
83 'Consoling' CONSOLE Type: Social
84 'Pacification' PACIFY Type: Social
85 'Tracking' TRACKING Type: Personal
86 'Studying' KNOWLEDGE_ACQUISITION Type: Social
87 'Concentration' CONCENTRATION Type: Personal
88 'Discipline' DISCIPLINE Type: Personal
89 'Observation' SITUATIONAL_AWARENESS Type: Personal
90 'Writing' WRITING Type: Cultural "Wordsmith"
91 'Prose' PROSE Type: Cultural "Writer"
92 'Poetry' POETRY Type: Cultural
93 'Reading' READING Type: Cultural
94 'Speaking' SPEAKING Type: Cultural
95 'Coordination' COORDINATION Type: Personal
96 'Balance' BALANCE Type: Personal
97 'Leadership' LEADERSHIP Type: Social
98 'Teaching' TEACHING Type: Social
99 'Fighting' MELEE_COMBAT Type: MilitaryAttack
100 'Archery' RANGED_COMBAT Type: MilitaryAttack
101 'Wrestling' WRESTLING Type: MilitaryUnarmed
102 'Biting' BITE Type: MilitaryUnarmed
103 'Striking' GRASP_STRIKE Type: MilitaryUnarmed
104 'Kicking' STANCE_STRIKE Type: MilitaryUnarmed
105 'Dodging' DODGING Type: MilitaryDefense
106 'Misc. Object' MISC_WEAPON Type: MilitaryWeapon
107 'Knapping' KNAPPING Type: MilitaryMisc
108 'Military Tactics' MILITARY_TACTICS Type: Normal
109 'Shearing' SHEARING Type: Normal
110 'Spinning' SPINNING Type: Normal
111 'Pottery' POTTERY Type: Normal
112 'Glazing' GLAZING Type: Normal
113 'Pressing' PRESSING Type: Normal
114 'Beekeeping' BEEKEEPING Type: Normal
115 'Wax Working' WAX_WORKING Type: Normal
116 'Climbing' CLIMBING Type: Personal
117 'Gelding' GELD Type: Normal
118 'Dance' DANCE Type: Normal
119 'Music' MAKE_MUSIC Type: Normal
120 'Singing' SING_MUSIC Type: Normal
121 'Keyboard Instrument' PLAY_KEYBOARD_INSTRUMENT Type: Normal
122 'Stringed Instrument' PLAY_STRINGED_INSTRUMENT Type: Normal
123 'Wind Instrument' PLAY_WIND_INSTRUMENT Type: Normal
124 'Percussion Instrument' PLAY_PERCUSSION_INSTRUMENT Type: Normal
125 'Critical Thinking' CRITICAL_THINKING Type: Normal
126 'Logic' LOGIC Type: Normal
127 'Mathematics' MATHEMATICS Type: Normal
128 'Astronomy' ASTRONOMY Type: Normal
129 'Chemistry' CHEMISTRY Type: Normal
130 'Geography' GEOGRAPHY Type: Normal
131 'Optics Engineer' OPTICS_ENGINEER Type: Normal
132 'Fluid Engineer' FLUID_ENGINEER Type: Normal
133 'Papermaking' PAPERMAKING Type: Normal
134 'Bookbinding' BOOKBINDING Type: Normal Includes quire-making


  • Bay Watcher
    • View Profile
Re: DFHack lua script - Modifying Skills after embark
« Reply #6 on: December 10, 2017, 01:29:43 am »

Thanks. That's cool, but most of my jobs have more than 5 labors involved. I really only want to share profession names in common with the df.professions. Part of my AssignJobs algorithm checks the default professions and checks if there are jobs matching those 2 profession names, then generates skill values for any of the labors listed in my table.

I think if I could just drop the min/max values. Then list the labors horizontally instead of vertically. I think I'd resolve the issue, but I like the idea of generating varying ranges.


  • Bay Watcher
    • View Profile
Re: DFHack lua script - Modifying Skills after embark
« Reply #7 on: December 16, 2017, 03:29:41 am »

This could be a new topic in of itself, but I figured since it is directly related to the topic of this thread it would be bad etiquette.

Anyways, I'm looking for insight and wisdom.
This script pimps out all the dwarves in a fort. Although I'm considering a mechanism for bypassing dwarves, it will likely be whether they have a custom profession set or not. The algorithm, although not yet rewritten for the new data structure, will go something like this:

Loop dwarves
>Select random "dorf_prof" entry (I will talk about this in a minute)
>>Check if there are jobs available for the required profession(s) of the dorf_prof
>>>If the required profession can be applied without issue, it is applied to the dwarf.
>>If there are more professions from the dorf_prof available to be inspected, then it starts randomly looping/selecting them.
>>>Each profession is checked for job openings, if there is a job opening then that profession is applied to the current dwarf.

This is where my issue comes in. I don't want my dwarves doing tooo much, and I don't want to be too restrictive in what jobs a dwarf can do.. except when I don't want my dwarves doing two jobs within a dorf_prof.

So, as promised, dorf profs are custom profession configurations custom professions, this includes required professions (ie. built-in df professions), priority professions to ensure work gets done, and then others to have variety and hit even more bases. On top of the professions it includes dorf_types, which can apply improved stat rolls for certain attributes, and even skills actually (this was a convenient way to make soldiers, because dorf_types can proc via random chance[some anyway]).

Back to the issue though, I can't seem to make myself happy with regards to how I solve profession exclusions/restrictions. Right now I have a 'busy' field which is intended to dictate how arbitrarily busy it will make a dwarf. When the dorf is 1.0 busy with all its assigned professions, then it can't add any more, nor if the dwarf is 0.88 busy can it assign a profession which has busy=0.121 defined in it. I am thoroughly displeased with this however.

Because, in addition to already figuring out how many dorfs can be in a profession (which is most important for the required professions, but is still considered for all professions), and also at what ratios to the total population. In addition to that I have another ratio to finely tune by having the busy field.

I can think of at most three solutions:
-busy field: professions are allotted a busy score, and dorfs can't surpass 1.0 in their total busy score.
-bit flags/(or probably just shared id numbers): professions are assigned a bit in a <to be determined>-bit wide field, and then professions that overlap are thus exclusive to one another
-random chance: assign a chance to roll each profession inside a dorf_profs profession(I'm gonna rename this to dorf_jobs after this post)

1) Already hate this, because its a perplexing ratio that makes the table setup super painful!
2) This could be nice, but I don't really~ want professions to be truly exclusive of one another. AND it could be even more difficult to setup adequately, and without major issues.
3) I kind of like this best, but it would be another ratio. It would also move all the pain inside dorf_profs, which I am quite happy with as it is now with no changes! Maybe that is where the pain needs to be though, since dorf_jobs share some professions.

So if anybody is reading this, hit me with some opinions, ideas, or your local weather.