Party time! :P Congrats on the new release! :DEasier methods exist...
Auto-unsuspend is set to 100 ticks, but for the sake of FPS death I think it could easily be set to 600. Once every half in-game day should work, tho I always play with 200 dwarfs and even big construction designations (over 100 blocks used at once) makes them swarm like flies. Not sure how 600 ticks would work on a smaller number of dwarfs.
And some food for the brain. While I was trading huge quantities yesterday and was watching my dwarfs swarming the trade depot bringing goods + close too 1000 stone blocks I was thinking about how to speed up this process. What I wonder is if it is possible that wheelbarrows could be assigned to trade depot. Now lets say that you could assign 100 wheelbarrows to the trade depot (like you can to stockpile), by each adding +1% speed to the dwarfs that caries the goods to and off the depo. So the dwarfs would not be running around with wheelbarrows to bring goods to the depot but they would just be faster when doing the bring goods to trade depot job. Not sure where the wheelbarrows would be stored tho. In case of trade depot which entity creates a job - a storage where goods are stored or the trade depot? I know it's the storage that actually creates jobs for hauling but in case of trade depot it could be different. Any ideas on this one?
Auto-unsuspend is set to 100 ticks, but for the sake of FPS death I think it could easily be set to 600. Once every half in-game day should work, tho I always play with 200 dwarfs and even big construction designations (over 100 blocks used at once) makes them swarm like flies. Not sure how 600 ticks would work on a smaller number of dwarfs.I was worried that 600 would be too slow with low FPS, although I could increase it or try to make it configurable, although I'm not exactly sure how persistent settings would work with Ruby scripts. Alternatively, we could add an automatic mode to the "resume" plugin to replace autounsuspend entirely.
Eastward 1 1I think this should be "fastdwarf 1 1" (in case anyone's confused).
lol damn phones... wasn't it fastdwarf? or did we name change and I've got an old script roaming around , lol.Auto-unsuspend is set to 100 ticks, but for the sake of FPS death I think it could easily be set to 600. Once every half in-game day should work, tho I always play with 200 dwarfs and even big construction designations (over 100 blocks used at once) makes them swarm like flies. Not sure how 600 ticks would work on a smaller number of dwarfs.I was worried that 600 would be too slow with low FPS, although I could increase it or try to make it configurable, although I'm not exactly sure how persistent settings would work with Ruby scripts. Alternatively, we could add an automatic mode to the "resume" plugin to replace autounsuspend entirely.Eastward 1 1I think this should be "teledwarf 1 1" (in case anyone's confused).
I have just been browsing the dfhack docs, and the steam-engine plugin has caught my eye (and imagination). But I am not sure if it is still current... at least, it doesn't appear to be running automatically, nor when I try 'enable steam-engine'. it says:It's a plugin, but it's not a plugin that provides commands you can run (so none of the commands you ran will work). The steam-engine documentation (http://dfhack.readthedocs.io/en/stable/docs/Plugins.html#steam-engine) should explain it. The key part:
steam-engine is not a recognized command.
steam-engine is a plugin but does not implement any commands
I googled around a bit but couldn't really find anything on it at all.
I am currently running the latest LNP with DF 43.05 & DFHack 43.05-beta on windows (and also on Linux but I haven't tried steam-engine on linux yet).
Any ideas how I should go about getting a steam engine running?
The steam-engine plugin detects custom workshops with STEAM_ENGINE in their token, and turns them into real steam engines.You need to create custom workshops in the raws, with "STEAM_ENGINE" in their token, and then the plugin should detect them and enable itself. There should be an example of this in the hack/raw folder.
-- Reset count if a new fort or reclaim, i.e. onload.init runs prior to load of embark screen.
if df.global.gps.screen.value == 0 then
okay I found this in a script in MW repository that is causing a slight issue.Yeah, that has nothing at all to do with the embark screen. That detects whether the tile in the upper left corner of the screen is blank, has a black background color, has a black foreground color, or doesn't have a bold/bright foreground color (it's exactly one of those four; I'm not sure which). I definitely would not rely on that. It looks to me like someone just found something that seemed to correspond to the embark screen sometimes and decided to use it.
df.global.gps.screen.value
in this:Code: [Select]-- Reset count if a new fort or reclaim, i.e. onload.init runs prior to load of embark screen.
if df.global.gps.screen.value == 0 then
and apparently this does not spot an embark screen. I'm not even sure what it is spotting. But the reality is I want it moved to the onMapload.init anyway, so the question is at onMapload.init. Is there a way to detect If a map is new? Basically I want this counter to reset every time a new embark has occurred on the map, not every time the map is loaded. And for it to occur when onMapload.init is loaded.
any ideas on a direction to head in?
df.viewscreen_choose_start_sitest:is_instance(dfhack.gui.getCurViewscreen())
was_embarking = false
dfhack.onStateChange.your_script_name = function(event)
if event == SC_VIEWSCREEN_CHANGED then
if df.viewscreen_choose_start_sitest:is_instance(dfhack.gui.getCurViewscreen()) then
was_embarking = true
end
elseif event == SC_MAP_LOADED then
if was_embarking then
print('do stuff')
end
-- make sure this doesn't fire again the next time a map loads, unless the user embarks again
was_embarking = false
end
end
But a couple I find are rather useful, and one I thought was odd for not being a DFHack script, it needs some reformatting to fit the motif of DFHack, but its a fix for handedness of gloves produced by reactions... As far as I can tell its just checking every X ticks for unhanded gloves then just switching them left, right, left, right. Its in ruby, so I'm not fully on board with the language, but I think I can read other rb scripts in DFHack, and update the format to fit. and post it to the github if your interested in a such a fix... I couldn't find a similar fix inside of DFHack, other than the one in create-item.Is that autofixhandedness? It sounds familiar, but I couldn't find it in any Git history, and I'm not entirely sure what it was used for. Maybe I just heard about it from MDF.
new information... apparently dfhack persistent created tables are deleted when a map site is abandoned, at least my test map was doing that... I just removed the line of code and the whole thing returned to normal operation all together, as the saved counter disappeared when I abandoned the fortress. is that as intended? I'm going with it at the moment, and stick to this fix, unless someone has some otherI don't think that's intentional. Had you saved before abandoning? Persistent tables are still saved as histfigs, I believe, and those shouldn't normally be deleted when you abandon a fortress, as long as the world stays around.
How can I determine site-specific position assignments, like captain of the guard? I can get entity-wide position assignments using historical_entity.positions.assignments, but I can’t find the site equivalent.This (https://github.com/DFHack/scripts/blob/f6862e8c4351bc2e319df3e2b925c9b65eb7f1bb/gui/extended-status.lua#L51) is what gui/extended-status uses to find managers. I couldn't find anything in my fort's world_site instance ("df.world_site.find(ui.site_id)"), but it has two entries in "df.world_site.find(ui.site_id).entity_links", with one's entity_id equal to ui.civ_id and the other's entity_id equal to ui.group_id, and the assignments for the latter (what gui/extended-status uses) change when I assign a manager.
But a couple I find are rather useful, and one I thought was odd for not being a DFHack script, it needs some reformatting to fit the motif of DFHack, but its a fix for handedness of gloves produced by reactions... As far as I can tell its just checking every X ticks for unhanded gloves then just switching them left, right, left, right. Its in ruby, so I'm not fully on board with the language, but I think I can read other rb scripts in DFHack, and update the format to fit. and post it to the github if your interested in a such a fix... I couldn't find a similar fix inside of DFHack, other than the one in create-item.Is that autofixhandedness? It sounds familiar, but I couldn't find it in any Git history, and I'm not entirely sure what it was used for. Maybe I just heard about it from MDF.
class AutoFixHandedness
def initialize
end
def process
return false unless @running
# the colection of GLOVES
gloves=df.world.items.other[:GLOVES]
hand=0 # which hand 0=right, 1=left
count=0
gloves.each do |glove|
# if glove is unhanded (right and left = false, fix it
unless glove.handedness[0] or glove.handedness[1]
#puts glove.id
glove.handedness[hand] = true
hand ^= 1 # switch hand for next glove
count += 1
end
end
puts "Found #{count} unhanded glove(s)." unless count == 0
end
def start
@onupdate = df.onupdate_register('autofixhandedness', 2000) { process }
@running = true
@item_next_id = 0
end
def stop
df.onupdate_unregister(@onupdate)
@running = false
end
def status
@running ? 'Running.' : 'Stopped.'
end
end
case $script_args[0]
when 'start'
$AutoFixHandedness = AutoFixHandedness.new unless $AutoFixHandedness
$AutoFixHandedness.start
when 'end', 'stop'
$AutoFixHandedness.stop
else
if $AutoFixHandedness
puts $AutoFixHandedness.status
else
puts 'Not loaded.'
end
end
new information... apparently dfhack persistent created tables are deleted when a map site is abandoned, at least my test map was doing that... I just removed the line of code and the whole thing returned to normal operation all together, as the saved counter disappeared when I abandoned the fortress. is that as intended? I'm going with it at the moment, and stick to this fix, unless someone has some otherI don't think that's intentional. Had you saved before abandoning? Persistent tables are still saved as histfigs, I believe, and those shouldn't normally be deleted when you abandon a fortress, as long as the world stays around.
Hmmm, I just checked one of my active reclaimed forts, df.global.world.world_data.active_site.entity_links and looked at the bottom of the list, numbers list the site id and relevant entity, usually player created/claimed site groups are near or at the end, went to that entity and grabbed this shot:Yeah. Is that in assignments? I couldn't find it in assignments_by_type.Isn't that it?Spoiler (click to show/hide)
Oh yeah! Is there a simple way to recenter a screen on a unit or whatnot that I'm just a moron and overlooked? I couldn't figure out dfhack.screen.zoom() syntax at all though I tried feeding it my unit position, cursor position, unit id, and a couple other things.dfhack.gui.revealInDwarfmodeMap() (it takes a df.coord, I believe). dfhack.screen.zoom() zooms the screen in and out.
Hmmm, there's an update screen check too isn't there? Was annoying trying out a "teleport to the cursor" hotkeyable script because if you have the cursor out of view you have to move before it will refresh your screen fully. Even tried having it just straight up list that tile as revealed through the dfhack.maps.getTileBlock(blah).designations[xmath][ymath] stuff with no luck.Some tools that need that just feed CURSOR_DOWN_Z and CURSOR_UP_Z to the dwarfmode screen, although that doesn't work at the lowest z-level. I've been meaning to add something to the Gui module to handle that appropriately.
Yes it was... I couldn't find it anywhere online... but its just a basic repeating script... It works, its set to 2000 ticks if I understand it correctly, and in fortress mode that's just about perfect timing, I couldn't tell you how "optimized" it is. Here you go:Is there a reason why MDF needs it? I'm fine with including it, but I don't want to enable it by default unless it fixes something that comes up in vanilla often.
yeah I had, and on abandon, it reset the counter when I claimed a new site on the same map. I can try and do some more testing but It may be next week and I'll get back to you. I've got a load of things I'm trying to accomplish between today and monday, so when I can, its back on my list. I don't want to have to fix that set of scripts anymore...Weird. I don't see an obvious reason why it would be doing that. I'll look into it, though.
Yes it was... I couldn't find it anywhere online... but its just a basic repeating script... It works, its set to 2000 ticks if I understand it correctly, and in fortress mode that's just about perfect timing, I couldn't tell you how "optimized" it is. Here you go:
Is there a reason why MDF needs it? I'm fine with including it, but I don't want to enable it by default unless it fixes something that comes up in vanilla often.
Edit: also, I don't see anything particularly wrong with the formatting...
entities[k].positions.own had that, .assignments just lists the histfig that it's linked to, never even thought to check assignments_by_type actually.Hmmm, I just checked one of my active reclaimed forts, df.global.world.world_data.active_site.entity_links and looked at the bottom of the list, numbers list the site id and relevant entity, usually player created/claimed site groups are near or at the end, went to that entity and grabbed this shot:Yeah. Is that in assignments? I couldn't find it in assignments_by_type.Isn't that it?Spoiler (click to show/hide)
That'll work even if not in dwarf mode?QuoteOh yeah! Is there a simple way to recenter a screen on a unit or whatnot that I'm just a moron and overlooked? I couldn't figure out dfhack.screen.zoom() syntax at all though I tried feeding it my unit position, cursor position, unit id, and a couple other things.dfhack.gui.revealInDwarfmodeMap() (it takes a df.coord, I believe). dfhack.screen.zoom() zooms the screen in and out.
Yeah, I was trying to work out if I was just missing something or what, but then the lua api didn't mention anything remotely like what I was after.QuoteHmmm, there's an update screen check too isn't there? Was annoying trying out a "teleport to the cursor" hotkeyable script because if you have the cursor out of view you have to move before it will refresh your screen fully. Even tried having it just straight up list that tile as revealed through the dfhack.maps.getTileBlock(blah).designations[xmath][ymath] stuff with no luck.Some tools that need that just feed CURSOR_DOWN_Z and CURSOR_UP_Z to the dwarfmode screen, although that doesn't work at the lowest z-level. I've been meaning to add something to the Gui module to handle that appropriately.
I wouldn't expect it to work in adventure mode, but I haven't checked.Quotedfhack.gui.revealInDwarfmodeMap() (it takes a df.coord, I believe). dfhack.screen.zoom() zooms the screen in and out.That'll work even if not in dwarf mode?
Right, sorry I should have said all the technical stuff :)Thanks :)
I'm using the LNP (PeridexisErrant's Starter Pack 0.43.05-r03) which has DF 43.05 along with DFHack 0.43.05-r1 (thread: http://www.bay12forums.com/smf/index.php?topic=126076.0)
To be clear, I have the min quality set to ordinary and filter set to *any. I have say 4 beds prebuilt. I place 1 bed down in planning mode and a dwarf hauls a bar of coke over.Yeah, that is weird. Let us know if you can reproduce it again, and upload the save if you can.
I don't think this happens on a new game. I feel like I did something at some point that triggered this behaviour because it reliably does this as soon as I load up the save (even if I already have the furniture constructed). But when I load an earlier save, it doesn't happen and dwarfs will wait until I construct a bed even if there is coke bars available.
I tried forbidding all my coke bars from the stocks screen. When they are forbidden, dwarfs properly use normal constructed beds. After unforbidding, it seems this has fixed the behaviour... Very strange!!! I'll post again if anything interesting happens...
If you use the autoassign DFHack thing from LNP, how does it actually decide when to let things Fish? If I try to manually assign labors, it naturally unassigns them, and I'm not sure how to make it turn someone to fishing."The autoassign DFHack thing" is vague, although since you mentioned labors, I assume you mean labormanager (which is similar to autolabor, but different). I think if you mouse over that option in PyLNP, you'll get a tooltip with a more descriptive name, but I haven't tried.
Or should I just turn that hack off and do it the old fashion, manual way?
If you use the autoassign DFHack thing from LNP, how does it actually decide when to let things Fish? If I try to manually assign labors, it naturally unassigns them, and I'm not sure how to make it turn someone to fishing."The autoassign DFHack thing" is vague, although since you mentioned labors, I assume you mean labormanager (which is similar to autolabor, but different). I think if you mouse over that option in PyLNP, you'll get a tooltip with a more descriptive name, but I haven't tried.
Or should I just turn that hack off and do it the old fashion, manual way?
From the labormanager docs (http://dfhack.readthedocs.io/en/stable/docs/Plugins.html?highlight=labormanager#labormanager), it sounds like there are a couple things you need to do:
- Have a fishery
- Run "labormanager allow-fishing" in the DFHack console (and pay attention to the warning: "This tends to result in most of the fort going fishing")
dfhack: line 66: 16014 Segmentation fault (core dumped) setarch i386 -R env LD_PRELOAD="$PRELOAD_LIB" ./libs/Dwarf_Fortress "$@"
There was an issue almost like that reported in alpha3. I'd be surprised if it was happening in alpha4, but I would try upgrading to r1 and see what happens.
... I doubt it has much to do with "source". You could try diverting a river or creating more water using "gui/liquids" (or "liquids") and see if more blood shows up. Maybe the grate has blood on it, although I'm not sure if that's possible.OK, I'll do some !!SCIENCE!!. May be a while until I report back, though.
blood spatter is insidious... remaining on objects for years waiting to be rehydrated and passed around again...
I'd use clean in dfhack and use it to get rid of splatters on items and the maps. and then see if it comes back.So, even if the grates were made a long time after the Giant fight, and with lots of soap available, what Amostubal says is likely how it is. Especially as I have not yet used "clean" in this fortress, apart from a few "spotcleans" (inclkuding removing the spatter on the grates only to see it return, perhaps washed off another dwarf).
I didn't even know the aur had a dfhack build, I just use the arch df install as a base for the graphics fixes and slap the rest of my stuff on top when it's available.This is what I do as well now. But I used the AUR just for the fun of it. But it seems slow to update, and I'm usually very bleeding edge on DF.
...but surely it couldn't be THE FUCKING APPEARANCE AND SIZE MODIFIERS!
unk_324 is indeed the appearance modifiers. It was identified in pull request #190 (https://github.com/DFHack/df-structures/pull/190), but there are still some unknown fields.Well naturally I just started getting back into a scripty mood so I missed that one, and several of them depend on the creature raw so we won't be able to have like nose_length and ear_height due to the ones populated from the creature, but unk_7[0] is height, unk_7[1] is broadness, unk_11 is the creature_mat number for the following tissues, in this case hair (24) for my modded dwarves (440), unk_12 is the hair/hair/sideburn/sideburn/beard/moustache styles, unk_13 is the hair/hair/sideburn/sideburn/beard/moustache style types, unk_14 is tissue length but weirdly spaced so [4] and [6] were hair length, [5] was 0, same for the other tissues, unk_16 is hair/skin/eye colors.
Well naturally I just started getting back into a scripty mood so I missed that one, and several of them depend on the creature raw so we won't be able to have like nose_length and ear_height due to the ones populated from the creature, but unk_7[0] is height, unk_7[1] is broadness, unk_11 is the creature_mat number for the following tissues, in this case hair (24) for my modded dwarves (440), unk_12 is the hair/hair/sideburn/sideburn/beard/moustache styles, unk_13 is the hair/hair/sideburn/sideburn/beard/moustache style types, unk_14 is tissue length but weirdly spaced so [4] and [6] were hair length, [5] was 0, same for the other tissues, unk_16 is hair/skin/eye colors.unk_7[0] and unk_7[1] are not height and broadness: they are the first and second body modifiers. As it turns out, those are commonly height and broadness, but you have to look up the caste’s body_appearance_modifier to know for sure. Similarly, to determine nose length and ear height, consult unk_8 with reference to the caste’s bp_appearance.modifiers.
I found a reference to [PERMITTED_JOB:ALCHEMIST] in another thread, so that might be it. Alternatively, maybe permitting a reaction that increases the alchemy skill would work too. Or you could just disable the tweak, which should allow you to at least assign the labor.
Sorry I'm not able to be more helpful. If you do find something that works, we could add it to the documentation.
Edit: another thread: http://www.bay12forums.com/smf/index.php?topic=110704.msg7053207;topicseen#msg7053207
So I am trying to work on the robustness of modtools/create-unit (still occasionally getting crashes when using it) and was wondering if anyone else that uses it has documented any crashes? I think I know what causes a couple of them, but I'm hoping to eliminate all crashes (as I would like to use it extensively). So if you ever use it and get a crash could you please tell me what command you used, if your game was paused or running when you used it, game mode, and anything else you can think of.
Another small bug with create-unit I found.
When invoking create-unit while a modal dialog is visible, dwarf fortress is left in a pseudo arena-mode.
This is most visible with the dwarf unit lists only showing names and not professions.
I also noticed slightly fewer crashes by invoking create-unit a bunch of times inside a dfhack.with_suspend(..). But this is no big surprise.
Please report bugs in the GitHub issue tracker (http://github.com/DFHack/dfhack/issues). (Bugs reported in this thread can be buried sometimes.)(Really. I wasn't aware this was an issue at all.)
I also noticed slightly fewer crashes by invoking create-unit a bunch of times inside a dfhack.with_suspend(..). But this is no big surprise.I missed this the first time - with_suspend shouldn't be necessary. Where are you running the script from?
I missed this the first time - with_suspend shouldn't be necessary. Where are you running the script from?It is a lua script started manually through the dfhack console (link later in post). It forces a pause before calling create-unit.
additionally since this script can be paired with "create-item", the unit can be given another civ, and invader tags, I did a little work creating mini 1 man invaders out of this for a proof of concept script created force invasion, made it about half way through it, but abandoned it when I realized I could effectively call for a 50 man army in waves of 10 and effectively guarantee a crash. So I shelved it until someone with a little better idea of whats going on could fix the crash issues.
Segmentation fault (core dumped)
[DFHack]# *** Error in `./libs/Dwarf_Fortress': free(): invalid next size (normal): 0x00007fffc4031b30 ***
Aborted (core dumped)
./hack/lua/gui.lua:177: attempt to compare nil with number
stack traceback:
./hack/lua/gui.lua:177: in method 'inClipGlobalXY'
./hack/lua/gui.lua:408: in method 'getMousePos'
./hack/lua/gui/widgets.lua:384: in method 'onRenderBody'
./hack/lua/gui.lua:460: in method 'render'
./hack/lua/gui.lua:447: in method 'renderSubviews'
./hack/lua/gui.lua:462: in method 'render'
./hack/lua/gui.lua:563: in function <./hack/lua/gui.lua:562>
[C]: in ?
... repeating for ever
*** Error in `./libs/Dwarf_Fortress': malloc(): smallbin double linked list corrupted: 0x00007fffc4072070 ***
Aborted (core dumped)
./hack/lua/gui.lua:158: attempt to perform arithmetic on a nil value (field '?')
stack traceback:
./hack/lua/gui.lua:158: in local 'fun'
./hack/lua/class.lua:98: in upvalue 'invoke_after_rec'
./hack/lua/class.lua:94: in upvalue 'invoke_after_rec'
./hack/lua/class.lua:127: in function <./hack/lua/class.lua:112>
(...tail calls...)
./hack/lua/gui.lua:563: in function <./hack/lua/gui.lua:562>
[C]: in ?
... repeating for ever
unit.relationship_ids.Lover
unit.relationship_ids.Spouse
Off-hand I'm not aware of anything else in the DFhack API that has capital letters in any field names.
In C++, your only option is "unit->relationship_ids[df::unit_relationship_type::Spouse]", which translates directly to "unit.relationship_ids[df.unit_relationship_type.Spouse]" in Lua - "unit.relationship_ids.Spouse" is equivalent and is just supported to make Lua scripts more readable. (It's also equivalent to "unit.relationship_ids[1]", which is much less readable.) Changing the array indices to lowercase wouldn't make sense because it wouldn't match the case of the enum items.
Usually, the ones in all caps are names from the raws, and the rest are only capitalized.
Does DFHack export any operation to change the display mode? I've found dfhack.screen.inGraphicsMode, but not any operation to change it. mifki must know how to do it to get TwbT to work, but is it an internal function to TwbT?I'm not really sure what you're talking about. The concepts of a separate text and graphics font are entirely specific to TWBT, unless you're talking about truetype, which DFHack doesn't support. There's no "text mode" that DFHack could switch to. TWBT is far more complicated than that and handles graphics on its own.
The usage I see for such an operation (outside of TwbT) is to change the mode to text when bringing up a DFHack display (such as e.g. gui/gm-editor) so the contents is legible, and then restore the mode to what it was on exit.
Did retiring my tavern and temple cause the dwarves that were using them to break?
It appears what I needed to do was use DFhack to retire both my temple and tavern as that seems to have made them function now
Well, given that a boolean function is defined, it ought to have some function, but I may very well be barking up the wrong tree regarding what that function is. (It's also use to set 'USE_GRAPHICS = dscreen.inGraphicsMode()' in the beginning of gui.lua).Does DFHack export any operation to change the display mode? I've found dfhack.screen.inGraphicsMode, but not any operation to change it. mifki must know how to do it to get TwbT to work, but is it an internal function to TwbT?I'm not really sure what you're talking about. The concepts of a separate text and graphics font are entirely specific to TWBT, unless you're talking about truetype, which DFHack doesn't support. There's no "text mode" that DFHack could switch to. TWBT is far more complicated than that and handles graphics on its own.
The usage I see for such an operation (outside of TwbT) is to change the mode to text when bringing up a DFHack display (such as e.g. gui/gm-editor) so the contents is legible, and then restore the mode to what it was on exit.
Because other than the tamers, the broken dwarves were in the temple/tavern
Well, given that a boolean function is defined, it ought to have some function, but I may very well be barking up the wrong tree regarding what that function is. (It's also use to set 'USE_GRAPHICS = dscreen.inGraphicsMode()' in the beginning of gui.lua).It does have a purpose - it returns what the GRAPHICS setting is in init.txt. See the Lua API documentation (https://dfhack.readthedocs.io/en/latest/docs/Lua%20API.html).
dfhack.screen.inGraphicsMode()
Checks if [GRAPHICS:YES] was specified in init.
USE_GRAPHICSIt's impossible to change without restarting DF because DF simply does not support changing that setting in-game. TWBT essentially reimplements a lot of pieces of libgraphics itself in order to accomplish something similar.
Contains the value of dfhack.screen.inGraphicsMode(), which cannot be changed without restarting the game and thus is constant during the session.
Did retiring my tavern and temple cause the dwarves that were using them to break? because most of them are just sitting there with "No Job" even though many of them have jobs, such as my animal trainers that are refusing to tame some of my baby animals for seemingly no reasonThis sounds like something that isn't DFHack-related. As far as I know, you can't use DFHack to retire a fortress. That's a vanilla feature.
EDIT: I forgot to mention, I tried using things such as workNow, but they caused absolutely nothing to change
Because other than the tamers, the broken dwarves were in the temple/tavern
Do they take jobs like "Drink" etc? Do you have burrows?
I didn't retire a fortress I retired a temple and a tavern IN the fortress, and I think that's DFhack because the pop-up I get when I hit the button has DFhack on itOh. The only DFHack feature there is the confirmation. Vanilla DF allows you to delete locations, but does so instantly. DFHack's "confirm" plugin simply intercepts the key you pressed, and only passes it to DF if you confirm that you want to delete the location.
Thanks lethosor! I suspected it was too easy to be true, and it was...Well, given that a boolean function is defined, it ought to have some function, but I may very well be barking up the wrong tree regarding what that function is. (It's also use to set 'USE_GRAPHICS = dscreen.inGraphicsMode()' in the beginning of gui.lua).It does have a purpose - it returns what the GRAPHICS setting is in init.txt. See the Lua API documentation (https://dfhack.readthedocs.io/en/latest/docs/Lua%20API.html).Quote from: https://dfhack.readthedocs.io/en/latest/docs/Lua%20API.html#screen-apidfhack.screen.inGraphicsMode()
Checks if [GRAPHICS:YES] was specified in init.Quote from: https://dfhack.readthedocs.io/en/latest/docs/Lua%20API.html#miscUSE_GRAPHICSIt's impossible to change without restarting DF because DF simply does not support changing that setting in-game. TWBT essentially reimplements a lot of pieces of libgraphics itself in order to accomplish something similar.
Contains the value of dfhack.screen.inGraphicsMode(), which cannot be changed without restarting the game and thus is constant during the session.
Incidentally, I've been driving myself nuts trying to get names.lua working again, the way mifki showed me:Update: I didn't understand what that line mifki suggested was doing, so after pondering it I figured out I was a moron and what mifki did was show me how to make dfhack pull up the setup_adventurest screen and then fake input the key to bring up the custom name screen. Needed to use page = 7 instead of subscreen = 3 to get it working as it was, but since I was already getting a feel for shit I decided that, since I know shit about dialogs, I should obviously drive myself nuts trying to figure them out.
local blah = df.new(df.viewscreen_setupadventurest) ; blah.subscreen = 3
...doesn't work anymore.
I tried all sorts of different ways of trying to point it at the name selection screen like blah.child=df.viewscreen_layer_choose_language_namest:new() all the way to shit like trying to have the names screen declared first and then create a .parent as the viewscreen_setupadventurest which was pretty good at crashing df, but I've had shit for luck getting it to work outside of a weird case where it put a screen up properly which wanted the right inputs but it was blank and just prevented me from doing shit besides devel/pop-screen-ing it away.
Not sure if this has come up recently, but I made a world with custom parameters, and was attempting to use CREATEITEM, and each time, no matter what I made, it would disappear upon unpausing, no matter what I attempt. Any thoughts/advice? Looks like this fort is by the books! lolThat's strange, it's working fine for me. What DFHack build and platform are you using? What items did you try to create? Do you have a unit selected? Does it work if you run "createitem floor" first? Does it work in another location? What about another save?
Not sure if this has come up recently, but I made a world with custom parameters, and was attempting to use CREATEITEM, and each time, no matter what I made, it would disappear upon unpausing, no matter what I attempt. Any thoughts/advice? Looks like this fort is by the books! lol
Not sure if this has come up recently, but I made a world with custom parameters, and was attempting to use CREATEITEM, and each time, no matter what I made, it would disappear upon unpausing, no matter what I attempt. Any thoughts/advice? Looks like this fort is by the books! lol
Since the item exists until you unpause the game, can you open it up in gm-editor and show us the flags? Particularly the garbage_collect flag, which specifically destroys any item as soon as the game is unpaused.
pondering about creating new worldgen events in adventure mode, like is it possible to just get an retired adventurer to be kidnapped by a night troll?
df.global.world.history.events:new()
df.global.world.history.events:insert('#',{new=df.history_event_hist_figure_abductedst,
year = *desired year*,
seconds = *desired tick in the year*,
id = df.global.hist_event_next_id,
target = *you*.id,
snatcher = *night troll*.id,
site = *your retired site*,
region = *uh, df.world_region.find(*a link to your df.global.world.world_data.region_map[k].sites[v].id=*your site*)*
*maybe? not sure what a viable shortcut for that is*
layer = *however the fuck you get the layer from the region link?*
}
)
df.global.hist_event_next_id = df.global.hist_event_next_id+1
Then you'd toss in the links to the relevant corrupting events I suppose?
# windows
ELSE(UNIX)
SET(ALLEGRO_DOWNLOAD_DIR ${stonesense_SOURCE_DIR}/win${DFHACK_BUILD_ARCH})
IF(DFHACK_BUILD_64)
download_file("http://download.gna.org/allegro/allegro-unstable-bin/5.1.12/allegro-msvc2015-x64-5.1.12.zip"
${ALLEGRO_DOWNLOAD_DIR}/allegro-msvc2015-x64-5.1.12.zip
"3abb0bd88251efde001a8b65fdf17683")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${ALLEGRO_DOWNLOAD_DIR}/allegro-msvc2015-x64-5.1.12.zip
WORKING_DIRECTORY ${ALLEGRO_DOWNLOAD_DIR})
download_file("http://download.gna.org/allegro/allegro-deps/1.2.0/allegro_deps-msvc2015-x64-1.2.0.zip"
${ALLEGRO_DOWNLOAD_DIR}/allegro_deps-msvc2015-x64-1.2.0.zip
"eb8d0f4569b84c36bef3733b40b00c72")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${ALLEGRO_DOWNLOAD_DIR}/allegro_deps-msvc2015-x64-1.2.0.zip
WORKING_DIRECTORY ${ALLEGRO_DOWNLOAD_DIR})
ELSE()
download_file("http://download.gna.org/allegro/allegro-unstable-bin/5.1.12/allegro-msvc2015-x86-5.1.12.zip"
${ALLEGRO_DOWNLOAD_DIR}/allegro-msvc2015-x86-5.1.12.zip
"86e6aeea828743107dc1f3a3d562e53d")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${ALLEGRO_DOWNLOAD_DIR}/allegro-msvc2015-x86-5.1.12.zip
WORKING_DIRECTORY ${ALLEGRO_DOWNLOAD_DIR})
download_file("http://download.gna.org/allegro/allegro-deps/1.2.0/allegro_deps-msvc2015-x86-1.2.0.zip"
${ALLEGRO_DOWNLOAD_DIR}/allegro_deps-msvc2015-x86-1.2.0.zip
"be096bfef256cb113e25dbb2eb7fd410")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ${ALLEGRO_DOWNLOAD_DIR}/allegro_deps-msvc2015-x86-1.2.0.zip
WORKING_DIRECTORY ${ALLEGRO_DOWNLOAD_DIR})
Yeah, it looks like that site is down. I'm not entirely sure where to get Allegro for Windows, then. Maybe https://github.com/liballeg/allegro5/releases would work, but those builds all contain "mingw" in their names, which worries me a bit. You might be better off disabling Stonesense for now unless you specifically want to work with it.
Isn't it fix/dead-units?thank you
local blah = 1
local id = "foo"
local repeatUtil = require 'repeat-util'
local function doStuff()
print("Blah: " .. blah)
blah = blah +1
print("Blah is now " .. blah)
if blah > 10 then
print("Blah is more than 10. Stopping.")
print("Has supposedly cancelled = " .. tostring(repeatUtil.cancel(id)))
end
end
repeatUtil.scheduleEvery(id,50,"frames",doStuff)
Can anybody work out a reason why this code continues after being cancelled, or is it a bug with repeat-util?Code: [Select]local blah = 1
local id = "foo"
local repeatUtil = require 'repeat-util'
local function doStuff()
print("Blah: " .. blah)
blah = blah +1
print("Blah is now " .. blah)
if blah > 10 then
print("Blah is more than 10. Stopping.")
print("Has supposedly cancelled = " .. tostring(repeatUtil.cancel(id)))
end
end
repeatUtil.scheduleEvery(id,50,"frames",doStuff)
function scheduleEvery(name,time,timeUnits,func)
cancel(name)
local function helper()
func()
repeating[name] = dfhack.timeout(time,timeUnits,helper)
end
helper()
end
I thought repeating functions not being able to cancel their own scheduling was a deliberate design decision.Why would it be? Expwnent seems to agree that it's unintentional. I can't see much of an advantage from preventing repeating functions from cancelling themselves (particularly since they can cancel other ones already).
local script = require 'gui.script'
script.start(function()
for blah = 1, 10 do
print("Blah: " .. blah)
script.sleep(50, "frames")
end
end)
I thought repeating functions not being able to cancel their own scheduling was a deliberate design decision.Why would it be? Expwnent seems to agree that it's unintentional. I can't see much of an advantage from preventing repeating functions from cancelling themselves (particularly since they can cancel other ones already).
-snip-Thanks. For my actual script I ended up cutting out the middlescript and just used dfhack.timeout() itself, as well as redesigning it so it didn't have to cancel itself :P I'll keep that method in mind if I have to do something like that in the future, it just goes to show that I really need to make learning how GUI-related stuff works as my next priority...
I thought repeating functions not being able to cancel their own scheduling was a deliberate design decision.Why would it be? Expwnent seems to agree that it's unintentional.
[DFHack]# .dwarffortress/dfhack: line 66: 22352 Segmentation fault (core dumped) setarch i386 -R env LD_PRELOAD="$PRELOAD_LIB" ./libs/Dwarf_Fortress "$@"
Do you have any weapon traps?
Specifically, it's http://www.bay12games.com/dwarves/mantisbt/view.php?id=9888, which is fixed in the next version.
-- Interface powered, user friendly, unit editor
--[====[
gui/gm-unit
===========
An editor for various unit attributes.
]====]
local gui = require 'gui'
local dialog = require 'gui.dialogs'
local widgets =require 'gui.widgets'
local guiScript = require 'gui.script'
local utils = require 'utils'
local args={...}
local target
--TODO: add more ways to guess what unit you want to edit
if args[1]~= nil then
target=df.units.find(args[1])
else
target=dfhack.gui.getSelectedUnit(true)
end
if target==nil then
qerror("No unit to edit") --TODO: better error message
end
local editors={}
function add_editor(editor_class)
table.insert(editors,{text=editor_class.ATTRS.frame_title,on_submit=function ( unit )
editor_class{target_unit=unit}:show()
end})
end
-------------------------------various subeditors---------
--TODO set local sould or better yet skills vector to reduce long skill list access typing
editor_skills=defclass(editor_skills,gui.FramedScreen)
editor_skills.ATTRS={
frame_style = gui.GREY_LINE_FRAME,
frame_title = "Skill editor",
target_unit = DEFAULT_NIL,
learned_only= false,
}
function list_skills(unit,learned_only)
local s_=df.job_skill
local u_skills=unit.status.current_soul.skills
local ret={}
for i,v in ipairs(s_) do
if i>=0 then
local u_skill=utils.binsearch(u_skills,i,"id")
if u_skill or not learned_only then
if not u_skill then
u_skill={rating=-1,experience=0}
end
local rating
if u_skill.rating >=0 then
rating=df.skill_rating.attrs[u_skill.rating]
else
rating={caption="<unlearned>",xp_threshold=0}
end
local text=string.format("%s: %s %d %d/%d",df.job_skill.attrs[i].caption,rating.caption,u_skill.rating,u_skill.experience,rating.xp_threshold)
table.insert(ret,{text=text,id=i})
end
end
end
return ret
end
function editor_skills:update_list(no_save_place)
local skill_list=list_skills(self.target_unit,self.learned_only)
if no_save_place then
self.subviews.skills:setChoices(skill_list)
else
self.subviews.skills:setChoices(skill_list,self.subviews.skills:getSelected())
end
end
function editor_skills:init( args )
if self.target_unit.status.current_soul==nil then
qerror("Unit does not have soul, can't edit skills")
end
local skill_list=list_skills(self.target_unit,self.learned_only)
self:addviews{
widgets.FilteredList{
choices=skill_list,
frame = {t=0, b=1,l=1},
view_id="skills",
},
widgets.Label{
frame = { b=0,l=1},
text ={{text= ": exit editor ",
key = "LEAVESCREEN",
on_activate= self:callback("dismiss")
},
{text=": remove level ",
key = "SECONDSCROLL_UP",
on_activate=self:callback("level_skill",-1)},
{text=": add level ",
key = "SECONDSCROLL_DOWN",
on_activate=self:callback("level_skill",1)}
,
{text=": show learned only ",
key = "CHANGETAB",
on_activate=function ()
self.learned_only=not self.learned_only
self:update_list(true)
end}
}
},
}
end
function editor_skills:get_cur_skill()
local list_wid=self.subviews.skills
local _,choice=list_wid:getSelected()
if choice==nil then
qerror("Nothing selected")
end
local u_skill=utils.binsearch(self.target_unit.status.current_soul.skills,choice.id,"id")
return choice,u_skill
end
function editor_skills:level_skill(lvl)
local sk_en,sk=self:get_cur_skill()
if lvl >0 then
local rating
if sk then
rating=sk.rating+lvl
else
rating=lvl-1
end
utils.insert_or_update(self.target_unit.status.current_soul.skills, {new=true, id=sk_en.id, rating=rating}, 'id') --TODO set exp?
elseif sk and sk.rating==0 and lvl<0 then
utils.erase_sorted_key(self.target_unit.status.current_soul.skills,sk_en.id,"id")
elseif sk and lvl<0 then
utils.insert_or_update(self.target_unit.status.current_soul.skills, {new=true, id=sk_en.id, rating=sk.rating+lvl}, 'id') --TODO set exp?
end
self:update_list()
end
function editor_skills:remove_rust(skill)
--TODO
end
add_editor(editor_skills)
------- civ editor
RaceBox = defclass(RaceBox, dialog.ListBox)
RaceBox.focus_path = 'RaceBox'
RaceBox.ATTRS{
format_name="$NAME ($TOKEN)",
with_filter=true,
allow_none=false,
}
function RaceBox:format_creature(creature_raw)
local t = {NAME=creature_raw.name[0],TOKEN=creature_raw.creature_id}
return string.gsub(self.format_name, "%$(%w+)", t)
end
function RaceBox:preinit(info)
self.format_name=RaceBox.ATTRS.format_name or info.format_name -- preinit does not have ATTRS set yet
local choices={}
if RaceBox.ATTRS.allow_none or info.allow_none then
table.insert(choices,{text="<none>",num=-1})
end
for i,v in ipairs(df.global.world.raws.creatures.all) do
local text=self:format_creature(v)
table.insert(choices,{text=text,raw=v,num=i,search_key=text:lower()})
end
info.choices=choices
end
function showRacePrompt(title, text, tcolor, on_select, on_cancel, min_width,allow_none)
RaceBox{
frame_title = title,
text = text,
text_pen = tcolor,
on_select = on_select,
on_cancel = on_cancel,
frame_width = min_width,
allow_none = allow_none,
}:show()
end
CivBox = defclass(CivBox,dialog.ListBox)
CivBox.focus_path = "CivBox"
CivBox.ATTRS={
format_name="$NAME ($ENGLISH):$ID",
format_no_name="<unnamed>:$ID",
name_other="<other(-1)>",
with_filter=true,
allow_other=false,
}
function civ_name(id,format_name,format_no_name,name_other,name_invalid)
if id==-1 then
return name_other or "<other (-1)>"
end
local civ
if type(id)=='userdata' then
civ=id
else
civ=df.historical_entity.find(id)
if civ==nil then
return name_invalid or "<invalid>"
end
end
local t={NAME=dfhack.TranslateName(civ.name),ENGLISH=dfhack.TranslateName(civ.name,true),ID=civ.id} --TODO race?, maybe something from raws?
if t.NAME=="" then
return string.gsub(format_no_name or "<unnamed>:$ID", "%$(%w+)", t)
end
return string.gsub(format_name or "$NAME ($ENGLISH):$ID", "%$(%w+)", t)
end
function CivBox:update_choices()
local choices={}
if self.allow_other then
table.insert(choices,{text=self.name_other,num=-1})
end
for i,v in ipairs(df.global.world.entities.all) do
if not self.race_filter or (v.race==self.race_filter) then --TODO filter type
local text=civ_name(v,self.format_name,self.format_no_name,self.name_other,self.name_invalid)
table.insert(choices,{text=text,raw=v,num=i})
end
end
self.choices=choices
if self.subviews.list then
self.subviews.list:setChoices(self.choices)
end
end
function CivBox:update_race_filter(id)
local raw=df.creature_raw.find(id)
if raw then
self.subviews.race_label:setText(": "..raw.name[0])
self.race_filter=id
else
self.subviews.race_label:setText(": <none>")
self.race_filter=nil
end
self:update_choices()
end
function CivBox:choose_race()
showRacePrompt("Choose race","Select new race:",nil,function (id,choice)
self:update_race_filter(choice.num)
end,nil,nil,true)
end
function CivBox:init(info)
self.subviews.list.frame={t=3,r=0,l=0}
self:addviews{
widgets.Label{frame={t=1,l=0},text={
{text="Filter race ",key="CUSTOM_CTRL_A",key_sep="()",on_activate=self:callback("choose_race")},
}},
widgets.Label{frame={t=1,l=21},view_id="race_label",
text=": <none>",
}
}
self:update_choices()
end
function showCivPrompt(title, text, tcolor, on_select, on_cancel, min_width,allow_other)
CivBox{
frame_title = title,
text = text,
text_pen = tcolor,
on_select = on_select,
on_cancel = on_cancel,
frame_width = min_width,
allow_other = allow_other,
}:show()
end
editor_civ=defclass(editor_civ,gui.FramedScreen)
editor_civ.ATTRS={
frame_style = gui.GREY_LINE_FRAME,
frame_title = "Civilization editor",
target_unit = DEFAULT_NIL,
}
function editor_civ:update_curren_civ()
self.subviews.civ_name:setText("Currently: "..civ_name(self.target_unit.civ_id))
end
function editor_civ:init( args )
if self.target_unit==nil then
qerror("invalid unit")
end
self:addviews{
widgets.Label{view_id="civ_name",frame = { t=1,l=1}, text="Currently: "..civ_name(self.target_unit.civ_id)},
widgets.Label{frame = { t=2,l=1}, text={{text=": set to other (-1, usually enemy)",key="CUSTOM_N",
on_activate= function() self.target_unit.civ_id=-1;self:update_curren_civ() end}}},
widgets.Label{frame = { t=3,l=1}, text={{text=": set to current civ("..df.global.ui.civ_id..")",key="CUSTOM_C",
on_activate= function() self.target_unit.civ_id=df.global.ui.civ_id;self:update_curren_civ() end}}},
widgets.Label{frame = { t=4,l=1}, text={{text=": manually enter",key="CUSTOM_E",
on_activate=function ()
dialog.showInputPrompt("Civ id","Enter new civ id:",COLOR_WHITE,
tostring(self.target_unit.civ_id),function(new_value)
self.target_unit.civ_id=new_value
self:update_curren_civ()
end)
end}}
},
widgets.Label{frame= {t=5,l=1}, text={{text=": select from list",key="CUSTOM_L",
on_activate=function ( )
showCivPrompt("Choose civilization", "Select units civilization",nil,function ( id,choice )
self.target_unit.civ_id=choice.num
self:update_curren_civ()
end,nil,nil,true)
end
}}},
widgets.Label{
frame = { b=0,l=1},
text ={{text= ": exit editor ",
key = "LEAVESCREEN",
on_activate= self:callback("dismiss")
},
}
},
}
end
add_editor(editor_civ)
------- counters editor
editor_counters=defclass(editor_counters,gui.FramedScreen)
editor_counters.ATTRS={
frame_style = gui.GREY_LINE_FRAME,
frame_title = "Counters editor",
target_unit = DEFAULT_NIL,
counters1={
"think_counter",
"job_counter",
"swap_counter",
"winded",
"stunned",
"unconscious",
"suffocation",
"webbed",
"soldier_mood_countdown",
"soldier_mood", --todo enum,
"pain",
"nausea",
"dizziness",
},
counters2={
"paralysis",
"numbness",
"fever",
"exhaustion",
"hunger_timer",
"thirst_timer",
"sleepiness_timer",
"stomach_content",
"stomach_food",
"vomit_timeout",
"stored_fat" --TODO what to reset to?
}
}
function editor_counters:fill_counters()
local ret={}
local u=self.target_unit
for i,v in ipairs(self.counters1) do
table.insert(ret,{f=u.counters:_field(v),name=v})
end
for i,v in ipairs(self.counters2) do
table.insert(ret,{f=u.counters2:_field(v),name=v})
end
return ret
end
function editor_counters:update_counters()
for i,v in ipairs(self.counter_list) do
v.text=string.format("%s:%d",v.name,v.f.value)
end
self.subviews.counters:setChoices(self.counter_list)
end
function editor_counters:set_cur_counter(value,index,choice)
choice.f.value=value
self:update_counters()
end
function editor_counters:choose_cur_counter(index,choice)
dialog.showInputPrompt(choice.name,"Enter new value:",COLOR_WHITE,
tostring(choice.f.value),function(new_value)
self:set_cur_counter(new_value,index,choice)
end)
end
function editor_counters:init( args )
if self.target_unit==nil then
qerror("invalid unit")
end
self.counter_list=self:fill_counters()
self:addviews{
widgets.FilteredList{
choices=self.counter_list,
frame = {t=0, b=1,l=1},
view_id="counters",
on_submit=self:callback("choose_cur_counter"),
on_submit2=self:callback("set_cur_counter",0),--TODO some things need to be set to different defaults
},
widgets.Label{
frame = { b=0,l=1},
text ={{text= ": exit editor ",
key = "LEAVESCREEN",
on_activate= self:callback("dismiss")
},
{text=": reset counter ",
key = "SEC_SELECT",
},
{text=": set counter ",
key = "SELECT",
}
}
},
}
self:update_counters()
end
add_editor(editor_counters)
wound_creator=defclass(wound_creator,gui.FramedScreen)
wound_creator.ATTRS={
frame_style = gui.GREY_LINE_FRAME,
frame_title = "Wound creator",
target_wound = DEFAULT_NIL,
--filter
}
function wound_creator:init( args )
if self.target_wound==nil then
qerror("invalid wound")
end
self:addviews{
widgets.List{
frame = {t=0, b=1,l=1},
view_id="fields",
on_submit=self:callback("edit_cur_wound"),
on_submit2=self:callback("delete_current_wound")
},
widgets.Label{
frame = { b=0,l=1},
text ={{text= ": exit editor ",
key = "LEAVESCREEN",
on_activate= self:callback("dismiss")},
{text=": edit wound ",
key = "SELECT"},
{text=": delete wound ",
key = "SEC_SELECT"},
{text=": create wound ",
key = "CUSTOM_CTRL_I",
on_activate= self:callback("create_new_wound")},
}
},
}
self:update_wounds()
end
-------------------
editor_wounds=defclass(editor_wounds,gui.FramedScreen)
editor_wounds.ATTRS={
frame_style = gui.GREY_LINE_FRAME,
frame_title = "Wound editor",
target_unit = DEFAULT_NIL,
--filter
}
function is_scar( wound_part )
return wound_part.flags1.scar_cut or wound_part.flags1.scar_smashed or
wound_part.flags1.scar_edged_shake1 or wound_part.flags1.scar_blunt_shake1
end
function format_flag_name( fname )
return fname:sub(1,1):upper()..fname:sub(2):gsub("_"," ")
end
function name_from_flags( wp )
for i,v in ipairs(wp.flags1) do
if v then
return format_flag_name(df.wound_damage_flags1[i])
end
end
for i,v in ipairs(wp.flags2) do
if v then
return format_flag_name(df.wound_damage_flags2[i])
end
end
return "<unnamed wound>"
end
function format_wound( list_id,wound, unit)
local name="<unnamed wound>"
if #wound.parts>0 and #wound.parts[0].effect_type>0 then --try to make wound name by effect...
name=tostring(df.wound_effect_type[wound.parts[0].effect_type[0]])
if #wound.parts>1 then --cheap and probably incorrect...
name=name.."s"
end
elseif #wound.parts>0 and is_scar(wound.parts[0]) then
name="Scar"
elseif #wound.parts>0 then
local wp=wound.parts[0]
name=name_from_flags(wp)
end
return string.format("%d. %s id=%d",list_id,name,wound.id)
end
function editor_wounds:update_wounds()
local ret={}
for i,v in ipairs(self.trg_wounds) do
table.insert(ret,{text=format_wound(i,v,self.target_unit),wound=v})
end
self.subviews.wounds:setChoices(ret)
self.wound_list=ret
end
function editor_wounds:dirty_unit()
print("todo: implement unit status recalculation")
end
function editor_wounds:get_cur_wound()
local list_wid=self.subviews.wounds
local _,choice=list_wid:getSelected()
if choice==nil then
qerror("Nothing selected")
end
local ret_wound=utils.binsearch(self.trg_wounds,choice.id,"id")
return choice,ret_wound
end
function editor_wounds:delete_current_wound(index,choice)
utils.erase_sorted(self.trg_wounds,choice.wound,"id")
choice.wound:delete()
self:dirty_unit()
self:update_wounds()
end
function editor_wounds:create_new_wound()
print("Creating")
end
function editor_wounds:edit_cur_wound(index,choice)
end
function editor_wounds:init( args )
if self.target_unit==nil then
qerror("invalid unit")
end
self.trg_wounds=self.target_unit.body.wounds
self:addviews{
widgets.List{
frame = {t=0, b=1,l=1},
view_id="wounds",
on_submit=self:callback("edit_cur_wound"),
on_submit2=self:callback("delete_current_wound")
},
widgets.Label{
frame = { b=0,l=1},
text ={{text= ": exit editor ",
key = "LEAVESCREEN",
on_activate= self:callback("dismiss")},
{text=": edit wound ",
key = "SELECT"},
{text=": delete wound ",
key = "SEC_SELECT"},
{text=": create wound ",
key = "CUSTOM_CTRL_I",
on_activate= self:callback("create_new_wound")},
}
},
}
self:update_wounds()
end
add_editor(editor_wounds)
------ Body editor
modifier_selector = defclass(modifier_selector, gui.FramedScreen)
function modifierString(mod)
local out = df.appearance_modifier_type[mod.type]
out = out:lower() --Make lowercase
out = out:gsub("_", " ") --Replace underscores with spaces
out = out:gsub("^%l", string.upper) --capitalises first letter
return out
end
function showModifierScreen(data)
modifier_selector{
frame_style = gui.GREY_LINE_FRAME,
frame_title = "Select a modifier",
target_unit = DEFAULT_NIL,
data = data
}:show()
end
function modifier_selector:set_value(value,index,choice)
for i,v in ipairs(choice.changes) do
self.changeType[v] = value
end
self:update_features()
end
function modifier_selector:on_select(index, choice)
dialog.showInputPrompt(modifierString(choice.mod),"Enter new value:",COLOR_WHITE,
tostring(self.changeType[choice.changes[1]]),function(new_value)
self:set_value(new_value,index,choice)
end)
end
function modifier_selector:update_features()
local out = {}
for i, v in ipairs(self.partPicked.modList) do
table.insert(out, {text = (modifierString(v.modifier) .. ": " .. self.changeType[v.changes[1]]), mod = v.modifier, changes = v.changes})
end
self.subviews.modifiers:setChoices(out)
end
function modifier_selector:init( info )
self.partPicked = info.data.choice --The part that was picked in editor_body
if info.data.choice.isPart then
self.changeType = target.appearance.bp_modifiers
else
self.changeType = target.appearance.body_modifiers
end
self:addviews{
widgets.FilteredList{
frame = {t=0, b=1,l=1},
view_id="modifiers",
on_submit=self:callback("on_select")
},
widgets.Label{
frame = { b=0,l=1},
text ={{text= ": back to part selector ",
key = "LEAVESCREEN",
on_activate= self:callback("dismiss")
},
{text=": select modifier ",
key = "SELECT",
}
}
},
}
self:update_features()
end
editor_body=defclass(editor_body,gui.FramedScreen)
editor_body.ATTRS={
frame_style = gui.GREY_LINE_FRAME,
frame_title = "Body modifier editor",
target_unit = DEFAULT_NIL,
}
function editor_body:bp_links()
local out = {}
local uc = self.ucaste
for i,v in ipairs(uc.bp_appearance.part_idx) do
out[i] = {["modId"] = uc.bp_appearance.modifier_idx[i], ["partId"] = uc.bp_appearance.part_idx[i]}
end
return out
end
--Following is a relic from my original change-appearance script. There's probably a more efficient way of doing this, but I'm not in the mood to be redesigning ;P
function editor_body:make_bplist()
local ret = {}
local bpm = self.ucaste.bp_appearance.modifiers
local links = self:bp_links()
local point = {}
for i, v in ipairs(bpm) do
local mod = v
local bpmname
if #mod.noun > 0 then
bpmname = mod.noun
else
bpmname = self.ucaste.body_info.body_parts[mod.body_parts[0]].name_singular[0].value
end
local changes = {}
for i2, v2 in ipairs(mod.body_parts) do
local partId = v2
for i3, v3 in ipairs(links) do
if v3.modId == i and v3.partId == partId then
table.insert(changes, i3) --?
end
end
end
if point[bpmname] then
table.insert(ret[point[bpmname]].modList, {["modifier"] = mod, ["changes"] = changes})
else
table.insert(ret, {["name"] = bpmname, ["modList"] = {[1] = {["modifier"] = mod, ["changes"] = changes}}})
point[bpmname] = #ret --Stores the index of the name for future additions
end
end
return ret
end
function editor_body:make_bodmodlist() --Version of make_bplist() to make a spoof version for body so it can be treated the same. Only makes modList
local ret = {}
local bm = self.ucaste.body_appearance_modifiers
for i, v in ipairs(bm) do
table.insert(ret, {["modifier"] = v, ["changes"] = {[1] = i}})
end
return ret
end
function editor_body:update_features()
self.bplist = self:make_bplist()
local out = {}
local uc = self.ucaste
self.bodmodlist = self:make_bodmodlist()
--Special case of body
--First check to discover if there are any body mods
if #uc.body_appearance_modifiers > 0 then
table.insert(out, {text = "Body", modList = self.bodmodlist})
end
for i,v in ipairs(self.bplist) do
table.insert(out, {text = v.name:gsub("^%l", string.upper), modList = v.modList, isPart = true})
end
self.subviews.body:setChoices(out)
end
function editor_body:choose_cur_bp(index, choice)
local data = {["choice"] = choice}
showModifierScreen(data)
end
function editor_body:init( args )
if self.target_unit==nil then
qerror("invalid unit")
end
self.urace = self.target_unit.race
self.ucritter = df.creature_raw.find(self.urace)
self.ucaste = self.ucritter.caste[self.target_unit.caste]
self:addviews{
widgets.FilteredList{
choices=self.features_list,
frame = {t=0, b=1,l=1},
view_id="body",
on_submit=self:callback("choose_cur_bp")
},
widgets.Label{
frame = { b=0,l=1},
text ={{text= ": exit editor ",
key = "LEAVESCREEN",
on_activate= self:callback("dismiss")
},
{text=": select feature ",
key = "SELECT",
}
}
},
}
self:update_features()
end
add_editor(editor_body)
------ Colors editor
ColorBox = defclass(ColorBox, dialog.ListBox)
ColorBox.focus_path = 'ColorBox'
ColorBox.ATTRS{
with_filter = true,
allow_none = false,
}
function showColorPrompt(title, text, tcolor, on_select, on_cancel, min_width,allow_other, data)
ColorBox{
frame_title = title,
text = text,
text_pen = tcolor,
on_select = on_select,
on_cancel = on_cancel,
frame_width = min_width,
allow_other = allow_other,
data = data
}:show()
end
function ColorBox:update_choices()
local choices = {}
for i,v in ipairs(self.mod.pattern_index) do
table.insert(choices, {text=patternString(self.mod.pattern_index[i]), index = i})
end
self.choices = choices
if self.subviews.list then
self.subviews.list:setChoices(self.choices)
end
end
function ColorBox:init(info)
self.mod = info.data.mod
self.target_unit = target
self:update_choices()
end
editor_colors=defclass(editor_colors,gui.FramedScreen)
editor_colors.ATTRS={
frame_style = gui.GREY_LINE_FRAME,
frame_title = "Colors editor",
target_unit = DEFAULT_NIL,
}
function getColor(id)
return df.descriptor_color.find(id)
end
function getPattern(id)
return df.descriptor_pattern.find(id)
end
function patternString(id)
local pattern = getPattern(id)
local prefix
if pattern.pattern == 0 then --Monochrome
return getColor(pattern.colors[0]).name
elseif pattern.pattern == 1 then --Stripes
prefix = "striped"
elseif pattern.pattern == 2 then --Iris_eye
return getColor(pattern.colors[2]).name .. " eyes"
elseif pattern.pattern == 3 then --Spots
prefix = "spotted" --that's a guess
elseif pattern.pattern == 4 then --Pupil_eye
return getColor(pattern.colors[2]).name .. " eyes"
elseif pattern.pattern == 5 then --mottled
prefix = "mottled"
end
local out = prefix .. " "
for i=0, #pattern.colors-1 do
if i == #pattern.colors-1 then
out = out .. "and " .. getColor(pattern.colors[i]).name
elseif i == #pattern.colors-2 then
out = out .. getColor(pattern.colors[i]).name .. " "
else
out = out .. getColor(pattern.colors[i]).name .. ", "
end
end
return out
end
function editor_colors:change_color(index,patternId)
self.target_unit.appearance.colors[index] = patternId
end
function editor_colors:update_features()
local uc = self.ucaste
local out = {}
for i,v in ipairs(uc.color_modifiers) do
table.insert(out, {text=uc.color_modifiers[i].part:gsub("^%l", string.upper), mod = uc.color_modifiers[i], index = i})
end
self.subviews.colors:setChoices(out)
end
function editor_colors:choose_cur_feature(index,choice)
self.chosenFeature = choice
local data = {ucaste = self.ucaste, mod = choice.mod} --data to pass to color prompt
showColorPrompt("Choose color", "Select features color",nil,function ( id,choice )
self:change_color(self.chosenFeature.index, choice.index)
end,nil,nil,true, data)
end
function editor_colors:init( args )
if self.target_unit==nil then
qerror("invalid unit")
end
self.urace = self.target_unit.race
self.ucritter = df.creature_raw.find(self.urace)
self.ucaste = self.ucritter.caste[self.target_unit.caste]
self:addviews{
widgets.FilteredList{
choices=self.features_list,
frame = {t=0, b=1,l=1},
view_id="colors",
on_submit=self:callback("choose_cur_feature")
},
widgets.Label{
frame = { b=0,l=1},
text ={{text= ": exit editor ",
key = "LEAVESCREEN",
on_activate= self:callback("dismiss")
},
{text=": select feature ",
key = "SELECT",
}
}
},
}
self:update_features()
end
add_editor(editor_colors)
-------------------------------main window----------------
unit_editor = defclass(unit_editor, gui.FramedScreen)
unit_editor.ATTRS={
frame_style = gui.GREY_LINE_FRAME,
frame_title = "GameMaster's unit editor",
target_unit = DEFAULT_NIL,
}
function unit_editor:init(args)
self:addviews{
widgets.FilteredList{
choices=editors,
on_submit=function (idx,choice)
if choice.on_submit then
choice.on_submit(self.target_unit)
end
end
},
widgets.Label{
frame = { b=0,l=1},
text ={{text= ": exit editor",
key = "LEAVESCREEN",
on_activate= self:callback("dismiss")
},
}
},
}
end
unit_editor{target_unit=target}:show()
That adds Body Modifier and Color entries, which frankly I think look good enough from a first pass to be in the main version.
Hmm, problem still persists even though I've removed the Weapon Traps. Mind you, the traps were not completed, they were only planned, so no one could've stepped in them.
After about half an hour of testing, this seems to have done the trick.Hmm, problem still persists even though I've removed the Weapon Traps. Mind you, the traps were not completed, they were only planned, so no one could've stepped in them.
Try TWBT 5.xx (not Next). There's a bug in Next that may cause crashes.
-- Change focus level of needs for units
local usage = [====[
modtools/set-need
=====================
This allows for modifying the focus levels for needs on units.
Arguments:
-need id
the id/type of the need to change. Alternatively "all" to apply the value to all.
examples:
1
DrinkAlcohol
all
-value
the value to set focus to. Defaults to 400 if not included (the value focus resets to when normally fulfilled in game).
Negative values need to have a \ before the negative symbol
examples:
250
\-5000
-target id
the unit id of the target unit. If unspecified, will check for a selected unit.
examples:
0
28
-needlist
displays a list of need ids.
]====]
local utils = require 'utils'
validArgs = validArgs or utils.invert({
'help',
'needlist',
'need',
'value',
'target'
})
local args = utils.processArgs({...}, validArgs)
if args.help then
print(usage)
return
end
if args.needlist then
for i,v in ipairs(df.need_type)do
print(i .. " (" .. v .. ")")
end
return
end
--Find the target
local targ
if not args.target then
if dfhack.gui.getSelectedUnit(true) then
targ = dfhack.gui.getSelectedUnit(true)
else
error 'Specify a target'
end
else
if df.unit.find(tonumber(args.target)) then
targ = df.unit.find(tonumber(args.target))
else
error ('Could not find target: '.. args.target)
end
end
args.target = targ
--Work out the need
local doAll
if not args.need then
error 'Specify a need'
end
if (args.need):lower() == "all" then
doAll = true
elseif tonumber(args.need) then
args.need = tonumber(args.need)
elseif df.need_type[args.need] then
args.need = df.need_type[args.need]
else
error 'Invalid need'
end
--Set the value
args.value = tonumber(args.value) or 400
--Doing the thing
local currentNeedTableId
for i, v in ipairs(args.target.status.current_soul.personality.needs) do
if doAll or v.id == args.need then
v.focus_level = args.value
if not doAll then
return
end
end
end
-- Change beliefs value of a unit
--[[ Issues/Potential changes
- Doesn't cause needs to change
- Changes don't respect creature's cultural weights (not sure how you'd model that, anyway)
- Step sets to a particular value rather than a random range
]]
local usage = [====[
modtools/set-belief
=====================
This allows for altering the beliefs of units.
Arguments:
-belief #
the id/token of the belief to change. Alternatively "all" to apply the value to all... if you want to do that for some reason.
examples:
1
LOYALTY
all
-value #
the value to set the belief strength to. Defaults to 0
examples:
-15
45
-step #
alternative to value. Increase/decrease along description levels by #.
Negative values need to have a \ before the negative symbol.
examples:
2
\-1
-add #
alternative to value. Adds to current value.
Negative values need to have a \ before the negative symbol.
examples:
15
\-20
Without valid value/step/add, defaults to value style at 0.
-target #
the unit id of the target unit. If unspecified, will check for a selected unit.
examples:
0
28
-belieflist
displays a list of need ids.
]====]
local utils = require 'utils'
validArgs = validArgs or utils.invert({
'help',
'belieflist',
'belief',
'value',
'target',
'step',
'add'
})
local args = utils.processArgs({...}, validArgs)
if args.help then
print(usage)
return
end
if args.belieflist then
for i,v in ipairs(df.value_type)do
print(i .. " (" .. v .. ")")
end
return
end
--Find the target
local targ
if not args.target then
if dfhack.gui.getSelectedUnit(true) then
targ = dfhack.gui.getSelectedUnit(true)
else
error 'Specify a target'
end
else
if df.unit.find(tonumber(args.target)) then
targ = df.unit.find(tonumber(args.target))
else
error ('Could not find target: '.. args.target)
end
end
args.target = targ
--Checks has a soul, probably
if not targ.status.current_soul then
error 'Unit has no soul'
end
--Store shorthand for use later
local beliefs = args.target.status.current_soul.personality.values
--Work out the trait
local doAll
if not args.belief then
error 'Specify a belief'
end
if (args.belief):lower() == "all" then
doAll = true
elseif tonumber(args.belief) then
args.belief = tonumber(args.belief)
elseif df.value_type[args.belief] then
args.belief = df.value_type[args.belief]
else
error 'Invalid belief'
end
local ranges = {
[1] = {["low"] = -50, ["high"] = -41, ["mid"] = -45},
[2] = {["low"] = -40, ["high"] = -26, ["mid"] = -33},
[3] = {["low"] = -25, ["high"] = -11, ["mid"] = -18},
[4] = {["low"] = -10, ["high"] = 10, ["mid"] = 0},
[5] = {["low"] = 11, ["high"] = 25, ["mid"] = 18},
[6] = {["low"] = 26, ["high"] = 40, ["mid"] = 33},
[7] = {["low"] = 41, ["high"] = 50, ["mid"] = 45}
}
local function clamp(val, low, high)
if val < low then
return low
elseif val > high then
return high
end
return val
end
local function getStep(value)
for i,v in ipairs(ranges) do
if (value >= v.low) and (value <= v.high) then
return i
end
end
end
local pointers = {}
--[[ Structure: pointers[#1] = #2
#1 is the value's type
#2 is the index entry for unit.status.current_soul.personality.values
]]
local function buildPointers()
for i, v in ipairs(beliefs) do
pointers[v.type] = i
end
end
--Returns unit's current value for given belief
local function getCurBeliefValue(unit, beliefId)
local upers = unit.status.current_soul.personality
if pointers[beliefId] then
return upers.values[pointers[beliefId]].strength
elseif upers.cultural_identity ~= -1 then
return df.cultural_identity.find(upers.cultural_identity).values[beliefId]
else
return 0 --outsiders have no culture
end
end
--Ultimately changes value strength. Changes an existing entry if possible, otherwise creates a new one. Uses pointers.
local function changeBelief(beliefId, value)
local upers = args.target.status.current_soul.personality
local value = clamp(value, -50, 50)
if pointers[beliefId] then --belief already exists on unit
upers.values[pointers[beliefId]].strength = value
--Done!
else --Makes new belief (assumes it's fine to have a personal value entry that's technically the same step as their culture's)
upers.values:insert("#", {new = df.unit_personality.T_values, type = beliefId, strength = value})
--Add this new info to pointers, in case of doAll
pointers[beliefId] = #upers.values-1
--Done!
end
end
local function stepUp(beliefId)
local curStep = getStep(getCurBeliefValue(args.target, beliefId))
local changed = clamp(curStep + args.step, 1, 7)
changeBelief(beliefId, ranges[changed].mid)
end
--Setup for add changes
local function doAdd(beliefId)
changeBelief(beliefId, getCurBeliefValue(args.target, beliefId) + args.add) --No need to check if new value would be beyond range, value is clamped during changeBelief()
end
--Before making changes, make a table that records the unit's individual beliefs
buildPointers()
--Both check for a valid value/alternative and do the thing
if tonumber(args.step) then --Do steps
if doAll then
for i,v in ipairs(df.value_type) do
stepUp(i)
end
else
stepUp(args.belief)
end
return
elseif tonumber(args.add) then --Do adds
if doAll then
for i,v in ipairs(df.value_type) do
doAdd(i)
end
else
doAdd(args.belief)
end
return
else --Doing value or defaulting to value
if not tonumber(args.value) then --If value is missing or invalid, set to 0
args.value = 0
end
--Do values
if doAll then
for i,v in ipairs(df.value_type) do
changeBelief(i, args.value)
end
else
changeBelief(args.belief, args.value)
end
return
end
-- Change personality trait value of a unit
--[[ Issues/Potential changes
- Doesn't cause needs to change
- Changes don't respect target creature's natural limits
- Step sets to a particular value rather than a random range
]]
local usage = [====[
modtools/set-personality
=====================
This allows for altering the personality of units.
Arguments:
-trait #
the id/token of the trait to change. Alternatively "all" to apply the value to all... if you want to do that for some reason.
examples:
1
HATE_PROPENSITY
all
-value #
the value to set the personality to. Defaults to 50
examples:
0
75
-step #
alternative to value. Increase/decrease along description levels by #.
Negative values need to have a \ before the negative symbol
examples:
2
\-1
-add #
alternative to value. Adds to current value.
Negative values need to have a \ before the negative symbol.
examples:
15
\-20
Without valid value/step/add, defaults to value style at 50.
-target #
the unit id of the target unit. If unspecified, will check for a selected unit.
examples:
0
28
-traitlist
displays a list of need ids.
]====]
local utils = require 'utils'
validArgs = validArgs or utils.invert({
'help',
'traitlist',
'trait',
'value',
'target',
'step',
'add'
})
local args = utils.processArgs({...}, validArgs)
if args.help then
print(usage)
return
end
if args.traitlist then
for i,v in ipairs(df.personality_facet_type)do
print(i .. " (" .. v .. ")")
end
return
end
--Find the target
local targ
if not args.target then
if dfhack.gui.getSelectedUnit(true) then
targ = dfhack.gui.getSelectedUnit(true)
else
error 'Specify a target'
end
else
if df.unit.find(tonumber(args.target)) then
targ = df.unit.find(tonumber(args.target))
else
error ('Could not find target: '.. args.target)
end
end
args.target = targ
--Checks has a soul, probably
if not targ.status.current_soul then
error 'Unit has no soul'
end
--Store shorthand for use later
local traits = args.target.status.current_soul.personality.traits
--Work out the trait
local doAll
if not args.trait then
error 'Specify a trait'
end
if (args.trait):lower() == "all" then
doAll = true
elseif tonumber(args.trait) then
args.trait = tonumber(args.trait)
elseif df.personality_facet_type[args.trait] then
args.trait = df.personality_facet_type[args.trait]
else
error 'Invalid trait'
end
local ranges = {
[1] = {["low"] = 0, ["high"] = 9, ["mid"] = 5},
[2] = {["low"] = 10, ["high"] = 24, ["mid"] = 17},
[3] = {["low"] = 25, ["high"] = 39, ["mid"] = 32},
[4] = {["low"] = 40, ["high"] = 60, ["mid"] = 50},
[5] = {["low"] = 61, ["high"] = 75, ["mid"] = 68},
[6] = {["low"] = 76, ["high"] = 90, ["mid"] = 83},
[7] = {["low"] = 91, ["high"] = 100, ["mid"] = 95}
}
local function clamp(val, low, high)
if val < low then
return low
elseif val > high then
return high
end
return val
end
local function getStep(value)
for i,v in ipairs(ranges) do
if (value >= v.low) and (value <= v.high) then
return i
end
end
end
--Ultimately change the trait
local function setValue(traitId, value)
traits[traitId] = clamp(value, 0, 100)
end
--Changes the trait value using step style
local function stepUp(traitId)
local curStep = getStep(traits[traitId])
local changed = clamp(curStep + args.step, 1, 7)
setValue(traitId, ranges[changed].mid) --In theory could instead have a random number between low and high
end
local function doAdd(traitId)
setValue(traitId, traits[traitId] + args.add)
end
--Both check for a valid value/alternative and do the thing
if tonumber(args.step) then
--Do step
if doAll then
for i,v in ipairs(traits) do
stepUp(i)
end
else
stepUp(args.trait)
end
return
elseif tonumber(args.add) then
--Do add
if doAll then
for i,v in ipairs(traits) do
doAdd(i)
end
else
doAdd(args.trait)
end
return
else --Doing value or defaulting to value
if not tonumber(args.value) then
args.value = 50
end
if doAll then
for i,v in ipairs(traits) do
setValue(i, args.value)
end
else
setValue(args.trait, args.value)
end
return
end
Some Python experiments (with Python 3.6):Spoiler (click to show/hide)
It doesn't support much yet, and there are some stability issues. It doesn't support accessing DF memory or running scripts directly from the console (it uses a "pyscript" command for that right now, but I'm working on a better way to allow plugins to register their own script types). I've come up with some ways that could make most of the Lua API available to Python, though, so that should be an improvement over the Ruby plugin.
print(f"""\
save path: {dfhack.world.ReadWorldFolder()}
weather: {dfhack.world.ReadCurrentWeather()}
date: {dfhack.world.ReadCurrentDay()}-{dfhack.world.ReadCurrentMonth()}-{dfhack.world.ReadCurrentYear()}
""")
save path: arena1
weather: 0
date: 2-0-1
About the only useful things it can do are pausing and unpausing the game (dfhack.world.SetPauseState()) and setting the current weather (dfhack.world.SetCurrentWeather()). There are also some functions in the dfhack.internal module that work, but not all of them (some take pointers, some return tables, etc.).strength = df.global.world.units.active[x].body.physical_attributes.STRENGTH.value
willpower = df.global.world.units.active[x].status.current_soul.mental_attributes.WILLPOWER.value
But what if I could just have all of my scripts reference a single locationstrength = unit[x].attribute.STRENGTH.value
willpower = unit[x].attribute.WILLPOWER.value
And then have a different script that links the two. This way if Toady ever changes how the data are structured I only need to change a single location (the link between the two) instead of changing every single script. It would also allow linking of things that are separate in DF (like unit and histfig) and putting them together so that you can reference them both with a single unit.x declaration.It's possible to add structures/enums/bitfields/etc. that aren't actually in DF - there are a few things like that in df-structures already. You're describing making some fields of structures available through other structures, though, which is more complicated and harder to maintain.
If you really want to access unit traits like that, you could make your own wrapper tables and use metatables (with __index and __newindex) to implement something similar to that. Here's (https://github.com/lethosor/dwarf-manipulator/blob/90d9e344f9313ee9a6869b5ad12326d012d76033/manipulator/utils.lua#L505) an example of something similar I did to attach extra data to units. (It won't do exactly what you want, since adding a "skills" table with the data you want won't update the unit's skills when you change the table, unless you set up a metatable on it too.)
32-bit versions of Dwarf Therapist all expected "default absolute offsets" (i.e. the absolute addresses you get when DF loads at its default base address, with ASLR disabled) in its layout INI files, and that's what export-dt-ini.lua generates for 32-bit and 64-bit. For example, the proper offset for "cur_year_tick" in 0.43.05 Win64 should be 0x141904240.
I found that DT has hard-coded the default base address for win32 (https://github.com/splintermind/Dwarf-Therapist/blob/x64/src/dfinstancewindows.cpp#L209), that explains the value 0x13fc00000: it is actually the difference between the default base addresses for win32 and win64. If I change this value, it should work with layout from the script.
m_base_addr = base_addr - 0x00400000;
That's technically correct for 32-bit processes (and is the same thing DFHack uses - see "include/Memory.h"), but for 64-bit ones it should subtract 0x140000000 instead.
mifki, that's kinda the thing i need. you did it via twbt, right? i want to get a step further and save the data of each tile. like if there is a fortress wall, which kind of rock it's made of and of course its z coordinate. Would this be doable via a script plugin?
To be more clear about my goal:
I want to create a 1st person walkable worldmap with UE4. it's mostly a learning project for me, to get a hold of the coding traps for a open world game (map chunking/LOD stuff etc). To show different tree types and ground vegetation, maybe even fauna out of the map data would be so awesome :)
To be more clear about my goal:
I want to create a 1st person walkable worldmap with UE4. it's mostly a learning project for me, to get a hold of the coding traps for a open world game (map chunking/LOD stuff etc). To show different tree types and ground vegetation, maybe even fauna out of the map data would be so awesome :)
Ok so, Atkana is fucking awesome apparently. I saw the script they set up to manipulate appearance and such during adventurer creation and thought "wow, I bet that could be gotten into gm-unit" and tried but failed at getting it fully working so I showed them right?Got attribute editor folded into the mix, it's really damn convenient having all these accessible from a single interface, btw.
http://www.bay12forums.com/smf/index.php?topic=164709.msg7501676#msg7501676
To be more clear about my goal:
I want to create a 1st person walkable worldmap with UE4. it's mostly a learning project for me, to get a hold of the coding traps for a open world game (map chunking/LOD stuff etc). To show different tree types and ground vegetation, maybe even fauna out of the map data would be so awesome :)
Have you looked at Armok Vision?
r2 is up! (https://github.com/DFHack/dfhack/releases/tag/0.43.05-r2)Thanks to everyone involved for their hard work!
It looks like the change to allow Ruby scripts from separate paths can break Ruby scripts if the folder they're in has an apostrophe in its name.
item=u.inventory[#u.inventory-1].item
But of course that only works if you can pick it up.Apostrophes aren't that odd, given their use in possessives. I'd say non-English characters are more an issue, and they aren't going away anytime soon.It looks like the change to allow Ruby scripts from separate paths can break Ruby scripts if the folder they're in has an apostrophe in its name.
I would call this a feature. Folder names with spaces or other odd characters need to be submerged in magma.
Well, if double quotes are forbidden anyway, I suppose my script is more useful than I thought.Apostrophes aren't that odd, given their use in possessives. I'd say non-English characters are more an issue, and they aren't going away anytime soon.It looks like the change to allow Ruby scripts from separate paths can break Ruby scripts if the folder they're in has an apostrophe in its name.
I would call this a feature. Folder names with spaces or other odd characters need to be submerged in magma.
The following characters are forbidden for Windows folder names:
\ / : * ? " < > |
Linux only forbids / and \0.
So back to my original point/problem: Anyone got a preferred goto dummy product they use for their reactions-that-actually-trigger-eventful?
[PRODUCT:0:1:LIQUID_MISC:NONE:WATER][PRODUCT_DIMENSION:2000]
with code that cancels the creation with "call_native.value=false"
Huh, so it'll still trigger even if the reaction's only product has a 0% chance of being made? If so, is it actually necessary to cancel the creation with code?So back to my original point/problem: Anyone got a preferred goto dummy product they use for their reactions-that-actually-trigger-eventful?
I useCode: [Select][PRODUCT:0:1:LIQUID_MISC:NONE:WATER][PRODUCT_DIMENSION:2000]
with code that cancels the creation with "call_native.value=false"
Probably not :PHuh, so it'll still trigger even if the reaction's only product has a 0% chance of being made? If so, is it actually necessary to cancel the creation with code?So back to my original point/problem: Anyone got a preferred goto dummy product they use for their reactions-that-actually-trigger-eventful?
I useCode: [Select][PRODUCT:0:1:LIQUID_MISC:NONE:WATER][PRODUCT_DIMENSION:2000]
with code that cancels the creation with "call_native.value=false"
Where is "dfhack.screen" located?To do transparent display in c++ is quite easy. Either subclass the screen you want and then call parent->render (not sure the exact naming) or use vmethod interposing to replace the render and then call INTERPOSE_NEXT(render) (which calls the next inserted function - usually the original)
It's referenced by gui.lua, and the referenced operation is similar to one defined in Screen.cpp, but the profile differs, so there's a translation layer somewhere. However, my searches have failed to find any matching file (or contents in files I've looked at).
What I'm trying to do is to create "transparent" screens that can be used as overlays on top of DF's native UI (the current attempt is to provide an overlay that shows where non displayed sites are located pre embark [e.g. lairs and refugee camps]).
So far I've managed to get a "transparent" DFHack window where I can paint things only at selected tiles, which is sufficient for my purposes technically. The problem I have is that I can't "unpaint" what's painted: skipping rendering of no longer desired tiles doesn't do anything, although I suspect it would if I let DF advance a frame (which may be where I end up anyway, since it would be nice to allow movement while the overlay is present, but callback based operation is a bit messy).
Another alternative (which is the source if the question) is to read what's on the tile and before I put the overlay there and cache the info to paint the original info on the overlay to emulate it being cleared on that tile (or just paint all of it with the original data to start with).
Edit: Corrected the above by replacing "tick" with "frame".
Okay, so I finally got around to trying this out and it seems (at least based on my tests using DFhack 0.43.05-r2):Probably not :PHuh, so it'll still trigger even if the reaction's only product has a 0% chance of being made? If so, is it actually necessary to cancel the creation with code?So back to my original point/problem: Anyone got a preferred goto dummy product they use for their reactions-that-actually-trigger-eventful?
I useCode: [Select][PRODUCT:0:1:LIQUID_MISC:NONE:WATER][PRODUCT_DIMENSION:2000]
with code that cancels the creation with "call_native.value=false"
I hate Fortress Mode modding :'(Me too. The raws modding too :P.
function reaction() local args={...}; printall(args); end
Oh derp I didn't think to do that when trying to work stuff out.I hate Fortress Mode modding :'(Me too. The raws modding too :P.
Can you print all the args to lua? I.e.Code: [Select]function reaction() local args={...}; printall(args); end
Someone told me this mod adds a little bit of mouse functionality for some menus - any chance that extends to the conversation menus in Adventure Mode? Those things are infuriating to scroll through after awhile.mousequery only deals with the fortress mode map. Don't the adventure mode menus have search functionality built-in in vanilla DF?
Hi all,
I don't know if it has been mentioned but I might have found something odd with planning tool.
I use labormanager and planning tool and once in a while, for some reason it don’t place item anymore, even if I have it. I think the trigger is when I use the alert and burrow to get all dwarves inside and once the gate close, I release the alert and it stops working. Not 100% sure tho...
I would also add something else, labormanager doesn't work very well with hauler... it’s a low priority that I have to switch to autolabor once in a while to get everything laying around.
Btw dfhack does make this game far much enjoyable! :)
It shouldn't matter. In fact ALL my reactions use custom buildings. Hmm maybe something broke and nobody noticed?Oh derp I didn't think to do that when trying to work stuff out.I hate Fortress Mode modding :'(Me too. The raws modding too :P.
Can you print all the args to lua? I.e.Code: [Select]function reaction() local args={...}; printall(args); end
It only receives 6 args, where the 7th should be the call_native: 1 is a reaction, 2 is a reaction product item table, 3 is a unit, 4 is a table of items, 5 is a table of reaction reagents, 6 is a table of items.
Maybe it's something related to the reaction using a custom building? That's the only thing I could think of that deviates slightly from the norm.
Search functionality is only in the deeper conversation menus like if you're picking from a list of incidents to summarize - but it isn't available nor would it be useful at the base menu where you select the main options. What bothers me is like when I want to ask a lot of people the same thing, and so every time I have to type the + or - key x amount of times to get to the option I want, and I have to do this a thousand times until I find someone who has the information I need. Thanks for the response, though, just checking.Someone told me this mod adds a little bit of mouse functionality for some menus - any chance that extends to the conversation menus in Adventure Mode? Those things are infuriating to scroll through after awhile.mousequery only deals with the fortress mode map. Don't the adventure mode menus have search functionality built-in in vanilla DF?
Search functionality is only in the deeper conversation menus like if you're picking from a list of incidents to summarize - but it isn't available nor would it be useful at the base menu where you select the main options. What bothers me is like when I want to ask a lot of people the same thing, and so every time I have to type the + or - key x amount of times to get to the option I want, and I have to do this a thousand times until I find someone who has the information I need. Thanks for the response, though, just checking.Which ones aren't "deeper"? I'm getting this after I choose a person to talk to, which has a filter:
Yes the first menu is what I'm referring to. What I'm really talking about with the mouse is really probably only for the sake of shaving off a few seconds per encounter (which does add up, mind you), but when applied to the aforementioned "deeper" menus I'm sure there'd be a lot of benefit in that area as well. Or hotkeys to each option like in the combat menu would be great, that way doing simple things where you often remember the hotkey for it would go by so much quicker. Like asking people permission to stay the night. It just gets a little tedious and it makes my eyes sore.Search functionality is only in the deeper conversation menus like if you're picking from a list of incidents to summarize - but it isn't available nor would it be useful at the base menu where you select the main options. What bothers me is like when I want to ask a lot of people the same thing, and so every time I have to type the + or - key x amount of times to get to the option I want, and I have to do this a thousand times until I find someone who has the information I need. Thanks for the response, though, just checking.Which ones aren't "deeper"? I'm getting this after I choose a person to talk to, which has a filter:Are you just referring to the first menu where you can choose who to talk to?Spoiler (click to show/hide)
Hum seems to have some trouble with digging invaders in 64 i think as the commit is "Win64 fix" If they had corrected the plugin instead of deleting it would have been better ...The plugin was not deleted. It was excluded from being built. Clément just linked you to the code too.
As far as i can see in the digginginvader code there is no special code that are not 64bit compatible... and as no comment explain why ... may be someone with the code should try compiling and see ...
Don't adv-rumors and gather-quests cover most of these cases? I might have the names wrong btw, but I think those are the current ones.Search functionality is only in the deeper conversation menus like if you're picking from a list of incidents to summarize - but it isn't available nor would it be useful at the base menu where you select the main options. What bothers me is like when I want to ask a lot of people the same thing, and so every time I have to type the + or - key x amount of times to get to the option I want, and I have to do this a thousand times until I find someone who has the information I need. Thanks for the response, though, just checking.Someone told me this mod adds a little bit of mouse functionality for some menus - any chance that extends to the conversation menus in Adventure Mode? Those things are infuriating to scroll through after awhile.mousequery only deals with the fortress mode map. Don't the adventure mode menus have search functionality built-in in vanilla DF?
I've noticed very consistent crashes at the corners of region tiles, after entering then leaving them (playing advmode.) Fast traveling away often but not always counters this. Is there a particular plugin that could be causing this?
I've experienced it since the alpha releases.
The entire purpose of the alpha releases was for people to report issues like that. If you don't, they won't magically be fixed. Also, that is a crash.
That said, have you narrowed it down to dwarfvet? That is, if dwarfvet is completely disabled (run "unload dwarfvet" to be sure), does it still crash?
I'm continuing to look into other visitor information and unknown flags, if this information is useful, I'll keep you posted. If there's a better place to report this information, please let me know.Maybe https://github.com/dfhack/df-structures?
@lethosor: While at it might be worth noting that, reading dwarvet at line 655 (https://github.com/DFHack/dfhack/blob/master/plugins/dwarfvet.cpp), a naturalized human member of fort will be an Untamed Member of Own Civ who is not a Dwarf - which seems like it'd pass the checks for a reverted previously-tamed animal to unset civ_id, I guess breaking in the same way makeown does.I've never worked with dwarfvet, sorry. If there's an issue with makeown, is it documented and/or is there a fix?
function GmEditorUi:find_type()
local list={}
for i in pairs(df) do
table.insert(list,{text=('%s'):format(tostring(df[i]), i)})
end
guiScript.start(function(choice)
dialog.showListPrompt("Select type to find:",nil,COLOR_WHITE,list,
dialog.showInputPrompt(tostring(trg_key),"Value to find:",COLOR_WHITE,
"",function()
local tp = ""
self:pushTarget(df.choice.find(tp))
end))
end)
end
With lots of fucking around trying different local declarations, having the input spawned from outside the list, having the list return the choice local, and so forth, it feels like there's a "well Max, you've got the dumb because all you do is ________" method here but fuck if I can find it.function GmEditorUi:find_type()
local list={}
for i in pairs(df) do
table.insert(list,{text=('%s'):format(tostring(df[i]), i)})
end
guiScript.start(function()
local choice=guiScript.showListPrompt("Select type to find:",nil,3,list,nil,true)
if choice then
dialog.showInputPrompt(tostring(choice),"Value to find:",COLOR_WHITE,
"",function()
local tp = ""
self:pushTarget(df.choice.find(tp))
end)
end
end)
end
Man, still can't figure out what is breaking this, it's slapped into gm-editor and it loads up the df.types properly, but I've lost track of the ways I've tried to make it pass the type from the list prompt to the input prompt so you could scroll down and find the history_event entry and get prompted to enter 334 resulting in gm-editor opening df.history_event.find(334) for you, right?
Several things wrong that I noticed:Code: (gm-editor tinkering) [Select]function GmEditorUi:find_type()
local list={}
for i in pairs(df) do
table.insert(list,{text=('%s'):format(tostring(df[i]), i)})
end
guiScript.start(function()
local choice=guiScript.showListPrompt("Select type to find:",nil,3,list,nil,true)
if choice then
dialog.showInputPrompt(tostring(choice),"Value to find:",COLOR_WHITE,
"",function()
local tp = ""
self:pushTarget(df.choice.find(tp))
end)
end
end)
end
In the armies structure, army_T_members, unk_4 to unk_c are counters for hunger, thirst and stored_fat. The unit data is located in counters2.A pull request would be nice. I opened https://github.com/DFHack/df-structures/issues/211 so it doesn't get buried here.
DFHack plugin writing question (win32 currently, if it makes a difference):Crashes at exit in the runtime library are almost always caused by memory arena corruption, which means that you're writing to a freed pointer or overrunning an allocated buffer, which usually means a fencepost error somewhere.
I'm trying to write a plugin, but I'm puzzled by some random DF crashes, and I'm unsure whether I am to blame or whether my plugin is merely a canary exposing existing DF bugs.
DF occasionally crashes at game cleanup or game exit and the cause every time I've looked at it has been pointer access violation in ntdll.dll, and I've seen similar DF behavior (without knowledge of where it crashed) in the past, in particular with the 0.40.X versions (LNP, so DFHack was present). My plugin allocates memory but does not write to any DF structure (but reads a lot), although simulated key input is used to order the cursor on the pre embark map to move (and the underlying data structures to be loaded by DF).
However, the crashes happen when DF cleans up its data, not when my plugin tries to clean up its own stuff (of course, I've screwed up that as well, numerous times), which has already happened at that point. If I understand it correctly, failure on my part to clean up plugin memory allocation will merely result in a memory leak, not a crash, as DF itself has no knowledge of that memory and thus shouldn't do anything at all with it.
Writing data outside of the bounds of my own structures could potentially corrupt DF data, but most of the time it results in a crash at the time the plugin writes it, and it seems unlikely that kind of error would occasionally screw up DF's data but never cause the plugin to crash despite quite a few plugin test/fix/retest cycles for various errors/added code.
Hm. Toying with gui.FramedScreen for an indicator. Noticed that when fps/gfps < 1+numbers of screen I'm dismissing, screen:dismiss() can briefly blink game screen black.All screens will do that, sometimes. If you decrease your FPS setting to 10 (you can use [Alt][-] by default) and enter and exit the options (esc) menu, you'll notice a black screen when exiting but not when entering (probably).
Given that, is there a reason beyond onDismiss() to not just set the screen object to nil and have it poof without blinking? Graphical glitches, or..?No! First, that won't work without modifying the list of screens in df.global.gview.view (although maybe that's what you meant). Also, it will leak memory, because the screen won't be deleted properly, and might result in feed()/render()/logic() getting called on the wrong screen for a frame (or worse, crashing).
local myscreen = dfhack.gui.getCurViewscreen()-- df.global.gview.view.child.child
dfhack.timeout(100, "frames", function()
myscreen.parent.child = nil
dfhack.timeout(2, "frames", function()
newscreen:dismiss()
dfhack.println("screen is dismissed " .. tostring(newscreen:isDismissed()))
end)
end)
C++ doesn't have garbage collection.Exactly. Lua screens also contain a pointer to an underlying C++ object (that's what "_native" is), and that is what won't get garbage-collected (possibly other things as well).
Hm, cool....Though, upon relook, seems I cursed/cheered too soon. gui/extended_status still changes path, so something like keybinding add Shift-Alt-G@overallstatus "gaydar -citizens" in dfhack.init will fail to work with it activated...So, yeah. Seems I just unknowingly duplicated small part of it's code. Not sure now if I made any entirely superfluous functions, given that it'd need pretty much the same code to inherit keybindings and such.Yeah, that's a downside. There's been talk of adding ways for Lua scripts to modify screens like that, but it'd be a lot of effort.
keybinding add Shift-Alt-G@overallstatus|dfhack/lua/status_overlay "gaydar -citizens"
Is there a way to remove a tag from a unit by commands?It depends on what tag you're referring to. There's no way to "remove a tag" in a text-based way for a specific unit, as you would in a text editor. However, some tags cause certain flags/fields/etc. to be set on units, which you can change in-game on a per-unit basis. What tag(s) are you interested in?
Or must it be removed by an interaction or syndrome? if so, how do i input those?
I'm interested in the NOSTUN tag in particular.That looks like something that can only be done by modifying the creature raws (which would affect every creature of that race) or through a syndrome.
Just like that?I'm interested in the NOSTUN tag in particular.That looks like something that can only be done by modifying the creature raws (which would affect every creature of that race) or through a syndrome.
Given my experience, I'd say some raw changes only affect new creatures, but I'd suspect others affect all of them. The difference ought to be whether the info is copied into the unit on creation (in which case it ought to be possible to modify individually) or not. Note that this is speculation only: I haven't tried it out.
I wouldn't be surprised if changing the adult size of a creature would cause maturing (but already existing) creatures to take on the new size while those already mature would remain unchanged, i.e. raw changes affect only creatures that refer to the changed data for some processing.
I think twbt bears too much investigation into its stability to be so intractably integrated with dfhack that it can't be unloaded or disabled. It seems to me there's a level of denial about its responsibility for dfhack's crashprone reputation.
Issues with DFHack documentation on Github (They're stored separately where I can't find any way to raise Issues):Not sure where you were looking, but it's definitely on Github in the docs folder of https://github.com/dfhack/dfhack/
I've tried to create a DFHack plugin and have encountered a number of issues with the documentation on how to do this (the documentation is rather helpful, so this is a set of smaller issues). I'd also need help actually understanding how the process works.I suppose that's a fair point, although you don't need it to build DFHack - it just enforces a few checks. All of the code in the DFHack repo passes it currently: https://travis-ci.org/DFHack/dfhack
- The Compile document https://dfhack.readthedocs.io/en/stable/docs/Compile.html (https://dfhack.readthedocs.io/en/stable/docs/Compile.html) claims Python is needed only for documentation. This isn't quite correct as the How to Contribute document https://dfhack.readthedocs.io/en/stable/Contributing.html#contributing-code (https://dfhack.readthedocs.io/en/stable/Contributing.html#contributing-code) says you have to pass a Python script test before submitting code (which not all the DFHack copied from Github passed did, by the way).
- The latter document above assumes the reader has far too much knowledge of both Github and DFHack's use of Github for me to make sense of the process (which might be due to my lack of understanding, and it doesn't help that I have no prior experience of Github). It seems Github repos can be set up in several ways, and it isn't clear how DFHack is set up.Basic process:
A guess:
- Create a fork of the development branch (based on the second point plus some of the first one).
- Do work in branches of your fork, pull request (weird terminology for pushing something back without anyone actually being involved in accepting it?) the work back to your development branch and then pull request that back to your master branch and then pull request that back to the DFHack development branch. Look like overkill for a simple plugin, though.
- Presumably, the last step should be done only after some sanity checking and actual acceptance by a trusted DFHack maintainer, but it seems this is based only on trust?
Thanks Max. I haven't done anything with any travis linking: I've just run the script (once I managed to find it). Since it's a matter of code in my case the description etc. script stuff doesn't apply to me. Finding how to get the small soft compiler environment to stop using tabs took a few search passes to find the setting, but I've finally gotten rid of the tabs. The script pointed out where trailing spaces were, so it was a matter of going through that list to eliminate them.There's no demand for 4-space indentation for scripts:
If I get the hang of how Github is supposed to be used I'll consider placing my scripts there, in which case your script summary will come in handy (although I'll have to find out how to get Notepad++ to stop using tabs). Come to think of it, no, I won't put my script there, because of the stupid demand for 4 character indentation, which wastes far too much space (I use 2 in my scripts, but have resigned myself to suffering lines being pushed off the page with the code).
Four space indents for C++. Never use tabs for indentation in any language.(Now, I do have objections to 1-space indentation, because it's hard to read, but 2-space indentation is fine if you really want to do that. If you're running into issues with lines being too long, either you have things nested too far or you should break lines up at some point.)
I think twbt bears too much investigation into its stability to be so intractably integrated with dfhack that it can't be unloaded or disabled. It seems to me there's a level of denial about its responsibility for dfhack's crashprone reputation.To add on what Roses said, these are some reasons why it's not included in DFHack. Plugins that refuse to be disabled/unloaded are hard to deal with. I'm not up-to-date with how stable recent TWBT builds are, but having seen several crashes linked to it in the past, I'm a bit wary of using it much myself.
TWBT doesn't come with DFHack, it's a separate thing. And as far as I know, even if you have it installed it can be disabled by changing the print mode.I'm pretty sure Uzu Bash was talking about disabling it in-game with "disable twbt" or "unload twbt", both of which are impossible (it enables itself if it detects PRINT_MODE:TWBT in init.txt, and cannot be unloaded, ever).
I feel like this
helps keep a groove
when I'm scripting
because it reminds me
which blocks to check for closure
by visually
delineating
the relevant
nesting
and lack thereof
in my
subsections.
While this
causes the lines to skew
way over to the side
after just a couple of
chunks
which ends up
losing the vertical
component
of the visual
relationships
and can
make it harder
to catch errors
or even just
clean up
sloppy
and cluttered
bits of code.
Edit:
Trying to understand the pull request logic, my impression is that a pull request contains all the differences between the base and the head (terms used by web pages describing it). If that's the case, don't you need a separate branch for each item = (future) pull request? If you create plugin 1 and plugin 2 (or bug fix 1 and 2) they ought to be accepted/rejected separately?
Why no tabs requirement, anyway? Problems on *guessing* Mac systems?No, it's not platform specific at all. Mostly consistency and issues with tabs displaying differently in different environments.
With a marginal amount of additional experience, I conclude it's probably not possible to just magically get the header generation script to run from Visual Studio without the addition of some glue. The reason for this is that DFHack and df-structures are separate repos and separate entities, and separate directories (on my machine), so something would have to tell Visual Studio in my DFHack fork->branch->clone where to look for the df-structures fork->branch->clone. Adding this glue is probably trivial when you know how..df-structures should be a submodule and cloned in your DFHack/library/xml folder. You can make changes there and push them to your fork as with any other repo, although you may need to run "git checkout master" first.
[lua]# ~table.unpack{1,2,3}
1 2 3
[lua]# ~table.unpack{nil,1,2,3}
nil 1 2 3
Is there any quick way to clear a room of eighty beak dogs in gobbo towers?
_____
That's why I asked about the dragonfire, I should have been more specific as to the use.
It doesn't show in the 'ls' menu and it isn't finding it with 'help exterminate'. Would it kill creatures on the screen or entire world?Loaded revealed tiles, I think. Just type 'exterminate' and it should give you a list of target races. Then use exterminate <race name> to kill them.
Loaded revealed tiles
'Loaded' as in 'currently loaded'. Just the things in the site you're visiting, or a certain radius around you in the wilderness.Loaded revealed tiles
So, that would be all the things encountered in the past day's travel, pretty much?
Or everywhere I have been since day one?
region-pops list [pattern]:
Lists encountered populations of the region, possibly restricted by pattern.
region-pops list-all [pattern]:
Lists all populations of the region.
From http://dfhack.readthedocs.io/en/latest/docs/_auto/base.html#region-pops:I've seen that, and it's kind of why I'm confused, hence my asking. If the encountered only shows the population of things that've shown up, why would the numbers be different between the list and list-all?Quote from: http://dfhack.readthedocs.io/en/latest/docs/_auto/base.html#region-popsregion-pops list [pattern]:
Lists encountered populations of the region, possibly restricted by pattern.
region-pops list-all [pattern]:
Lists all populations of the region.
Because there are certain ones you haven't encountered, I'm assuming? I'm not familiar with the exact terminology here, but "region" may include more than your local map.My wording on that was bad, sorry. I was wondering more why like, my region-pops list shows Giant Tigers at 1, while region-pops list-all had about...18, I think it was?
If I recall correctly, region-pops looks at a DF structure of flora/fauna spanning a 7*7 world tile area with your fortress in the middle. This also explains why you can get stuff like ocean creatures reported even though you're not embarked particularly close to the ocean.This though would make sense...it'd be everything in the surrounding area, which miiiight end up showing up sometime? Hm. That helps make me less confused, thanks.
Determining what the technical difference is between the reports requires a study of the script, though.
EDIT: Not sure if this is place to make a suggestion or not, but would it be possible to add a confirmation of the script being on or off to warn-stuck-trees and warn-starving? I assume it's on to begin with, but it'd make it easier to tell.Are you talking about a way to tell whether those scripts are enabled? The default configuration uses the "repeat" command to run those scripts periodically, and I don't know if that has a way to tell whether a certain timeout is enabled or not.
Yeah, I wasn't sure how to actually tell if those scripts were on or not. I am very newbish to learning how to DFhack, I wasn't aware there was a repeat going on. But that makes sense.EDIT: Not sure if this is place to make a suggestion or not, but would it be possible to add a confirmation of the script being on or off to warn-stuck-trees and warn-starving? I assume it's on to begin with, but it'd make it easier to tell.Are you talking about a way to tell whether those scripts are enabled? The default configuration uses the "repeat" command to run those scripts periodically, and I don't know if that has a way to tell whether a certain timeout is enabled or not.
value('sched_size',df.squad_schedule_entry:sizeof())
Is there a way to get access to df structure enum value attributes from Lua? C translates it into macros, but that's of little help from a script. The things I'm currently after are the "color" attribute of df.unit-thoughts.xml:emotion_type and "caption" of unit_thought_type in the same file. As a bonus, it wouldn't hurt to know how to interpret the "divider" attribute of emotion_type.df.enum_type.attrs[value] (or df.enum_type.attrs.name).
I have a problem with the devel/export-dt-ini script. I get the wrong size for the structure "squad_schedule_entry" ("sched_size" in DT's world) using 0.43.05-r2 win32.What does "df.squad_schedule_entry:new():_field('name'):sizeof()" give you? Also, are you saying 0x34 should be the correct size? I don't think there's a way the size could be wrong unless df-structures' layout is wrong, because it should use C++'s sizeof() under the hood (and squad_schedule_entry doesn't inherit from anything, although that shouldn't matter either). Are you taking padding into account?
The line is:Code: [Select]value('sched_size',df.squad_schedule_entry:sizeof())
I get 0x40 instead of 0x34. I assume df-structures is good since it is what I used to get the correct size. I am not sure where the error comes from (C++ code generation or the script itself?).
With a marginal amount of additional experience, I conclude it's probably not possible to just magically get the header generation script to run from Visual Studio without the addition of some glue. The reason for this is that DFHack and df-structures are separate repos and separate entities, and separate directories (on my machine), so something would have to tell Visual Studio in my DFHack fork->branch->clone where to look for the df-structures fork->branch->clone. Adding this glue is probably trivial when you know how...I have three or four working directories for DFHack on my system (including one that I keep "clean" because I only use it for release builds: the only thing I ever do with it is bring it to a specific commit, followed by running the 32-bit and 64-bit build scripts). It's also very easy to switch between branches within a given repo. It's easy enough to have a DFHack repo in which the subrepo for df-structures is tracking a working branch of df-structures, although getting here does require using the git command line; you'll have to do a git submodule update --checkout whenever you switch a working directory between branches that are tracking different branches of the df-structures of the submodule.
Also, I'm not too keen on pushing the new headers into my current DFHack fork->branch->repo, as that will break the plugin I have there, so it would have to be a separate clone (which shouldn't be too hard to create). It can also be mentioned that df-structures
is ahead of the development branch of DFHack, as of my last check (a small bug Quietust fixed within 15 minutes of it being reported wasn't present in DFHack develop a few days later when I cloned my stuff, and I don't know what other things are ahead).
function printlnc(text, color)
dfhack.color(color)
dfhack.println(text)
dfhack.color(COLOR_RESET)
end
function bellyup ()
local geo_biome = df.world_geo_biome:new ()
geo_biome.layers:delete()
end
bellyup ()
As far as I understand, :delete() should not do anything when there is no applicable delete operation ("Destroys the object with the C++ delete operator. If destructor is not available, returns false." from the DFHack Lua API documentation). Is that a bug in the DFHack 64 bit Windows implementation?This script kills DF 64 bit but does nothing on DF 32 bit on Windows 10.1:Usually crashes like this is when stuff is misaligned (IIRC) i.e. when the underlying structures are not correct.Code: [Select]function bellyup ()
As far as I understand, :delete() should not do anything when there is no applicable delete operation ("Destroys the object with the C++ delete operator. If destructor is not available, returns false." from the DFHack Lua API documentation). Is that a bug in the DFHack 64 bit Windows implementation?
local geo_biome = df.world_geo_biome:new ()
geo_biome.layers:delete()
end
bellyup ()
Ignore the fact that the code isn't doing anything useful: I've already realized that...
function bellyup ()
local unit = df.unit:new()
unit.social_activities:delete()
--local geo_biome = df.world_geo_biome:new ()
--geo_biome.layers:delete()
end
bellyup ()
after reading Warmist's comment, and the behavior is the same, despite a completely different structure is attacked. The common denominator (that I can identify) is that both structures attempted to be deleted are <stl-vector> ones, and I don't think it matters if they contain data or not.
I doubt it's a structure mismatch issue, as I changed the script to:Oh you are doing something very strange... See the std::vector is not a pointer, so you are deleting a part of struct which is very invalid in all version, and you should not do itCode: [Select]function bellyup ()
after reading Warmist's comment, and the behavior is the same, despite a completely different structure is attacked. The common denominator (that I can identify) is that both structures attempted to be deleted are <stl-vector> ones, and I don't think it matters if they contain data or not.
local unit = df.unit:new()
unit.social_activities:delete()
--local geo_biome = df.world_geo_biome:new ()
--geo_biome.layers:delete()
end
bellyup ()
Yes, as I said initially, I've realized the pointer isn't actually a naked pointer but embedded in an object, so I'm in no way claiming it's sensible to do it. The thing is that the DFHack Lua API documentation seems to indicate the operation should do nothing when not valid (as well as return 'false' which I haven't checked to see if the 32 bit version does), rather than just run off the cliff.
Thanks ab9rf.library/xml is df-structures. It's a subrepo, and if you go into it's a full repository that you can manipulate in the same manner as any other repository. If you change the commit to which that repo points, the parent repository will track that as a change as to which commit the parent repository will track. This is, in fact, what you basically have to do if you make a change to dfhack code that relies on a newer df-structures release.
Given that the github structure has one structure for DFHack and another for DF structures, I never considered that the off site organization might somehow pull the apparent separate DF structures entity into the DFHack one, although I was aware that there are a lot of things I could never find in the online DFHack file structure.
Given your comments, I managed to get git to tell me there's a "git submodule status" command, and that command shows there is indeed a "library/xml" submodule, which looks to be very similar to the df structures one (i.e. it should be the one). This means it should be reasonably easy to have a "complete" dfhack structure whose purpose is to support df structures work as you said.
How would we know if you're using an outdated version?Possibly by being familiar with the bug and knowing that it has been recently fixed, for instance. Site building being glitched sounds like the kind of bug people would notice fairly easily if it affected them. Here's a video of the site creation bug: https://gfycat.com/SpanishVigorousInexpectatumpleco
Sadly twbt doesn't play nicely with adventure mode.I've used it in adventure mode for a good bit, and this is the only major issue I've encountered. The other problem is that the yellow descriptive text in the bottom left corner (when looking at things) flows off screen with TWBT, but it's usually not too important.
Go into travel mode and approach a site until the map zooms in a bit, particularly the first thing you do after loading a save, if it doesn't crash when the map zoom happens, it'll usually do it if you leave travel mode there, or upon re-entering it.Sadly twbt doesn't play nicely with adventure mode.I've used it in adventure mode for a good bit, and this is the only major issue I've encountered. The other problem is that the yellow descriptive text in the bottom left corner (when looking at things) flows off screen with TWBT, but it's usually not too important.
My questions are, if I have a couple dozen custom skills that I want to track, will adding them in this way cause any issues further down the line that I am not seeing?Yes. When (not if) Toady adds more skills, you won't be able to distinguish yours from the new ones. Maybe negative numbers would work, but I'm not sure.
And is there any way to add a visual representation of these skills? (i.e. when 'V'isualizing a unit you can see Novice Wood Cutter, could I set it up to display my custom skills somehow?I'm not sure how DF displays skill names. If it uses a function, we probably wouldn't be able to patch that, and depending on how it works, using invalid (to DF) skill IDs could crash.
As a bonus question, can this same method (utils.insert_or_update) be used to store custom attributes and traits? And will information stored like this be lost in save/reload?insert_or_update isn't that special - if the way in which attributes and traits are stored is similar to skills, you could probably use it. I'm guessing it would be saved, but maybe not.
Is there a way to do a bodyswap?If by "bodyswap" you mean to change a creature into something else, I think there's a script to do that (and it may have some issues, so I'd make sure to have a backup before trying). If you mean swapping the bodies between two creatures I assume you could convert them one at a time, but if you wanted it done completely you'd probably have to record all the features of them and apply adjustment of them to the other one afterwards (things like the shape of the nose, hair color, weight, etc).
Is there a way to do a bodyswap?If by "bodyswap" you mean to change a creature into something else, I think there's a script to do that (and it may have some issues, so I'd make sure to have a backup before trying). If you mean swapping the bodies between two creatures I assume you could convert them one at a time, but if you wanted it done completely you'd probably have to record all the features of them and apply adjustment of them to the other one afterwards (things like the shape of the nose, hair color, weight, etc).
there should be an Adv-bodyswap script left over from uhh one of the dfhack folks remaking warmist scripts but if that got lost in the recent updates of dfhack then uhh uh oh shoot I don't know which one to port, my copy is a tad rigged for my tastes and I know there's a bodyswap script that allows for switching back to your original body that's some where in dfhack's old versions... or in dfusion.Is there a way to do a bodyswap?If by "bodyswap" you mean to change a creature into something else, I think there's a script to do that (and it may have some issues, so I'd make sure to have a backup before trying). If you mean swapping the bodies between two creatures I assume you could convert them one at a time, but if you wanted it done completely you'd probably have to record all the features of them and apply adjustment of them to the other one afterwards (things like the shape of the nose, hair color, weight, etc).
I meant swapping the bodies of a decoy adventurer and a megabeast, as a hacky way of playing an actual worldgen megabeast.
My questions are, if I have a couple dozen custom skills that I want to track, will adding them in this way cause any issues further down the line that I am not seeing?Yes. When (not if) Toady adds more skills, you won't be able to distinguish yours from the new ones. Maybe negative numbers would work, but I'm not sure.
I would write a small script that would always assign custom skills above whichever is the highest game skill. That way SPELL_CASTING could be 135 or 146, it would just always be tracked as #skills+1.Wouldn't that break save compatibility each time the number of skills changes? You'd be better off using arbitrarily large numbers, or moving backwards from the highest possible value.
I was toying around trying to get this fixed so it shows a list of retired adventurer flag=true units to swap back to if targeted on yourself:there should be an Adv-bodyswap script left over from uhh one of the dfhack folks remaking warmist scripts but if that got lost in the recent updates of dfhack then uhh uh oh shoot I don't know which one to port, my copy is a tad rigged for my tastes and I know there's a bodyswap script that allows for switching back to your original body that's some where in dfhack's old versions... or in dfusion.Is there a way to do a bodyswap?If by "bodyswap" you mean to change a creature into something else, I think there's a script to do that (and it may have some issues, so I'd make sure to have a backup before trying). If you mean swapping the bodies between two creatures I assume you could convert them one at a time, but if you wanted it done completely you'd probably have to record all the features of them and apply adjustment of them to the other one afterwards (things like the shape of the nose, hair color, weight, etc).
I meant swapping the bodies of a decoy adventurer and a megabeast, as a hacky way of playing an actual worldgen megabeast.
edit : uhh oh wow there is no adv-bodyswap lua file or plugin at all in the files, there is an adv-fort but no adv-bodyswap.
--Assume Direct Control of the unit you're viewing. This Can Hurt You.
--[====[
assumecontrol
=============
Allows you to temporarily or permanently swap bodies with another unit.
Animals and other non-historical figures can be glitchy if you travel as one, be careful!
]====]
local utils = require 'gui'
local dialog = require 'gui.dialogs'
function assumeControl(new,old)
local actold
for i,j in ipairs(df.global.world.units.all) do
if j.id==df.global.world.units.active[0].id then
actold=j.id
break
end
end
local active=df.global.world.units.active
local old
old=df.unit.find(actold)
local new
new=dfhack.gui.getSelectedUnit(true)
if new==nil then
qerror("Unable to Assume Control!")
end
local actnew
for k,v in pairs(active) do
if v==new then
actnew=k
break
end
end
if actnew==nil then
qerror("Attempt to Assume Control has failed?")
end
if dfhack.gui.getSelectedUnit(true)==active[0] then
local choices={}
for k,v in pairs(active) do
if dfhack.units.getNemesis(active[k]).flags.RETIRED_ADVENTURER==true then
local nems=active[k]
table.insert(choices,{text=nems.name.first_name,nems=k})
end
dialog.showListPrompt("Unit choice", "Choose unit to return to:", COLOR_WHITE,choices,
function (idx,choice)
dfhack.units.getNemesis(choice).flags.ACTIVE_ADVENTURER=true
dfhack.units.getNemesis(choice).flags.ADVENTURER=true
dfhack.units.getNemesis(choice).flags.RETIRED_ADVENTURER=false
choice.status.current_soul.personality.flags[1]=true
dfhack.units.getNemesis(old).flags.ACTIVE_ADVENTURER=false
dfhack.units.getNemesis(old).flags.RETIRED_ADVENTURER=true
old.status.current_soul.personality.flags[1]=false
return
end)
end
end
active[actnew]=active[0]
active[0]=new
local target = dfhack.units.getNemesis(new)
if target then
local nwnem=dfhack.units.getNemesis(new)
local olnem=dfhack.units.getNemesis(old)
if olnem then
olnem.flags.ACTIVE_ADVENTURER=false
olnem.flags.RETIRED_ADVENTURER=true
olnem.unit.status.current_soul.personality.flags[1]=false
olnem.unit.idle_area.x=olnem.unit.pos.x
olnem.unit.idle_area.y=olnem.unit.pos.y
olnem.unit.idle_area.z=olnem.unit.pos.z
end
if nwnem then
nwnem.flags.ACTIVE_ADVENTURER=true
nwnem.flags.RETIRED_ADVENTURER=false
nwnem.flags.ADVENTURER=true
nwnem.unit.status.current_soul.personality.flags[1]=true
for k,v in pairs(df.global.world.nemesis.all) do
if v.id==nwnem.id then
df.global.ui_advmode.player_id=k
end
end
end
else
qerror("Assuming Direct Control! Current target may not last long!")
end
end
assumeControl(new,old)
Ok, either someone please tell me about a replacement to Dfusion's freaky Friday-like change adventurer script or just tell me how to re-add Dfusion.
Well, the easy part is set a target grab (dfhackgetselectedunit or whatnot) and grab hist_id, pass it to the find command (what was the dfhack one? not the df.global.find(k) one or whatnot) and it'll pull up the df.global.world.history.hist_figs[k] for you, then have it set flags[0] to true, which should add that unit to the Specific Person part of the adventurer selection screen, I've gm-editor'D a bodyswitch but it was all sorts of slapdash, it should just be a matter of unlinking your unit from df.global.world.units.active[0] and setting your new adventurer target in there, plus tracking down any hanging links left in the hist_fig part I think?
I was toying around trying to get this fixed so it shows a list of retired adventurer flag=true units to swap back to if targeted on yourself:I meant swapping the bodies of a decoy adventurer and a megabeast, as a hacky way of playing an actual worldgen megabeast.there should be an Adv-bodyswap script left over from uhh one of the dfhack folks remaking warmist scripts but if that got lost in the recent updates of dfhack then uhh uh oh shoot I don't know which one to port, my copy is a tad rigged for my tastes and I know there's a bodyswap script that allows for switching back to your original body that's some where in dfhack's old versions... or in dfusion.
edit : uhh oh wow there is no adv-bodyswap lua file or plugin at all in the files, there is an adv-fort but no adv-bodyswap.Code: (assumecontrol.lua) [Select]--Assume Direct Control of the unit you're viewing. This Can Hurt You.
--[====[
assumecontrol
=============
Allows you to temporarily or permanently swap bodies with another unit.
Animals and other non-historical figures can be glitchy if you travel as one, be careful!
]====]
local utils = require 'gui'
local dialog = require 'gui.dialogs'
function assumeControl(new,old)
local actold
for i,j in ipairs(df.global.world.units.all) do
if j.id==df.global.world.units.active[0].id then
actold=j.id
break
end
end
local active=df.global.world.units.active
local old
old=df.unit.find(actold)
local new
new=dfhack.gui.getSelectedUnit(true)
if new==nil then
qerror("Unable to Assume Control!")
end
local actnew
for k,v in pairs(active) do
if v==new then
actnew=k
break
end
end
if actnew==nil then
qerror("Attempt to Assume Control has failed?")
end
if dfhack.gui.getSelectedUnit(true)==active[0] then
local choices={}
for k,v in pairs(active) do
if dfhack.units.getNemesis(active[k]).flags.RETIRED_ADVENTURER==true then
local nems=active[k]
table.insert(choices,{text=nems.name.first_name,nems=k})
end
dialog.showListPrompt("Unit choice", "Choose unit to return to:", COLOR_WHITE,choices,
function (idx,choice)
dfhack.units.getNemesis(choice).flags.ACTIVE_ADVENTURER=true
dfhack.units.getNemesis(choice).flags.ADVENTURER=true
dfhack.units.getNemesis(choice).flags.RETIRED_ADVENTURER=false
choice.status.current_soul.personality.flags[1]=true
dfhack.units.getNemesis(old).flags.ACTIVE_ADVENTURER=false
dfhack.units.getNemesis(old).flags.RETIRED_ADVENTURER=true
old.status.current_soul.personality.flags[1]=false
return
end)
end
end
active[actnew]=active[0]
active[0]=new
local target = dfhack.units.getNemesis(new)
if target then
local nwnem=dfhack.units.getNemesis(new)
local olnem=dfhack.units.getNemesis(old)
if olnem then
olnem.flags.ACTIVE_ADVENTURER=false
olnem.flags.RETIRED_ADVENTURER=true
olnem.unit.status.current_soul.personality.flags[1]=false
olnem.unit.idle_area.x=olnem.unit.pos.x
olnem.unit.idle_area.y=olnem.unit.pos.y
olnem.unit.idle_area.z=olnem.unit.pos.z
end
if nwnem then
nwnem.flags.ACTIVE_ADVENTURER=true
nwnem.flags.RETIRED_ADVENTURER=false
nwnem.flags.ADVENTURER=true
nwnem.unit.status.current_soul.personality.flags[1]=true
for k,v in pairs(df.global.world.nemesis.all) do
if v.id==nwnem.id then
df.global.ui_advmode.player_id=k
end
end
end
else
qerror("Assuming Direct Control! Current target may not last long!")
end
end
assumeControl(new,old)
Commenting out those lines makes it work just fine, though it just spits an error when you dismiss the box atm, my lua mojo ran out.
Thank you! 8) I'm so glad that interest in bodyswap hasn't completely disappeared and that someone worked on updating it. I was sad to see DFusion and, particularly, Rumrusher's "Adventure Tools" (aka, "adv_tools") get abandoned (http://www.bay12forums.com/smf/index.php?topic=139553.msg7034131#msg7034131), but I miss bodyswap most of all.These:
BTW: Which lines are the ones that need to be commented out?
if dfhack.gui.getSelectedUnit(true)==active[0] then
local choices={}
for k,v in pairs(active) do
if dfhack.units.getNemesis(active[k]).flags.RETIRED_ADVENTURER==true then
local nems=active[k]
table.insert(choices,{text=nems.name.first_name,nems=k})
end
dialog.showListPrompt("Unit choice", "Choose unit to return to:", COLOR_WHITE,choices,
function (idx,choice)
dfhack.units.getNemesis(choice).flags.ACTIVE_ADVENTURER=true
dfhack.units.getNemesis(choice).flags.ADVENTURER=true
dfhack.units.getNemesis(choice).flags.RETIRED_ADVENTURER=false
choice.status.current_soul.personality.flags[1]=true
dfhack.units.getNemesis(old).flags.ACTIVE_ADVENTURER=false
dfhack.units.getNemesis(old).flags.RETIRED_ADVENTURER=true
old.status.current_soul.personality.flags[1]=false
return
end)
end
end
don on me that I still haven't fix the invoke script to add a new dialog option than just changing or converting some options.Now I wanna get out of "woodcarving mode" and back into "hackery mode" and try to figure out how to make a list prompt version of that, though I'd have gone with allconvo.lua or convocontrol.lua myself.
this probably would fix and lead to some more dialog options in adventure mode or setting up Scripts that trigger on conversation.
being able to mandatory force folks to accepting hearthperson roles, or taking folks positions away seems ok.
probably call it the dialog-unlocker.
So, I've been working on a little change to the RPC stuff in DFHack, and due to its nature, I think it needs a bit of discussion.
PR Here: https://github.com/DFHack/dfhack/pull/1173
Basically, it allows the user to turn on being able to use the DFHack RPC stuff from outside their computer. This is dangerous, obviously, which is why not all RPC functions are available. Biggest two that are disabled are the run command and the lua command, both of which can potentially wreck your everything due to being able to run arbitrary code.
So, opinions?
Again, any way to eat things from the ground?Nope, but you can e.g. haul the item before trying to eat.
Again, any way to eat things from the ground?Nope, but you can e.g. haul the item before trying to eat.
I don't know whether this is a Dwarf Therapist or a DFHack issue, I think it could be either one, because DT only uses when DFHack gives it, but I also read that they DT may be connecting and disconnecting to DF memory every time it refreshes... so I don't know. I also reported this bug in DT GitHub, hoping that either you or the DT guys will find the cause for this.DFHack currently doesn't provide any data to DT. (Well, you can generate layout files for DT using DFHack, but DT is independent from DFHack otherwise, and you should run into the same issues regardless of whether you're actually using DFHack.)
I don't think you need hands to haul. See if you can get the item there through hackery.I meant a script. Dragons don't have hands, you know.Again, any way to eat things from the ground?Nope, but you can e.g. haul the item before trying to eat.
I don't think you need hands to haul. See if you can get the item there through hackery.I meant a script. Dragons don't have hands, you know.Again, any way to eat things from the ground?Nope, but you can e.g. haul the item before trying to eat.
Well, you can make a backup, and have a learning experience.
I don't understand how reading the DF memory could screw up the data structure (while not saying it's impossible just because of my limited imagination), but making use of it (as in changing character data, such as nick names, job allocations, etc.) might.
If DT can cause the issue, it would probably be good to have a save where you an provoke it with some degree of reliability to investigate what's happening.
I would be careful with saving in the middle of a migrant/caravan/siege arrival, though, as I believe that has been thought to trigger the issues.
Does there already exist a DFhack script to allow the remains of tame or trained animals to be butchered? I searched the DFhack documentation, but I couldn't find anything.
If not, how would I go about flipping the dead_dwarf flag on a specific corpse back to false? I don't want to bring it back to life. But I do want to stop the usual pet-burial behavior in order to butcher it, instead. (See this post (http://www.bay12forums.com/smf/index.php?topic=161408.msg7341491#msg7341491) in relation to issue 1275 (http://www.bay12games.com/dwarves/mantisbt/view.php?id=1275).)
function GmEditorUi:find_type()
local list={}
for i in pairs(df) do
table.insert(list,{text=('%s'):format(tostring(df[i]), i), type=i})
end
guiScript.start(function()
dialog.showInputPrompt("Testing","Value to find:",COLOR_WHITE,"",
function()
dialog.showListPrompt("Select type to find:",nil,COLOR_WHITE,list,
function()
local key = self:currentTarget()
self:pushTarget(df.key.find("..input.."))
end)
end)
end)
end
Gone through a bunch of iterations incidentally, tried stripping it down to just the dialogs, tried having it call a "just select_type" function from a find_type one, tried copying bits of the various similar scripts in gm-editor itself, but I still can't get it to pass the values on properly, just gives the "attempt to index a nil value" for something I just assigned a value for in the prior steps.
When speaking about fixing things, how hard would it be to fix falconne's automaterial/automelt to work like his search/buildingplan/dwarfmonitor/resume/pasture assignment in regards to lua viewscreens (in that the first wouldn't disappear while one is up, like the latter don't)?Are you working with a Lua overlay/dialog or something? I've seen a couple plugins that don't display under overlays, and I imagine the two you mentioned are similar and not too hard to change. Getting mouse input passed through to mousequery could be hard, though.
(mousequery falls somewhere in-between, where it displays data, but doesn't place floors)
((Though at least it looks like one might be able to replicate automelt's gui frontend entirely, I don't see how to do it for automaterial.))
(((For constructmultiz, I worked around this by making the viewscreen poof while building constructions, but that's not exactly ideal.)))
I'm not sure what you're saying here. Mousequery isn't a screen, so you can't send keys to it (and it really only looks at the mouse status in the enabler global anyway, from what I remember).
Just tested whether mousequery respects gui.simulateInput/screen:sendInputToParent (with and without flickering on-off). [It doesn't].
Combat skills can scale upwards to a functionally impossible to reach degree, meaning that simply reaching Legendary in a combat skill only means they've just started climbing the ranks of the legendary warriors of Dwarf Fortress. A Legendary +100 warrior will more regularly hit and deal more damage than a "mere" Legendary +10, although it takes nearly three-quarters of a million more experience points to get there.
Okay I was doing some work on fixing a modtool script that is, I'm guessing, out dated or just incomplete. The particular one was skill-change.lua
So after a few changes to fix things such as actually forcing rating increase (otherwise you could just add 100,000 exp and it wont change the rating) and stopping it from jumping the L+5 mark(rating 20) on skills. the issue now is that I know certain skills have a higher max than rating 20. considering this quote from the wiki under skills:QuoteCombat skills can scale upwards to a functionally impossible to reach degree, meaning that simply reaching Legendary in a combat skill only means they've just started climbing the ranks of the legendary warriors of Dwarf Fortress. A Legendary +100 warrior will more regularly hit and deal more damage than a "mere" Legendary +10, although it takes nearly three-quarters of a million more experience points to get there.
My issue is that it doesn't clearly define which skills these are. I'm fairly certain the weapon skills are considered in this, such as AXE and CROSSBOW.... I need a full list so that I'm not making this script cap a skill that doesn't have a cap. Anyone have that info?
Okay I was doing some work on fixing a modtool script that is, I'm guessing, out dated or just incomplete. The particular one was skill-change.lua
So after a few changes to fix things such as actually forcing rating increase (otherwise you could just add 100,000 exp and it wont change the rating) and stopping it from jumping the L+5 mark(rating 20) on skills. the issue now is that I know certain skills have a higher max than rating 20. considering this quote from the wiki under skills:QuoteCombat skills can scale upwards to a functionally impossible to reach degree, meaning that simply reaching Legendary in a combat skill only means they've just started climbing the ranks of the legendary warriors of Dwarf Fortress. A Legendary +100 warrior will more regularly hit and deal more damage than a "mere" Legendary +10, although it takes nearly three-quarters of a million more experience points to get there.
My issue is that it doesn't clearly define which skills these are. I'm fairly certain the weapon skills are considered in this, such as AXE and CROSSBOW.... I need a full list so that I'm not making this script cap a skill that doesn't have a cap. Anyone have that info?
I'm pretty sure you can set any skill to higher than 20 with DFHack without any problems (at least that's what I remember from the last time I tested this). It's just that only certain skills actually benefit from having higher than 20. So the first order fix would just be to allow the number to go as high as you want.
df.global.ui_advmode.conversation[insert number here].choices.choice
option.Is the vanilla hard coded workshops, their tile data, possible to be edited directly through dfhack?First one is possible, but hard-ish. You need to vmethod-interpose a building vmethod (i think it's called "render"). This can only be done by writing a plugin.
I had an idea but I really wanted to change a few shop images completely...
and it sounds like its possible. But I can't find the right direction to get there... I'm not talking TWBT...as TWBT only goes sooo far. I'm talking actually changing every single location to exactly what I want it to be...
Besides directly editting these through DFHack I've also considered what it would take to copy building menus to new buildings... like pulling the menu/reactions for the still to a new custom building. anyways if anyone has an idea of how this could be done.. I'd appreciate it.
Is the vanilla hard coded workshops, their tile data, possible to be edited directly through dfhack?First one is possible, but hard-ish. You need to vmethod-interpose a building vmethod (i think it's called "render"). This can only be done by writing a plugin.
I had an idea but I really wanted to change a few shop images completely...
and it sounds like its possible. But I can't find the right direction to get there... I'm not talking TWBT...as TWBT only goes sooo far. I'm talking actually changing every single location to exactly what I want it to be...
Besides directly editting these through DFHack I've also considered what it would take to copy building menus to new buildings... like pulling the menu/reactions for the still to a new custom building. anyways if anyone has an idea of how this could be done.. I'd appreciate it.
Second one could be faked i think. There is eventful plugin (https://dfhack.readthedocs.io/en/stable/docs/Lua%20API.html#functions) that allows displaying any sidebar you want on a building.
Has anyone ever toyed with the concept of a offscreen viewport? You know, rendering parts of the map that are offscreen without moving the viewport.It's DONE!*
If it requires writing custom rendering, fine by me. I just want a bit of guidance so i know (roughly) where to aim at this whale.
Has anyone ever toyed with the concept of a offscreen viewport? You know, rendering parts of the map that are offscreen without moving the viewport.I guess it depends on what you mean by that question. Warmist has provided an answer that, if I understand it correctly, allows you to render a different part of an embark(?). I suspect that's the info you're after.
If it requires writing custom rendering, fine by me. I just want a bit of guidance so i know (roughly) where to aim at this whale.
Time to do this with random wild animals as a elf i guess.man I remember doing this with a mod.
So, what sort of DFHackery would I have to do to make monotheism? Is there even a way?This is really stuff expected to be covered in the Myth & Magic arcs (and not necessarily the first one).
So, what sort of DFHackery would I have to do to make monotheism? Is there even a way?This is really stuff expected to be covered in the Myth & Magic arcs (and not necessarily the first one).
In the nearer time frame, it depends on what you mean with monotheism.
- If you mean there's only a single god in the world, you could presumably remove all but one and then search all units to replace their deity references with only that one or none (I'd check if the same can be done with hist figs).
- If you mean there's only a single god in each pantheon you could do the same but for each civ.
In both of the cases you'll probably end up with a somewhat inappropriate single aspect type of god, though.
However, neither method would stop development of worship of e.g. megabeasts, and the somewhat broken worship inheritance system where children inherit the mother's gods plus a random one (or none) with duplicates possible remains.
Using the DFHack advfort stuff, and I was able to make a masons workshop, and a couple gabbro blocks out of a piece of gabbro stone...but after making one set of blocks, and having more stone, I can't see to make anymore blocks.
Additionally I'm trying to make a metalsmiths forge, but it doesn't seem to want to accept my fireproof block as a material...
I also can't make any stone items at the masons now that I've used it once, with any stone at the workshop...
I am very confused, some help would be super awesome!
Using the DFHack advfort stuff, and I was able to make a masons workshop, and a couple gabbro blocks out of a piece of gabbro stone...but after making one set of blocks, and having more stone, I can't see to make anymore blocks.
Additionally I'm trying to make a metalsmiths forge, but it doesn't seem to want to accept my fireproof block as a material...
I also can't make any stone items at the masons now that I've used it once, with any stone at the workshop...
I am very confused, some help would be super awesome!
More information, I've found that what's happening is that the build process isn't finishing. I am not sure why..I can leave it forever ticking away as my adventurer gets hungry and thirsty and tired while it never builds, saying (1) for the ticks, or in a few cases (-1). When I watch these fail to build, they start decreasing as usual, but when they hit 0 they either jump back to 1, or drop down to -1, and then fail. If they stay on 0 for a couple of steps they finish and I can use them...
Workarounds would be super helpful!
Found picking up and dropping items repeatedly fixed some of this nonesense...
but now the built smelter and wood furnace don't have the "TAB" option as a workshop, only the masons shop seems to be a workshop...so I can't do anything anywhere but there...any ideas?
Is there a DFHack or script implementation of a naming UI? I'm considering a script that would involve (re)naming of at least regions and rivers, and would rather use something already available than slog through that mess myself.
I'm talking about changing the entities' names as displayed on the world map pre embark, which means changing the multi part language based names (and I suspect these entities do not have any simple string "name" field to modify anyway). The fallback would indeed be to re-implement some version of the native naming screens, but it's not my favorite wheel to reinvent.
That much I know, and that's fine for simple movement, but climbing, for example, has multiple menus to sort through, which means I can't just tell my adventurer to climb north-east in one go.from my dabble into this mess it seems like the whole thing is tied to careful movement options and that seems to have no data in gm-editor.
Is there a DFHack tool that will allow you to 'reroll' your starting seven?
IE - you are looking to make a generational fort, and want an even mix of male/female, but you get to the skill select screen and notice that the game has coughed up 7 females. Instead of exiting out, and re-entering and selecting your site and doing all the busy work, you could just hit a key and reroll the seven initial dwarves.
Is there such a thing?
lua "dfhack.gui.getCurViewscreen().parent.breakdown_level=df.interface_breakdown_types.NONE dfhack.gui.getCurViewscreen().breakdown_level=df.interface_breakdown_types.STOPSCREEN"
I'm talking about changing the entities' names as displayed on the world map pre embark, which means changing the multi part language based names (and I suspect these entities do not have any simple string "name" field to modify anyway). The fallback would indeed be to re-implement some version of the native naming screens, but it's not my favorite wheel to reinvent.Feel free to grab anything useful from here: https://github.com/maxthyme/scripts/blob/master/names.lua though I don't see any reason why it wouldn't be possible to make it check for the embark screen > currently displayed region/etc name and use that for the target. I'd try to do it myself but I'm in a woodcrafty mode, not a luahackery mode.
I'm talking about changing the entities' names as displayed on the world map pre embark, which means changing the multi part language based names (and I suspect these entities do not have any simple string "name" field to modify anyway). The fallback would indeed be to re-implement some version of the native naming screens, but it's not my favorite wheel to reinvent.Feel free to grab anything useful from here: https://github.com/maxthyme/scripts/blob/master/names.lua though I don't see any reason why it wouldn't be possible to make it check for the embark screen > currently displayed region/etc name and use that for the target. I'd try to do it myself but I'm in a woodcrafty mode, not a luahackery mode.
You can just add the reactions directly in the entity file... Unless you need go do it later to avoid worldgen issues.
If that is the case, then you will need to edit the correct entity in df.global.world.entities. If the historical entity doesn't have a proper fields (I didn't check, so it may not), then the lists in (*entity_raw).workshops will probably do the trick (found in df.global.world.raws.entities or (*historical_entity).entity_raw)
Be careful! The historical entities (but not the raw entities) are saved! Either way you need to make sure not to add duplicate entries.
b=require "plugins.eventful"
b.addReactionToShop("TAN_A_HIDE","LEATHERWORKS")
IIRC, addReactionToShop either didn't work at all, or worked very poorly last time I tried to use it.I think addReactionToShop has some limit on shops...
Some completely useless info on it not working here. (http://www.bay12forums.com/smf/index.php?topic=150775.msg6389451#msg6389451) Damn, I wish I could remember how and why it didn't work.
Edit: it may have been fixed at some point though, so if it works let me know. Can't have outdated info clogging the mind...
except they are unaccessible, in red, as if they don't have the right reagents, the reactions have 0 reagents so it can't be that.
except they are unaccessible, in red, as if they don't have the right reagents, the reactions have 0 reagents so it can't be that.
Now I remember! That was the problem! I'm pretty sure I never figured out why it did that, so I gave up and worked something else out.
By the way: If you want, it would be fairly easy to write a script that removed reactions from the sidebar list just before it is shown, but after the game populates it. You just use the same method as in my last post, but instead of adding new buttons, you iterate the list and remove the ones you don't want. (The procedure should be similar to what change-build-menu does to remove workshops, just with different lists and greatly simplified due to being able to use eventful to trigger the code).
The only issue is that, last I knew, eventful.postWorkshopFillSidebarMenu did not work for furnaces.
I think you could have the reaction (and building) present in the entity from the beginning but delete it from the menu everytime the menu is opened up, unless a certain flag is set. That way it would be like "adding" the reaction when a specific criterion is meant, but in actually it would be just not removing it. I planed to do something similar for creating tech trees
except they are unaccessible, in red, as if they don't have the right reagents, the reactions have 0 reagents so it can't be that.
Now I remember! That was the problem! I'm pretty sure I never figured out why it did that, so I gave up and worked something else out.
By the way: If you want, it would be fairly easy to write a script that removed reactions from the sidebar list just before it is shown, but after the game populates it. You just use the same method as in my last post, but instead of adding new buttons, you iterate the list and remove the ones you don't want. (The procedure should be similar to what change-build-menu does to remove workshops, just with different lists and greatly simplified due to being able to use eventful to trigger the code).
The only issue is that, last I knew, eventful.postWorkshopFillSidebarMenu did not work for furnaces.I think you could have the reaction (and building) present in the entity from the beginning but delete it from the menu everytime the menu is opened up, unless a certain flag is set. That way it would be like "adding" the reaction when a specific criterion is meant, but in actually it would be just not removing it. I planed to do something similar for creating tech trees
Basically that's what I did... actually since its a complex script setup; the buildings are added to a abnormal location (MACHINES not WORKSHOP or FURNACES) I just built a quick reader that reads the opening of the raw file for the building list and a few other interesting bits of info. It also reads the opening to the reaction list which stores a section of info for reaction-triggers and could contain other bits of info later for restrictions.... all of this is done prior to the object token so DF itself ignores it all completely as if its a comment section. What it allows is the dynamic construction of the reaction triggers and putting the buildings wherever the player wants. even added in a section for entity control so the first part of the info for each building is the entity allowed the item ie "- MOUNTAIN BUILDING INFO....." means the building will only be added to Dwarfs. Lastly which I'm not going to bother, I can work out restrictions in similar ways and stop reactions from being accessible, although I totally ignored it this project... since I'm already restricting via not entering the buildings into the entity build menu until the script is turned on.
The smaller project which I'm working on that the whole of the code is based off is named "pipe-dreams" which will be an integrated part of LDF, but will also be standalone launched when I get it completed... probably in a week... even though it wont be ready for the new DF version until the next DFHack is ready. Its based on some of your scripts Roses, I hope you don't mind.
Probably multiple weeks. I don't actually have a good estimate yet.
Where do you need hands at? I'm not the greatest at things (more brute force than anything), but I don't mind trying. I also have a couple of programmer friends on call. I guess what I'm asking is what needs to be done to get a test DFHack version up and going?We mostly need to verify that structure layouts are right, and fix ones that have changed. It's hard for me to explain exactly what to do there, but if you can compile a bleeding-edge version of DFHack (with up-to-date structures, which might require an extra "git pull" in library/xml), you could poke around at things in the Lua interpreter and point out anything that looks wrong or crashes.
I'd be looking at it myself but naturally can't, but what is the new stuff in the xml export exactly?
--WARNING below value should be: "general_ref::vtable","1","0x8","0x4","vmethod","getType","general_ref_type",""
value('ref_type',0x8)
What is the best method for someone who is not a developer (my coding abilities extend to semi-complex python scripting and some basic Java coding) to find out the progress towards having DFHack up to date with DF? I don't want to bother the devs or spam threads every time there's a new DF version. Instead, is there a way for me to find this out directly, on my own? If so, could some kind soul point me in that direction?In the past, we have pointed people to "v0.xx.yy.lst" in https://github.com/dfhack/df-structures. There isn't one yet for 0.44.x, though, because the Linux build was only just released. Here (https://github.com/DFHack/df-structures/blob/master/v0.43.05.lst) is 0.43.05's; "UNCHECKED" is (potentially) bad, "ALIGNED" is good, "VERIFIED" is human-checked, so hopefully entirely right.
I meant the: "Various additional data in the XML export" from the release notes, is there not anything relevant to getting dfhack working added this time?Toady added global addresses in 0.44.01, which are in a global table in the DF executable, nothing to do with the XML export.
I would have thought you already had a set of basic sanity checking tools for the structures that are mapped?Yeah, we have some tools (cl-linux-debug, although it wasn't as useful without a Linux build). Mifki is actually working on another one right now, a Lua script.
I'm thinking of something that crawls through df.global depth first and verifies that fields with known bounds are within those,Not sure what exactly this part means - it's nearly impossible to check primitive field sizes (integers)
and probably crashing (with a log indication of where) when assumed pointers don't point to legal locations as it attempts to follow the pointers.We can check pointer validity, but it's a little slow. Checking vector validity isn't too hard either, but other things (like strings) can be more complicated.
- The most trivial sanity check would probably be to verify that enum values either have a legal value or -1.Mifki's script does this, I think. (-1 is only legal for some.)
- Another check would be to verify that a value that's a reference into a vector is within that vector's range, or that an identifier (such as a hist fig id) lies within the range of the value of the first entry and the last one. That would require meta info on the field to specify what it refers to, though.The problem there is that "ref-target" attributes from the XML files aren't available to Lua, so Lua scripts can't tell which vector a field like "unit.histfig_id" is for. cl-linux-debug can do this, I think.
- A third check would be to verify that arrays that should be the same length are (such as the coordinate vectors where X and Y are stored in separate arrays). Again, meta info would be required.Assuming you mean vectors; there's no way to check this reliably for C arrays. There are relatively few cases where we could use this metadata, and fewer still where we'd run into valid vectors that should have the same dimensions but don't, so it's probably easier to just check things like that manually.
- A fourth check would be to check that arrays have the correct length, such as arrays that should be the size of the world width/height. Meta info required, of course.
Edit: Some rather brief poking around using some of my scripts as probes only found a single issue: the DFHack Lua scriptery that displays which key a command is bound to always prints a question mark for me (as seen in gui/gm-editor's help screen, where all the commands are presented as "?: ...". The bound keys seem to work when pressed, however. I guess this is really not a structure issue, though, but a script one, for the next stage of the update.That's because the "keydisplay" offset is missing on Win64 (I'm assuming you're using that, since that's the only platform that has any sort of 0.44.02 support but not that offset, but specifying is helpful). That points to a map of interface_key's to strings, which DF uses to display keys, and DFHack relies on as well (in Screen::getKeyDisplay()). You'll see the same issue in things like the manipulator plugin, so it's not Lua- or script-specific.
From devel/dt-export-ini.lua:Sounds like you want 2*sizeof(void*), aka 2*dfhack.getArchitecture()/8.Code: [Select]--WARNING below value should be: "general_ref::vtable","1","0x8","0x4","vmethod","getType","general_ref_type",""
value('ref_type',0x8)
I think this one should be 0x10 on 64 bits platforms (getType is the third method in general_ref vtable). Does the comment mean there is a proper way to get the value or should I add a test based on the architecture?
Minor result from poking around:Yeah, that's probably the case, thanks. That's a "new" dwarf, right? (One generated in 0.44)
Found a dorf with a misc_unit_trait.id = 63 (nil)
.value =45
I suspect this means new misc_trait_type values have been added, since I can't find any value out of bounds on half a dozen 0.43.05 dorfs.
:The bounds comment intended to refer to the contents of the field, such as an X coordinate being 0 - X_Dim - 1 or -30000. The intended purpose of that kind of checks (as well as those of vectors) would be to flag that the field might no longer be at that location.
I'm thinking of something that crawls through df.global depth first and verifies that fields with known bounds are within those,
:Minor result from poking around:Yeah, that's probably the case, thanks. That's a "new" dwarf, right? (One generated in 0.44)
Found a dorf with a misc_unit_trait.id = 63 (nil)
.value =45
I suspect this means new misc_trait_type values have been added, since I can't find any value out of bounds on half a dozen 0.43.05 dorfs.
Sounds like you want 2*sizeof(void*), aka 2*dfhack.getArchitecture()."2*(dfhack.getArchitecture()/8)" more exactly. So, there is no way in the current script API to know where "getType" is in the vtable.
The comment is referring to a line from CSV files exported by some df-structures-related tool that I've never really used much. I believe the old Perl script to generate DT layouts uses those CSV files, so maybe that's why the comment is there.
What is the best method for someone who is not a developer (my coding abilities extend to semi-complex python scripting and some basic Java coding) to find out the progress towards having DFHack up to date with DF? I don't want to bother the devs or spam threads every time there's a new DF version. Instead, is there a way for me to find this out directly, on my own? If so, could some kind soul point me in that direction?In the past, we have pointed people to "v0.xx.yy.lst" in https://github.com/dfhack/df-structures. There isn't one yet for 0.44.x, though, because the Linux build was only just released. Here (https://github.com/DFHack/df-structures/blob/master/v0.43.05.lst) is 0.43.05's; "UNCHECKED" is (potentially) bad, "ALIGNED" is good, "VERIFIED" is human-checked, so hopefully entirely right.
There are usually other things to be done too. You can look for the "r1" milestone under https://github.com/dfhack/dfhack/issues, although it doesn't currently have anything assigned.
"2*(dfhack.getArchitecture()/8)" more exactly. So, there is no way in the current script API to know where "getType" is in the vtable.Whoops, fixed. You could also use something like df.reinterpret_cast('pointer',1):sizeof(). Apparently df.sizeof('pointer') isn't supported, though.
Thanks! That's exactly what I was looking for.If you're talking about the .lst file, it's not always kept up-to-date, unfortunately, but we'll try.
There's something off with unit.status.current_soul.performance_skills.The historical_figure_info structure claimed to contain a pointer to unit_personality, when it actually pointed to a structure containing a unit_personality plus an integer, so when that one reported a size mismatch, I updated the original and thus misaligned unit_soul. That should be fixed now.
- df.global.world.units.all[].job.mood_skill: Enum out of whack with 4-5 digit values.Uninitialized memory - you can ignore that.
- df.global.world.units.all[].body.body_plan.interaction [].interaction.material_breath: Several values of 20, with current max = 19. The one I looked at was a cat and other data indicates it's a licking interaction, and also a head-bump interaction with the same value for the same cat.The constructor for that class fills in a bogus value.
- df.global.world.entities.all[].armies[].controller: Following this pointer crashed both gui/gm-editor and my script. The pointer address looked reasonable.Try again with the latest structures - army_controller was badly misaligned before.
There weren't Linux builds of DF for a while, so that's behind. You could run devel/dump-offsets and provide the output of that, but I'm not sure what else needs to be done.
Edit: looks like that was done already.
/home/clement/projects/DFHack/dfhack/library/modules/Maps.cpp: In function 'const char* DFHack::sa_feature(df::enums::feature_type::feature_type)':
/home/clement/projects/DFHack/dfhack/library/modules/Maps.cpp:94:24: error: 'underworld_from_layer' is not a member of 'df::enums::feature_type'
case feature_type::underworld_from_layer:
^~~~~~~~~~~~~~~~~~~~~
/home/clement/projects/DFHack/dfhack/library/modules/Maps.cpp:94:24: note: suggested alternative: 'feature_underworld_from_layer'
case feature_type::underworld_from_layer:
^~~~~~~~~~~~~~~~~~~~~
feature_underworld_from_layer
make[2]: *** [library/CMakeFiles/dfhack.dir/build.make:1479: library/CMakeFiles/dfhack.dir/modules/Maps.cpp.o] Error 1
However, the changes today to dfhack/develop and df structures/master do not compile for me (I compiled with a freshly pulled df structures/master yesterday without issues).From what commit did you try to build? I made a fix last night for unit_personality, and I've confirmed that it builds correctly on both Windows (32-bit and 64-bit) and Linux (via Travis CI). If you're compiling on Windows, be sure to do a clean build, because there's a strange script bug that's preventing partial rebuilds from working correctly.
feature_underworld_from_layer was recently changed into underworld_from_layer. This suggests that your df structures sub repo may not be up to date.
However, the changes today to dfhack/develop and df structures/master do not compile for me (I compiled with a freshly pulled df structures/master yesterday without issues). It should be noted that I do not try to compile plugins. I wouldn't be surprised if my poor compatibility with git is the cause, though.
(or none of the df structure changes between DFHack releases can be accessed), so I update that with "git pull".The DFHack develop branch should usually point to something more recent in df-structures, although not necessarily the master branch.
True, of course, but when I started it was still on a 0.43.05 release, and I want to look at the latest changes. It those aren't compatible with the DFHack develop branch I have to wait for it to catch up anyway.(or none of the df structure changes between DFHack releases can be accessed), so I update that with "git pull".The DFHack develop branch should usually point to something more recent in df-structures, although not necessarily the master branch.
From what commit did you try to build? I made a fix last night for unit_personality, and I've confirmed that it builds correctly on both Windows (32-bit and 64-bit) and Linux (via Travis CI). If you're compiling on Windows, be sure to do a clean build, because there's a strange script bug that's preventing partial rebuilds from working correctly.
Good to know that it works for you, but that question was directed specifically at PatrikLundell (who was complaining about build errors with my most recent commits).From what commit did you try to build? I made a fix last night for unit_personality, and I've confirmed that it builds correctly on both Windows (32-bit and 64-bit) and Linux (via Travis CI). If you're compiling on Windows, be sure to do a clean build, because there's a strange script bug that's preventing partial rebuilds from working correctly.
commit 1489e7db824cc6323de14772fbf0f121ecd0860b
Yes, I did perform a "make clean". When I did "make -j8 install" it rebuilt everything from source again so I know it appeared to have worked.
Note that this tools has to be used before embarking as it manipulates the data DF uses to generate the embark details.Thanks, but I want to do it post-embark.
Creating rock out of thin air should be possible as well, and I think there are tools for that kind of manipulation, as well as morphing single tiles. There's a tool for water/magma manipulation that can also create obsidian.Yeah, I know that tiles can be manipulated into rock/obsidian/open space/water/magma. But I'm interested in another kind of manipulation: abundant, dumb layer rock -> mineable ore. I've seen a post somewhere where it was stated that new mineral veins can be injected via DFHack, but it had no details how to do that exactly.
Note that this tools has to be used before embarking as it manipulates the data DF uses to generate the embark details.Thanks, but I want to do it post-embark.Creating rock out of thin air should be possible as well, and I think there are tools for that kind of manipulation, as well as morphing single tiles. There's a tool for water/magma manipulation that can also create obsidian.Yeah, I know that tiles can be manipulated into rock/obsidian/open space/water/magma. But I'm interested in another kind of manipulation: abundant, dumb layer rock -> mineable ore. I've seen a post somewhere where it was stated that new mineral veins can be injected via DFHack, but it had no details how to do that exactly.
Ancient code warning: https://gist.github.com/warmist/11218191 but it should work on any version (?)I'm not very familiar with understanding scripts yet. What should it do?
I've happened to see a couple of DFHack commands that might be what Rusty_knight wants: changelayer and changevein. You might want to take a look at those (I haven't actually tried to use them).Problem is these two commands aren't scripts, but plugins. Yes, they have source code, but it's in C++.
I don't quite understand why plugin/vs script is an issue. If you're using them directly from the console there isn't much of a difference (if any). C(++) isn't that far removed from Lua that it should be impossible to translate the logic if you're trying to understand what it does to make your own code or script, although there are some gotchas, but all the data references are the same (slightly different syntax, but the same paths).I've happened to see a couple of DFHack commands that might be what Rusty_knight wants: changelayer and changevein. You might want to take a look at those (I haven't actually tried to use them).Problem is these two commands aren't scripts, but plugins. Yes, they have source code, but it's in C++.
I don't quite understand why plugin/vs script is an issue. If you're using them directly from the console there isn't much of a difference (if any). C(++) isn't that far removed from Lua that it should be impossible to translate the logic if you're trying to understand what it does to make your own code or script, although there are some gotchas, but all the data references are the same (slightly different syntax, but the same paths).Me knowing syntax of neither C++ nor LUA is an issue. And not knowing inner workings of dfhack & the game too.
Update this for the latest version.Are you talking about 0.44.02? If so, that's not the way to go about asking that. We've been working on it fairly regularly since 0.44 came out. It's not an instantaneous process.
Update this for the latest version.As lethosor said, they're working their butts off to update DFHack, but it's quite a lot of work (the pay is lousy, and abuse is poor sustenance). If you meant creation of a new thread I would expect that to happen when a somewhat stable DFHack becomes available.
Update this for the latest version.As lethosor said, they're working their butts off to update DFHack, but it's quite a lot of work (the pay is lousy, and abuse is poor sustenance). If you meant creation of a new thread I would expect that to happen when a somewhat stable DFHack becomes available.
...Dwarf Fortress 0.44.02/hack/scripts/biomemanipulator.lua:250: decompose fails to match 'r' in 0.44.02-alpha1
stack traceback:
[C]: in function 'error'
...Dwarf Fortress 0.44.02/hack/scripts/biomemanipulator.lua:250: in global 'decomposeDFHackReleaseNumber'
...Dwarf Fortress 0.44.02/hack/scripts/biomemanipulator.lua:270: in global 'isDFHackOlderThan'
...Dwarf Fortress 0.44.02/hack/scripts/biomemanipulator.lua:3238: in global 'Make_Profile'
...Dwarf Fortress 0.44.02/hack/scripts/biomemanipulator.lua:4862: in local 'fun'
...sktop\DFSTUF~1\Dwarf Fortress 0.44.02\hack\lua\class.lua:98: in upvalue 'invoke_after_rec'
...sktop\DFSTUF~1\Dwarf Fortress 0.44.02\hack\lua\class.lua:127: in global 'BiomeManipulatorUi'
...Dwarf Fortress 0.44.02/hack/scripts/biomemanipulator.lua:9303: in global 'Show_Viewer'
...Dwarf Fortress 0.44.02/hack/scripts/biomemanipulator.lua:9310: in global 'biomemanipulator'
...Dwarf Fortress 0.44.02/hack/scripts/biomemanipulator.lua:9313: in local 'script_code'
...ktop\DFSTUF~1\Dwarf Fortress 0.44.02\hack\lua\dfhack.lua:562: in function 'dfhack.run_script_with_env'
(...tail calls...)
uhh not sure where to put this but got an indication from DFHack that a dwarf was stuck in a tree, dwarf immediately got down from tree, using alpha prerelease 1That should probably be put on the altar of fixed bugs, as the stuck-in-trees bug should be one of those fixed with 0.44.01 so the DFHack detector might be redundant (or not, since I there was a post about it still happening).
uhh not sure where to put this but got an indication from DFHack that a dwarf was stuck in a tree, dwarf immediately got down from tree, using alpha prerelease 1Sounds like Toady's fix is working, then. I'll take out that script from the default config, at least.
I've installed DFHack 0.44.02-alpha1 to check the 'startdwarf' mod, and it isn't working. It throws an error about missing memory addresses or something. I tried to look for the new address with Cheat Engine, but it's very hard for values that (almost) don't change.That address is tricky to find because it isn't a global. It hasn't been a priority historically either, but I'll see if I can get the script to find it to work.
Any advices, other than waiting for a new version?
I have written a small lua script that wakes up selected sleeping dwarf.Post it here or make a pull request in https://github.com/dfhack/scripts (if you know how) and we'll take a look. I'm wondering if it would be better as an option in "siren" instead of a separate script, but either way, having that would be useful.
(it is mostly based on siren script but kinda simpler and more verbose)
What is the procedure to have it included in DFHack?
Using gui/liquids, a permaflow that is flowing downwards is labeled "inv_8". It is "inv_9" if flowing off the east edge of the map. Haven't tested others.Naturally-occurring or created with DFHack?
Well... they keep gathering fruits that dont exactly exist, nothing harvested they just climb around the tree till theyre done and continue gathering on the ground empty handed kinda odd but no one is starving in a tree yetuhh not sure where to put this but got an indication from DFHack that a dwarf was stuck in a tree, dwarf immediately got down from tree, using alpha prerelease 1Sounds like Toady's fix is working, then. I'll take out that script from the default config, at least.
Naturally-occurring, in a save untouched by DFHack.Using gui/liquids, a permaflow that is flowing downwards is labeled "inv_8". It is "inv_9" if flowing off the east edge of the map. Haven't tested others.Naturally-occurring or created with DFHack?
I have written a small lua script that wakes up selected sleeping dwarf.Post it here or make a pull request in https://github.com/dfhack/scripts (if you know how) and we'll take a look. I'm wondering if it would be better as an option in "siren" instead of a separate script, but either way, having that would be useful.
(it is mostly based on siren script but kinda simpler and more verbose)
What is the procedure to have it included in DFHack?
What is the purpose of "rube-autogen-win.rb" script and how can I run it?It's not something you can run directly, which is why it isn't in the scripts folder. It's auto-generated Ruby code (for Windows, in this case) that deals with DF structures somehow.
yo, here's a script that lets you add custom artifacts (https://gist.github.com/Putnam3145/2d1609e935f91f2e50371c9787914b6b). It uses anon entries, so it's for testing only at this point, no way am I releasing anything with this until the artifact_record is mapped out better. Assumptions in writing are here (https://github.com/DFHack/df-structures/issues/226); for the most part, it works perfectly. Artifacts can be recovered from sites as expected.Nice, I was just thinking about wondering if this could be done way back when I started fucking around with artifake.
I'm getting an immediate crash when attempting to rename a region folder. Not exactly a major problem, but just so you know. Windows 64-bit 44.02a1.How/where exactly are you renaming it?
Edit: if you mean in the "start playing" menu, I found and fixed the issue there.Yeah, that's the one.
dfhack.run_script
Can I install (replace the DLL) DFHack while DF is running? I want to save two dwarves that got bugged into a water reservoir with a teleport, but obviously can't exit the game.
Wow already out for the new build? That's incredible.Yeah, we have all of the global addresses now, which helps some.
Did Toady end up giving the project some information to make it easier?
I thought he was talking about that stuff a while back.
Can I install (replace the DLL) DFHack while DF is running? I want to save two dwarves that got bugged into a water reservoir with a teleport, but obviously can't exit the game.Why don't you simply save, exit DF, install DFHack, restore the save, and THEN teleport? You can even copy your existing save before saving anew to keep (sort of) a backup.
Haven't checked if anyone has mentioned this already, but if i get a companion in adventure mode and travel, immediate crash. It works when i disable dfhack though. Also crashes when i (Z), even without a companion.
EDIT: I'm not sure how to give a crash log
EDIT2: Seems to work better without a tile set
Windows antivirus says createitem is a trojan in the latest dfhack alpha included in per. starter pack. False positive?
for de,oe in pairs(df.global.world.army_controllers.all) do
if oe.unk_60 == nil then else
print ( de,oe.unk_60.anon_6)
for e,o in pairs(df.global.world.armies.all) do
if oe.id == o.controller_id then
print (e)
df.global.world.armies.all[e].members:insert("#",{new=true,nemesis_id=df.global.world.nemesis.all[math.random(1,999)].id}) -- this is the part that inserts a unit to the army
end
end
end
uh, doesn't that more just kinda... randomly insert some nemesis records
also:
1. you should be using dfhack.random.new():random(#df.global.world.nemesis.all) instead of math.random(1,999), since nemesis.all is a C++ vector and therefore 0-indexed and also this will pick from every nemesis unit instead of just the first 999
2. you should probably be doing some stuff to check if the nemesis record is for a valid (i.e. not dead) hist fig anyway, probably by going through the nemesis records before you go through all the armies and putting them into a new table
3. is there not an army id in army controllers? you could just use df.army.find(id) instead of making this whole thing O(n2)
Haven't checked if anyone has mentioned this already, but if i get a companion in adventure mode and travel, immediate crash. It works when i disable dfhack though. Also crashes when i (Z), even without a companion.
EDIT: I'm not sure how to give a crash log
EDIT2: Seems to work better without a tile set
There was a bug in TWBT related to fast travel, I either didn't fully fix it or something has changed, anyway I see what's happening now, hopefully I'll fix it this time.
So how exactly does one assign a ghost to a sqaud? Do they show up, or do you have to do something special?how I did it was finding the military menu's squad position section then filling out one of the ten options with a historical id of the dead unit that turn into a ghost.
I got two little questions about DFHack, regarding a succession world that seems to have been going for about 50 years cumulative so far since the 0.43.05. The save works for the new version.1. I believe someone has hacked in step ladders into a succession fortress and thought about doing a similar operation for pedestals (etc.), but I would guess that kind of surgery requires raw hacking, and so would better be asked about in the mod forum (where there's a greater chance of getting answers from people who know about such things).
1) Is it possible to use DFhack in the ability to build new buildings for existing civilisations, so we can for example build pedestals and display cases?
2) Is it possible to use DFhack to change the mineral composition of an existing world in the new version?
I got two little questions about DFHack, regarding a succession world that seems to have been going for about 50 years cumulative so far since the 0.43.05. The save works for the new version.1. I believe someone has hacked in step ladders into a succession fortress and thought about doing a similar operation for pedestals (etc.), but I would guess that kind of surgery requires raw hacking, and so would better be asked about in the mod forum (where there's a greater chance of getting answers from people who know about such things).
1) Is it possible to use DFhack in the ability to build new buildings for existing civilisations, so we can for example build pedestals and display cases?
2) Is it possible to use DFhack to change the mineral composition of an existing world in the new version?
2. If you take about hacking an existing fortress, the answer is most probably yes (there was a similar question recently), but you'd probably have to create the script for what you want yourself (or change one tile at a time using scripts that probably exist).
If you talk about changing the world pre embark (or in between embarks) the answer is yes: http://www.bay12forums.com/smf/index.php?topic=164658.0 (http://www.bay12forums.com/smf/index.php?topic=164658.0).
Hi, I have a little question/request.A follow on question to lethosor's:
Can DFHack tell me what the names of my civilisations deities are ? or can it do that in future ?
I'm building a bunch of temples. My civ has 12 deities. I want to build a statue of each deity.
But, instead of writing it all down on paper, I'd like to be able to see my civ's deities in the DFHack window, so I can look at it while I'm specifying statue designs.
It's probably possible to do now, although there isn't a script that does that that I know of. What exactly do you mean by a "civilization's deities"? I'm not seeing a direct link to deities from historical entities/civs, but I'll investigate.
A follow on question to lethosor's:
Do you actually want the deities associated with your civ (I haven't found any direct links either, by the way), or do you want the ones your citizens (and possibly residents) worship (or possibly both)?
The civ list is probably static for the purpose of a fortress, while the citizen/resident one would grow over time as you acquire people who originate from other civs.
I mean, when I create a temple zone, I can choose to dedicate it to any of the deities that my citizens worship.Oh, is that where that list comes from? I was trying to figure it out - in one world, it also happened to be all of the dwarven deities, but not in another world.
but I'd like to be able to see that list of the deities my citizens worship, somewhere, so that I can look at it in the dfhack window, while specifying images for my craftsmen/sculptors to create.
:lua for _,d in pairs(ui_sidebar_menus.location.deities) do if d then print(dfhack.df2console(dfhack.TranslateName(d.name))) end end
English names: (note that I just added "true" as a second parameter to TranslateName):lua for _,d in pairs(ui_sidebar_menus.location.deities) do if d then print(dfhack.df2console(dfhack.TranslateName(d.name,true))) end end
Note that you need to have the temple deity selection menu open for these to work. The second (English) one seems to match the text displayed in that list, although I don't know if it matches the slab creation list, so I provided both.
I was trying to figure it out - in one world, it also happened to be all of the dwarven deities, but not in another world.
Edit: I'm seeing 12 deities listed in a fort with 7 citizens, and each citizen only worships one deity (with some overlap), so the list is coming from somewhere else.
I haven't done much with deities (I'm using omni temples since my rather failed attempt to use dedicated ones caused the visitors to cram into temples rather than petition or research, for, or do whatever else they were supposed to be at the fortress for), but if I remember correctly, the list of deities you can create temples for initially contains the ones your civ worships (and I believe Kat is correct in that this is a subset of the race set), regardless of whether anyone worships those. The list of available deities then grows as "foreign" ones are worshiped by the population.I was trying to figure it out - in one world, it also happened to be all of the dwarven deities, but not in another world.
Edit: I'm seeing 12 deities listed in a fort with 7 citizens, and each citizen only worships one deity (with some overlap), so the list is coming from somewhere else.
Cool. Thanks !
About your finding 12 deities listed in a 7-population fort. I'm wondering if that might be your parent civilisation's pantheon, and you just haven't got migrants yet that worship the others in the pantheon.
I think each independent civilisation generates its own pantheon, so if there's more than one dwarf civilisation, then the list will not include all dwarven deities in the world.
I haven't done much with deities (I'm using omni temples since my rather failed attempt to use dedicated ones caused the visitors to cram into temples rather than petition or research, for, or do whatever else they were supposed to be at the fortress for), but if I remember correctly, the list of deities you can create temples for initially contains the ones your civ worships (and I believe Kat is correct in that this is a subset of the race set), regardless of whether anyone worships those. The list of available deities then grows as "foreign" ones are worshiped by the population.Any idea how to get that list (without having to start creating a new temple)?
Is the Startdwarf script not working or am I doing something incorrect?It will work in the next version, unless you're using the 64-bit Linux build.
Any idea how to get that list (without having to start creating a new temple)?
I've seen that as well, and it's a good idea, but unfortunately I don't think it helps us to find *where* it's stored, unless that's part of the "extra" info (as that is actually extracted using DFHackery, and so that code could be studied). Since Legends Mode can export it, it can presumably also display it, but then we're probably back at pulling data from a UI screen rather than whatever the actual source is.Any idea how to get that list (without having to start creating a new temple)?
Not sure how helpful this would be, but using the exportlegends thing, and looking at it with worldviewer, you can see a civilisation and see their list of native deities.
so, it would appear to be saved somewhere within the games data, right ?
It will work in the next version, unless you're using the 64-bit Linux build.
Sweet, thanks.
http://dfhack.readthedocs.io/en/stable/docs/_auto/devel.html#devel-inject-raws might work, but I haven't used it. There should be more directions if you try running the script. Make a backup of your save first! This script can cause corruption if you use it incorrectly.
Regarding deities list:df.global.world.entities.all[k].unknown1b.worship has various numbers that I can't find a obvious relation to anything else, and the unknown1b.unknown1a has some which kinda sorta make sense as a list of histfig id numbers.
No, I've only seen the list in game when trying to build a temple. The only other way I can think of at the moment is the rather brute force action of going through all hist figs to find the deities, check if they are civ tagged (I don't know if they are or even should be) to possibly get the starting list.
I can get the deities from a single sapient's relations, so getting it for all residents would be a matter of going through the units list and look at the ones that are residents.
The second part is fairly reasonable, but I don't like the first part. There must be somewhere in the data structures where you can find a civ's deities, as I very much doubt Toady uses the stupid brute force approach above. If there's a list of deities somewhere and they're tagged with civs somehow you can probably easily scan through that list (it shouldn't be very long), but a (unmapped) list of deity hist fig Ids in the civ entity would be even easier for the base (I think you still have to scan the units to get the current set, unless it's cached/built during game play).
Regarding deities list:I did a bit of poking around in a save-file with a hex-editor, and I can confirm that deities are not civ tagged. Furthermore, each entity (including subgroups) has a table of deities. I don't know exactly where it falls in relation to the other data, save that it's shortly before the randomly-generated positions (used for law-givers, priests, and the like.)
No, I've only seen the list in game when trying to build a temple. The only other way I can think of at the moment is the rather brute force action of going through all hist figs to find the deities, check if they are civ tagged (I don't know if they are or even should be) to possibly get the starting list.
@Urlance Woolsbane: Save game hacking is a bit outside of the scope for the task, although I wouldn't be surprised if it was located at the same location there as it is in DF itself (i.e. the location MaxTM pointed at).That was my working assumption. Certainly, what I've seen of DF-Structures seems to line up with my research on the save-format.
df.global.world.entities.all [my_civ].unknown1b.unk32bYou should use df.historical_entity.find(my_civ) instead, assuming my_civ is ui.civ_id. Your way might work for small worlds, but most of the "all" vectors in worlds can skip IDs. It's possible that this one doesn't ever skip IDs, but just to be safe, find() will always return something with the right ID (or nil).
This appears to occur in 0.43.05 as well; reported at https://github.com/DFHack/df-structures/issues/228Naturally-occurring, in a save untouched by DFHack.Using gui/liquids, a permaflow that is flowing downwards is labeled "inv_8". It is "inv_9" if flowing off the east edge of the map. Haven't tested others.Naturally-occurring or created with DFHack?
loaded plugin stocks; DFHack build 0.44.02-alpha1-0-g43b19c89
loading plugin stonesense
dlopen(~/Downloads/df/df_osx_44_02 DFHack TWBT Tergel/hack/plugins/stonesense.plug.dylib, 6): Symbol not found: ___sincos_stret
Referenced from: ~/Downloads/df/df_osx_44_02 DFHack TWBT Tergel/hack/libs/liballegro.5.2.2.dylib
Expected in: flat namespace
in ~/Downloads/df/df_osx_44_02 DFHack TWBT Tergel/hack/libs/liballegro.5.2.2.dylib
Can't load plugin stonesense
loading plugin strangemood
the 64-bit version is using absolute file references while the 32-bit version is using relative locationsI see absolute paths to liballegro in the 64-bit log, but they're pointing to things on your machine, so that's not the issue. I see nothing that could be described as relative locations in the 32-bit log, unless you mean "stonesense", but that's just the plugin's name.
Are you talking about "tweak makeown"?That's the one
What DFHack version are you using?44 alpha1. Didn't updated to beta yet.
I'm pretty sure beta1 fixes it, so try that when you get a chance.Yea, it works. But I'll wait for twbt for beta :D
devel/inject-raws tool ITEM_TOOL_PEDESTAL tool ITEM_TOOL_DISPLAY_CASE
devel/inject-raws reaction MAKE WOODEN DISPLAY CASE
will inject a reaction called [REACTION:MAKE] instead of [REACTION:MAKE WOODEN DISPLAY CASE], which would be a very bad thing (though not insurmountable)Here's the problem with the reaction:Try enclosing it in quotes - that should ensure that "MAKE WOODEN DISPLAY CASE" is handled as a single argument instead of 4.
It is listed as [REACTION:MAKE WOODEN DISPLAY CASE]
I am concerned that doingCode: [Select]devel/inject-raws reaction MAKE WOODEN DISPLAY CASE
will inject a reaction called [REACTION:MAKE] instead of [REACTION:MAKE WOODEN DISPLAY CASE], which would be a very bad thing (though not insurmountable)
Here's the problem with the reaction:Try enclosing it in quotes - that should ensure that "MAKE WOODEN DISPLAY CASE" is handled as a single argument instead of 4.
It is listed as [REACTION:MAKE WOODEN DISPLAY CASE]
I am concerned that doingCode: [Select]devel/inject-raws reaction MAKE WOODEN DISPLAY CASE
will inject a reaction called [REACTION:MAKE] instead of [REACTION:MAKE WOODEN DISPLAY CASE], which would be a very bad thing (though not insurmountable)
You're including the reaction argument, correct?
Also, build server update: it requires some extra setup for new DF versions, apparently, so builds won't be up for a while.
devel/inject-raws reaction "MAKE WOODEN DISPLAY CASE"
[DFHack]# devel/inject-raws reaction MAKE_WOODEN_DISPLAY_CASE
WARNING: THIS SCRIPT CAN PERMANENLY DAMAGE YOUR SAVE.
This script attempts to inject new raw objects into your
world. If the injected references do not match the actual
edited raws, your save will refuse to load, or load but crash.
Did you make a backup? (y/n): y
Injecting reaction MAKE_WOODEN_DISPLAY_CASE
Now without unpausing save and reload the game to re-read raws.
[DFHack]# devel/inject-raws reaction "MAKE WOODEN DISPLAY CASE"
WARNING: THIS SCRIPT CAN PERMANENLY DAMAGE YOUR SAVE.
This script attempts to inject new raw objects into your
world. If the injected references do not match the actual
edited raws, your save will refuse to load, or load but crash.
Did you make a backup? (y/n): y
Invalid option: MAKE WOODEN DISPLAY CASE
if mode and string.match(kv, '^[%u_]+$') then
if mode and string.match(kv, '^[%u_ ]+$') then
Yeah, looks like an issue with the script. In hack/scripts/devel/inject-raws.lua, try changing this line (it's near the end):Code: [Select]if mode and string.match(kv, '^[%u_]+$') then
to this:Code: [Select]if mode and string.match(kv, '^[%u_ ]+$') then
The only change is adding a space before the ] character.
I'll look into a better way of fixing that. Thanks!
Yeah, looks like an issue with the script. In hack/scripts/devel/inject-raws.lua, try changing this line (it's near the end):Code: [Select]if mode and string.match(kv, '^[%u_]+$') then
to this:Code: [Select]if mode and string.match(kv, '^[%u_ ]+$') then
The only change is adding a space before the ] character.
I'll look into a better way of fixing that. Thanks!
Yeah, but if someone decides to use another character in a reaction name (or any raw identifier), it'll break again. Unless there are restrictions on the characters that can be in an identifier, it might be better to treat anything besides "reaction" or a building/item type as an identifier. (Or we could just assume other characters won't be used; that might be easier.)
Could you explain this one? How would other characters be used? The [ and ] characters I get delimit the raw identifier. Until recently, we didn't have spaces used in reaction names, it seems their first use was with ASSEMBLE STONE AXE and before that spaces in names were instead delimited with _s. If I remember rightly, that specific reaction went in for adventure mode sites, which was slightly after the primary 0.43 release.People could use other characters in identifiers. Like "CREATE-WOODEN-DISPLAY-CASE", or "Create wooden display case!". The square brackets in the line I asked you to patch contain the characters that script considers to be valid in identifiers - they're not representing the square brackets in the raw files (I'm not sure if that's what you meant).
Also assuming the save doesn't spontaneously combust, there should be some lazy newb script for updating a 0.43.05 game to the latest version.It would be nice, but as far as I know, we've never done this for previous DF releases, and it would also require raw edits.
Here's a new release for 0.44.03 that may or may not work (test it and find out!): https://github.com/DFHack/dfhack/releases/tag/0.44.03-alpha1
Here's a new release for 0.44.03 that may or may not work (test it and find out!): https://github.com/DFHack/dfhack/releases/tag/0.44.03-alpha1
Wow, everything's happening so quickly!That also means few of the builds have been verified manually. I'm fairly certain all of the global and vtable addresses are right, assuming the scripts that find them are working, but I had to fill in a few things manually. If people could report back that certain builds work, that would be great.
Yeah, but if someone decides to use another character in a reaction name (or any raw identifier), it'll break again. Unless there are restrictions on the characters that can be in an identifier, it might be better to treat anything besides "reaction" or a building/item type as an identifier. (Or we could just assume other characters won't be used; that might be easier.)
Yeah, but if someone decides to use another character in a reaction name (or any raw identifier), it'll break again. Unless there are restrictions on the characters that can be in an identifier, it might be better to treat anything besides "reaction" or a building/item type as an identifier. (Or we could just assume other characters won't be used; that might be easier.)
Thank you for the update. I've had no problems using any of the commands I'm familiar with the use of.Are you just looking to change the nickname, or the entire name? The error I'm seeing in "names" indicates that it's been broken for a long time (maybe since 0.42). "gui/rename" can change nicknames. The reason gui/gm-editor doesn't work for what you're trying is because units can have up to 3-4 names (unit, histfig, soul? alternate identity?), and the one that's displayed can vary. You can use dfhack.units.setNickname() from Lua, but that only changes the nickname (as you can probably tell).
That said, I've been unable to rename any units. I'm not sure if "names" is working correctly, or if I'm just using it wrong, or if I should be using some other command entirely.
Meanwhile "gm-editor" can update the unit's name in the unit list, but doesn't seem update the unit's translated name (IE: I can rename a unit Urist Olonreg, but it doesn't appear as "Urist Geargloves", rather it still uses it's original name.)
Has anyone identified how to make the game's Resident info screens show up for creatures who were initially created as Animals rather than as Persons?The screens you get with 'u'-'v'? That's viewscreen_unitst, if I remember that correctly, which is fairly easy to create in Lua:
s=df.viewscreen_unitst:new() -- note that this crashes if a world isn't loaded, as I just found out
s.unit = some_unit
dfhack.screen.show(s)
I don't know what's up with animal-men-turned-citizens not having this option. A save might help, if you have one.
So far, there have been 146 downloads of the 64-bit Windows build and 13 of the 32-bit one. I haven't heard any reports from anyone using them - can I assume they're working, or can someone confirm? (A check for the Linux builds would be nice too.)
Has anyone identified how to make the game's Resident info screens show up for creatures who were initially created as Animals rather than as Persons?The screens you get with 'u'-'v'? That's viewscreen_unitst, if I remember that correctly, which is fairly easy to create in Lua:Code: [Select]s=df.viewscreen_unitst:new() -- note that this crashes if a world isn't loaded, as I just found out
I don't know what's up with animal-men-turned-citizens not having this option. A save might help, if you have one.
s.unit = some_unit
dfhack.screen.show(s)
[NOT_LIVING]
Kanil: I'm having trouble figuring out the "names" script - have you used it before? Do you know what it's supposed to do in various situations? It looks like it's supposed to open the custom name screen, but I'm not seeing how that could end up changing a unit's name. Did you just find it in the documentation? I'm inclined to remove or rewrite it.I've never used it before, so I'm not sure what it's supposed to do -- this is the first time I've wanted something renamed. I found it both in the documentation and in "ls" under scripts.
:As far as I can see from the description the modded animal people behave the same as unmodded Gremlins do (I haven't had any since 0.43.05, but I doubt this has changed). Thus, it's a low priority vanilla DF issue which Toady probably won't touch until animal people are reworked (whenever that will be).Has anyone identified how to make the game's Resident info screens show up for creatures who were initially created as Animals rather than as Persons?The screens you get with 'u'-'v'? That's viewscreen_unitst, if I remember that correctly, which is fairly easy to create in Lua:Code: [Select]s=df.viewscreen_unitst:new() -- note that this crashes if a world isn't loaded, as I just found out
I don't know what's up with animal-men-turned-citizens not having this option. A save might help, if you have one.
s.unit = some_unit
dfhack.screen.show(s)
As far as I can see from the description the modded animal people behave the same as unmodded Gremlins do (I haven't had any since 0.43.05, but I doubt this has changed). Thus, it's a low priority vanilla DF issue which Toady probably won't touch until animal people are reworked (whenever that will be).That should be part of the Myth Arc. Toady has talked about incorporating them into creation myths, allowing for varied and sundry permutations.
Interesting but altering the starting amount of dwarves (and as Woolsbane points out, possibly inserting sentient pets as citizens?) is also a development that may happen by the time we reach starting scenarios which may have a degree of flexibility on the settings for now that are hardcoded.Quite probably, but embark scenarios are easily a few years down the road, and I'm trying to get a head start on them...
Explaining where animal people come from may not necessarily mean their behavior is changed at that time, but we'll eventually see what appears in the various Myth & Magic arcs. The Myth & Magic bag is bursting at the seams (each section of it on the dev page except one is larger than the complete contents of this arc).It's ambitious, to be sure. Even if Toady spreads it out over several releases, I imagine we'll still be in for nearly two years between the last 00.44.xx release and the first Myth and Magic one. You're right that certain plans may go the way of gambling, recipes, and roadside inns...
My best guess is that DF assumes there are always at least 7 creatures and deals with the first 7 unconditionally. I'm a bit surprised that livestock affects it, but I guess it makes sense.If I had to guess, it's either vestigial or connected to creatures such as kobolds, that count as livestock but act like citizens. Regardless, it's odd.
I highly doubt there's a single address that contains a single number that can affect this. I'm pretty sure start_dwarf_count is used in the loop that creates units in DF, but there are likely several places that assume there are at least 7.And therein lies my problem. Start_dwarf_count works fine until I press 'e', at which point the game does any number of things behind the scenes. It might be one subroutine that's to blame, it might be twenty.
The fact that code isn't writable doesn't matter - start_dwarf_count is in the code section and isn't normally writeable as well, but we have a way to patch read-only memory.
I wrote a script for helping to create twbt override (https://gist.github.com/cvuchener/c176e7d0bee18e5c6c75c7ac5bded6f7). When Meph tried it on a custom workshop (http://www.bay12forums.com/smf/index.php?topic=138754.msg7654933#msg7654933), it found the buildings_other_id "WEAPON_UPRIGHT". The script is not very good at choosing the best ID but it should choose at least a valid one. Do you see an issue with my script? Or does this category really contains custom workshop and is not well named?Weapopn_Upright is a valid building ID, it's the adamantine sword in the underground fortresses that connect to the underworld.
I wrote a script for helping to create twbt override (https://gist.github.com/cvuchener/c176e7d0bee18e5c6c75c7ac5bded6f7). When Meph tried it on a custom workshop (http://www.bay12forums.com/smf/index.php?topic=138754.msg7654933#msg7654933), it found the buildings_other_id "WEAPON_UPRIGHT". The script is not very good at choosing the best ID but it should choose at least a valid one. Do you see an issue with my script? Or does this category really contains custom workshop and is not well named?Just to confirm, you are not using DFHack 0.44.02-alpha1, correct? That version had an issue with buildings_other_id.
I wrote a script for helping to create twbt override (https://gist.github.com/cvuchener/c176e7d0bee18e5c6c75c7ac5bded6f7). When Meph tried it on a custom workshop (http://www.bay12forums.com/smf/index.php?topic=138754.msg7654933#msg7654933), it found the buildings_other_id "WEAPON_UPRIGHT". The script is not very good at choosing the best ID but it should choose at least a valid one. Do you see an issue with my script? Or does this category really contains custom workshop and is not well named?Just to confirm, you are not using DFHack 0.44.02-alpha1, correct? That version had an issue with buildings_other_id.
The only way to know for sure is to test it - that's why it was released! That said, most things should work. I haven't had any issues myself, but I don't use a lot of DFHack tools on a regular basis. If you're willing to report issues that you run into, then feel free to download it and play around.Ahh, fair enough! Best i set autosave to seasonal, then, before i get started. Perhaps with backups enabled for once!
Kanil: I'm having trouble figuring out the "names" script - have you used it before? Do you know what it's supposed to do in various situations? It looks like it's supposed to open the custom name screen, but I'm not seeing how that could end up changing a unit's name. Did you just find it in the documentation? I'm inclined to remove or rewrite it.I've got a new version with fixes for various bugs and whatnot up with a pull for it and the various format/random missed spaces/trying to include the --[[blah]]-- documenation properly that seems fine according to travis.
<identity: 0x7fffa854edb0>
id = 7853
name = <language_name: 0x7fffa854edb8>
race = 572
caste = 0
histfig_id = -1
unk_4c = 33318
birth_year = 0
birth_second = 118
anon_1 = 363899
anon_2 = -1
anon_3 = -1
anon_4 = -1
anon_5 = -1
anon_6 = <vector<identity_unk_94*>[0]: 0x7fffa854ee30>
anon_7 = <vector<identity_unk_94*>[0]: 0x7fffa854ee48>
birth_year and birth_second are off by one field (histfig_id too, it is 33318).[lua]# ~df.identity.find(2541)
<identity: 000000003AC6ECD0>
id = 2541
name = <language_name: 000000003AC6ECD8>
race = 495
caste = 1
histfig_id = -1
unk_4c = 38030
birth_year = 3
birth_second = 131
anon_1 = 127964
anon_2 = -1
anon_3 = 1889
anon_4 = 132
anon_5 = 828
anon_6 = <vector<identity_unk_94*>[0]: 000000003AC6ED80>
anon_7 = <vector<identity_unk_94*>[0]: 000000003AC6ED98>
[lua]# ~df.historical_figure.find(38030)
<historical_figure: 000000006C14DA90>
profession = 79
race = 495
caste = 1
sex = 1
orientation_flags = <orientation_flags: 000000006C14DA98>
appeared_year = 131
born_year = 131
born_seconds = 127964
curse_year = -1
curse_seconds = -1
birth_year_bias = 0
birth_time_bias = 0
old_year = -1
old_seconds = -1
died_year = -1
died_seconds = -1
name = <language_name: 000000006C14DAC8>
civ_id = 894
population_id = 844
breed_id = -1
cultural_identity = -1
anon_1 = 6597
flags = <BitArray<>: 000000006C14DB58>
unit_id = 5126
unit_id2 = 5126
id = 38030
unk4 = 0
entity_links = <vector<histfig_entity_link*>[6]: 000000006C14DB78>
site_links = <vector<histfig_site_link*>[0]: 000000006C14DB90>
histfig_links = <vector<histfig_hf_link*>[6]: 000000006C14DBA8>
info = <historical_figure_info: 000000006C302ED0>
unk_f0 = nil
unk_f4 = nil
unk_f8 = nil
unk_fc = nil
unk_100 = 0
unk_v4019_1 = -1
I just figured it out, and I will provide insight on what I think happened.Dwarvet drives me nuts in adventure mode so I just delete it when I do a new dfhack version, I wonder if it wasn't the source of the hang as it runs at times when it never needs (travel mode changes?), and yeah it'll just drop into the relevant folders fine, I copy over my scripts since I've got personal projects/variations in there but other than that it's easy as that.
How I got it unstuck: I pressed the enter key in the DFHack menu.
What I think happened: I had earlier typed "ls" so i could see the command list. The command list was printed, and the line immediately after was blinking _, as what I would normally expect.
When i tried saving, the game got stuck in not-responding, and DF was using no processor resources. I was a bit confused, so I waited a long while with no progress.
I then pressed enter in the DFHack window. This caused the display to change as so:
(https://cdn.discordapp.com/attachments/209085745166680065/396863848503836672/unknown.png)
My deduction here is this: the "ls" script continues running until after enter is pressed again. This prevents "clearing animal hospitals" or any other number of "it's time to save" subroutines from going through. This stalls Dwarf Fortress itself from performing any saving.
Luckily, I didn't close DF before I figured this out, so my save is safe!
Question though, can I safely paste the new DFHack version over my DF Folder that has the alpha version on it? This experience has made me crave some of those new bugfixes.
what does Dwarvet do anyway?
Yeah, but the personal store of rep doesn't seem to track everything properly either, and people don't seem to care about the entity linked fame since I previously tried making myself a legendary hero/hunter/bandit slayer/artifact finder and all via the reputation fields for shit effect, but doing it to a direct relationship field worked as seen above.so in a odd way adventurers can get into a relationship, it kinda tied to your fame.
Question time!
1. I know that the equip-item script can be used to put pretty much any item on any unit, but does it actually have an effect? If I put a helmet on a dog will it actually help protect it's head? Will the dog even keep it equipped, or will it just unequip it right away?
2. Does the equip-script and item-trigger play well with each other?
3. With the addition of the SYN_CONCENTRATION_ADDED and SYN_IDENTIFIER and such, does the add-syndrome script correctly add concentrations and respect the dilution factors?
4. I'm not sure add-syndrome is working correctly for syndromes that add effects like bleeding, or for syndromes that have targeted body parts. It seems that a syndrome applied normally from in-game comes with a wound that actually does the targeting/bleeding/etc... Has anyone else noticed this or am I just doing something wrong?
Hi guys is there a way to force a siege to end?
I have a bug whereby I was besieged by goblins who immediately seem to have left the map (I searched everywhere for them including using the 'reveal' hack) but the *SIEGE* tag remains.
Is there a way I can use DFhack to force this status to end? Looked in the commands list but couldn't see anything.
I'm on the same issue with add-syndrome. Syndrome-util is setting wound_id to -1 and I guess that it prevent a lot of symptoms from working. We had the same issue in 0.34 and is was fixed by creating a fresh wound (as far as my memory goes).
Interactions are not working either when added with add-syndrome. Perhaps there is more to insert that the list of symptoms. I need to compare in gm-editor a naturally infected creature with a dfhack infected one to check this.
Im stuck on how to get a list of units dieties to show such info in my manipulator extension. Has anyone done this in cpp or lua? Units have a little array called relationship_ids which only lists a few relationships like pet,father,mother. There is a "unit_relationship_type" enum which continues to list many more interesting relationships like friend, grudge, worship, hero.. but I cant find the keys which that could be for. Maybe its unimplemented or found in historical figure data?You're referring back to something undefined ("such info") so it's hard to understand what you're after. Deities worshiped by units can be found via the relations (I believe this thread had an exercise where a fortress' deities were located, plus those of citizens "imported" from elsewhere).
if(histfig_links[i]->getType() == df::histfig_hf_link_type::DEITY) { .... }
So there is a vector under historical figure called histfig_links, with elements containing "target_hf" and "link_strength" but puzzlingly no link_type.The histfig_hf_link class is polymorphic - its type is determined not by reading a variable but by executing code. Many other object types in DF work exactly the same way - item, building, and general_ref, just to name a few.
Then we have headers concerning these links, for various link_types including dieties. eg.
..include\df\histfig_hf_link_deityst.h
Ive no idea how these access the fh_link types and dont find this used anywhere in dfhack repo, but i found a lua snippet online using them
...seems there is a whole feature Ive been unaware of, I may be able to just do:
if(histfig_links->getType() == df::histfig_hf_link_type::DEITY) { .... }
Fingers crossed for that. Im nearly ready to start trying to compile an unwieldy heap of modifications now.
I'm on the same issue with add-syndrome. Syndrome-util is setting wound_id to -1 and I guess that it prevent a lot of symptoms from working. We had the same issue in 0.34 and is was fixed by creating a fresh wound (as far as my memory goes).
Interactions are not working either when added with add-syndrome. Perhaps there is more to insert that the list of symptoms. I need to compare in gm-editor a naturally infected creature with a dfhack infected one to check this.
Interactions aren't working either? Hmm, I hope we can get that fixed. I use interactions from syndromes a lot.
Id guess not without brainwashing them all to be pacifist KittyKat. Though Im trying to include enough information in the labors screen to tell what is winding them up.
Did some more research by comparing a spawned unit in arena mode with the secret (interactions work ok) with one that received the syndrome.
The wound is properly created (in 'body') by DF when the syndrome is added. It contain a curse info with all the proper data, said curse is then applied to the unit.
The only divergence was the lack of a reinfection id and count, but after modifying syndrome-util to add it, nothing changed.
Everything in body, in curse and in syndrome is adequate. I'm still searching...
local synName = 'Pyromaniac (fireballs, directed ash, firejet)'
dfhack.run_script('modtools/add-syndrome', '-target', unit.id, '-syndrome', synName, '-resetPolicy', 'DoNothing')
Its curious the unit_relationship_type enum has loads of relations including worship, grudge, hero, aunt.. but the relationship_ids structure under unit is just an int array of the first 9 enums, which even if it were longer couldnt store multiple relations of same type.The stuff under info.relationships also contains duplicates of stuff under info.reputation.wanted[k] like type and level, changing those values are how I did my reputation experiment:
Ive dug around quite a bit looking for a vector that includes the other relations, and ive dug around looking for code or conversation on those other relations but no luck yet.
Gods are historical figures, there is a script to make them but it cant assign them to anyone.
I read that df rejigged the relations info in v43 maybe the defs havent quite caught it all yet.
In getNemesis(unit)->figure->info->relationships->... there are 4 half-anon lists with more anon vectors inside them.
I will have a look at legends viewer source next it should have a handle on gods and things...
Looks like anon_2 and anon_3 under nemesis[k].figure.info.relationships[v] in a given unit are the same as the reputation.wanted[k].type and .level with the same values/effects on fame when edited:Spoiler (click to show/hide)
Did some more research by comparing a spawned unit in arena mode with the secret (interactions work ok) with one that received the syndrome.
The wound is properly created (in 'body') by DF when the syndrome is added. It contain a curse info with all the proper data, said curse is then applied to the unit.
The only divergence was the lack of a reinfection id and count, but after modifying syndrome-util to add it, nothing changed.
Everything in body, in curse and in syndrome is adequate. I'm still searching...
Quoting myself, but after figuring it out I believe you might be interested.
Dfhack wise, the syndrome is properly added as far as interaction goes. But Kruggsmash made a video about turning dwarves into necromancers so I knew that it had to work.
I tested with an inhaled stone that provides interactions and the units were not using it either, so taking inspiration from generated necromancy I removed usage hints and it worked.
As it seems, ATTACK is not working anymore, but no hint now has the same effect (instead of "all the time" from 0.34) and the unit will use the interaction in combat. The arena does not respect those rules, so it's probably a bug. Mystery... kinda solved, I guess?Spoiler: The interaction (click to show/hide)
It is added with :Code: [Select]local synName = 'Pyromaniac (fireballs, directed ash, firejet)'
dfhack.run_script('modtools/add-syndrome', '-target', unit.id, '-syndrome', synName, '-resetPolicy', 'DoNothing')
That does not explain issues with bleeding or drowsiness. Those symptoms are a little complicated as those counters were not in body, probably in body parts. Again, it is worth testing it with inhalation before going further.
Thanks Quietust - this should relieve me of much future puzzling !You can also use df.type.is_instance() instead of link:getType():
unit = dfhack.gui.getSelectedUnit()
for _, link in ipairs(df.historical_figure.find(unit.hist_figure_id).histfig_links) do
if df.histfig_hf_link_deityst:is_instance(link) then
deity = df.historical_figure.find(link.target_hf)
-- do something with deity
end
end
You can, but it's generally much more efficient to use .getType() - the only time you really need to use is_instance is when you're dealing with a class that doesn't have a .getType() vmethod (e.g. a Viewscreen).Thanks Quietust - this should relieve me of much future puzzling !You can also use df.type.is_instance() instead of link:getType():Code: [Select]unit = dfhack.gui.getSelectedUnit()
for _, link in ipairs(df.historical_figure.find(unit.hist_figure_id).histfig_links) do
if df.histfig_hf_link_deityst:is_instance(link) then
deity = df.historical_figure.find(link.target_hf)
-- do something with deity
end
end
You can, but it's generally much more efficient to use .getType() - the only time you really need to use is_instance is when you're dealing with a class that doesn't have a .getType() vmethod (e.g. a Viewscreen).Thanks Quietust - this should relieve me of much future puzzling !You can also use df.type.is_instance() instead of link:getType():Code: [Select]unit = dfhack.gui.getSelectedUnit()
for _, link in ipairs(df.historical_figure.find(unit.hist_figure_id).histfig_links) do
if df.histfig_hf_link_deityst:is_instance(link) then
deity = df.historical_figure.find(link.target_hf)
-- do something with deity
end
end
There is a small flat array in unit named relationship_ids which only contains refs to 10 things like mother, father, pet, ridermount. These are the first 10 enums in unit_relationship_type.h. Perhaps that array is longer than currently defined, but it doesnt seem capable of containing multiple links of same type anyhow. Maybe its just a small cache of those first 10 rels.It cannot be longer than it is currently, because if that were the case, everything following it (including unit.job, unit.body, unit.counters, unit.status, and more) would be wrong, because it's a C array ("int32_t relationship_ids[10];" in df/unit.h).
Hmm, just had a sudden "hard close". Not exactly sure what caused it.I think the word you're looking for is "crash". There aren't any other situations I can think of where DF would suddenly close except when it crashes.
Has taverns or missions been tested with DFhack yet?
I found a bugged save after a hard close can possibly be fixed by escaping immediately after load, selecting export local image, and cycling through the levels. Then the game might save again and be playable, to observe the hard close again...If DF crashes, it will not touch the actual save folder itself at all (it saves temporary files to data/save/current, but ignores them when you restart DF). In other words, a crash (except when saving the game) will not permanently affect your save. I highly doubt that exporting images has anything to do with a crash, because that feature pretty much only reads from the map and hasn't been changed in years. If you think you observed it affecting a crash in some way, then that crash is probably due to undefined behavior somewhere, which means that exporting a map could affect it somehow, sometimes, if you're lucky, due to any number of semi-random factors (that could vary depending on your system, etc.). And I know I'm being pedantic, but please don't be afraid to use the word "crash" - it's a lot easier to search for.
Can you write me a script that teleports you in adv mode to the cursor with "l"? It would make navigating laggy areas more bearable.Write one for you, personally?
--Teleports unit to cursor
--[====[
flashstep
=========
A hotkey-friendly teleport that places you where your cursor is.
]====]
function flashstep(flash,step)
local flash = df.global.world.units.active[0]
local step
if df.global.ui_advmode.menu == df.ui_advmode_menu.Look then
step = df.global.cursor
else
qerror("No [l] cursor located! You kinda need it for this script.")
end
local unitoccupancy = dfhack.maps.getTileBlock(flash.pos).occupancy[flash.pos.x%16][flash.pos.y%16]
flash.pos.x = step.x
flash.pos.y = step.y
flash.pos.z = step.z
if flash.pos.x==step.x then
local stepvisibility = dfhack.maps.getTileBlock(flash.pos).designation[flash.pos.x%16][flash.pos.y%16]
stepvisibility.hidden=false
end
if not flash.flags1.on_ground then unitoccupancy.unit = false else unitoccupancy.unit_grounded = false end
end
flashstep(flash,step)
Lethosor: "Maybe its just a small cache of those first 10 rels." - It *cannot* be longer than it is currently, ...figure.info.relationships.links might be it, although I'm seeing garbage for year_tick (0xaaaaaaaa, which means it's uninitialized in my setup), so that structure might be off a bit.
Ok, thats something. We all still seem to be in the situation that *no one here knows* where to find units friends, friendly terms, aquaintances... And whatever info is in figure.info.relationships.links is unkeyed.
Im just putting that info as I find it, here, for others to notice, or duckduckgo later.
Lethosor: a crash (except when saving the game) will not permanently affect your save.
I wouldnt have expected it to because I noticed task-killing a game is usually fine, but I have bugged dozens of game saves, with silly division by zero, infinite loops or null pointer crashes occuring while in manipulator (while not doing any saving). I dont know the mechanism but a hard crash while in manipulator, on linux, can kill a save, and the process I described can repair it (not necessary to actually export the levels, just scan through them - maybe even just open and close the feature).
Can you write me a script that teleports you in adv mode to the cursor with "l"? It would make navigating laggy areas more bearable.Write one for you, personally?
Absolutely not.
Already have one?
Probably.Code: (flashstep.lua) [Select]--Teleports unit to cursor
--[====[
flashstep
=========
A hotkey-friendly teleport that places you where your cursor is.
]====]
function flashstep(flash,step)
local flash = df.global.world.units.active[0]
local step
if df.global.ui_advmode.menu == df.ui_advmode_menu.Look then
step = df.global.cursor
else
qerror("No [l] cursor located! You kinda need it for this script.")
end
local unitoccupancy = dfhack.maps.getTileBlock(flash.pos).occupancy[flash.pos.x%16][flash.pos.y%16]
flash.pos.x = step.x
flash.pos.y = step.y
flash.pos.z = step.z
if flash.pos.x==step.x then
local stepvisibility = dfhack.maps.getTileBlock(flash.pos).designation[flash.pos.x%16][flash.pos.y%16]
stepvisibility.hidden=false
end
if not flash.flags1.on_ground then unitoccupancy.unit = false else unitoccupancy.unit_grounded = false end
end
flashstep(flash,step)
you could also use dfhack's teleport script both to get your adventurer's ID and to teleport your adventurer that wayI think the "more bearable" part implied that the teleport script was annoying to use (which is fair - gui/teleport is nicer, in my opinion, but only works in fortress mode).
Yeah, in adventurer mode there is no reason to have a script which needs to be aimed but doesn't use the cursor. I can move myself around with gui/gm-editor easily enough too, or I can hit a keybind and poof over to where my cursor is currently.you could also use dfhack's teleport script both to get your adventurer's ID and to teleport your adventurer that wayI think the "more bearable" part implied that the teleport script was annoying to use (which is fair - gui/teleport is nicer, in my opinion, but only works in fortress mode).
When you build the source of DFHack, is that when the header files in dfhack>library>include>df are created?Yes. Specifically, it's the "generate_headers" target, or the one that generates codegen.out.xml.
I still don't understand what strainer is looking for, but it sounds like something along the lines of this:Can you explain what the fields you used are? anon_3 appears to be some kind of weight, and anon_1 is some sort of ID, I think, but it's hard to tell at a glance. If you can make a PR at https://github.com/dfhack/df-structures, that would be even better. anon names are unreliable and generally shouldn't be used except for experimentation.Spoiler (click to show/hide)
The snipped of a (unfinished) script gets reasonably close to what's shown on the relations screen, but doesn't get it correct all the time, and it also lists relations suppressed from that screen (but obviously present in df). I assume strainer is actually looking for something else, though.
--[[List of options I've noticed with changing that value:
0: Hero
1: Friend
2: Grudge
3: Bonded
6: Good for Business
7: Friendly Terms? (unsure)
10: Comrade
17: Loyal Soldier
18: Considers Monster (hm, could be interesting RP-wise)
26: Protector of the Weak
Others seemed to default to Friendly terms
with just few points on 7 as second relation.
Perhaps anon_1 and anon_5 may also matter.
--]]
enum unit_relationship_type : int16_t {
None = -1,
Pet,
Spouse,
Mother,
Father,
LastAttacker,
GroupLeader,
Draggee,
Dragger,
RiderMount,
Lover,
unk10,
Sibling, //
Child, //hero xxx
Friend, //friend 1
Grudge, //grudge 2
Worship, //bonded 3
AcquaintanceLong, //4 ?
AcquaintancePassing //5 ?
Bonded,
Hero,
ConsidersViolent, //4 ?
ConsidersPsychotic, //5 ?
GoodForBusiness, //goodfor business 6
FriendlyTerms, //friendly terms 8
ConsidersKiller, //8 ?
ConsidersMurderer, //9 ?
Comrade, //comrade 10
MemberOfRespectedGr //11 ?
MemberOfHatedGroup, //12 ?
EnemyFighter, //13 ?
FriendlyFighter, //14 ?
ConsidersBully, //15 ?
ConsidersBrigand, //16 ?
LoyalSoldier, //loyal soldier 17
ConsidersMonster, //considers monster 18
ConsidersStorytelle //19 ?
ConsidersPoet, //protectorofweek 20?
ConsidersBard,
ConsidersDancer,
Master,
Apprentice,
Companion,
FormerMaster,
FormerApprentice,
ConsidersQuarreler,
ConsidersFlatterer,
Hunter,
ProtectorOfTheWeak
'121yr fcs-2 Datos Blibberanok Expert Thrower (lv6 ap43 xp12)
Extremely gregarious.Very adoring,rude,leisurely. Rgds Friendship++ Peace-
Likes Surviving,platinum,shields,bolts,opposum men... Gods:Laven,Zul
Fam 2/6 Frn 4/6 wif lvr pet kil3 loy3 gru1 pri-1
I've seen a number of 23 entries both as (1, 7, 23) and (7, 23), but still don't know what 23 is. In at least one case the person was the spouse.
'121yr fcs-2 Datos Blibberanok Expert Thrower (lv6 ap43 xp12)
Extremely gregarious.Very adoring,rude,leisurely. Rgds Friendship++ Peace-- Mirth+
Likes Surviving,Outwork+,platinum,shields,a fabric,opposum men... Gods:Laven,Zul
Fam 2/6 Frn 4/6 wif lvr pet kil3 loy3 gru1 pri-1
i got problems with mousequery not responding to the mouse being in the bottom or right border.Mousequery hasn't changed at all. What specific mousequery feature are you referring to? "mousequery edge"? Are you using TWBT, and if so, does the issue occur with DFHack's (not TWBT's) version of mousequery?
this did not occur in 43.x.x but only in 44.x.x
i'm using a 5:4 screen and running DF windowed 1280x960.
maybe there's something wrong with the calculation of the position and/or size of the areas for mouse scrolling.
i used the 44.x LNP and the 1.9 Meph and it occured in both versions.i got problems with mousequery not responding to the mouse being in the bottom or right border.Mousequery hasn't changed at all. What specific mousequery feature are you referring to? "mousequery edge"? Are you using TWBT, and if so, does the issue occur with DFHack's (not TWBT's) version of mousequery?
this did not occur in 43.x.x but only in 44.x.x
i'm using a 5:4 screen and running DF windowed 1280x960.
maybe there's something wrong with the calculation of the position and/or size of the areas for mouse scrolling.
mousequery [plugin|rbutton|track|edge|live] [enabled|disabled]
plugin: enable/disable the entire plugin
rbutton: enable/disable right mouse button
track: enable/disable moving cursor in build and designation mode
edge: enable/disable active edge scrolling (when on, will also enable tracking)
live: enable/disable query view when unpaused
Disable one at a time, like "mousequery track disable", until whatever you're talking about doesn't work at all.It won't recognize newer versions than what it was built for, which is always the case. We're working on 0.44.04 support, so expect a release soon.Thanks for the fine work!
Do both of those installations include TWBT? If so, again, can you try with the mousequery plugin distributed with DFHack? I know that DFHack's version hasn't changed since 0.43.05, but I can't make any guarantees about TWBT's version of mousequery (or TWBT itself) not changing things. It doesn't look like TWBT's mousequery.cpp has changed, but it's possible that TWBT has changed something.i always use twbt and the mousequery feature that doesnt work correctly in 44.x is "edge".
Also, what specific mousequery feature are you referring to? Run "mousequery" and you should see a list of features, like this:Code: [Select]mousequery [plugin|rbutton|track|edge|live] [enabled|disabled]
Disable one at a time, like "mousequery track disable", until whatever you're talking about doesn't work at all.
plugin: enable/disable the entire plugin
rbutton: enable/disable right mouse button
track: enable/disable moving cursor in build and designation mode
edge: enable/disable active edge scrolling (when on, will also enable tracking)
live: enable/disable query view when unpaused
Anyway, if this doesn't occur with DFHack's mousequery plugin, downloaded from https://github.com/dfhack/dfhack/releases, there isn't anything I can do to fix it.
A release for 0.44.04: https://github.com/DFHack/dfhack/releases/tag/0.44.04-alpha1
"Something to do with trade representative" could possibly be what caused the crash I mentioned earlier too when I asked about missions and taverns, as I crashed somewhere in the middle of year two as well.Is it reproducible? If so, test without DFHack and see if it still occurs. If it only occurs with DFHack, please upload your save and explain how to reproduce the issue.
A question regarding the df.emotion_type attribute "divider" (defined in df.unit_thoughts.xml):From https://github.com/DFHack/df-structures/commit/d69705e288075c9d640cd19b42aa21884f9500b9, they are "stress divisors". Not sure on specifics, but Quietust would know more.
What does this attribute describe, and how is is supposed to be applied?
it works when put into an unmodded DF. it somehow doesn't work when put into the LNP.What does "doesn't work" mean?
A question regarding the df.emotion_type attribute "divider" (defined in df.unit_thoughts.xml):Every "thought" affects a dwarf's Stress based on the emotion it provoked, the dwarf's personality, and likely also how long ago it happened. The relevant "base stress" value for the thought is divided by the "divider" associated with the emotion, and the result is then added to the dwarf's Stress level. Positive versus negative decides whether stress goes up or down, and larger values result in smaller adjustments.
What does this attribute describe, and how is is supposed to be applied?
I could very well be posting in the wrong forum for this question, and if so let me know. I saw that the dev branch in the github repository was updated to 0.44.04 alpha1, and the most recent update included the TWBT mod. I installed dfhack (in Linux, 64 bit) and saw that it was indeed enabled, but the font widths are variable. Is there any way I can switch those back to being fixed with as the font renderings are bugged when they're that way? I tried modifying the data/twbt_init/init.txt file among others but nothing seems to have any effect. I am running the Pheobus tileset pack.It definitely has something to do with TWBT. TWBT isn't included in DFHack, so I don't know what you meant by "the most recent update included the TWBT mod" - you can get TWBT from https://github.com/mifki/df-twbt ("Releases"). Also, it sounds like you're dealing with a pack, because I've never heard of a "twbt_init" folder - init.txt is usually just in data/init/init.txt. I'm not sure what the twbt_init folder is for - it might be used if TWBT is enabled somehow, but I'm pretty sure TWBT doesn't deal with that folder directly.
P.S. I wasn't certain if this was a TBWT setting or a dfhack setting, which is why I'm posting here
It definitely has something to do with TWBT. TWBT isn't included in DFHack, so I don't know what you meant by "the most recent update included the TWBT mod" - you can get TWBT from https://github.com/mifki/df-twbt ("Releases"). Also, it sounds like you're dealing with a pack, because I've never heard of a "twbt_init" folder - init.txt is usually just in data/init/init.txt. I'm not sure what the twbt_init folder is for - it might be used if TWBT is enabled somehow, but I'm pretty sure TWBT doesn't deal with that folder directly.
Short version: it's not DFHack stuff, but your setup is strange enough that I'm not sure exactly how to get it working. My best advice is to install TWBT from the link above and see if that fixes anything, but you might also have to make some configuration changes.
I could very well be posting in the wrong forum for this question, and if so let me know. I saw that the dev branch in the github repository was updated to 0.44.04 alpha1, and the most recent update included the TWBT mod. I installed dfhack (in Linux, 64 bit) and saw that it was indeed enabled, but the font widths are variable. Is there any way I can switch those back to being fixed with as the font renderings are bugged when they're that way? I tried modifying the data/twbt_init/init.txt file among others but nothing seems to have any effect. I am running the Pheobus tileset pack.
P.S. I wasn't certain if this was a TBWT setting or a dfhack setting, which is why I'm posting hereSpoiler (click to show/hide)
it means that even when entering "mousequery edge enable" into the dfhack console, it doesnt activate the edge scrolling.it works when put into an unmodded DF. it somehow doesn't work when put into the LNP.What does "doesn't work" mean?
it means that even when entering "mousequery edge enable" into the dfhack console, it doesnt activate the edge scrolling.Oh, so it's not all of DFHack that doesn't work. It's possible that you have a mismatch between mousequery and DFHack versions, especially if you're using TWBT's mousequery. Or it's possible that TWBT's mousequery behaves differently.
Mousequery edge enable works with TWBT's version of mousequery, you just have to make sure it's the correct mousequery plugin installed. Generally if it's the wrong verison of mousequery it'll tell you in the dfhack window.He mentioned earlier that as long as too much doesn't change between the update, all that's needed is some fiddling with the header (and worrying about a few unforeseen bugs).
Unrelated question: Should 44.05 work with this version since it's only like one change or do memory addresses and such get changed for even minor edits?
Invoking: exportlegends info
...t Launcher\Dwarf Fortress/hack/scripts/exportlegends.lua:86: Cannot iterate field vector<world_construction*>.0: index out of bounds.
stack traceback:
[C]: in upvalue 'cb'
...t Launcher\Dwarf Fortress/hack/scripts/exportlegends.lua:86: in for iterator 'for iterator'
...t Launcher\Dwarf Fortress/hack/scripts/exportlegends.lua:211: in global 'export_more_legends_xml'
...t Launcher\Dwarf Fortress/hack/scripts/exportlegends.lua:772: in global 'export_legends_info'
...t Launcher\Dwarf Fortress/hack/scripts/exportlegends.lua:821: in local 'script_code'
...op 3\Tileset Launcher\Dwarf Fortress\hack\lua\dfhack.lua:562: in function 'dfhack.run_script_with_env'
(...tail calls...)
Invoking: die
It's known and fixed in DFHack 0.44.04-alpha1Perfect, thank you. :)
https://dfhack.readthedocs.io/en/latest/docs/NEWS-dev.html#dfhack-0-44-04-alpha1
That should be reported in the TWBT thread (although it already has): http://www.bay12forums.com/smf/index.php?topic=138754.0
I'm not aware of any fix yet besides what you did.
I seem to have an issue with the quicksave command. Normally I quicksave every time an enemy force is coming but it seems that, as soon as i save the game, the invaders which did not spawn yet on the map vanish before spawning, leaving all their armor, cloth and weapons just outside of the map. I can see the items in the stock overview as forbidden and marked red for no unobtainable and when I jump to them the cursor is placed right on the mapedge the siege came from, all the way on the last tile of the map. This also happens for merchants so all the food and items they carry is just dumped before entering the map. If I dont quicksave everything works fine and a whole bunch of enemies spawn and attack. If I save once all are spawned, everything seems fine...I've heard of a variety of issues like this in vanilla DF. http://www.bay12games.com/dwarves/mantisbt/view.php?id=9593 is related. I think saving before a siege or caravan (or migrant wave?) has entered the map prevents them from entering. I'm surprised that quicksave broke it - I would have expected it to work better than a save/load cycle. That probably means autosaves are affected too, since that's basically identical to what quicksave does.
Anybody else experiencing this? 0.44.04 with dfhack.
Is there a way to add a reaction to a workshop entirely through the command line? I am trying to test some of my reaction based scripts and it would be a lot easier if I could automate it. I saw there was dfhack.job.linkIntoWorld(job,new_id) but I wasn't sure if that would do what I wanted.dfhack.job.linkIntoWorld has something to do with setting up jobs (the things in the "j" screen, not the options workshops). If I misunderstood and you're talking about queueing jobs at specific workshops that happen to involve interactions, there are probably some examples of that. I was looking at autogems recently, but it's a bit complicated. I don't know much about eventful.
EDIT: Also, does the eventful onReactionComplete only work if the reaction starts with 'LUA_HOOK_TRIGGER'?
Request:Interesting idea. I'm not sure where that information is stored, but I can look into it.
Would it be possible to create a command that lists which cooking ingredients are favoured in the thoughts and preferences screen of you dwarves?
Idem for drink preferences?
Do you see the same with a save/load cycle?just booted up a save where I sent a queen to a 9 day hike and yeah looks like the game just brought them back instantly.
Rumrusher, you're talking about something (probably) different from the issue with caravans/migrants/etc. not arriving properly. (And sounds like it's not quicksave-specific, in any case.)Oh awesome! That's basically what I asked for, I see I should have searched harder, for it's already there!
Martinuzz: it turns out that you can search for preference types in the "dwarfmonitor prefs" screen - does this help?Spoiler (click to show/hide)
Note that you can get to this screen with Alt-M from the main fortress mode screen (with the default keybindings, anyway).
relations_list_attitude_type : int16_t
None = -1,
Hero,
Friend,
Grudge,
Bonded,
ConsidersViolent,
ConsidersPsychotic,
GoodForBusiness,
FriendlyTerms,
ConsidersKiller,
ConsidersMurderer,
Comrade,
MemberOfRespectedGroup,
MemberOfHatedGroup,
EnemyFighter,
FriendlyFighter,
ConsidersBully,
ConsidersBrigand,
LoyalSoldier,
ConsidersMonster,
ConsidersStoryteller,
ConsidersPoet,
ConsidersBard,
ConsidersDancer,
ConsidersQuarreler,
ConsidersFlatterer,
Hunter,
ProtectorOfTheWeak
Can you write me a script that teleports you in adv mode to the cursor with "l"? It would make navigating laggy areas more bearable.Write one for you, personally?
Absolutely not.
Already have one?
Probably.Code: (flashstep.lua) [Select]--Teleports unit to cursor
--[====[
flashstep
=========
A hotkey-friendly teleport that places you where your cursor is.
]====]
function flashstep(flash,step)
local flash = df.global.world.units.active[0]
local step
if df.global.ui_advmode.menu == df.ui_advmode_menu.Look then
step = df.global.cursor
else
qerror("No [l] cursor located! You kinda need it for this script.")
end
local unitoccupancy = dfhack.maps.getTileBlock(flash.pos).occupancy[flash.pos.x%16][flash.pos.y%16]
flash.pos.x = step.x
flash.pos.y = step.y
flash.pos.z = step.z
if flash.pos.x==step.x then
local stepvisibility = dfhack.maps.getTileBlock(flash.pos).designation[flash.pos.x%16][flash.pos.y%16]
stepvisibility.hidden=false
end
if not flash.flags1.on_ground then unitoccupancy.unit = false else unitoccupancy.unit_grounded = false end
end
flashstep(flash,step)
Hi. I'm trying to determine selected or followed dwarf id from lua script. In first case it's a matter of calling one method, but I can't find anything for "follow dwarf" part.
Seems like I can get data of a dwarf in the center of sceen (some times I'll get two or more dwarves on one tile, but that's irrelevant for now).
I need to do this only when I'm in "follow dwarf" mode, and, since it's my first attempt at scripting for DFHack, I dunno how to determine if game is in this mode.
Is there a script that deteriorates EVERYTHING that isn't stockpiled or worn/carried/owned? I have sooooo many sekeltons, bolts, arrows, armor, food, dead traders, god knows what creatures lying dead in front of my gates and cleaning everything up is annoyingly FPS consuming. I managed to get corpses and refuse deteriorate with the scripts but failed to manage a clever check for whether or not the stuff is in a stockpile...A stopgap until someone proposes a solution: dump all the junk underneath an open side-raising drawbridge and lower the drawbridge on top of it. The items will be obliterated.
I have to remedy my former statement: have a siege, wait until half of them arrive (like half the squad). quicksave, let the rest arrive, die, restart DF, load game, no more soldiers arrive.
snip- found what i was looking for in search.Save in df/hack/scripts as flashstep.lua.
someone already made an adventure mode quick teleportation script called Flashstep
Im not sure how to get a script into DF and then set it to a hotkey but ill figure it out.
I have it!The reason those items do not have an explicit position is because they're either in a container or in a unit inventory. Containers may be in inventories as well, so their positions are the positions of the containers/units carrying them. That's not a bug in DFHack, but DF glitching the carrying creature (assuming you look at cases where they haven't arrived/left properly). Also note that the unit flag called "dead" by DFHack isn't actually an indicator of the unit being dead, but rather something more like inactive, so I think merchants on the way onto the map and those that have just left have that flag set. Likewise, invaders who haven't arrived properly yet can have this flag set.
I found an error using dfhack, items are not lying were the carrier "died". I found gloves and armor in the df.world.items.all list with a position of -30000 and traced its carrier in the units.all and units.active list. The swordsman was not part of the units.active but found in the units.all with a valid position right on the map edge (I don't know why he died there but he never made it onto the map) so my plan is now to traverse the lists, find dead units that have a valid map position, check their items and if their position is invalid, I update the position or even mark them for garbage_collection.
Many thanks from everybody :D
I have it!The reason those items do not have an explicit position is because they're either in a container or in a unit inventory. Containers may be in inventories as well, so their positions are the positions of the containers/units carrying them. That's not a bug in DFHack, but DF glitching the carrying creature (assuming you look at cases where they haven't arrived/left properly). Also note that the unit flag called "dead" by DFHack isn't actually an indicator of the unit being dead, but rather something more like inactive, so I think merchants on the way onto the map and those that have just left have that flag set. Likewise, invaders who haven't arrived properly yet can have this flag set.
I found an error using dfhack, items are not lying were the carrier "died". I found gloves and armor in the df.world.items.all list with a position of -30000 and traced its carrier in the units.all and units.active list. The swordsman was not part of the units.active but found in the units.all with a valid position right on the map edge (I don't know why he died there but he never made it onto the map) so my plan is now to traverse the lists, find dead units that have a valid map position, check their items and if their position is invalid, I update the position or even mark them for garbage_collection.
Many thanks from everybody :D
Actually I meant I found a dfhack workaround for a DF glitch :D I finally can mark all the damn "edge items" using a single script and then autodump them away :D dfhack was the saviour, not the offender :P
I have yet to find a way to add the items to the map block though, I see them using the cursor but I cant select them, only dump them using autodump which complains about the items not being part of a block. But if "dead" doesn't mean dead, I still have to figure out how to distinguish between alive breathing entities and ... well ... guys who died at the border of the map.
Nope I must say the alpha is very very stable. It rarely crashed for me and when it crashed it was always when I tried to build something like farm plots or walls and this I had also with 0.42.Ok, you should still generally report crashes even if they're not new, but that one has been reported already and linked to TWBT. I'm not sure if it's been fixed yet, but check the TWBT thread for details. Other than that, that's good news. I should have time to finish things up for a r1 in a couple days.
I found the address of a function in DF that I would like to call from a DFHack plugin.
How would I go about doing that?
It takes a pointer to and art_image_ref and an int pointer, and returns a pointer to an art_image, generating a new one if necessary.
Well I'd also like to know how to keep the pointers stored in DF-structures, so they can be updated for newer versions.
Well I'd also like to know how to keep the pointers stored in DF-structures, so they can be updated for newer versions.
I don't think df-structures have support for storing addresses of and/or calling arbitrary functions (as opposed to vmethods).
Btw, I'm just using viewscreen_image_creatorst to make it load all art_image_chunks.
Which version of DF do you have and which version of dfhack are you trying to install?
Btw, I'm just using viewscreen_image_creatorst to make it load all art_image_chunks.Japa: if you can get this to work, I'd prefer it to having a function pointer in symbols.xml.
So I saw someone had a similar problem to me, but they never pasted the output. I am unable to get DFHack to load any plugins upon start, I am unzipping Vanilla DF to a folder, extracting DFHack right on top (and get the dialog box asking if I want to overwrite SDL.dll so I know the files should be dropping where they should be), but I'm getting error messages upon start. I've tried both r3, then r1 and am getting the same problem with both; stderr.txt output is as follows:For some reason it can't read any files, except dfhack.init-example somehow. Did you install DFHack as a different user?Spoiler (click to show/hide)
df::art_image * (__thiscall *getImage)(df::world*,df::art_image_ref *, int *) = (df::art_image * (__thiscall*)(df::world*, df::art_image_ref *, int *))df::global::getArtImage;
works. Not sure if the first argument needs to be world though.For some reason it can't read any files, except dfhack.init-example somehow. Did you install DFHack as a different user?
DF version is df_43_05_win32
DFHack version I've tried is dfhack-0.43.05-r3.1-Windows-32 and then dfhack-0.43.05-r1-Windows-32
This is on a windows XP machine if that matters.
There may be a problem with how dfhack is looking for file on XP actually :I doubt that - the last time the plugin-loading code changed significantly was before DFHack 0.40.24-r4 (https://github.com/DFHack/dfhack/commit/4fc6cb6f175aebc96bf7ec4629a90cd05b71e8ee#diff-bcb993821b53968bc228a308c6193963).
I gave a try to current dfhack on a 32bits PC that has XP and exact same problem : dfhack does not find any plugin despite they're all there.Ok, what if you try the older one?
Last time i used dfhack on that same system XP 32bits machine, it was dfhack 42.06 alpha-2 a couple of years ago, and it worked without a problem.
Nope I must say the alpha is very very stable. It rarely crashed for me and when it crashed it was always when I tried to build something like farm plots or walls and this I had also with 0.42.it ran stable enough for 5 hours without any problems and 2 years into the game for me.
QuoteI gave a try to current dfhack on a 32bits PC that has XP and exact same problem : dfhack does not find any plugin despite they're all there.Ok, what if you try the older one?
Last time i used dfhack on that same system XP 32bits machine, it was dfhack 42.06 alpha-2 a couple of years ago, and it worked without a problem.
I think it's tracked to an extent, and would be possible to display with DFHack, but I don't believe there's a tool like the one you described.Understood, thanks for the info.
That was what I was looking for. It looks like it matches the behavior described here (http://www.bay12forums.com/smf/index.php?topic=139553.msg7289387#msg7289387). DFHack is supposed to copy files from dfhack-config/default/ to dfhack-config/, so somehow it wasn't able to do that.
Again, did you install DFHack as a different user?
Can you upload your stderr.log file (in the DF folder)?
unit->status.current_soul->personality.unk_v4019_1
Development of the OS windows clone "Reactos (https://www.reactos.org/) has exploded since recently moving to github and may have an XP replacement in beta this year.If you go by the text, it means the dorfs are less negative to being out on the surface than normal dorfs, including being blinded by the sun and rained/snowed upon (and I assume this applies only to normal precipitation, not evil rain). That should make those dorfs suitable candidates for jobs such as herbalists, hunters, fisherdwarves, and wood cutters.
I found a personality key -
"likes working outdoors and grumbles only mildly..." is in :Code: [Select]unit->status.current_soul->personality.unk_v4019_1
Values are 0 =none, 1 = "...at least for a while", 2 ="...only mildly at inclement weather".
Apparently this means the unit cares less about sleeping on floors and things, but has nothing to do with caveAdapt.
Please don't try to support Windows XP - it's been unsupported for almost four years now, terribly insecure and a general PITA. If you're using it, upgrade to Linux or a newer version of Windows as soon as possible.if you're not forced to use directX, i recommend any linux/ubuntu version.
function delayReaction(n,job)
n = n - 1
if n<1 then return end
dfhack.timeout(1,'ticks',function () delayReaction(n,job) end)
end
Will cause a job to take as many ticks as you specify (n). Linking this with eventfuls onJobInitiated seems to be enough for delayed reactions. I tested by making a dwarf work a single reaction for a full day.
I don't see where that modifies completion_timer; is it missing something?
function delayReaction(n,job)
n = n - 1
if n<1 then return end
job.completion_timer = -1
dfhack.timeout(1,'ticks',function () delayReaction(n,job) end)
end
That's what I get for not copy/pasting
Does anyone have much experience with dissecting the reports from DF? I am trying to get the units involved in combat reports, for instance we have a combat dodge report "Dwarf 3 attacks Dwarf 4 but she jumps away!". Now I know I can use the report.id and look through all the active units to find which unit has that report.id in their reports field, but what I can't do is figure out which is which. The only thing I could think of would be to compare the names and match them that way, but that would mean I would have hard code each string option.I've done some report work for multiplayer: probably this (https://github.com/warmist/df_multiplay/blob/master/messages.lua#L60) is what you are looking for?
Does anyone have much experience with dissecting the reports from DF? I am trying to get the units involved in combat reports, for instance we have a combat dodge report "Dwarf 3 attacks Dwarf 4 but she jumps away!". Now I know I can use the report.id and look through all the active units to find which unit has that report.id in their reports field, but what I can't do is figure out which is which. The only thing I could think of would be to compare the names and match them that way, but that would mean I would have hard code each string option.I've done some report work for multiplayer: probably this (https://github.com/warmist/df_multiplay/blob/master/messages.lua#L60) is what you are looking for?
Does anyone have much experience with dissecting the reports from DF? I am trying to get the units involved in combat reports, for instance we have a combat dodge report "Dwarf 3 attacks Dwarf 4 but she jumps away!". Now I know I can use the report.id and look through all the active units to find which unit has that report.id in their reports field, but what I can't do is figure out which is which. The only thing I could think of would be to compare the names and match them that way, but that would mean I would have hard code each string option.I've done some report work for multiplayer: probably this (https://github.com/warmist/df_multiplay/blob/master/messages.lua#L60) is what you are looking for?
I think he wants to know IDs of Dwarf 3 and Dwarf 4 in "Dwarf 3 attacks Dwarf 4 but she jumps away!". If so, maybe you can't do it at all - something happened, the game generated a report, and that's it, only the text is stored.
for k,v in pairs(df.global.world.raws.creatures.all[RACEID].raws) do print(v.value);end
into the console, replacing RACEID with the number you found.
What happens is that the saved data of existing creatures will be reinterpreted according to the new raws. So for example if you change the variation rules (height,width,hair color, etc), existing creatures will keep theiror printall(df.race.find(raceid)) (AFAIR)
existing variation numbers but new ones will use the new rules; on the other hand if you change say the material value multiplier, it'll change existing creatures too.
You can view the raws the game is using for your race by finding the race id of your creature, then enteringCode: [Select]for k,v in pairs(df.global.world.raws.creatures.all[RACEID].raws) do print(v.value);end
into the console, replacing RACEID with the number you found.
New release! https://github.com/DFHack/dfhack/releases/tag/0.44.05-r1
I have a question. Is there a way to temporary hide UI improvements brought by DFHack from the game? I mean searches, advanced views, new windows etc.- Stop DF
disable add-spatter autochop autodump autogems automaterial automelt autotrade building-hacks buildingplan confirm dwarfmonitor embark-tools eventful fix-armory hotkey-commands manipulator mousequery power-meter rename rendermax resume search siege-engine steam-engine stockflow stockpiles stocks title-version trackstop tweak zone
There's also TWBT - I'm not sure if you're using or counting that, but it can't be disabled once it's been enabled, so if you want to stop that, you do have to restart DF in the process.
Or "unload -all" to unload all plugins, which should stop most UI changes (except for a few implemented by scripts that create new screens), then "load -all" to load them again. Note that loading them won't actually enable them; you'll have to enable them all manually again, or run "script dfhack.init" to re-run dfhack.init, which would probably work for most things.
These are all the plugins that currently hook into DF virtual methods - usually this affects the UI in some way, but a few might not (like building-hacks). The corresponding "disable" command for convenience:Code: [Select]disable add-spatter autochop autodump autogems automaterial automelt autotrade building-hacks buildingplan confirm dwarfmonitor embark-tools eventful fix-armory hotkey-commands manipulator mousequery power-meter rename rendermax resume search siege-engine steam-engine stockflow stockpiles stocks title-version trackstop tweak zone
There's also TWBT - I'm not sure if you're using or counting that, but it can't be disabled once it's been enabled, so if you want to stop that, you do have to restart DF in the process.
would it be possible to expand the right hand side list? i mean in most cases it shows like 10 rows when there's a lot of empty/black space below and i have to scroll down a terribly long list - like when watching a tile's or creature's items or selecting materials for a building.Not easily. That might be better as a DF suggestion (not sure if it's been suggested yet). I know some of the newer sidebar menus support that, as well as some older ones like the "b" and "k" menu (is that what you meant by "watching a tile's items"? It's taking up the full screen height for me in 0.44.05).
Alright, so I've asked similar questions before, but this one is a little more concrete. Does anyone know of a way to make attacks able to hit from more than one square away?my first idea was the spear. those weapons could only hit an enemy with the point if they were far enough.
I just spent the last hour adding attacks to units in arena mode and changing all the flags and unk variables to try all the various combinations but I had no luck. The attacks only ever connected and generated a report when the two units were within 1 square of each other. It makes me think it isn't possible, but I'm trying to create special skills that can hit from more than one square away and still using the standard attack action (think something like a magic flame whip).
Can't you hit someone with a spear at point blank? You can literally hold the spear right under the blade and shove it in to someone who's just about on top of you. The spear is a bit unwieldy at point blank, but is absolutely not worthless.Alright, so I've asked similar questions before, but this one is a little more concrete. Does anyone know of a way to make attacks able to hit from more than one square away?my first idea was the spear. those weapons could only hit an enemy with the point if they were far enough.
I just spent the last hour adding attacks to units in arena mode and changing all the flags and unk variables to try all the various combinations but I had no luck. The attacks only ever connected and generated a report when the two units were within 1 square of each other. It makes me think it isn't possible, but I'm trying to create special skills that can hit from more than one square away and still using the standard attack action (think something like a magic flame whip).
well, using a spear (3m and longer) effective would mean you keep the enemy at distance and if they come closer, you can still hit them with the blunt shaft, but no more stabby stabby.Can't you hit someone with a spear at point blank? You can literally hold the spear right under the blade and shove it in to someone who's just about on top of you. The spear is a bit unwieldy at point blank, but is absolutely not worthless.Alright, so I've asked similar questions before, but this one is a little more concrete. Does anyone know of a way to make attacks able to hit from more than one square away?my first idea was the spear. those weapons could only hit an enemy with the point if they were far enough.
I just spent the last hour adding attacks to units in arena mode and changing all the flags and unk variables to try all the various combinations but I had no luck. The attacks only ever connected and generated a report when the two units were within 1 square of each other. It makes me think it isn't possible, but I'm trying to create special skills that can hit from more than one square away and still using the standard attack action (think something like a magic flame whip).
Alright, so I've asked similar questions before, but this one is a little more concrete. Does anyone know of a way to make attacks able to hit from more than one square away?Was it by adding action attack? Because i might be misremembering but i think i had attacks happen which ignored distance by adding a new (or modifying old) action.
I just spent the last hour adding attacks to units in arena mode and changing all the flags and unk variables to try all the various combinations but I had no luck. The attacks only ever connected and generated a report when the two units were within 1 square of each other. It makes me think it isn't possible, but I'm trying to create special skills that can hit from more than one square away and still using the standard attack action (think something like a magic flame whip).
Alright, so I've asked similar questions before, but this one is a little more concrete. Does anyone know of a way to make attacks able to hit from more than one square away?Was it by adding action attack? Because i might be misremembering but i think i had attacks happen which ignored distance by adding a new (or modifying old) action.
I just spent the last hour adding attacks to units in arena mode and changing all the flags and unk variables to try all the various combinations but I had no luck. The attacks only ever connected and generated a report when the two units were within 1 square of each other. It makes me think it isn't possible, but I'm trying to create special skills that can hit from more than one square away and still using the standard attack action (think something like a magic flame whip).
teleport to the square away on the tick the attack's going to hit then teleport back to starting location
If you use the function provided in teleport.lua, it should be completely side-effect free
How do I use steam engine plugin? Documentation about when it enables itself is very vague, literally "detects custom workshops with STEAM_ENGINE in their token".
So, I need to create a new building with [STEAM_ENGINE] tag? How should I define it? It is mentioned afterwards that power is transmitted through edge tiles "that looks like gearboxes". What happens if I do NOT define tiles to look like gearboxes?
BTW, I use latest LNP if it helps.
out.printerr("%s has no gear tiles - ignoring.\n", wslist[i]->code.c_str());
1. I'd assume it's autoinjected unless proven otherwise.OK, I feel like an idiot now. Instead of asking (stupid) questions I should've looked into dfhack directory. There it is, hack/raw. It contains everything I asked for, every definition, every reaction. I think I'll just copy everything that belongs to steam engine from there and try to gen a world.
2. I'd guess that you can let it be built however you damn well please, with any items you want it to, since it uses a building definition. If I were to implement a "piston", I'd make it a tool or trap component.
If you hold the spear underneath the blade, you can simply use it like a difficultly balanced dagger. That's far from being denied use of the business end of a spear. There would be a bit of hangage but at that point, it's more a question of if someone can actually command a hold on your weapon and not just whether you can touch a hostile or not.well, using a spear (3m and longer) effective would mean you keep the enemy at distance and if they come closer, you can still hit them with the blunt shaft, but no more stabby stabby.Can't you hit someone with a spear at point blank? You can literally hold the spear right under the blade and shove it in to someone who's just about on top of you. The spear is a bit unwieldy at point blank, but is absolutely not worthless.Alright, so I've asked similar questions before, but this one is a little more concrete. Does anyone know of a way to make attacks able to hit from more than one square away?my first idea was the spear. those weapons could only hit an enemy with the point if they were far enough.
I just spent the last hour adding attacks to units in arena mode and changing all the flags and unk variables to try all the various combinations but I had no luck. The attacks only ever connected and generated a report when the two units were within 1 square of each other. It makes me think it isn't possible, but I'm trying to create special skills that can hit from more than one square away and still using the standard attack action (think something like a magic flame whip).
however this maneuver puts the wielder in a high risk of losing their weapon.If you hold the spear underneath the blade, you can simply use it like a difficultly balanced dagger. That's far from being denied use of the business end of a spear. There would be a bit of hangage but at that point, it's more a question of if someone can actually command a hold on your weapon and not just whether you can touch a hostile or not.well, using a spear (3m and longer) effective would mean you keep the enemy at distance and if they come closer, you can still hit them with the blunt shaft, but no more stabby stabby.Can't you hit someone with a spear at point blank? You can literally hold the spear right under the blade and shove it in to someone who's just about on top of you. The spear is a bit unwieldy at point blank, but is absolutely not worthless.Alright, so I've asked similar questions before, but this one is a little more concrete. Does anyone know of a way to make attacks able to hit from more than one square away?my first idea was the spear. those weapons could only hit an enemy with the point if they were far enough.
I just spent the last hour adding attacks to units in arena mode and changing all the flags and unk variables to try all the various combinations but I had no luck. The attacks only ever connected and generated a report when the two units were within 1 square of each other. It makes me think it isn't possible, but I'm trying to create special skills that can hit from more than one square away and still using the standard attack action (think something like a magic flame whip).
That, and 3m spears would probably count as pikes at that point..
Why are you setting first_name to "", by the way? I seem to remember taking something like that (if not exactly that) out because it produced unwanted behavior in fortress or arena mode - did you test both?
There is also an issue with names. In 44.05 the newly created unit get their arena name (ie Dog #12345) as their "First name" and I can't figure a way to wipe it except invoking the name method to overwrite it with a proper civ name. This does not respect the usual naming values and even emptying it cause DF reapply the name right after.For what it's worth, I noticed this while messing around with the 43.05 version of Masterwork's Hermit Mode. One can order as many "hermits" from the starting workshop as you like; the kill script only triggers once the last task is finished. In the course of my borkery, I noticed that some units had the Arena naming scheme. I'm not sure what made a difference, though I wouldn't be surprised if it were something caste-related.
I've tried with TWBT mousequery and with vanilla mousequery.
I've tried with STANDARD, 2D, and TWBT print mode.
I've tried with the intro video on and off.
I've tried with mousequery edge enabled and disabled.
I've tried with mousequery live enabled and disabled.
I've tried while pause, while not paused, and I've tried clicking on the ramps.
But I've never seen the Z levels change by just using the mouse. I have no idea how that is happening.
I've tried with TWBT mousequery and with vanilla mousequery.i only got problems with mousequery when using multilevel view.
I've tried with STANDARD, 2D, and TWBT print mode.
I've tried with the intro video on and off.
I've tried with mousequery edge enabled and disabled.
I've tried with mousequery live enabled and disabled.
I've tried while pause, while not paused, and I've tried clicking on the ramps.
But I've never seen the Z levels change by just using the mouse. I have no idea how that is happening.
I've tried with TWBT mousequery and with vanilla mousequery.i only got problems with mousequery when using multilevel view.
I've tried with STANDARD, 2D, and TWBT print mode.
I've tried with the intro video on and off.
I've tried with mousequery edge enabled and disabled.
I've tried with mousequery live enabled and disabled.
I've tried while pause, while not paused, and I've tried clicking on the ramps.
But I've never seen the Z levels change by just using the mouse. I have no idea how that is happening.
using meph's pack, aswell as with peridexerrant's, it often fails to react on the mouse position (mousequery edge enabled) when there are no tiles in the mouse location on the current layer. the same position scrolls perfect when multilevel view is disabled.I've tried with TWBT mousequery and with vanilla mousequery.i only got problems with mousequery when using multilevel view.
I've tried with STANDARD, 2D, and TWBT print mode.
I've tried with the intro video on and off.
I've tried with mousequery edge enabled and disabled.
I've tried with mousequery live enabled and disabled.
I've tried while pause, while not paused, and I've tried clicking on the ramps.
But I've never seen the Z levels change by just using the mouse. I have no idea how that is happening.
What problems?
using meph's pack, aswell as with peridexerrant's, it often fails to react on the mouse position (mousequery edge enabled) when there are no tiles in the mouse location on the current layer. the same position scrolls perfect when multilevel view is disabled.I've tried with TWBT mousequery and with vanilla mousequery.i only got problems with mousequery when using multilevel view.
I've tried with STANDARD, 2D, and TWBT print mode.
I've tried with the intro video on and off.
I've tried with mousequery edge enabled and disabled.
I've tried with mousequery live enabled and disabled.
I've tried while pause, while not paused, and I've tried clicking on the ramps.
But I've never seen the Z levels change by just using the mouse. I have no idea how that is happening.
What problems?
i do not know what method is used to check for the mouse position, but it fails too often. also with multilevel view it is impossible to place constructions (floor, fortification) on open space - you have to use the arrowkeys to navigate on open space.
why are there two different versions then, if one works as intended and the other doesn't?using meph's pack, aswell as with peridexerrant's, it often fails to react on the mouse position (mousequery edge enabled) when there are no tiles in the mouse location on the current layer. the same position scrolls perfect when multilevel view is disabled.I've tried with TWBT mousequery and with vanilla mousequery.i only got problems with mousequery when using multilevel view.
I've tried with STANDARD, 2D, and TWBT print mode.
I've tried with the intro video on and off.
I've tried with mousequery edge enabled and disabled.
I've tried with mousequery live enabled and disabled.
I've tried while pause, while not paused, and I've tried clicking on the ramps.
But I've never seen the Z levels change by just using the mouse. I have no idea how that is happening.
What problems?
i do not know what method is used to check for the mouse position, but it fails too often. also with multilevel view it is impossible to place constructions (floor, fortification) on open space - you have to use the arrowkeys to navigate on open space.
I don't know what's included in the packs, but with mousequery plugin from twbt package you definitely can place constructions on open space. Edge scroll seems to work ok too but I can't be sure as I'm not using it myself, just tested a bit now.
DFHack build: 0.44.05-r2-0-g0e7ab278
Identifying DF version.
Loading hack\symbols.xml ... OK
v0.44.04 SDL win32 (windows): PE: 0x5a5687e6
v0.44.04 SDL win64 (windows): PE: 0x5a568f07
v0.44.05 SDL win32 (windows): PE: 0x5a5bb383
v0.44.05 SDL win64 (windows): PE: 0x5a5bbc62
v0.44.04 linux32 (linux): MD5: 73d7441bd0b819f9526d2ab07e4e7bf5
v0.44.04 linux64 (linux): MD5: ef636f3e494ec726d7995c1bc1423ca3
v0.44.05 linux32 (linux): MD5: 33e2bc29e7051576461ac444fdeaa191
v0.44.05 linux64 (linux): MD5: cfe90090090db079cf0ef75eda3c47c4
v0.44.04 osx32 (darwin): MD5: 4a5f7aad456d91fd073f0636cb1b2a22
v0.44.04 osx64 (darwin): MD5: bd9675557f27f4951ce3a411c371878c
v0.44.05 osx32 (darwin): MD5: a6962c195a4d7674c6e581c3609b60d8
v0.44.05 osx64 (darwin): MD5: b6a3104e7a90ed4b68f43893c5bb54c2
Loaded 12 DF symbol tables.
Unable to retrieve version information.
PE timestamp: 0x5aa2f81a
If there's a 0.44.07 release that comes out in the next day or so to fix the crashes reported in 0.44.06, we might skip support for 0.44.06, though.
General question about dfhack: what's its inner timing in regards to the game's engine inner timing?They've been direct access since DFHack moved to being in-process. I don't remember when exactly this was, but it was probably around 2009-2010.
- At what point of the game loop does DFhack read and update its properties?
- Or, if all the properties are direct access to actual value in memory, then at what stage in the game loop do dfhack scripts get executed?
- what is the liberty of the scripter to move its script execution earlier or later in the game loop?Impossible, and I don't know why you would want to. Is there something specific you're trying to do? If so, there's probably already a way to do it that doesn't require hooking into the game loop at different points.
(Is the timing different with DFHack plugins? Are there several native game events that can trigger a script execution from DFhack or do they always play at the same stage in the game loop?)Plugins have access to C++ features and can do whatever they want - they can spin off separate threads, for example, and run things whenever they want. However, the whole point of the per-tick hook is that it's safe to access game data at that point, because DF's simulation thread isn't doing anything else.
- can you do a very rough presentation of the order of computations that take place inside a native game loop?Nope. Disassembly could reveal some stuff, but it would take a lot of effort.
Again, is there something specific you're looking for? These questions seem like they have a purpose that isn't specified.Hey, long time!
Hey, long time!Welcome back!
I assume it's about disabling the native pathfinding loop (http://www.bay12forums.com/smf/index.php?topic=169806.0).YIKES.
Again, is there something specific you're looking for? These questions seem like they have a purpose that isn't specified.Hey, long time!
I assume it's about disabling the native pathfinding loop (http://www.bay12forums.com/smf/index.php?topic=169806.0).
Wow, welcome back!Hey thanks, don't get your hopes up, just checked in, heard there's something new. I see you're still hammering on the gfx. :)
Now we will get proper access to DFgraphics repo finally :DWhat do you mean? Did I not gave access to others? Sh!t. :o
Wow, welcome back!Hey thanks, don't get your hopes up, just checked in, heard there's something new. I see you're still hammering on the gfx. :)
Now we will get proper access to DFgraphics repo finally :DWhat do you mean? Did I not gave access to others? Sh!t. :o
RL. Some depressing shit happened I had to deal with, by the time it was mostly over I had some other stuff on my mind.Wow, welcome back!Hey thanks, don't get your hopes up, just checked in, heard there's something new. I see you're still hammering on the gfx. :)
Real life happened or lost interest, may I ask?Now we will get proper access to DFgraphics repo finally :DWhat do you mean? Did I not gave access to others? Sh!t. :o
AFAIK only you had full admin rights.
RL. Some depressing shit happened I had to deal with, by the time it was mostly over I had some other stuff on my mind.Wow, welcome back!Hey thanks, don't get your hopes up, just checked in, heard there's something new. I see you're still hammering on the gfx. :)
Real life happened or lost interest, may I ask?Now we will get proper access to DFgraphics repo finally :DWhat do you mean? Did I not gave access to others? Sh!t. :o
AFAIK only you had full admin rights.
And Peredexiserrant seems to have GOD mode turned on in the repo, so you did have someone to bugger after all. 8)
I'm talking about this http://www.bay12forums.com/smf/index.php?topic=155882.msg6921078#msg6921078Hmm, IDK. He is owner now. I checked the permissions, there are only two levels: member/owner. I seem to remember I did give him admin rights back then, so it beats me. :(
QuoteI assume it's about disabling the native pathfinding loop (http://www.bay12forums.com/smf/index.php?topic=169806.0).YIKES.
That's definitely something that can only be done in a plugin and through lots of reverse-engineering, if it can be done at all.
@mifki - a few posts below that I did get owner-access, and there's ~10ish people with member access. Fricy, I , and as of now Jecowa all have owner access to add new people too. Is there something you'd like to do with the repos?
(http://www.bay12forums.com/smf/index.php?topic=155882.msg6926426#msg6926426 )
Yes, adjusting a unit's path is trivial. Patching the pathfinding engine is not, depending on what you want to do.QuoteI assume it's about disabling the native pathfinding loop (http://www.bay12forums.com/smf/index.php?topic=169806.0).YIKES.
That's definitely something that can only be done in a plugin and through lots of reverse-engineering, if it can be done at all.
Relax, it might not be as bad as you think. We just need to think a little bit out of the box. By overwriting unit.path.dest, we managed to hijack the native pathfinding surprisingly easily, actually (for a very basic proof of concept). More about that on the other thread.
Now back to DFHack : the reason why I'm asking questions about the game loop sequence and when DFHack steps in, is to have a general idea of when some data is written to/from the world. The goal is to have a very vague idea of when it's (kind of) OK to read from it without a big-ass mutex, and if it's even OK to read from it at all because of any unforeseen issue (something like : gotcha, the game immediately overwrites the data you've just read because you're reading a very volatile, late game state meant only for just before frame rendering). If we'rte only trying to build some search indices from the world topology, then it's OK if it's not 100% accurate or up-to-date or real-time. And if it's definitley not OK to read anything (or set unit.path.dest) at the time DFHack runs, then my goal is to have a vague idea of when can I move (within a game loop) my processing. You have answered that part very clearly : with just a DFHack script, I can't.
So you said that with a plugin (a DFHack plugin I assume, there's not such thing as a DF plugin, correct?) I can intercept any call to virtual functions. By "intercept", do you mean detect or do you mean override? Are we talking DFHack functions or native DF functions? I can't imagine that you'd have entry points (headers???) to every native function, but then again I don't have a deep understanding of DFHack plugins. Quietust said (I think, I'm already forgetting who said this and if I'm quoting properly) that the only way to hijack the call to a native DF function is to patch the game; did I misunderstand what he said?
So if we assume that the pathfinding function is virtual and if we assume that I want to run my own code instead, written in C++ as a DFHack plugin, then what's the underlying code base? Like, would it be possible (without it being too clumsy or hard to maintain) to use stuff like C++11 and std::async ? (Don't worry about sync'ing the threads, leave that to me). Is 'std' available at all?
You are free to look into it of course, but I'm pretty sure the pathfinding engine doesn't use any virtual methods that you can hook intoOK so that settled it, no worries. We'll have to stick to our hacky method that overwrites unit.path.dest.
I'm not sure what that would achieve, assuming you're looking to improve performance, not change the path as such. Having DF first calculate the path, then calculate a path using an alternative method and then replace the original path with the hacked one is bound to be costlier. In order to replace the pathing you'd at least have to hack the DF code to produce a short fake path that your alternative can pick up, but if you've managed to do that you can just as well call your replacement code from that stub to produce a real path. Obviously, in order to do this you'd have to locate the relevant code, figure out what the parameters are (including variations), and find out what the results are (quite possibly an updated path in the unit plus some kind of return value to indicate success/failure (with possible indications of the kind of failure)).You are free to look into it of course, but I'm pretty sure the pathfinding engine doesn't use any virtual methods that you can hook intoOK so that settled it, no worries. We'll have to stick to our hacky method that overwrites unit.path.dest.
This is exactly what I meant by having DF call a custom pathfinding function (it's what I said requires C++).I'm not sure what that would achieve, assuming you're looking to improve performance, not change the path as such. Having DF first calculate the path, then calculate a path using an alternative method and then replace the original path with the hacked one is bound to be costlier. In order to replace the pathing you'd at least have to hack the DF code to produce a short fake path that your alternative can pick up, but if you've managed to do that you can just as well call your replacement code from that stub to produce a real path.You are free to look into it of course, but I'm pretty sure the pathfinding engine doesn't use any virtual methods that you can hook intoOK so that settled it, no worries. We'll have to stick to our hacky method that overwrites unit.path.dest.
Obviously, in order to do this you'd have to locate the relevant code, figure out what the parameters are (including variations), and find out what the results are (quite possibly an updated path in the unit plus some kind of return value to indicate success/failure (with possible indications of the kind of failure)).If you're just writing something that computes unit.path.dest, you probably don't need any of that.
So if we assume that the pathfinding function is virtual and if we assume that I want to run my own code instead, written in C++ as a DFHack plugin, then what's the underlying code base? Like, would it be possible (without it being too clumsy or hard to maintain) to use stuff like C++11 and std::async ? (Don't worry about sync'ing the threads, leave that to me). Is 'std' available at all?It is not virtual. There might be some virtual methods that happen to get called during pathfinding, but I doubt it, so don't count on it. You'd have to go back to the 1990s for the STL not to be available. As for DFHack's purposes, we support GCC 4.8 (and newer) and MSVC 2015 (exactly), so any C++11 stuff should be available. C++14 support is dicey, and C++17 is probably not something you can rely on.
C++ 11
When I used digtype on gold nuggets in the latest alpha build my game crashed. Its happened twice so I've stopped using it. Anything I can do to help with this report?I couldn't reproduce this. I designated a native gold vein for digging, gave the command and the dwarves started digging for me. How fast came the crash for you? Immediately, or a bit later?
When I used digtype on gold nuggets in the latest alpha build my game crashed. Its happened twice so I've stopped using it. Anything I can do to help with this report?I couldn't reproduce this. I designated a native gold vein for digging, gave the command and the dwarves started digging for me. How fast came the crash for you? Immediately, or a bit later?
For what it's worth I have to mention that I did use the 3dveins command right after embark on this map.
dfhack 44.07-a1, twbt, no raw changes.
Grown wood was recently discovered to be sorted as a exclusively non-plant/animal object when sorted by stockpiles, as well as its further products (cages, goods, logs, armor, training weapons) when produced by elves with the occasional bug where the objects are mis-represented, would it be at all possible to identify the boolean as mentioned by Toady and apply the value and description onto objects at will? I may just be behind in my knowledge of the GM-editor's new and/or forgetting its current functions though since i haven't used df-hack since 43.05item_flags2.grown?
I would think it opens possibilities somewhere for people who want to pursue using these objects.
Can artifact-quality level items be generated with dfhack (looks like create-item can support only masterwork quality level)? If not, is it due to the need to write artifact item into the history of the world?Without knowing what the script looks like, artifacts indeed require extra efforts, such as creating an artifact record for them and enter that into the artifact structure. You'd also need to set the artifact flag in the item, but that's trivial. I don't know if you also have to enter an entry about its creation in the history. Someone has made some kind of artifake mod, which you may want to look at (although that still doesn't guarantee what's done there is "complete").
-- Makes it so that the world will always have certain artifacts in certain sites when world loads.
--@ module = true
--Author Putnam
local usage = [===[
modtools/custom-artifact
=====================
This tool, when run, checks if the specific item has an artifact record somewhere in the world
and places the artifact at a valid site (which can be constrained by arguments) if it is not found.
Arguments::
-itemType type
the type of item that will have an artifact spawned
examples:
WEAPON:ITEM_WEAPON_PICK
RING
-itemMat mat
the material of the newly generated item
examples:
INORGANIC:IRON
CREATURE_MAT:DWARF:BRAIN
PLANT_MAT:MUSHROOM_HELMET_PLUMP:DRINK
-name name
the item's name
should be set, will just go with no name if not
-amount num
the minimum artifacts required
will continue to create new artifacts until this amount is reached
this argument is optional and will default to 1
-ignoreExisting
will create (num) artifacts named (name) regardless of existing artifacts of that type
do not use in onload scripts, will overrun world with that artifact type given enough reloads
-specificEntityType [ entity1 entity2 ... ]
entity filter, will only spawn in these entity's sites
examples:
MOUNTAIN
EVIL
FOREST
PLAINS
-specificSiteType [ site1 site2 ... ]
site filter, will only spawn in these site types
examples:
CAVE_DETAILED
DARK_FORTRESS
TREE_CITY
CITY
-help
displays this message
]===]
local utils=require('utils')
validArgs = utils.invert({
'itemType',
'amount',
'itemMat',
'specificEntityType',
'specificSiteType',
'ignoreExisting',
'name',
'help'
})
function getItemType(item) --just kinda grabbed this from item-trigger, like the help dialogue above
local itemType
if item:getSubtype() ~= -1 and dfhack.items.getSubtypeDef(item:getType(),item:getSubtype()) then
itemType = df.item_type[item:getType()]..':'..dfhack.items.getSubtypeDef(item:getType(),item:getSubtype()).id
else
itemType = df.item_type[item:getType()]
end
return itemType
end
function mysplit(inputstr, sep) --https://stackoverflow.com/questions/1426954/split-string-in-lua
if sep == nil then
sep = "%s"
end
local t={} ; i=1
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
t[i] = str
i = i + 1
end
return t
end
local function findTranslation(str)
for k,v in ipairs(df.global.world.raws.language.translations) do
if v.name==str then return k,v end
end
return false
end
function createArtifact(itemType,itemSubtype,name,material,entityFilter,siteFilter)
local newArtifact = df.artifact_record:new()
newArtifact.id=df.global.artifact_next_id
local rng=dfhack.random.new(os.time(),df.global.artifact_next_id)
local eligibleSites={}
for _,site in ipairs(df.global.world.world_data.sites) do
if site.civ_id~=-1 then
local thisSiteType=df.site_type[site.type]
if thisSiteType and (not entityFilter or entityFilter[df.historical_entity.find(site.civ_id).entity_raw.code]) and (not siteFilter or siteFilter[thisSiteType]) then
table.insert(eligibleSites,site.id)
end
end
end
local pickedSite=eligibleSites[rng:random(#eligibleSites)+1]
newArtifact.anon_1=-1000000 --TODO: REPLACE REPLACE REPLACE REPLACE REPLACE ALL OF THESE ANON IS BAD
newArtifact.anon_2=-1000000
newArtifact.anon_3=-1000000
newArtifact.anon_4=pickedSite
newArtifact.anon_5=1
newArtifact.anon_6=-1
newArtifact.anon_7=-1
newArtifact.anon_8=-1
newArtifact.anon_12=pickedSite
newArtifact.anon_13=1
newArtifact.anon_14=-1
newArtifact.anon_15=-1
newArtifact.anon_16=-1
newArtifact.anon_17=250
newArtifact.anon_18=0
newArtifact.anon_19=3
if name then
newArtifact.name.has_name=true
newArtifact.name.first_name=name
end
local newUnit = df.unit:new() --temp boi
newUnit.pos.x=df.global.world.units.active[0].pos.x
newUnit.pos.y=df.global.world.units.active[0].pos.y
newUnit.pos.z=df.global.world.units.active[0].pos.z
local newItem = df.item.find(dfhack.items.createItem(itemType,itemSubtype,material.type,material.index,newUnit))
newUnit:delete()
newUnit=nil
newItem.id=df.global.item_next_id
newItem.pos.x=-30000
newItem.pos.y=-30000
newItem.pos.z=-30000
local artifactRef=df.general_ref_is_artifactst:new()
artifactRef.artifact_id=newArtifact.id
newItem.general_refs:insert('#',artifactRef)
newItem.flags.artifact=true
newItem.flags.artifact_mood=true
newItem.maker=-1
newItem.masterpiece_event=-1
newItem.quality=df.item_quality.Artifact
newArtifact.item=newItem
local newEvent=df.history_event_artifact_storedst:new()
newEvent.artifact=newArtifact.id
newEvent.unit=-1
newEvent.histfig=-1
newEvent.site=pickedSite
df.global.world.history.events:insert('#',newEvent)
df.global.world.artifacts.all:insert('#',newArtifact)
df.world_site.find(pickedSite).artifacts:insert('#',newArtifact)
df.global.artifact_next_id=df.global.artifact_next_id+1
end
if moduleMode then
return
end
local args = utils.processArgs({...}, validArgs)
if args.help or not args.itemType or not args.itemMat then
print(usage)
return
end
local itemType,itemSubtype
local itemTypeSplit=mysplit(args.itemType,':')
local itemTypeStr,itemSubtypeStr=itemTypeSplit[1],itemTypeSplit[2]
itemType=df.item_type[itemTypeStr]
if not itemType then
qerror('Could not find item type: '..args.itemType)
end
if #itemTypeSplit>1 then
local temp
for _,itemdef in ipairs(df.global.world.raws.itemdefs.all) do
if itemdef.id == itemSubtypeStr then
itemSubtype=itemdef.subtype
break
end
end
if not itemSubtype then
qerror('Could not find item type: '..args.itemType)
end
end
args.amount=args.amount and tonumber(args.amount) or 1
local totalArtifactsOfItemType=0
if not args.ignoreExisting then
for k,v in ipairs(df.global.world.artifacts.all) do
if getItemType(v.item)==args.itemType then
totalArtifactsOfItemType=totalArtifactsOfItemType+1
end
end
end
local itemMat=args.itemMat and dfhack.matinfo.find(args.itemMat) or false
if totalArtifactsOfItemType<args.amount then
for i=totalArtifactsOfItemType,args.amount-1,1 do
createArtifact(itemType,itemSubtype,args.name,itemMat,args.specificEntityType and utils.invert(args.specificEntityType),args.specificSiteType and utils.invert(args.specificSiteType))
end
end
Sorry. I checked for pull requests but not issues, so I missed it.The reason I made an issue rather than a pull request was that I had made a few other changes I wanted comments on before creating a pull request...
Edit: merged the flags2.killed change, and confirmed that it no longer affects a test caravan in ways that it shouldn't.
I drafted woodcutter in squad to equip leather armor, he won't cut trees afterwards. After killing target dorf gets stuck on a tile, I teleported him somewhere else, but he still won't move. Retire/unretire - weapons are not in equipment list now.Not at all. It's caused by the known vanilla issue of civilian jobs with "uniform" tools (hunter/miner/wood cutter) having the civilian uniform conflicting with the military one. In practice, this means you cannot have these jobs active while wearing a military uniform, as part of the uniform is a military weapon (which may be of the same type, but still has to be a different object). Don't draft dorfs with these jobs or you're going to have "issues".
How likely these problems are caused by alpha dfhack?
Understandable, but usual forbid/dump/cleanowned didn't help for some reason. Other bugs are new to me though.Is there a reason why you expected forbid/dump/cleanowned to solve that problem?
it's nothing new from our perspective.
Other bugs are new to me though.I specifically didn't equip axes, dwarf dropped axe, picked weapon and then never picked axe again.
The woodcutter's axe is part of the WOODCUTTER uniform. This uniform is REPLACED by the MILITIA uniform (which may lack weapons defined, but that doesn't matter) when the woodcutter is drafted. Once you remove the bugger from the militia an axe for wood cutting purposes will be picked up (assuming one is available, if the one used previously is rendered unavailable by being part of the uniform for a currently unfilled militia slot). This is a vanilla DF issue.it's nothing new from our perspective.Other bugs are new to me though.I specifically didn't equip axes, dwarf dropped axe, picked weapon and then never picked axe again.
I was just wondering if hack's bugfixing scripts could affect these things.
I am attempting to make an adventurer a full citizen of a fort after retire and reclaim.you should be able to cross reference an Adventurer who starts from the fort for the adventurer's who don't but really it just aligning the Citizen's id (the unit's Civ, Population, and probably group ids to match with the starting citizen's ids everything else seems to be not really needed) with the rest of the fort once your in Fort mode.
It looks like I am able to do that using the script gui/gm-editor
I found that the necessary step is to add a reference to the former adventurere's historical figure id in histfig_ids on the historical_entity for the local group.
That seems to work, however I am concerned about later crashes, as I cannot figure out how to add a reference to the historical figure object in the hist_figures array. Will it be a problem to skip that step?
Secondly, it seems like the nemesis ids may be related, but I can't figure out what a nemesis is.
Any other things I should look out for?
Thanks for your help.
you should be able to cross reference an Adventurer who starts from the fort for the adventurer's who don't but really it just aligning the Citizen's id (the unit's Civ, Population, and probably group ids to match with the starting citizen's ids everything else seems to be not really needed) with the rest of the fort once your in Fort mode.
I do remember someone making a script that converts units to your Fort might be Called Makeown.
seen this happen when I modded the game to give me All general Poison classed creatures and the end result made it so Night trolls only attack if someone arms themselves.
now on the Dragon problem I have no clue, outside of Dfhack the dragon to plop out eggs(using an empregnate script) then editing the eggs with Gm-editor to be fertile and pray the mother doesn't try to eat her kids in the process.
if your already modding the dragons then probably scan through their raws to see if any token is causing them to attack, or if Breathing fire is a Greeting.
you probably should have set the Civid to the fort to skip the step and headache of training them as that might be the fault.
i just found that every last unworn bit of adamantine cloth and armor in my game has f* rotted down to "xXadamantine cloakxX" inside of 19.5 months.Could be the new item damage mechanics. Armor takes damage in combat, and dwarves will swap them out for new ones.
Could be the new item damage mechanics. Armor takes damage in combat, and dwarves will swap them out for new ones.
no combat. it was never worn, stored in a locked 3x3.You didn't have refuse enabled on the stockpile, did you?
it rotted worse than the clothing it was intended to replace.
A quick update on this, I've found that adding the references to the nemesis/hist fig entries is not necessary, since loading from a save synchronizes them. Working on figuring out how to perform same from a script. when I do I'll submit a pull request. If anyone can point me to documentation of the data model exposed to scripts in dfhack, that would be _extremely_ helpful.you should be able to cross reference an Adventurer who starts from the fort for the adventurer's who don't but really it just aligning the Citizen's id (the unit's Civ, Population, and probably group ids to match with the starting citizen's ids everything else seems to be not really needed) with the rest of the fort once your in Fort mode.
I do remember someone making a script that converts units to your Fort might be Called Makeown.
Thanks. Googling found the makeown command in the tweak plugin, but that is not sufficient to make my adventurer accept labor assignments. (it sets the civ and some flags on the unit - resident, etc) I suspect something has changed in the game that renders that command irrelevant. Based on a hint in the forums I found what I posted previously, which does work, however I'm not sure if I'm just missing something that is not completely working. If anyone can tell me whether it's possible to change an existing reference using gui/gm-editor, that would be helpful.
edit: I just had an epiphany what a nemesis is: it is the corresponding unit id for the historical figure... not sure why anything worked without it, but hey.
I am having a few problems with modtools/create-unit. I am trying to use it in an old mod I am updating to the latest DF version, but I have reproduced the problems in a vanilla .05 save moved to both DF .07/DFHack .07-alpha1 and DF .09/DFhack .09-alpha1. I could *not* reproduce some of them in a fresh embark on a fresh .09 install, but all I did there was create a 5 year pocket world and spawn dogs next to the starting wagon. All of this is 64bit win 7, by the way.Spoiler: details (click to show/hide)
Numbering for units without a name given is broken. Most of the time, the number that the creature displays is a very large, sometimes negative number. Further spawned units of that race increment the number as expected. Occasionally, the number starts at 1 as expected. Given that one time I saw an opossum 5(the first of its kind spawned) immediately after I just spawned a coati 5 (the 5th of its kind), I would guess that the variable for unit numbering isn't being initialized and the code branch that deals with numbering/naming creatures who are the first of their kind isn't executing or is missing the code to assign it to 1. Either that or there is a bad pointer address/pointer math somewhere (don't know whether the code for it is in C or Lua).What mode are you in (fortress, arena, adventure)? The initialization behavior varies between them. I thought I had fixed it in one, but it might have broken in another.
The -domesticate flag throws an error, and units with it but not -setUnitToFort are friendly, not fort pets (the latter may be intended behavior, I'm not sure). Here is the stack trace:To script authors: this is why you NEVER use "anon" variables. Submit a PR to give them names!
...FE temp\DFFE 14.04/hack/scripts/modtools/create-unit.lua:344: Cannot write field unit.T_enemy.anon_4: not found.
stack traceback:
[C]: in metamethod '__newindex'
...FE temp\DFFE 14.04/hack/scripts/modtools/create-unit.lua:344: in global 'domesticate'
...FE temp\DFFE 14.04/hack/scripts/modtools/create-unit.lua:537: in local 'script_code'
...y\Desktop\games\DFFE temp\DFFE 14.04\hack\lua\dfhack.lua:562: in function 'dfhack.run_script_with_env'
(...tail calls...)
I am having a few problems with modtools/create-unit. I am trying to use it in an old mod I am updating to the latest DF version, but I have reproduced the problems in a vanilla .05 save moved to both DF .07/DFHack .07-alpha1 and DF .09/DFhack .09-alpha1. I could *not* reproduce some of them in a fresh embark on a fresh .09 install, but all I did there was create a 5 year pocket world and spawn dogs next to the starting wagon. All of this is 64bit win 7, by the way.Spoiler: details (click to show/hide)
Thank you for the testing, this give me some new theories about what causes new units to be hostile for a few frames upon spawn. I believe that it was caused by DF's enemy cache but it seems that any units keep their wild behavior as a predator, prey or curious beast until DF goes and refresh their AI. Is there a "wild animal" cache somewhere?
I need to replace some hacks in create-unit. Meanwhile, if you open the script and remove the three anon_x lines at line 344, create-unit may work again but with strange effects.
What mode are you in (fortress, arena, adventure)? The initialization behavior varies between them. I thought I had fixed it in one, but it might have broken in another.All the create-unit stuff I have been doing is in fortress mode.
To script authors: this is why you NEVER use "anon" variables. Submit a PR to give them names!
Boltgun: either they were given names or some other unnamed fields were given names. I'll try to investigate what the right names should be.
Edit: fixed: https://github.com/DFHack/scripts/commit/96b118b2e7fe8d91b59e0cc1c7951bf27a5e6698
Can you try replacing the 3 lines with the fixed ones I linked instead of deleting them? I doubt they have much to do with your issue, but I want to be safe.Ok, getting out the fresh vanilla 5 year pocket embark and testing this a bit (.09+dfhack .09, 64 bit win7).
"spawnunit CAT MALE" (which uses modtools/create-unit) produced a cat called "Cat 1" in fortress mode for me, and I'm using a tool that initializes all memory to 0xAA, so if the ID weren't initialized, it should have been a large negative number. Can you provide a specific command that produces a badly-numbered unit?
What's the deal with all the "exterminate" command's pronouns? Options include "him", "her", and "it" / "that". The script will print a message if you choose wrongly.The only reason I can see for the rejection is if it's the wrong target (e.g. two units at the same tile). However, there's only a 50% chance of it catching an error at the cost of annoyance when you just want to get rid of a bugger without examine it's gender first.
Seems like unnecessary confusion for something that isn't at all relevant to the function. Why not just "exterminate target" or "exterminate selected"?
It doesn't "catch" anything - it always kills the selected unit. It just points out that you got the gender wrong.Hm, I was fairly sure I've had to change the gender in the command and try again when it said it was wrong. Ah well.
Agreed. I'll add those and get rid of the message.You included "this" as one of the alternatives to "this" in the description. :P
Is it possible to add a hotkey for changevein, similar to the one that already exists for digv ?Yes, use the keybinding command. That's how the one for digv is set up - take a look at dfhack.init.
I like the main parts of my fortress having same wall and floor colours, so I use the changevein feature quite often to get rid of ore/gems. And while it's working perfectly, it might be something I do 50 - 100 times.
So I'm just wondering if there is a possible way to avoid bringing up the dfhack window, type the command, then switch back to df.
I'm trying to figure out, is there any way to create aquifers? I was trying to use tiletype but its not that intuitive to figure out.An adjusted version of the drain-aquifer script might work. Getting it to show up just where you want would be tricky. It looks like it uses the "water_table" tile flag, and I'm not sure if tiletypes can change that.
Is it possible to add a hotkey for changevein, similar to the one that already exists for digv ?
I like the main parts of my fortress having same wall and floor colours, so I use the changevein feature quite often to get rid of ore/gems. And while it's working perfectly, it might be something I do 50 - 100 times.
So I'm just wondering if there is a possible way to avoid bringing up the dfhack window, type the command, then switch back to df.
Yes, use the keybinding command. That's how the one for digv is set up - take a look at dfhack.init.
Tried adding this to dfhack.init.Quote##############################
# Generic dwarfmode bindings #
##############################
# show all current key bindings
keybinding add Ctrl-F1 hotkeys
keybinding add Alt-F1 hotkeys
# toggle the display of water level as 1-7 tiles
keybinding add Ctrl-W twaterlvl
# with cursor:
# designate the whole vein for digging
keybinding add Ctrl-V digv
keybinding add Ctrl-Shift-V "digv x"
# change vein to specified inorganic stonetype
keybinding add Ctrl-Z changevein SHALE
But getting thisQuote
Syntax: changevein < inorganic material ID >
It's probably something really basic missing, so it not getting the SHALE part.
What am I doing wrong ?
I'm having some smaller questions about scripting.
1. Is there a way to get the year of embark? You can get the current year, but I didn't find a way to check how many years the fort already exist
2. How can I get the 'name' of a creature that doesn't have a normal name?
3. How can I get the name of my fort?
Thanks in advance! :D
fortressAgeTicks = df.global.ui.fortress_age*10
fortressAgeDays = df.global.ui.fortress_age*10/1200
fortressAgeYears = df.global.ui.fortress_age*10/403200
creatureName = df.creature_raw.find(unit.race).caste[unit.caste].caste_name[0]
fortressName = dfhack.TranslateName(df.world_site.find(df.global.ui.site_id).name)
fortressNameInEnglish = dfhack.TranslateName(df.world_site.find(df.global.ui.site_id).name,true)
I'm working on a mod that spawns a bunch of creatures of a custom race using DFHack, and I was hoping I could keep the creatures from fighting each other, while allowing them to fight other units. Does anyone know if there's some way to do this, either via raw editing or dfhack scripting?
Edit: using [OPPOSED_TO_LIFE] + [NOT_LIVING] works, but it's not ideal, because I'd prefer my creatures be animals instead of undead. i could probably also make an entity for the creatures i'm making, but again, i'd prefer for them to behave like animals instead.
Edit 2: actually i was wrong, [OPPOSED_TO_LIFE] + [NOT_LIVING] doesn't actually work :/
Hi, can someone help me with tiletypes? I have successfully changed some tiles to undiscovered and to subterranean, but I cant seem to make a working aquifer.
I used "paint aquifer 1", then range 10:10:1, then enter. The soil shows as damp now. But when I dig into it, it doesnt produce any water.
Hi, can someone help me with tiletypes? I have successfully changed some tiles to undiscovered and to subterranean, but I cant seem to make a working aquifer.
I used "paint aquifer 1", then range 10:10:1, then enter. The soil shows as damp now. But when I dig into it, it doesnt produce any water.
if df.global.cursor.x < 0 then qerror('The cursor must first be placed on the screen!') end
local blockFlags = dfhack.maps.getTileBlock(pos2xyz(df.global.cursor)).flags
blockFlags.update_liquid = true
blockFlags.update_liquid_twice = true
blockFlags.update_temperature = true
blockFlags.designated = true
blockFlags.has_aquifer = true
blockFlags.check_aquifer = true
Plugin twbt was not built for this version of DFHack.
Plugin: (something), DFHack: 0.44.09-r1
1. Dwarf Therapist...I tried dwarf therapist...
for 2 you just want to use dfhack.units.isCitizen(unit), which checks if the unit's group_id is the fort's group_id and checks a whole lot of flags as well. (https://github.com/DFHack/dfhack/blob/48a61420b779607e2a46e084675516c769c46d7f/library/modules/Units.cpp#L437)Ooh, that looks useful. It doesn't return true for mercenaries though, which is a little bit of a problem. However, everything there I can port over to lua and modify, so if I can adjust that to handle mercs and such then I am fine. I did some poking around and found that AFAICT long term residents never have those flags, but also are never listed as members under the player's group_id in any of their historical figure entity links, and they may or may not be linked to the player's group_id entity at all*. The common link between all of the fort owned units appears to be the appropriate civ_id plus none of those flags. Also, switching the civ_id and visitor flag on visitors or switching the civ_id on wandering wildlife lists them as civ members/tame pets (but with the interface behaving as if they are long term residents), so I guess that no flags + civ_id is the thing I am looking for.
*as an aside, how do you get the full list of what is in df.histfig_entity_link_type? I can pull out numbers for specific types like MERCENARY and MEMBER, but I can't figure out how to iterate through and see all the types and their corresponding IDs or otherwise figure out what other IDs are. Maybe I suck at searching, but I also can't find where this stuff is defined in the git repo. I don't need to know it (I think), but it would be nice to know for future reference.Use ipairs(), not pairs(). In the Lua interpreter, "@" is shorthand for printall_ipairs() (which is like printall() but uses ipairs()):
@df.histfig_entity_link_type
There are other prefix characters you can use that are listed in the banner when you enter the Lua interpreter for the first time.Furthermore, I make the assumption water freezes when the temperature is 0 degrees Celsius and thaws at a temperature above that [as freezing/thawing is what I'm really after in the end], so if it's know to behave differently, I'd like to know.I'm pretty sure it thaws at 0 °C and freezes when it drops just below that. In other words, nether cap won't freeze water.
Thanks for the freezing info. That's useful to know.Furthermore, I make the assumption water freezes when the temperature is 0 degrees Celsius and thaws at a temperature above that [as freezing/thawing is what I'm really after in the end], so if it's know to behave differently, I'd like to know.I'm pretty sure it thaws at 0 °C and freezes when it drops just below that. In other words, nether cap won't freeze water.
As for teleporting an adventurer around, that sounds like something mapping utilities like Isoworld might do.
The entire purpose of the alpha builds is for you to report things like that. I will try to look into it. Does "stocks show" on its own also crash? Are you sure you're holding down the right modifier keys (not forgetting or missing shift/Ctrl)? What screen are you pressing ctrl-shift-z on?
Turns out there was another cause to the "v" crash too. I got it to happen once in normal gameplay, but I couldn't reproduce it afterwards without breaking things intentionally, but it should be addressed now too.
If all goes to plan, binaries for https://github.com/DFHack/dfhack/releases/tag/0.44.10-beta1 will be uploaded in an hour or two, and it should fix that issue (and others!).
Is there a plan to add any mission related plugins or fixes? Or may be there are some (i'm mostly interested in fixes for squads leaving on a mission forever)Toady is actively working on fixes, and he has an advantage when it comes to fixing complicated things like that (we barely have any information on missions, as far as I know), so no, nobody has tried fixing those in DFHack yet.
a solution to this might be to insert the missing units back into a squad that is already returning from a completed mission.Is there a plan to add any mission related plugins or fixes? Or may be there are some (i'm mostly interested in fixes for squads leaving on a mission forever)Toady is actively working on fixes, and he has an advantage when it comes to fixing complicated things like that (we barely have any information on missions, as far as I know), so no, nobody has tried fixing those in DFHack yet.
I've been using workflow for my clothing industry for a while now but there are 2 things I cannot make work :I added your buildingplan stuff to the issue tracker (https://github.com/DFHack/dfhack/issues) but I'm not sure how to handle the workflow stuff. When you get a chance, could you look through the workflow-tagged issues (https://github.com/DFHack/dfhack/issues?utf8=%E2%9C%93&q=label%3Aworkflow+), make sure yours aren't already listed, then report them if they're not?
1- If I want to process plants into thread with a separate constraint for each.
Say I want 50-60 threads of pig tail and 50-60 threads of hemp, I cannot make it work even using the advanced options and selecting a specific plant. I get a message telling me it's not possible.
If I do the thread of any plant then there is no issue.
2-Say I want 50-60 cloth shirts and 10-20 cloth shirts in human size, I can make both on the workshop but when I create a worflow constraint it doesn't recognize they're different so if my dwarven sized shirt limit is reached it will stop the human sized one, I've found no way to add the size as an option even in advanced mode, I can just select quality and material.
I apparently didn't remember to update my last post, but I did fix it, so it should work in whatever the next version ends up being.I've been using workflow for my clothing industry for a while now but there are 2 things I cannot make work :I added your buildingplan stuff to the issue tracker (https://github.com/DFHack/dfhack/issues) but I'm not sure how to handle the workflow stuff. When you get a chance, could you look through the workflow-tagged issues (https://github.com/DFHack/dfhack/issues?utf8=%E2%9C%93&q=label%3Aworkflow+), make sure yours aren't already listed, then report them if they're not?
1- If I want to process plants into thread with a separate constraint for each.
Say I want 50-60 threads of pig tail and 50-60 threads of hemp, I cannot make it work even using the advanced options and selecting a specific plant. I get a message telling me it's not possible.
If I do the thread of any plant then there is no issue.
2-Say I want 50-60 cloth shirts and 10-20 cloth shirts in human size, I can make both on the workshop but when I create a worflow constraint it doesn't recognize they're different so if my dwarven sized shirt limit is reached it will stop the human sized one, I've found no way to add the size as an option even in advanced mode, I can just select quality and material.
Is there any instructions on building a C++ application with cmake using dfhack-client, particularly RemoteClient. I did not find anything in the doc and I have an issue with compatibility between dfhack's protobuf and my system protobuf.DFHack uses an older protobuf 2, so you might have to specify that in your proto files. You could also probably use DFHack's protobuf compiler, which gets built along with DFHack. I'd need more details about the compatibility issue for anything else.
Is there a way to show magma or water being present in caverns from the embark screen? Been using embark-assistant so I know that there will be magma in a certain range but I don't know which tile it's present in which makes it harder for me to determine where exactly I want to embark.You *could* use embark-assistant to look for single tile embarks with magma pipes as a rather heavy handed way to locate which tile the pipe is in. Apart from that, I don't think there are any ready made scripts for it, so you'd have to roll your own.
Is there a way to show magma or water being present in caverns from the embark screen? Been using embark-assistant so I know that there will be magma in a certain range but I don't know which tile it's present in which makes it harder for me to determine where exactly I want to embark.You *could* use embark-assistant to look for single tile embarks with magma pipes as a rather heavy handed way to locate which tile the pipe is in. Apart from that, I don't think there are any ready made scripts for it, so you'd have to roll your own.
When it comes to cavern water I don't know if there is a way to determine if water will appear (apart from embarking and looking at what DF generated, discard the probe embarks until a desirable one is found and then embark "properly"). There is a parameter for the probability of water being present, and I know water will not be present if it's below 10. However, there is no value that guarantees water will be present.
You don't need to teleport an adventurer around for Isoworld, as far as I can determine (I'm not familiar with it, so I've just taken a quick look), but it looks like it uses DF exported maps for starters, and except for site maps, those maps only go down to the mid tile level, not the in-game one, and for that level you can just use pre embark manipulation to shift focus.Turns out it was related to a script Bearskie uses to stitch together whole maps. He recently mentioned it in the IsoWorld thread, but I'm not sure if he's posted it anywhere.
DFHack 64-bit GCC 7.3.0 seems to work fine in MacOS X 10.6, 10.7, and 10.12. I don't really know what to test or look for exactly. Createitem, Reveal, Unreveal, Mousequery, Autolabor, and Labormanager commands all seem to work. Then I tried installing TWBT on Lion and Sierra and the main feature and multilevel both seem to work.That's basically all I need to be confident that it works across OS X versions - thanks!
Does GCC do anything cool for us? I think you told me before, but I forgot.
Is there a way to generate a random dwarf ex nihilo or force migration waves or something similar? I'd like to check preferences of a large number of dwarves as generated by the game."force migrants" or maybe "migrants-now" (not sure why we have two...)
this won't change location of all files, but only of those which can be (by design) installed onto different location. Be warned that there is many shared components which will be installed into shared repositories on drive C: without any possibility to change their path.
We have no choice as to what compiler to allow on Windows. You must use the Visual Studio 2015 compiler, because that's what DF uses, and if DFHack is built with a different compiler than DF, it will either fail to compile or crash on startup. Even if we wanted to allow other compilers, it's simply not possible.
We aren't opposed to other IDEs, though, and if you can get Eclipse to work with the VS2015 compiler, great. If there's a way we can adjust the CMake configuration to support that, or document the process somehow, that's even better. I don't know of anyone that has done that yet, but it might be possible.
Eclipse does support C++, but I don't know how to get it to use the VS2015 compiler. Or where to find said compiler. As you said, it might be possible, but I have no idea how to do it.I linked to it in your last thread (http://www.bay12forums.com/smf/index.php?topic=170687.msg7763923#msg7763923): https://www.visualstudio.com/vs/older-downloads/#microsoft-build-tools-2015-update-3
However, I came to this thread for completely unrelated reason. DFHack includes a script extra-gamelog.lua. This script, after slight modification, allows me to log when siege is present on gameload, which I need for SoundSense, and this part works great. But, if I see correctly, this script also should log events like workshop completion, mayor election (different than vanilla DF), and most importantly to me siege end. It seems it doesn't do it by default. Anyone knows how to trigger logging of these items?Not sure why it would only log sieges. You're only running the script once, right?
I linked to it in your last thread
However, I came to this thread for completely unrelated reason. DFHack includes a script extra-gamelog.lua. This script, after slight modification, allows me to log when siege is present on gameload, which I need for SoundSense, and this part works great. But, if I see correctly, this script also should log events like workshop completion, mayor election (different than vanilla DF), and most importantly to me siege end. It seems it doesn't do it by default. Anyone knows how to trigger logging of these items?Not sure why it would only log sieges. You're only running the script once, right?
I assumed that if it is enabled in the *.init, it runs in loop, constantly. But I get only the "onload" part of the logs from the srcipt (like season info), not the "loop" part (like workshop completion). Maybe my assumption was wrong. Does it have to be enabled in special way to get the loop/repeated part?Which "*.init" exactly is it enabled in? Is it running "modtools/extra-gamelog enable"? (The "enable" part is important.) Have you unloaded and loaded a world before noticing this issue?
Well, again, I don't know for sure if that's enough. BenLubar has had luck compiling for Windows on Linux, though, so I assume he knows what works. Not sure how closely he follows this thread, but if you have questions, he's usually around on IRC/GitHub.I assumed that if it is enabled in the *.init, it runs in loop, constantly. But I get only the "onload" part of the logs from the srcipt (like season info), not the "loop" part (like workshop completion). Maybe my assumption was wrong. Does it have to be enabled in special way to get the loop/repeated part?Which "*.init" exactly is it enabled in? Is it running "modtools/extra-gamelog enable"? (The "enable" part is important.) Have you unloaded and loaded a world before noticing this issue?
Edit: reported several issues I identified at https://github.com/DFHack/dfhack/issues/1287
modtools/extra-gamelog enable
"force migrants" or maybe "migrants-now" (not sure why we have two...)
Note that the criteria for limiting migrants do apply, so you might only get 2 waves worth of migrants before no more arrive for the rest of the season.
EDIT: Oh, I see, these are issues I'm talking about.What does "changed event_loop() to global" mean? I didn't say anything about that, and event_loop() having the local keyword doesn't have anything to do with the issues I mentioned.
EDIT2: I moved call of event_loop() to log_on_load(), and changed event_loop() to global (after looking up how to do it, I didn't know LUA conventions). This causes the siege info to work as intended (I think), causes mayor info to work (though I'm not sure if as intended, because it is logged every time the fort is loaded), and the building info still doesn't work. But honestly, the only thing I personally needed was siege info for SoundSense, so I'm OK. Thanks for help.
If you install VS on a drive different from C: you can't use a number of the scripts as the are, as they're hard coded to C:, and if you change them to work you have to copy them out and replace them with the original for the blasted git to allow you to upload anything (it won't accept your local modifications staying local: you either have to commit them [which trashes the original if accepted], or delete the remove).You should absolutely be able to change the build scripts and avoid committing them without Git complaining. The only time Git would complain is if the build scripts also change in DFHack's repo, and that happens rarely and can be worked around without you having to get rid of (or commit) your local changes.
I've also found that I can't set up stonesense for compilation because the setup gui script uses a symbol that still points to C: (which I can't change without old stuff installed there breaking). A clean install of the computer and applications where everything goes to D: might work, unless you have stuff that's still hard coded for C:. Fortunately, I don't really care about compiling stonesense (Only tried it because of issues that turned out to be due it internal DF git setup upgrades at the same time).
Nothing better than that? thistleknot (http://www.bay12forums.com/smf/index.php?topic=168411.msg7711270#msg7711270) used attribute data from 5000 dwarves. I would be interested for something on this scale, but I don't know how it was done.You didn't say you wanted 5000 of them - the normal criteria for migrants apply to those scripts, so they won't work if it's too early (as well as if you have too many recent migrants). Thistleknot says he used 5000 "generated" dwarves, which could be from the arena or using spawnunit or modtools/create-unit (which use the arena unit-creation code). I don't know if the variety distribution of migrants is any different, but I can't think of a way to come up with 5000 dwarves otherwise without ~100+ fortresses.
Nothing happens when I type these commands after a fresh embark. Does it need time or a minimum wealth before migrants appear?
Nothing better than that? thistleknot (http://www.bay12forums.com/smf/index.php?topic=168411.msg7711270#msg7711270) used attribute data from 5000 dwarves. I would be interested for something on this scale, but I don't know how it was done.
local spawnNumber = 10 -- replace this with the number of dwarves you want to spawn
local x,y,z = pos2xyz(df.global.cursor)
if not z then
qerror('First place your cursor on the screen to choose a spawn location!')
end
for n = 1,spawnNumber do
dfhack.run_command("modtools/create-unit -race DWARF -caste MALE -name MOUNTAIN -setUnitToFort -location [ "..x.." "..y.." "..z.." ]")
end
EDIT: Oh, I see, these are issues I'm talking about.What does "changed event_loop() to global" mean? I didn't say anything about that, and event_loop() having the local keyword doesn't have anything to do with the issues I mentioned.
EDIT2: I moved call of event_loop() to log_on_load(), and changed event_loop() to global (after looking up how to do it, I didn't know LUA conventions). This causes the siege info to work as intended (I think), causes mayor info to work (though I'm not sure if as intended, because it is logged every time the fort is loaded), and the building info still doesn't work. But honestly, the only thing I personally needed was siege info for SoundSense, so I'm OK. Thanks for help.
local function event_loop()
...ames\DFv44.10-64/hack/scripts/modtools/extra-gamelog.lua:67: attempt to call a nil value (global 'event_loop')
stack traceback:
...ames\DFv44.10-64/hack/scripts/modtools/extra-gamelog.lua:67: in function <...ames\DFv44.10-64/hack/scripts/modtools/extra-gamelog.lua:13>
I had to remove "local" from lineOh, okay, that's because of Lua's scoping rules - inside functions, you can only access things that are global (without the "local" keyword), or things that are local and defined before the current function. event_loop is defined near the end of the script, so that makes sense. Thanks - that'll be good to keep in mind for whoever fixes the script.Code: [Select]local function event_loop()
Otherwise I get DFHack error:Quote...ames\DFv44.10-64/hack/scripts/modtools/extra-gamelog.lua:67: attempt to call a nil value (global 'event_loop')
stack traceback:
...ames\DFv44.10-64/hack/scripts/modtools/extra-gamelog.lua:67: in function <...ames\DFv44.10-64/hack/scripts/modtools/extra-gamelog.lua:13>
Atomic Chicken: use "for n = 1, spawnNumber", not the stuff with the while loop. I agree that a quantity flag would be useful (like modtools/create-item), and was thinking of adding one but never got around to it.
To be fair, while loops aren't always bad, of course - I'm a bit paranoid after https://github.com/DFHack/dfhack/issues/1285. Thanks for the soon-to-be-PR.Atomic Chicken: use "for n = 1, spawnNumber", not the stuff with the while loop. I agree that a quantity flag would be useful (like modtools/create-item), and was thinking of adding one but never got around to it.
Oh wow, brain fart right there. Edited accordingly. I'll get a PR for that quantity arg in place shortly.
Is there a way/script to use DFhack to change a dwarf's emotions to counteract the current 'bug' of sorts that's leading to emotion runaway? Or perhaps another utility? I updated from 9 to 10 because I'd heard the stability was better but didn't realize 10 was where the emotion issue was occurring.Some people have had luck with the remove-stress command, although it only works for the short term (you'll have to run it repeatedly). Thoughts can definitely be modified/removed (the misery plugin does the opposite) but I haven't looked at the issues in 0.44.10 much. You could look at what misery does and see if there's a way to modify thoughts (maybe with gui/gm-editor?).
To be fair, while loops aren't always bad, of course - I'm a bit paranoid after https://github.com/DFHack/dfhack/issues/1285. Thanks for the soon-to-be-PR.Atomic Chicken: use "for n = 1, spawnNumber", not the stuff with the while loop. I agree that a quantity flag would be useful (like modtools/create-item), and was thinking of adding one but never got around to it.
Oh wow, brain fart right there. Edited accordingly. I'll get a PR for that quantity arg in place shortly.Is there a way/script to use DFhack to change a dwarf's emotions to counteract the current 'bug' of sorts that's leading to emotion runaway? Or perhaps another utility? I updated from 9 to 10 because I'd heard the stability was better but didn't realize 10 was where the emotion issue was occurring.Some people have had luck with the remove-stress command, although it only works for the short term (you'll have to run it repeatedly). Thoughts can definitely be modified/removed (the misery plugin does the opposite) but I haven't looked at the issues in 0.44.10 much. You could look at what misery does and see if there's a way to modify thoughts (maybe with gui/gm-editor?).
:When I tried to push things with the scripts changed but not added and commited git complained. It was a while ago as I've given up (as is typically the case with git...) and copy the blasted install-debug.bat script back and forth (and git didn't like a working copy of the script with a different name either).If you install VS on a drive different from C: you can't use a number of the scripts as the are, as they're hard coded to C:, and if you change them to work you have to copy them out and replace them with the original for the blasted git to allow you to upload anything (it won't accept your local modifications staying local: you either have to commit them [which trashes the original if accepted], or delete the remove).You should absolutely be able to change the build scripts and avoid committing them without Git complaining. The only time Git would complain is if the build scripts also change in DFHack's repo, and that happens rarely and can be worked around without you having to get rid of (or commit) your local changes.
I've also found that I can't set up stonesense for compilation because the setup gui script uses a symbol that still points to C: (which I can't change without old stuff installed there breaking). A clean install of the computer and applications where everything goes to D: might work, unless you have stuff that's still hard coded for C:. Fortunately, I don't really care about compiling stonesense (Only tried it because of issues that turned out to be due it internal DF git setup upgrades at the same time).
What stonesense build script are you referring to? The only things that contain "C:" in the build folder are build/win64/package-debug.bat and build/win64/package-release.bat, and they don't deal with stonesense. plugins/stonesense/CMakeLists.txt does not contain "C:".
:
When I tried to push things with the scripts changed but not added and commited git complained.That's not a situation where Git can complain.
Stonesense:The annoying thing there is that it's not even a build script, but the generate-MSVC-gui.bat script, which starts to download stuff and then barf when the stonesense checkbox has been checked, so it blows up during configuration of what to build eventually. It's probably due to the %XXX% symbol having to point to the C drive for old stuff (like the web browser) to continue to work.Is this just an assumption, or did you see an error message pertaining to the C drive? I checked again, and "C:\" and "%XXX%" don't appear in stonesense (and if the latter was just a placeholder, there aren't any batch files in stonesense that could be using a symbol like that). An error message would help us figure out what's going wrong.
The script "modtools/create-unit" lets the game handle preference generation (alongside a bunch of other stuff) for the units it spawns. As such, you could use something like this for your research:Thanks for the script.
[...]
@Bearskie: You may want this for your script to save on entering the unit id manually:
local unit = df.unit.find (df.global.ui_advmode.player_id)
%XXX% is a placeholder for %program files (86)% or something along those lines.So your program files folder is on your C: drive but VS isn't in that? BenLubar made this change (https://github.com/DFHack/dfhack/commit/43be1c7a6a2aedcd8eed4a204bf98bae91b4481f) 10 days ago - does that help?
I believe I did see an error message saying that something or other couldn't be found at the C: drive, and I think it pointed down the program file guff path to where VS would have been placed if it had been on the C: drive.
I'm using the the command line (git xxx <guess at parameters here>). "git help" provides a dozen commands, most of which I've never used, and most of the ones I need aren't listed. The "documentation" is horrible, resulting in a web page that only provides overall syntax, but not any parameters, nor any description of the commands. Web searches are needed to find commands, and sub repo juggling was found in a comment somewhere half way through a discussion of failed attempts.You should be using branches instead of several copies of the repo, which can get rather large. And I think you have been using branches, which confuses me more. You should be able to use "git checkout" to switch between branches, and changes you've made and committed on a branch will only be visible when that branch is checked out, so you can do work on multiple branches/PRs in the same clone.
As it currently stands, I can stumble by with git and a frequent "remove everything and download from scratch" approach. For instance, I won't create a new pull request in a (sub) repo before the previous one is done as I don't want to try to juggle several instances of it (and the complete DFHack surrounding it that's required for the sub repo stuff to be tested), as pull requests are frequently requested to be changed, so the local copy needs to be prepared for making changes to be pushed up.
For the record, my DFHack clone dates back to 2014, when I first started working on DFHack. So yes, you can get by with only cloning it once. (I do have a second clone for some administrative stuff, like syncing branches on GitHub without touching all of the files in my main clone and needing a rebuild, but I haven't had to re-clone that either.)<snip>
- git clone yourproject.git - downloads the project from github the first time (you theoretical only need to do this once)
If you have any problems, backup the project to another folder and reclone it from the github.
DO NOT DO THIS! The "-a" flag to git commit will commit EVERYTHING (except untracked files), so if you picked out individual changes to add with "git add", "git commit -a" will throw away all of that and commit everything (and lead to confusion because it doesn't match what "git status" showed).
- git add filename.file - adds a file to the list of files getting sent. (or git add . to add all the red untracked files)
- git status - do this once more to make sure it all looks right
- git commit -am "change description" - this makes a save point or something with a comment of what you just changed.
Thanks jecowa.Jecowa was talking about DFHack forks, which is also what you're talking about.
That's partially covered here: https://dfhack.readthedocs.io/en/stable/docs/Compile.html (https://dfhack.readthedocs.io/en/stable/docs/Compile.html) (I've gotten that from Lethosor).
I'm not interested in cloning "my" project (I wouldn't put any development stuff there unless forced to): I'm interested in contributing to DFHack, so its various (sub)repos is what's of interest. My stuff on github is there only because it's required for pull requesting to work (apart from my scripts, which are there only so they can be downloaded by others: I don't do any work on them there).
git status lies. It can tell you things are up to date while you can clearly see the local file differs from the one on github. It also doesn't tell you which branch it thinks you're on, and it couldn't care less about whether it's in sync with your github fork branch (it probably doesn't even know about it).It doesn't lie, if you use "git fetch" or "git pull" to update your local copy. I use it constantly, and it does help avoid confusion.
Essential git commands not mentioned:You can change remote URLs with "git remote set-url": https://git-scm.com/docs/git-remote. No need to nuke anything.
git remote -v and git remote add <get the path syntax right for your DFHack (sub)repo fork> (and git remote remove <got the path syntax wrong> to undo it without nuking).
git branch <your_update_branch>
git checkout <your_update_branch>
When I run into problems I copy the file I've changed to a safe place, nuke the failed local repo and reclone to wipe git's faulty internal state. I'm getting better at it, so it's probably down to less than once per pull request.I wouldn't describe it as faulty myself, but if you need help sorting something out, I'd be willing to work with you if you can provide "git status" output and a description of what you're trying to do.
Also, don't use the web interface to update your fork's files, as it changes the <EOL> indication to one Travis rejects.It has never done this for me. It might be due to your editor using DOS-style newlines (\r\n), so change that if you can. Git locally changes newlines to \n by default on Windows, but maybe GitHub's UI does not, so copying a file in like that could be problematic.
So your program files folder is on your C: drive but VS isn't in that? BenLubar made this change (https://github.com/DFHack/dfhack/commit/43be1c7a6a2aedcd8eed4a204bf98bae91b4481f) 10 days ago - does that help?
I wouldn't know; I haven't tried it. It's fairly small, though. Is there a reason you can't install it on multiple computers if necessary?
@SlimeOfSteel: When I installed VS partial older versions insisted on ending up on the C: drive, with 14.0 correctly landing on D:. However, I suspect it may be due to the %ProgramFiles(x86)% variable pointing at C:\..., so you may want to check that variable. Note that I'm mostly guessing, though. It that guess is correct, you'd probably have to make sure nothing dependent on it is installed on the C: drive, or keep switching it back and forth depending on what you do.
Interestingly enough, the win32 build/install/package scripts all useSo your program files folder is on your C: drive but VS isn't in that? BenLubar made this change (https://github.com/DFHack/dfhack/commit/43be1c7a6a2aedcd8eed4a204bf98bae91b4481f) 10 days ago - does that help?
I have never built DFHack on Windows, so I am not sure it help: when I build Dwarf Therapist, I use %VS140COMNTOOLS%VsDevCmd.bat to setup the environment for cmake. It is not the same directory you are using in the linked patch, but it works with non-standard install paths (my MSVC2015 is not installed on the system disk).
call "%VS140COMNTOOLS%vsvars32.bat"
I don't remember if there was an issue doing that with the win64 scripts, and I'm not sure why it wasn't done for win32 package scripts. Ben or others might know.@Lethosor:Ok, yeah, that sounds like something the existing scripts can't support without changes. Maybe there's a way we can make the VS tools path user-configurable (and ignored by Git), but I'm not familiar enough with batch scripts to do that.
BenLubar's change *should* result in me not having to change install-debug for every clone. However, %ProgramFiles(x86)% is the variable that points to C: and has to do so. There is one structure on the C: drive where old program installs are and one on D: where new ones, including most of VS (it's installation placed versions older than 14 on C: anyway), are (I had to change the install location when bloatware filled the OS partition, as the PC vendor apparently doesn't care that this happens).
I don't have multiple clones as I nuke them when done, and yes, I'm using branches, but given how much I trust git not to screw up things I don't trust it to switch between branches and not destroy/corrupt data. It's fairly easy to get into a state I can't get out of (I know you can do HEAD surgery and whatnot, but I don't know how to do that).It's really hard to destroy or corrupt data. Sometimes you'll end up with merge conflicts that are hard to resolve when pulling or merging, but Git will keep you from pulling/merging things that will overwrite your changes, and some form of "git reset" will get you out of a merge if you decide to give up on it.
git remote is one command that isn't listed by the help (and my git installation is less than a year old and made according to DFHack instructions, so it shouldn't be ancient). When I've tried git help <command> it hasn't produced anything useful (like e.g. listing parameters), and I think it didn't seem to recognize the command at all.Fair, but "git help remote" gives me a 233-line man page that definitely lists parameters and is basically the same as https://git-scm.com/docs/git-remote. The syntax for commands is in the "Synopsis" section.
I do add and commit files individually (and have to do so to not include install-debug.bat when in the top/main structure).Yeah, that complaint was directed at Jecowa. That's good.
I certainly don't want to download the old junk in my fork, as I want to work with the latest stuff, and apparently the fork cannot be updated without first downloading the data locally and then push it up (apart from nuking the fork and recreating it, of course). The fork's main branch only acts as an administrative placeholder to create github branches so pull request can be made (and those branch copies have to be updated by pushing the newly cloned/pulled stuff up).This is true, but you can fetch from the upstream remote ("git fetch origin" if "origin" is your name for DFHack's copy), then create a new branch based on origin/some-branch (e.g. origin/master or origin/develop) if you use "origin" ("git checkout origin/some-branch", then "git checkout -b my-new-branch"), and that will give you a new branch "my-new-branch" that's based on DFHack's some-branch without having to update your own some-branch.
Missing commands: I was referring to jecowa's list. As I said, "git remote remove" can be used to remove failures so so "git remote add" can be tried again, without nuking, and I assume your command has parameters for modifying the path of an existing element, thus allowing the usage of one command instead of two.Yeah, it does. "git remote set-url [--push] <name> <newurl>" from the man page (https://git-scm.com/docs/git-remote), so e.g. "git remote set-url bob https://github.com/bob/dfhack"
The mentioning of which of a bazillion different git structure organizations DFHack uses ought to be required even for those familiar with git (i.e. "git flow" as mention in your earlier post), and the usage of a main repo with multiple sub-repos (docked in the top structure under other names, at least in the case of df-structures->xml) ought to be documented as well, as I assume that isn't immediately apparent.Honestly, "we do all work on the develop branch" has been enough people familiar with Git for most development. The "git flow" page is interesting for theory and all, but in most cases, working off the develop branch is all that matters for contributors.
<EOL>: I've changed my text editor settings to satisfy Travis' demands, but I'm unsure if it had rejected things before that or if the change was made because I read it would demand it (and I haven't checked that the settings have been retained when it has updated itself). When I tried the web UI to cut out the administrative steps in updating a minor change Travis rejected the file, while it was accepted when pushed up (and I didn't touch it in between). I wouldn't exactly be surprised if the web UI behavior differed between browsers and OS's, though (Firefox on Windows, in my case). Also, I haven't changed any settings in VS (and don't know if you can modify line terminations there), but I don't remember what kind of file it was (i.e. script, XML, or code).I suspect Firefox on Windows (or maybe anything on Windows) includes the carriage returns when pasting, which causes the issue. Are you sure your editor is saving with UNIX-style newlines?
According to the instructions: Same as before: https://dfhack.readthedocs.io/en/stable/docs/Compile.html (https://dfhack.readthedocs.io/en/stable/docs/Compile.html), i.e. via Chocolatey.Yeah, "origin" is the default name for the remote for the URL you passed to "git clone". Some people clone their own fork and make a new remote called "upstream" or something that points to DFHack's. Others (like me and you) clone DFHack's and make a new remote for their own fork. Either way works.
"origin" is what DFHack (on github) is called when cloned. So far I've mostly nuked the local clone and recloned it to get the latest, but I've had some success with fetching it, creating a branch locally (same name as in my github repo) from it, check it out, and push that up to my github repo (with the occasional accidental attempt to push it up to origin, which (fortunately) doesn't work). I haven't used any -b switch, though.
Those who do not know git and try to figure out anything about the organization by reading the git documentation will just have to guess at which workflow description sort of matches what's used, and using the insider description that you're just working off develop doesn't help one bit (and it is correct only for the top repo anyway) when you don't know what a "develop" is or what git hierarchy item it might be an instance of.It's not an "insider description" - it's mentioned on https://dfhack.readthedocs.io/en/latest/Contributing.html, and it says "develop branch" in both cases.
<EOL>: Given that browser file uploading involves drag&drop of the file, not its contents, I'm fairly sure it's not a cut-and-paste issue. Checking Notepad++ it claims to use "Unix (LF)" still, but as I said, it might have been code, in which case VS is the "editor".Oh, I didn't know you were uploading the file directly (that's a newer feature than the online editor, which I'm more familiar with and does allow editing file contents).
Trace/BPT trap DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib ./dwarfort.exe "$@"
So... is there a hack that allows one to engrave built floors or walls? Or to fix engravings being on only one side of a wall?Wall and floor buildings do not have the fields required for engravings, and since those structures are defined by DF, DFHack can't add them.
Actually, engravings, once made, can be moved literally anywhere.Hm, yes, it looks like you're correct. Engravings appear to just be tied to a position rather than to what's in that position, in which case building definitions are irrelevant for the placement. Whether DF will display engravings moved everywhere or only in certain locations (such as natural surfaces) is a different issue. How would DF display an engraving claiming to face in one (or many) directions in the air or in a building (such as a trade depot), and single tile constructions (such as build walls/floors)? It's not impossible an engraving in the same location as a built floor would be "displayed" under the floor, for instance.
When I was investigating the egnraving definitions, I was able to move engravings onto grass, where they worked as normal.
regarding the size of such a pillar, it's absolutely sane to give it such value: it's as big or even bigger than a dragon!
akthually… it's 11.2 m3
Physics calculations assume a tile size of 2x2x2.8m tiles, with a realtime FPS of 10.so you mean 2x2x2.8m and 0,20m for the floor?
No, including the floor.time to update the wiki then :)
I investigated how the physics work, and those were the actual measurements used.
No, including the floor.time to update the wiki then :)
I investigated how the physics work, and those were the actual measurements used.
So... is there a hack that allows one to engrave built floors or walls? Or to fix engravings being on only one side of a wall?I removed the constructed wall check from the advfort engraving job and it works fine so you should totally be able to slap a new job into the queue for a dorf and have them pop out an engraved whatever.
still not completely certain every dead unit has the flags2.killed flag setI tried it and all the dead/missing list, even bridge-squashed units, and starved ones have it so it does look like it could be the real "dead" flag. "deactivated" could be a name for 'dead' if its decided to fix it.
Thanks for those explanations, Atomic Chicken.ghosts are the lingering dead units that stay on the map that have a ghost timer, until they get put to rest or the map unloads them, which I guess is why adventure mode doesn't seem to make ghosts unless a player hangs out in a spot long enough to cause one to rise(not to say ghosts don't exist in adventure mode just that would be amazing to see someone create a ghost).
That would mean normal ghosts go through the process of:
- Unit dies. both "dead" and "killed" are set, and a death cause incident is generated (the incident presumably is generated when the incident happened, which may be relevant for a drawn out death).
- Unit returns as a ghost, and "dead" and "killed" are reset.
- Ghost expires/gets slabbed/buried, and "dead" and "killed" are set again?
Are dwarves in fell moods still able to use ghosts?Still open on the bug tracker. (http://www.bay12games.com/dwarves/mantisbt/view.php?id=4681)
---
- Ghost expires/gets slabbed/buried, and "dead" and "killed" are set again?
What about husks? Do they keep their souls?
It was last updated in 2011, though, so it's possible that it was fixed unintentionally at some point since then. If anyone can reproduce it in 0.44 or so, that would be helpful.
So how do I actually USE the remove-stress script, I put it in using the all argument and I get an error, do I actually have to remove-stress from EVERY dwarf individually?Can you copy and paste the exact error here? Right clicking the title bar should give you an option to copy if selecting doesn't work on Windows.
remove-stress all
remove-stress -all
and now I haveSpoiler (click to show/hide)
It's almost certainly possible - DFHack can access pretty much anything that DF stores in memory, which includes unit positions. And if it ever was possible, it wouldn't have been removed. The exact strategy depends on what you're looking for. Are you looking for a unit on the fortress mode map (i.e. can you select it with u or v)? Or is it something on the world map? Or the local adventure mode map?There was this Roc who is most certainly alive (it doesn't show him dying in legends mode), and apparently he's in a fortress called Treatymyths, but I've explored the whole place and I can't quite find anything. So I guess he could be anywhere on the world map
df.global.world.units.activeWhere can I find that?
Yeah, they basically are suspended jobs. Were you using autounsuspend before you reclaimed?
Maybe there's a way for autounsuspend to detect buildingplan jobs, but I'm not sure. Checking for "unknown material" might do it.
That's the DFHack location of one of the unit vectors inside of DF which can be accessed with scripts (which you'd probably have to write yourself to get exactly what you want) or the DFHack gui/gm-editor.df.global.world.units.activeWhere can I find that?
The "resume" plugin draws over planned buildings (which are suspended) with a green X, and other suspended buildings with a yellow X. I've found what it uses to do that, so I think I can get it to work with autounsuspend too.Yeah, they basically are suspended jobs. Were you using autounsuspend before you reclaimed?
Maybe there's a way for autounsuspend to detect buildingplan jobs, but I'm not sure. Checking for "unknown material" might do it.
I thought I was, but I spun up a new fortress and I can't have been. I must have had it off while I was building the initial fortress.
I really really really need help.. I didnt embark with enough food but i have no idea how to spawn in more... createitem CHEASE:COW 200 isnt working...Ok, for one thing, you spelled "cheese" wrong. And for another thing, you can't just guess syntax for commands and expect it to work. createitem takes an item type, then a space, then a material (then optionally a number). You gave it just one argument before the number, and it's unclear if it's supposed to be the item type or material, so it won't work like that.
DF Hack is my only chance at fixing this.
item CREATURE_MAT:creature:material
where "material" should be replaced with "CHEESE", "creature" with the creature you want (e.g. "COW"), and "item" with the item type.I really really really need help.. I didnt embark with enough food but i have no idea how to spawn in more... createitem CHEASE:COW 200 isnt working...Ok, for one thing, you spelled "cheese" wrong. And for another thing, you can't just guess syntax for commands and expect it to work. createitem takes an item type, then a space, then a material (then optionally a number). You gave it just one argument before the number, and it's unclear if it's supposed to be the item type or material, so it won't work like that.
DF Hack is my only chance at fixing this.
Here is a page on the DF wiki about createitem: http://dwarffortresswiki.org/Utility:DFHack/createitem
I searched for "cheese" and found "CHEESE" listed under Materials -> Body parts. The example syntax given is:Code: [Select]item CREATURE_MAT:creature:material
where "material" should be replaced with "CHEESE", "creature" with the creature you want (e.g. "COW"), and "item" with the item type.
That same page links to http://dwarffortresswiki.org/index.php/DF2014:Item_token for a list of valid item types. I searched for "cheese" and found "CHEESE" listed, so replace "item" with "CHEESE".
I'll test it in a moment, but I think that should do it. You can add a number at the end if you want, of course.
Edit: yeah, that works.
You could also try gui/create-item if you prefer GUIs. See dfhack.readthedocs.io for help - it takes an argument that lets it create multiple items at once.
createitem CHEESE CREATURE_MAT:COW:CHEESE
orcreateitem CHEESE CREATURE_MAT:COW:CHEESE 200
etc.When i type in gui/create-item nothing happens... also I used createitem CHEESE CREATURE_MAT:COW:CHEESE 200 that you just posted and nothing happened.. it just said no unit selectedgui/create-item opens a screen in the DF window. Did you check there?
When i type in gui/create-item nothing happens... also I used createitem CHEESE CREATURE_MAT:COW:CHEESE 200 that you just posted and nothing happened.. it just said no unit selectedYou need to select a unit using (k), (v) or the (u)nits screen. Press v, move the cursor over one of your units, and run createitem with the same arguments again.
Is it possible to delete objects with DF hack?Yes. Autodump can do that, for instance (man autodump to get a description of the command).
Can somebody post the whole command string for both autodump destroy and clean mapIt's literally what you posted. "clean map" and "autodump destroy" work fine for me. For autodump, you need to mark items for dumping in-game, like the link I posted says: https://dfhack.readthedocs.io/en/stable/docs/Plugins.html#autodump
Another question.. Can you use DF hack to delete dwarves?The reason items don't disappear immediately when you destroy them is that the command marks them for destruction, but they're not actually removed until DF resumes. This, in turn, is because you can't just disintegrate items, but have to clean them out properly so ownership, container contents/holder, etc. are removed without leaving references to objects that just vanished.
Alright it said 34 items marked for destroy but i dont know how to finish it, the items are still there.
The reason items don't disappear immediately when you destroy them is that the command marks them for destruction, but they're not actually removed until DF resumes. This, in turn, is because you can't just disintegrate items, but have to clean them out properly so ownership, container contents/holder, etc. are removed without leaving references to objects that just vanished.And the reason you have to unpause is because DFHack doesn't actually know how to do all of that cleanup, so it just sets a special flag and tells Dwarf Fortress itself to do that cleanup, and that only happens when the game is unpaused (once every 100 frames, if I recall correctly).
Can you exile dwarves?Why do you want to do that?
The reason items don't disappear immediately when you destroy them is that the command marks them for destruction, but they're not actually removed until DF resumes. This, in turn, is because you can't just disintegrate items, but have to clean them out properly so ownership, container contents/holder, etc. are removed without leaving references to objects that just vanished.And the reason you have to unpause is because DFHack doesn't actually know how to do all of that cleanup, so it just sets a special flag and tells Dwarf Fortress itself to do that cleanup, and that only happens when the game is unpaused (once every 100 frames, if I recall correctly).Can you exile dwarves?Why do you want to do that?
Oh? How do i do that?Just send undesirable dwarves on a mission. You don't need dfhack for that.
And the children go off the cliff?Oh? How do i do that?Just send undesirable dwarves on a mission. You don't need dfhack for that.
http://dwarffortresswiki.org/index.php/DF2014:Mission
unit_soul's unk_4410_1 and unk_4410_2 are focus related. They could be unit_personality's current_focus and undistracted_focus which have moved two field further. I am not sure since I don't really know how they supposed to work. Maybe strainer can confirm.Thanks for working this out Clément. When I use ..soul->unk_4410_1 as current_focus and ..soul->unk_4410_2 as undistracted_focus my previous rating system works just like before. After embark their working range seems to be around 75 to 125 each. Ill copy this table you posted in Therapists thread for the record here:
struct derived {
struct base super;
/* ... */
};
Although, this is usually how you do inheritance in C, it may not use the exact same layout as C++ classes. When deriving non-POD types, GCC optimize for space and and does not count the padding at the end of the base class. Among offsets used by DT, this affects history_event_hist_figure_diedst and item_crafted.Thanks. I fear then generated codegen.h may have some issues with gcc builds. Generated structure for derived classes useCode: [Select]struct derived {
Although, this is usually how you do inheritance in C, it may not use the exact same layout as C++ classes. When deriving non-POD types, GCC optimize for space and and does not count the padding at the end of the base class. Among offsets used by DT, this affects history_event_hist_figure_diedst and item_crafted.
struct base super;
/* ... */
};
It should work fine with MSVC, where, IIRC, the tail padding is kept.
Yes. When I was recently using codegen_c_hdr to analyse osx binary, I modified it to just output all the fields from base class directly, and then all the layouts were correct.
diff --git a/codegen_c_hdr.pl b/codegen_c_hdr.pl
index de6a3d2..1f73ec7 100644
--- a/codegen_c_hdr.pl
+++ b/codegen_c_hdr.pl
@@ -225,7 +225,7 @@ sub render_global_class {
}
indent {
if ($parent) {
- push @lines, "struct $parent super;";
+ push @lines, "struct $parent super;" if (!$linux);
} elsif ($has_rtti) {
push @lines, "struct vtable_$rtti_name *vtable;";
}
@@ -245,12 +245,21 @@ sub render_global_class {
push @lines_full, @lines;
}
sub render_struct_fields {
- my ($type) = @_;
+ my ($type, $super_prefix) = @_;
+ $super_prefix = '' if (!$super_prefix);
+
+ my $parent = $type->getAttribute('inherits-from');
+ if ($linux && $parent) {
+ my $ptype = $global_types{$parent};
+ render_struct_fields($ptype, $super_prefix.'super_');
+ push @lines, "/* end of parent: $parent */";
+ }
for my $field ($type->findnodes('child::ld:field')) {
my $name = $field->getAttribute('name') ||
$field->getAttribute('ld:anon-name');
$name = '_' . $name if !$stdc and $name and $name =~ /^(sub|locret|loc|off|seg|asc|byte|word|dword|qword|flt|dbl|tbyte|stru|algn|unk)_|^effects$/;
+ $name = $super_prefix . $name if $name;
render_item($field, $name);
$lines[$#lines] .= ';';
}
Does it look right?
Yes. When I was recently using codegen_c_hdr to analyse osx binary, I modified it to just output all the fields from base class directly, and then all the layouts were correct.
Yes. When I was recently using codegen_c_hdr to analyse osx binary, I modified it to just output all the fields from base class directly, and then all the layouts were correct.
Can you share your modifications?
Good day.First, it's gui/autogems, not gui\autogem. You can literally run "gui/autogems" in the console at any time (like most other commands) and the screen will open, as long as you have a world loaded. There's also a "G: Opts" option in the o-W menu, right next to "g: Auto cut gems", which will also open the screen.
I cant find gui\autogem UI in game menus and in DFHack’s documentation no keybinding to this function like other gui scripts... Can someone help me?
First, it's gui/autogems, not gui\autogem. You can literally run "gui/autogems" in the console at any time (like most other commands) and the screen will open, as long as you have a world loaded. There's also a "G: Opts" option in the o-W menu, right next to "g: Auto cut gems", which will also open the screen.
As ever, its very amusing that Toady publishes the next release the day after a preceeding DF_hack update for the now previous release, may i ask how the follow up for the current (as of this post 44.11) DF version's DF_hack is coming along?This time it wasn't that unexpected, as Toady said he'd make a release about a week from the previous post. However, I think it was still a good idea to wrap the changes up in a release, since there are people who won't upgrade to the latest DF until there's a DFHack for it, so I expect the release will still see some use.
Is there any way to disable the thing when you're selecting materials to build things and it automatically pulls up the one you used last?Remove the line "enable automaterial" from dfhack.init.
So, can I just copy the contents of the example into a file named dfhack.init in order to create one or...?Is there any way to disable the thing when you're selecting materials to build things and it automatically pulls up the one you used last?Remove the line "enable automaterial" from dfhack.init.
Or remove the line "enable automaterial" from whatever file it's in...So, can I just copy the contents of the example into a file named dfhack.init in order to create one or...?Is there any way to disable the thing when you're selecting materials to build things and it automatically pulls up the one you used last?Remove the line "enable automaterial" from dfhack.init.
Also, remove-wear isn't working for me, I don't get any errors, but nothing is happening, adding -all to it does nothing as well... is it borked in the 44.11 alpha?From checking the docs, it takes "all", not "-all".
hmm I probably should look into finding out how to give labors to units who end up with the "No labors available" screen
edit: oh it seems there's no unit_flags1.dead flag in this alpha
"dead" has been renamed "inactive" since 0.44.10-r2.And the reason for that is that flags2.killed is the one that tells if a unit is dead. All killed units are inactive, but all inactive units are not killed (e.g. inbound/outbound). Also consider whether you actually need to access the flags directly, or can use the Units.isKilled/.isActive/.isMerchant etc. functions instead.
string.gsub(unit.first_name,"^%l",string.upper)
string.gsub(dfhack.units.getVisibleName(unit).first_name,"^%l",string.upper)
And the reason for that is that flags2.killed is the one that tells if a unit is dead. All killed units are inactive, but all inactive units are not killed (e.g. inbound/outbound). Also consider whether you actually need to access the flags directly, or can use the Units.isKilled/.isActive/.isMerchant etc. functions instead.
Also, flags1[1] is much less readable. Saying you need to "test functions first" is a bad excuse - you can look at the source of isDead here (https://github.com/DFHack/dfhack/blob/8a1979b8a7aecee299c0d74720ee37eeaef9fee5/library/modules/Units.cpp#L387) - it's literally just two flag checks that indicate dead units. If you really care about inactive units instead, either use "flags1.inactive" or "not dfhack.units.isActive(unit)".
I can't actually USE the console, it loads and everything, but typing does nothing.....
If you could all specify that you're using Windows, that would help.
Anyone else has this problem with Win x64 build (0.44.12-alpha1):
When using "die" to exit DF, the DF's window disappear, but the DFHack's window still hangs there, and the whole Dwarf Fortress.exe process doesn't exit too? I need to kill it manually with process explorer.
Anyone else has this problem with Win x64 build (0.44.12-alpha1):
When using "die" to exit DF, the DF's window disappear, but the DFHack's window still hangs there, and the whole Dwarf Fortress.exe process doesn't exit too? I need to kill it manually with process explorer.
With Win x64 build (0.44.12-alpha1), and the 0.44.11 alpha as well, the DFHack window does hang for me for longer than expected, closing on its own after 15s or so. I haven't had to end the process (after noting it indeed did eventually close the first few times, now I just go on with my business. I only use "die" when testing quick things in disposable worlds.) Windows 10.
it seems the new dfhack adds a new folder called 'included' which I'm wondering if that suppose to add something?
Anyone else has this problem with Win x64 build (0.44.12-alpha1):I'm actually seeing a totally different problem - with the 32-bit version on Windows 7 x64, it outright crashes if I type "die" in the console, but if I exit the game cleanly, it works fine. The 64-bit version doesn't seem to be affected.
When using "die" to exit DF, the DF's window disappear, but the DFHack's window still hangs there, and the whole Dwarf Fortress.exe process doesn't exit too? I need to kill it manually with process explorer.
Probably not the platform you're looking for tests, but I managed to use "die" successfully ingame using that new 64 bit build.Ok, then what platform are you using?
Crap, forgot to finish the description. Windows 64 bit.Probably not the platform you're looking for tests, but I managed to use "die" successfully ingame using that new 64 bit build.Ok, then what platform are you using?
That was exactly the platform I was looking for. People above reported issues with Windows, so I put up Windows builds to test. (Then I put up other ones as they finished.) Thanks for the feedback.Crap, forgot to finish the description. Windows 64 bit.Probably not the platform you're looking for tests, but I managed to use "die" successfully ingame using that new 64 bit build.Ok, then what platform are you using?
Using:Okay, that's not great. Has it occurred in older DFHack versions for you? Ab9rf said it happened for her a while ago too (on Windows), i.e. before 0.44.10.
DFHack version 0.44.12-alpha1 (development build 0.44.12-alpha1-35-g350ead26) on x86_64 [build ID: 180711000]
on: Windows 10 64 bit,
my results using "die" in Fortress Mode from a paused play screen are the same as I reported with the other build above, Dwarf Fortress window closes immediately, DFHack closes some seconds later, noticeably later than I recall from earlier versions, but not problematically hanging like Saiko Kila reported.
Okay, that's not great. Has it occurred in older DFHack versions for you? Ab9rf said it happened for her a while ago too (on Windows), i.e. before 0.44.10.
Also, is it possible that DF is actually crashing? Some people turn off crash reports and then think DF is just closing silently when it's actually crashing - could that be the case? Also, sometimes the Ruby plugin can interfere with crash reports - maybe delete hack/plugins/ruby.plug.dll and see what happens?
At any rate, we've done all that we can think of at this point to fix the issue. We already know that MSVC isn't standards-compliant in at least some respect here (with _Exit() not working as it should), so I'm inclined to leave it as-is and blame MSVC for any further "die" issues, unless other people have ideas for fixing it.
Have you tried the GCC 4.8 version? From https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html, GLIBCXX_3.4.22 is from GCC 6.1.0, so if you don't have that or newer, you will need the GCC 4.8 build.
I don't know how to turn off crash reports, but if you tell me where to check, I will. I doubt it's turned off, because the copy I used to test "die" was a newly extracted vanilla copy with your new DFHack installed, and no settings changed other than choosing a smaller world and a shorter world gen.Crash reports are a system setting - nothing to do with how new your DFHack installation is, or other DFHack settings. I just thought Ruby could be interfering with them, but if you have them turned off, it could still be that DFHack is crashing without you knowing. I'm not sure how to turn them on on Windows.
:lua ~df.reinterpret_cast('int32_t', 1)
I often just throw my old game folders into a "DF Stuff" folder rather than deleting them, on the theory I might want to recover something later, and searching that I find my most recent (and only) errorlog.txt is 0.44.03, and looks unrelated.errorlog.txt is DF only - DFHack doesn't touch it. And it's really mostly harmless warnings from DF.
Ubuntu almost never updates packages across major versions. From the GCC release history (https://gcc.gnu.org/releases.html), in April 2016, GCC 5.3 was the newest available (5.4 was released after Ubuntu 16.04 came out, so it probably replaced the 5.3 package later on since it was a minor upgrade).Have you tried the GCC 4.8 version? From https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html, GLIBCXX_3.4.22 is from GCC 6.1.0, so if you don't have that or newer, you will need the GCC 4.8 build.
The gcc 4.8 build did in fact work. On my system, gcc --version comes back with version 5.4.0.
For fun I checked to see if newer versions of gcc were available for ubuntu 16.04 and I did find 6.0.1 which still wasn't high enough. Huh, I know that ubuntu could be a little slow in updating its dev tools but I had no idea it was that bad.
Anyways, it works now. Thanks!
I've failed to see anything indicating which mid level tile the volcano would appear in, though.
Mid level tile = the 16 * 16 tiles that make up a world tile. The term "mid level tile" is the one Toady used when asked in a FotF about what he called the different levels a year or two ago. Thus, a default embark covers 4*4 mid level tiles. (I assume that's what the rather terse "What?" was asking).I've failed to see anything indicating which mid level tile the volcano would appear in, though.
What?
Cant you simulate a volcano? change the tiletype to open-space and magma flows, the edges to obsidian. Repeat for x zlvls till the magma lake is reached.Not pre embark, as the structure you'd hack hasn't been generated yet. I assume you could build a simulated "volcano" that way post embark, though, although I don't know if that would provide any magma rain to fill it. Anyway, that's not what I'm interested in doing.
Mid level tile = the 16 * 16 tiles that make up a world tile. The term "mid level tile" is the one Toady used when asked in a FotF about what he called the different levels a year or two ago. Thus, a default embark covers 4*4 mid level tiles. (I assume that's what the rather terse "What?" was asking).I've failed to see anything indicating which mid level tile the volcano would appear in, though.
What?
They've also been called region tiles, although region tiles have also been used for a different level. Because of the risk for confusion, I've decided to adopt the terminology Toady provided when asked. I tried searching for Toady's answer, but failed to find it, though (I'm not very compatible with either the board or bug tracker search functions).I think this (http://www.bay12forums.com/smf/index.php?topic=159164.msg7526005#msg7526005) is the answer you are talking about (it's from FotF 08.2017)
I think this (http://www.bay12forums.com/smf/index.php?topic=159164.msg7526005#msg7526005) is the answer you are talking about (it's from FotF 08.2017)
Ha ha, I'm not sure official names are useful -- they are often the worst names since the purpose of the structures has changed over the years, etc. In any case, we have the 16x16 world tiles, which are called "feature shells" <...>
I just ran into the game-ending open-legends script bug described here (https://github.com/DFHack/dfhack/issues/805#issuecomment-195660593). The embark explodes in brutal fashion a little while after loading the save (http://dffd.bay12games.com/file.php?id=13910), revealing all tiles, regenerating the map, etc. Odd, since it looks like this commit (https://github.com/DFHack/lethosor-scripts/pull/3/commits) fixed that problem.
Anyway, judging from the comments in the issue page and related pages, I guess this save is done for?
To help any possible future searchers: Save is massively bugged, one a certain day the entire map is revealed, including HFS. All dug stone is changed to undug stone as if the dwarves never mined out the fortress, and all buildings on the surface collapse. The only tiles not replaced by their original stone are those which have items or dwarves standing in them. In short, just about the entire embark is obliterated. This is the same thing described in this reddit post (https://www.reddit.com/r/dwarffortress/comments/3htqdx/wtf_just_happened_all_tiles_are_revealed_and_map/), this mantis issue (http://www.bay12games.com/dwarves/mantisbt/view.php?id=9052), and these (https://github.com/DFHack/dfhack/issues/805#issuecomment-195660593) DFHack (https://github.com/DFHack/dfhack/issues/886) threads (https://github.com/DFHack/lethosor-scripts/pull/3).
Oh no. I just included a little workshop to run open-legends in my pack. :-/Can the workshop work within the constraints I mentioned above? They seem strict, but maybe people will still find it useful. Worth noting that this issue has probably been around since open-legends was written in 0.40-ish.
I hope this can be fixed, or i have to delete it again.
Hey guys, longtime player/luker/fan of DF and all her utilities.This sounds good! I saw someone ask for that somewhere recently (maybe Reddit?), so it will be appreciated.
Couple questions:
How interested would you guys be in an updated(graded) make-monarch script?
I've used the underlying logic to make the script applicable to all noble positions in entity.positions.own ("MONARCH", "DUKE", "COUNT", "BARON", "DIPLOMAT", "GENERAL", "LIEUTENANT", "CAPTAIN", "OUTPOST_LIAISON") and made it so that you can "demote" a unit without raising another.
I noticed the github repo doesn't have a contributing guidelines, so here I am rather than just doing a PR. Also the original make-monarch doesn't have any licensing attached to it, so while I'd be happy to release under a GNU GPLv3 I'm not quite sure if that's kosher.Which repo? DFHack/dfhack has https://github.com/DFHack/dfhack/blob/master/Contributing.rst and https://github.com/DFHack/dfhack/blob/master/LICENSE.rst. DFHack/scripts is exclusively a subproject of DFHack/dfhack, so all DFHack guidelines/licensing/etc. apply to it as well. That includes the DFHack license, which is Zlib. For that reason, I'm strongly opposed to GPLv3 - the current script is already Zlib-licensed, and I have no idea what kinds of hoops we'd have to jump through to deal with something GPL-licensed instead (I'd much rather not include it in that case).
https://github.com/DFHack/dfhack/blob/master/Contributing.rst and https://github.com/DFHack/dfhack/blob/master/LICENSE.rst.
Evidently I never implemented that, so sorry, but it's on the list for r2 now.
I'm unaware of a way to fix it unless you have backups (it might be possible to copy over the region-*.dat files).
Does the fix-fat-dwarf script still make any sense? The bug it originally referred to has been closed for a long time. Perhaps some of the other fixes are now also redundant?It could probably be removed. I don't think any others are outdated, though.
Apologies in advance if this is not the right place to ask and for asking a potentially scrubbish question, but is there any way to call the dfhack.units.getUnit() function or something analogous from inside the anonymous-script modtool?What function? Are you thinking of dfhack.gui.getSelectedUnit() or something else? If you have a unit selected in-game, there's no situation where that function wouldn't work.
I'm at my wits' end and I'd greatly appreciate some help if anybody would be willing.
so I take it open-legends doesn't break down on Advmode given the nature of that game loading and unloading maps?I don't think that's a safe assumption. I don't know if anyone has ever even used it in adventure mode.
What function? Are you thinking of dfhack.gui.getSelectedUnit() or something else? If you have a unit selected in-game, there's no situation where that function wouldn't work.
For that you want df.unit.find(id).Yes! I could kiss you. Whereabouts is "unit" located, though? I can't seem to find it to take a look.
When the script tries to call dfhack.units.getUnit() I get the error "(anonymous lua script):1: attempt to call a nil value (field 'getUnit')" and I don't know why.The function doesn't exist, and has never existed. "Attempted to call a nil value" means the thing you're trying to call (right before the parentheses) is nil, similar to "undefined" in other languages, and therefore can't be called.
Could the function be outside of the scope of anonymous-script for some reason? I am not a smart man.
The function doesn't exist, and has never existed. "Attempted to call a nil value" means the thing you're trying to call (right before the parentheses) is nil, similar to "undefined" in other languages, and therefore can't be called.
I'm not sure where you're looking to find "df.unit.find" - it's a function, much like dfhack.units.[anything](), and is defined by DFHack as well - it doesn't have a location, whatever that means.
Sorry, I didn't realize you were looking in the C++ files - my mistake, "location" makes sense then. The C++ API is defined there. As Clément pointed out, exactly which functions are exposed to the Lua API are different, and defined in LuaApi.cpp (and the docs, assuming they're kept up-to-date).The function doesn't exist, and has never existed. "Attempted to call a nil value" means the thing you're trying to call (right before the parentheses) is nil, similar to "undefined" in other languages, and therefore can't be called.
I'm new to doing anything like this, sorry if this is like pulling teeth with me.
Putnam solved it but I'm still curious, if I were to call "dfhack.units.getPosition(unit)" for example that would refer to dfhack/library/modules/Units.cpp line 131 "Units::getPosition(df::unit *unit)", right?
Why then am I not be able to refer to Units.cpp line 92 "*Units::getUnit (const int32_t index)" as dfhack.units.getUnit(index)?
I don't understand what you mean when you say the function doesn't exist when I can see it right there at line 92. Am I missing something fundamental? Go easy on me. ;)I'm not sure where you're looking to find "df.unit.find" - it's a function, much like dfhack.units.[anything](), and is defined by DFHack as well - it doesn't have a location, whatever that means.
I was just wondering in which file in the dfhack directory the unit type (and find function) is defined, I guess 'location' is confusing or just plain wrong terminology. My apologies. Either way I'm further along to getting it working now. Thanks for the help.
modtools/anonymous-script "print(pos2xyz(dfhack.gui.getSelectedUnit().pos))"returns something like: "83 86 4", which I'm pretty sure is three integers, why will modtools/spawn-flow not accept the resulting xyz as its location argument using something like:
modtools/spawn-flow -location [ modtools/anonymous-script "pos2xyz(dfhack.gui.getSelectedUnit().pos)" ] -flowType MistI get "spawn-flow.lua:82: Cannot write field coord.x: integer expected". Is the anonymous script failing to return the result to spawn-flow, or is really a type mismatch?
modtools/anonymous-script
pos2xyz(dfhack.gui.getSelectedUnit().pos)
and can't turn either of those into numbers that it expects. (I think it also requires 3 numbers, in any case.)In adventure mode the script can cause demons to escape from hell when you leave the area you ran open-legends in. Other people have mentioned this so I thought it was well known.so I take it open-legends doesn't break down on Advmode given the nature of that game loading and unloading maps?I don't think that's a safe assumption. I don't know if anyone has ever even used it in adventure mode.
I've literally never heard of that happening, ever. It certainly wasn't mentioned in this thread, which it should have been. Where did you see this? As this conversation has established, the issues caused by open-legends are not well-known.In adventure mode the script can cause demons to escape from hell when you leave the area you ran open-legends in. Other people have mentioned this so I thought it was well known.so I take it open-legends doesn't break down on Advmode given the nature of that game loading and unloading maps?I don't think that's a safe assumption. I don't know if anyone has ever even used it in adventure mode.
hmm this didn't hit me that this could happen as revealing the world also kinda unleashes demons from hellIn adventure mode the script can cause demons to escape from hell when you leave the area you ran open-legends in. Other people have mentioned this so I thought it was well known.so I take it open-legends doesn't break down on Advmode given the nature of that game loading and unloading maps?I don't think that's a safe assumption. I don't know if anyone has ever even used it in adventure mode.
...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:792: bad argument #1 to 'match' (string expected, got nil)
stack traceback:
[C]: in function 'string.match'
...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:792: in method 'setFilter'
...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:813: in function <...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:812>
[C]: in field 'on_change'
...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:166: in method 'onInput'
...top\Dorf\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui.lua:484: in method 'inputToSubviews'
...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:722: in method 'onInput'
...top\Dorf\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui.lua:484: in method 'inputToSubviews'
...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\dialogs.lua:224: in function <...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\dialogs.lua:217>
[C]: in ?
Not sure if this is the right place for this as it's related to the biome-manipulator script, but whenever I try to use the morph in the Geo Manipulation mode of biome-manipulator nothing happens, either when trying to either type the name of layer or selecting the layer through the list of options. I'm posting this in the dfhack thread because when trying to type I get an error message related to the gui plugin in dfhack console:Please don't double post. When an issue is with a specific tool that has its own thread, its better to post the issue there (only), and if it turns out to be a deeper DFHack issue, the tool maintainer would then identify if there's actually a DFHack issue.Code: [Select]...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:792: bad argument #1 to 'match' (string expected, got nil)
stack traceback:
[C]: in function 'string.match'
...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:792: in method 'setFilter'
...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:813: in function <...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:812>
[C]: in field 'on_change'
...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:166: in method 'onInput'
...top\Dorf\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui.lua:484: in method 'inputToSubviews'
...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\widgets.lua:722: in method 'onInput'
...top\Dorf\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui.lua:484: in method 'inputToSubviews'
...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\dialogs.lua:224: in function <...\DF-TES~1\Dwarf Fortress .44.12\hack\lua\gui\dialogs.lua:217>
[C]: in ?
I get this problem with a clean install of dwarf fortress 44.12 and the latest release of dfhack with nothing added but the biome-manipulator script itself. So I assume it should be completely reproducible.
embark-skills
Adjusts dwarves’ skills when embarking.
Note that already-used skill points are not taken into account or reset.
points N: Sets the skill points remaining of the selected dwarf to N.
points N all: Sets the skill points remaining of all dwarves to N.
max: Sets all skills of the selected dwarf to “Proficient”.
max all: Sets all skills of all dwarves to “Proficient”.
legendary: Sets all skills of the selected dwarf to “Legendary”.
legendary all: Sets all skills of all dwarves to “Legendary”.
points
Sets available points at the embark screen to the specified number. Eg. points 1000000 would allow you to buy everything, or points 0 would make life quite difficult.
prefchange
Sets preferences for mooding to include a weapon type, equipment type, and material. If you also wish to trigger a mood, see strangemood.
Valid options:
show: show preferences of all units
c: clear preferences of selected unit
all: clear preferences of all units
axp: likes axes, breastplates, and steel
has: likes hammers, mail shirts, and steel
swb: likes short swords, high boots, and steel
spb: likes spears, high boots, and steel
mas: likes maces, shields, and steel
xbh: likes crossbows, helms, and steel
pig: likes picks, gauntlets, and steel
log: likes long swords, gauntlets, and steel
dap: likes daggers, greaves, and steel
Feel free to adjust the values as you see fit, change the has steel to platinum, change the axp axes to great axes, whatnot.
function clear_craft_preferences()
local unit = dfhack.gui.getSelectedUnit()
local prefs = unit.status.current_soul.preferences
local crafttype = 4
local count = 0
local kk,vv,unk_counter
unk_counter=31415926
for kk,vv in ipairs(prefs) do
if vv.type == crafttype then
vv.type = -1
vv.item_type = -1
vv.creature_id = -1
vv.color_id = -1
vv.shape_id = -1
vv.plant_id = -1
vv.item_subtype = -1
vv.mattype = -1
vv.matindex = -1
vv.active = false
vv.prefstring_seed = unk_counter
count = count + 1
unk_counter = unk_counter + 1
end
end
print(count.." crafting preferences cleared.")
end
function clear_material_preferences()
local unit = dfhack.gui.getSelectedUnit()
local prefs = unit.status.current_soul.preferences
local material = -1
local count = 0
local kk,vv,unk_counter
unk_counter=31415926
for kk,vv in ipairs(prefs) do
if vv.mattype == material then
vv.type = -1
vv.item_type = -1
vv.creature_id = -1
vv.color_id = -1
vv.shape_id = -1
vv.plant_id = -1
vv.item_subtype = -1
vv.mattype = -1
vv.matindex = -1
vv.active = false
vv.prefstring_seed = unk_counter
count = count + 1
unk_counter = unk_counter + 1
end
end
print(count.." material preferences cleared.")
end
Hi,
I'm new. DFHack's pretty neat. Some of the documentation is bit poor though. :P
~df.global.world.raws.syndromes.all[123].ce[0].start
However, I cannot do it with end, since it's a reserved keyword in Lua (and technically it shouldn't be used at all for names). Invoking this will cause error:~df.global.world.raws.syndromes.all[123].ce[0].end
Replace .end with ["end"]
Hey guys, longtime player/luker/fan of DF and all her utilities.
Couple questions:
How interested would you guys be in an updated(graded) make-monarch script?
I've used the underlying logic to make the script applicable to all noble positions in entity.positions.own ("MONARCH", "DUKE", "COUNT", "BARON", "DIPLOMAT", "GENERAL", "LIEUTENANT", "CAPTAIN", "OUTPOST_LIAISON") and made it so that you can "demote" a unit without raising another.Spoiler (click to show/hide)
(dead group barons borking my site's progression and "inhereted" barons were the impetus for the script if you were wondering).
I noticed the github repo doesn't have a contributing guidelines, so here I am rather than just doing a PR. Also the original make-monarch doesn't have any licensing attached to it, so while I'd be happy to release under a GNU GPLv3 I'm not quite sure if that's kosher.
With the above script in mind; What do we know about the event(s) concering elevating a fortress (barony/county/duchy)?
Ideally, I'd like to have the script elevate the fort if you assign a position you're not yet on, but I've failed in finding anything relevant.
Is the 'prospect' command compiled into dfhack or is it implemented (or implementable?) via python or ruby script?"prospector.cpp" (which is the name of the plugin, but not the command it implements) is a compiled plugin, but I don't think there's anything preventing the logic from being implemented in script (I've copied parts of the logic at various time for use in my scripts).
And if the latter, can it be made to run during pre-embark on an arbitrary area of the world, or does it rely on reading memory that the game only populates for the actually currently selected embark zone?
"prospector.cpp" (which is the name of the plugin, but not the command it implements) is a compiled plugin, but I don't think there's anything preventing the logic from being implemented in script (I've copied parts of the logic at various time for use in my scripts).
That was renamed a long time ago. It should be unit.relationship_ids.GroupLeader instead - does that work for you?
First off, the siege engine plugin is throwing an error.Code: [Select]... Redux 07.26 - v0.41.4\hack\lua\plugins\siege-engine.lua:166: Cannot read field unit.relations: not found.
I took a quick gander at the lua code for siege-engine and checked it against the current data structure for units. It looks like the unit.relations vector and the fields within it were renamed at some point but siege-engine wasn't updated to match.
stack traceback:
[C]: in metamethod '__index'
... Redux 07.26 - v0.41.4\hack\lua\plugins\siege-engine.lua:166: in function 'plugins.siege-engine.getBaseUnitWeight'
... Redux 07.26 - v0.41.4\hack\lua\plugins\siege-engine.lua:174: in function 'plugins.siege-engine.getUnitWeight'
... Redux 07.26 - v0.41.4\hack\lua\plugins\siege-engine.lua:182: in local 'get_weight'
... Redux 07.26 - v0.41.4\hack\lua\plugins\siege-engine.lua:191: in function 'plugins.siege-engine.scoreTargets'
... Redux 07.26 - v0.41.4\hack\lua\plugins\siege-engine.lua:295: in function 'plugins.siege-engine.doAimProjectile'
[C]: in ?
Second, there is a problem with reaction-trigger. When a work order is placed (either through jobs/manager or by placing a work order in the individual workshop), commands are not executed/syndromes are not applied unless the reaction is the last one in the work order e.g. if I run the command "modtools/reaction-trigger -reactionName MAKE_PEARLASH -command [ devel/print-args "test" ]", place an order for 3 "make pearlash", the first and second reactions carried out at the kiln will not print anything, while the third will print "test". I extracted a fresh vanilla df+dfhack copy and did a bit of printlining, and it looks like the function in eventful.onJobCompleted.reactionTrigger is only called on the last reaction in a work order (which would mean that the underlying bug is in eventful).This is probably because since the new manager in 0.43, manager-created jobs have an "order" flag set on them, and there's just one per workshop (which is done repeatedly until the order is completed), rather than many jobs based on what was requested in the manager. I'm not familiar enough with the script to fix it quickly - any takers?
Thirdly, I am having weird intermittent problems with creating pets via modtools/create-unit. Sometimes the game will crash shortly after creating a unit. On one occasion, I also saw a created pet get into a full on fight with a citizen a while after it was created (with attack interactions used by the pet and melee weapons by the citizen). I made an attempt to reproduce the above bugs by spamming workshop reactions that run create-unit through reaction-trigger (which is how I normally use create-unit), but they didn't happen again. However, after leaving DF to run for a few minutes while this was going on, the game for some reason decided to consume about 17GB of memory (FYI, I have 16GB of ram), after which I had to kill DF because it was making my operating system unstable. I have never seen anything like that happen before.Sounds like something triggered undefined behavior that would usually crash (e.g. reading from a string that isn't actually a string) but you got unlucky.
Lastly, as a minor little thing I noticed, should the field "maker_race" in items be renamed something else? AFAICT it just controls the sizing of an item. The var name maker_race seems like a misnomer when dwarf produced, human sized equipment will have "maker_race" equal to the ID for humans.No idea how this stuff works, but if you test other combinations (e.g. human-produced dwarf-size equipment) and they behave the same, feel free to submit a PR or suggest a new name here.
EDIT: I tried fixing the error in the siege engine lua code. There is another error - math.pow on line 175 is throwing an "attempted to call a nil value" error. I just checked using the dfhack console lua interpreter, and the function math.pow doesn't exist.Geez, has nobody used siege-engine in the past 2 years? math.pow was replaced with the ^ operator when we switched to Lua 5.3 in 0.43.05 - try that.
...Yep, that fixed the error.
That was renamed a long time ago. It should be unit.relationship_ids.GroupLeader instead - does that work for you?
math.pow was replaced with the ^ operator when we switched to Lua 5.3 in 0.43.05 - try that.Ok, did that. There was also one other place where pow was used instead of ^. After replacing both of them with ^, I fired about 30 ballista rounds at a siege that showed up at the perfect moment without any errors or other problems. Hopefully that is it for siege-engine bugs.
Geez, has nobody used siege-engine in the past 2 years?Bear in mind that this doesn't show up if you just play around with siege-engine a bit using unskilled operators. I tried embarking in vanilla to test a couple of things, lucked out and landed on top of a Roc lair, and fired ballista bolts at both the Roc and a herd of boars for a while without any errors. I had to dig around in the c++ code to figure out why, but dabbling operators can't aim at specific creatures (only an area), and the buggy code is only executed when weighting targets for the random target creature selection algorithm. You have to fire a siege weapon with a trained operator at an area containing creatures to get an error thrown.
Are you using the -setUnitToFort argument, or anything else to adjust civ IDs? Better question: how exactly are you running create-unit (what arguments)?Here are the relevant script calls:
foe/reaction-trigger -reactionName DEPLOY_MINE_HE -command [ modtools/create-unit -race LANDMINE -caste HE -domesticate -location [ \\LOCATION ] ]
foe/reaction-trigger -reactionName MANUFACTURE_PROTECTAPONY_MINIGUN -command [ modtools/create-unit -race PROTECTAPONY_MINIGUN -caste NORMAL -domesticate -location [ \\LOCATION ] ]
foe/reaction-trigger -reactionName MANUFACTURE_SPRITEBOT -command [ modtools/create-unit -race SPRITEBOT -caste MALE -domesticate -location [ \\LOCATION ] ]
FYI foe/reaction-trigger is literally just modtools/reaction-trigger with bug fixes/expanded functionality (which doesn't matter in this case). I put the code for it here (http://www.bay12forums.com/smf/index.php?topic=171191.msg7813840#msg7813840) a couple of weeks back, if you want to take a look.Geez, has nobody used siege-engine in the past 2 years?Bear in mind that this doesn't show up if you just play around with siege-engine a bit using unskilled operators. I tried embarking in vanilla to test a couple of things, lucked out and landed on top of a Roc lair, and fired ballista bolts at both the Roc and a herd of boars for a while without any errors. I had to dig around in the c++ code to figure out why, but dabbling operators can't aim at specific creatures (only an area), and the buggy code is only executed when weighting targets for the random target creature selection algorithm. You have to fire a siege weapon with a trained operator at an area containing creatures to get an error thrown.
dfhack.timeout(1,'frames',function() gui.simulateInput(dfhack.gui.getCurViewscreen(),tostring(...)) end)
I wonder, is there existing or a possibility of a dfhack command that can work during worldgen or other high-lag states, that allow dfhack to inject a keyboard input on the next frame that DF advances? For instance, allowing one to pause worldgen's time advancement in it's tracks when the worldgen has become laggy enough to start ignoring key inputs. A similar case could be asked in times of extreme DF Fortmode lag, where it's difficult to regain control as the game doesn't seem to like listening for key inputs when a frame is taking more than a few seconds to pass.This is exactly what the "fpause" command is for - it's worked in fortress mode since before I joined, and it supports worldgen now too (as of 0.44.10-alpha1). No need to roll your own for this.
I think the most important reason was the long-standing bug which caused crashes when using siege ammo (and weapon traps). It was introduced in June 2016, and fixed only in late November 2017. Most people stopped using siege engines because of that.I don't remember seeing this linked to siege ammo on the weapon tracker - I think the main reason is that siege engines aren't used much, due to limitations which the plugin intends to address. Also, there were longstanding compatibility issues with TWBT (fixed in DFHack 0.43.05-r2) which may have discouraged people who insisted on TWBT from using it.
Ok, did that. There was also one other place where pow was used instead of ^. After replacing both of them with ^, I fired about 30 ballista rounds at a siege that showed up at the perfect moment without any errors or other problems. Hopefully that is it for siege-engine bugs.Ok, I think I fixed it in https://github.com/DFHack/dfhack/blob/1c137f9a358ff01346567cc0354b2096599428e5/plugins/lua/siege-engine.lua . If you could double-check and make sure my fixes work, since you're good at reproducing it, that'd be great.
Bear in mind that this doesn't show up if you just play around with siege-engine a bit using unskilled operators. I tried embarking in vanilla to test a couple of things, lucked out and landed on top of a Roc lair, and fired ballista bolts at both the Roc and a herd of boars for a while without any errors. I had to dig around in the c++ code to figure out why, but dabbling operators can't aim at specific creatures (only an area), and the buggy code is only executed when weighting targets for the random target creature selection algorithm. You have to fire a siege weapon with a trained operator at an area containing creatures to get an error thrown.Yeah, I figured it was something non-trivial - I played with it a bit when testing the above TWBT fixes, but probably not enough to trigger that. Thanks for the explanation.
Nice! That's good to hear, thank you.I wonder, is there existing or a possibility of a dfhack command that can work during worldgen or other high-lag states, that allow dfhack to inject a keyboard input on the next frame that DF advances? For instance, allowing one to pause worldgen's time advancement in it's tracks when the worldgen has become laggy enough to start ignoring key inputs. A similar case could be asked in times of extreme DF Fortmode lag, where it's difficult to regain control as the game doesn't seem to like listening for key inputs when a frame is taking more than a few seconds to pass.This is exactly what the "fpause" command is for - it's worked in fortress mode since before I joined, and it supports worldgen now too (as of 0.44.10-alpha1). No need to roll your own for this.
modtools/projectile-trigger -material INORGANIC:IRON -command [ modtools/anonymous-script "print(df.unit.find(tonumber(args[1])).pos)" \\FIRER_ID ]
¤ is nowhere on my keyboard and i have no idea what it represents; @# and ~ being in lua already are moot, since we're talking about strings here'¤' is a generic currency sign, but I'm not aware of it being used for anything anywhere, so it's rather pointless.
Ok, I think I fixed it in https://github.com/DFHack/dfhack/blob/1c137f9a358ff01346567cc0354b2096599428e5/plugins/lua/siege-engine.lua . If you could double-check and make sure my fixes work, since you're good at reproducing it, that'd be great.I got around to testing siege-engine. I fired about 50 rounds at a siege, and rounds were getting aimed correctly with no errors being thrown in the process. The code changes themselves look right too. I think everything is good.
If you replace the backslashes with another icon, PLEASE support both.It should be a find-and-replace matter - not sure what exactly happened with reaction-trigger, as I'm not involved with modtools scripts much. I don't mind supporting both for a while, but eventually (if we go down this route) I'll probably introduce warnings and then remove backslash support, to discourage people from using it and complaining when things don't work because they need 8 backslashes for it to work.
Completely scrapping the old way of reaction trigger with the newer version was... jarring, to say the least. At least when you have a mod that relies on around a thousand entries of it.
Hm, don't really know an use for ¤, though?Turns out this is the one symbol you mentioned that I've never even seen before. It's definitely not on American keyboards, and unlike £, I'm not even sure how to type it on my keyboard. I'd rather stick with ASCII characters, but thanks for the input.
[DFHack]# lua
Type quit to exit interactive lua interpreter.
Shortcuts:
'= foo' => '_1,_2,... = foo'
'! foo' => 'print(foo)'
'~ foo' => 'printall(foo)'
'^ foo' => 'printall_recurse(foo)'
'@ foo' => 'printall_ipairs(foo)'
All of these save the first result as '_'.
Maybe I'm blind, but what happened to item-syndrome?Use item-trigger + add-syndrome. Here is an example for an item that provides a constant buff:
Was it merged into item-trigger?
modtools/item-trigger -itemType ITEM_ARMOR_POWER -onEquip [ Worn ] -command [ modtools/add-syndrome -syndrome "power armor" -target \\UNIT_ID -resetPolicy DoNothing ]
modtools/item-trigger -itemType ITEM_ARMOR_POWER -onUnequip [ Worn ] -command [ modtools/add-syndrome -syndrome "power armor" -erase -target \\UNIT_ID ]
This one already exists as "tweak cage-slaughter". The rest would be possible, although possibly hard to fit in the UI, and I'm not sure if just setting the relevant flags is enough to trigger jobs.
- "Slaughter" (and possibly "Geld"?) hotkeys and corresponding flag indicators to the (q)Building menu of a built cage
Has anyone ever made script to watch for clothes/armor shortages? it is always hard to know how much you need to make of stuff to make your dwarves stop running around in rags.
Why not use autobutcher (https://dfhack.readthedocs.io/en/stable/docs/Plugins.html#autobutcher)? It butchers oldest adults and youngest kids (in order to allow oldest kids to became adult faster) and there is no need for caging (it actually completely ignores caged animals).The problem with autobutcher is actually precisely that it can't discriminate by caged status. Birds lay a lot of eggs and I don't want the FPS hit of having up to 300 of them running around (pop cap 50 for each of 6 bird species), so I prefer to just cage all the hatchlings since they don't need to graze anyway. But when those hatchlings mature in the cages, autobutcher will happily slaughter my (older) breeding adults in the pasture and "replace" them with (younger) adults in the cages, which I'd then have to manually put back in the pasture every 3 months so they could lay new eggs. Besides which, managing slaughters is not that big a deal that it requires full automation, it's just a little tedious now because not all the relevant information is on one screen.
Oh, neat, I'll give that a try.This one already exists as "tweak cage-slaughter". The rest would be possible, although possibly hard to fit in the UI, and I'm not sure if just setting the relevant flags is enough to trigger jobs.
- "Slaughter" (and possibly "Geld"?) hotkeys and corresponding flag indicators to the (q)Building menu of a built cage
That raises are more general question though: what's the criteria for which of the various tweaks and UI extensions are enabled by default (i.e. labor manager, tweak eggs-fertile) and which are hidden behind commands that have to be run explicitly every time (i.e. embark-assistant, tweak cage-butcher)?Actually, labormanager is not enabled by default, and tweak cage-butcher is. Maybe you're using a pack that has overridden these? labormanager is a play-style preference that shouldn't be forced on everyone by default. Also, tweak eggs-fertile was disabled for being too "cheaty" until https://github.com/DFHack/dfhack/pull/970 - I didn't remember accepting that, but apparently I did.
Actually, labormanager is not enabled by default, and tweak cage-butcher is.Oh, when I wrote labormanager I guess I meant manipulator; I think I got them mixed up since the title bar of the manipulator interface says "Manage Labors", which of course is what it lets me do. :)
In general, there are only a few categories of tools that are enabled by default:Has a case been made already for embark-assistant to be enabled by default, or at least enable-able with a red hotkey on the pre-embark screen so more people realize it exists? It seems to me that it's a straight upgrade over the vanilla site finder; it can do all the same things and more, without forcing the user to employ any of its extra filters. The main objection I could think of is maybe some folks don't want to know in advance what ores are present, but I can't imagine it would be too hard to leave that part of it off by default and give it a separate hotkey for people who want to display that information.
- things that are fairly uncontroversially improvements (tweak stable-cursor, tweak burrow-name-cancel, tweak kitchen-prefs-all, search, etc.)
- things that, when enabled, only add options in-game to start using them, but don't actually do anything until then (manipulator, autogems, autochop, automelt, autotrade, etc.)
- bugfixes
- keybindings (that don't conflict with vanilla keys and are unlikely to be triggered accidentally)
If by enabling embark-assistant you mean running it every time the embark screen shows up, that would negatively impact user experience, for one thing, since you'd have to press "esc" twice after (seemingly) entering the embark screen to get to the options menu (i.e. you have to "exit" a screen you didn't explicitly enter). It also has some screen size requirements, and would display poorly for people with smaller screens.Doesn't look that way to me; when I run "embark-assistant" all that happens at first is the "f: Find Desired Location" label disappears from the bottom area (because it's replaced by new hotkeys on the right), the new i/f/c/q hotkeys appear on the right, and the layers/materials details appear below the local map. A single "esc" still goes directly to the options menu; the only previous behavior that changes is "f" opens the advanced site finder instead of the stock one, but that's exactly the point. :)
Adding an option to enter it in the UI should definitely be doable, though.
For butcher/slaughter indicators, my concern about the unit list is a lack of available space, but I can look at it. What "stocks page" are you referring to? The vanilla z -> Animals screen definitely has both a slaughter and geld option, and I'm not aware of a separate one (even provided by DFHack) that lists animals.I think even at 80 columns the unit list has enough space to stick a little 2 or 4 character "Sl"/"Ge" or "Sltr"/"Geld" flag over on the right side, and hotkey labels on the bottom row just to the right of the ones DFHack already adds ("l: Manage Labors (DFHack)" and "q: Search").
This would be relatively easy (haven't looked at other pages). For hotkeys, just add these into dfhack.init:
- "Slaughter" (and possibly "Geld"?) hotkeys and corresponding flag indicators to the (u)Units:Pets/Livestock page
keybinding add S@unitlist/Livestock ":lua dfhack.gui.getSelectedUnit().flags2.slaughter = not dfhack.gui.getSelectedUnit().flags2.slaughter"
keybinding add X@unitlist/Livestock ":lua if(not dfhack.units.isFemale(dfhack.gui.getSelectedUnit())) then dfhack.gui.getSelectedUnit().flags3.unk29 = not dfhack.gui.getSelectedUnit().flags3.unk29 end"
These keys are also used by native suspend job/remove worker hotkeys, but my pets don't do jobs. keybinding add U@dwarfmode/Default ":lua dfhack.script_environment('gui/indicator_screen').getScreen({{text = function() if(dfhack.gui.getCurFocus():find('Livestock')) then return 's' else return '' end end, color = (8+COLOR_RED), notEndOfLine=true}, {text = function() if(dfhack.gui.getCurFocus():find('Livestock')) then return ': slaughter ' else return '' end end, color = function() if (#df.global.gview.view.child.child.units[df.global.gview.view.child.child.page]>0 and dfhack.gui.getSelectedUnit().flags2.slaughter) then return COLOR_YELLOW else return COLOR_WHITE end end}},{x=47, y = -3, width=14}):show() dfhack.script_environment('gui/indicator_screen').getScreen({{text = function() if(dfhack.gui.getCurFocus():find('Livestock')) then return 'x' else return '' end end, color = COLOR_LIGHTRED, notEndOfLine=true}, {text = function() if(dfhack.gui.getCurFocus():find('Livestock')) then return ': geld ' else return '' end end, color = function() if (#df.global.gview.view.child.child.units[df.global.gview.view.child.child.page]<1) then return COLOR_DARKGREY else if (dfhack.gui.getSelectedUnit().flags3.unk29) then return COLOR_YELLOW elseif(dfhack.units.isFemale(dfhack.gui.getSelectedUnit()) or dfhack.units.isGelded(dfhack.gui.getSelectedUnit())) then return COLOR_DARKGREY else return COLOR_WHITE end end end}},{x=62, y = -4, width=16}):show()"
Yeah, I've gelded and butchered people (pfft) just by setting the flag. (flags3.unk29, flags2.slaughter)This one already exists as "tweak cage-slaughter". The rest would be possible, although possibly hard to fit in the UI, and I'm not sure if just setting the relevant flags is enough to trigger jobs.
- "Slaughter" (and possibly "Geld"?) hotkeys and corresponding flag indicators to the (q)Building menu of a built cage
I think even at 80 columns the unit list has enough space to stick a little 2 or 4 character "Sl"/"Ge" or "Sltr"/"Geld" flag over on the right side, and hotkey labels on the bottom row just to the right of the ones DFHack already adds ("l: Manage Labors (DFHack)" and "q: Search").Drawing indicators in the middle of the unit list is a bit tricky because of highlighting (and figuring out where dwarves are displayed on the screen). Fitting in the options at the bottom shouldn't be hard, now that I'm actually looking at the screen.
On the stocks page (z->Animals) yes the slaughter/geld options and flags are there, what's missing on that page is the restrained/caged/pastured status indicator, which I think there's be plenty of room for alongside the existing flags ("D" for Domesticated, "T" for train, "W" for war; just add "R" for restrained, "C" for caged, "P" for pastured?).
How hard would it be to display a warning every time an unk of anon variable is referred to via script?Not a good idea!
If unk29 is the geld flag, that should be changed to a proper name (and ideally you would mention it rather than just using it in scripts). As for the long line - yeah, you're probably better off giving that as a script, because it's awfully hard to troubleshoot something like that.unk 29 in flags3 is set to true when gelded is toggled through the normal DF ui.
Edit: ok, flag 28 is called "gelded". Is flag 29 "to be gelded" or something, or did we just get 28 wrong entirely?
...
Taleden, maybe I was mistaken about how to exit embark-assistant, but it does replace vanilla functionality and has an "exit" option of some kind, which would be confusing if an "enter" action wasn't taken. I put the UI option idea in the issue tracker, so hopefully I remember to get to it. Also, keybindings wouldn't be as good as UI additions because they wouldn't be visible to the user in-game at all.
My "hotkey with no on-screen label" remarks were about Fleeting Frames' slaughter/geld suggestions, not related to yours at all.Taleden, maybe I was mistaken about how to exit embark-assistant, but it does replace vanilla functionality and has an "exit" option of some kind, which would be confusing if an "enter" action wasn't taken. I put the UI option idea in the issue tracker, so hopefully I remember to get to it. Also, keybindings wouldn't be as good as UI additions because they wouldn't be visible to the user in-game at all.
Yeah, looking more closely at the embark-assistant site finder options, it would need a few more tweaks to ensure that it is strictly an enhancement and completely replicates all vanilla functionality (i.e. right now it cannot search by elevation as such, although it can search for waterfalls or for flat embark zones). But if that were done then I think it would not need the "exit" option at all any more, since the only vanilla functionality that it masks is "f: Find Desired Location"; it might need a toggle to display the more detailed layer/ore information to satisfy folks who consider that a spoiler, but it could otherwise just stay on.
Meanwhile, when I suggested the interim hotkeys or keybinding to toggle it on, I meant to also imply the UI addition to make the user aware of the option, i.e. "a: Enable Advanced Finder"; I agree of course a hotkey with no on-screen label would be no better than the current situation, the whole idea there was to make more users aware of the existence of the tool. :)
I think "a: Advanced Finder" is certainly good enough.
dfhack-0.44.12-r1-Linux-64-gcc-7Not linux specific, happens on the windows version too.
tweak hotkey-clear is misbehaving (for me)
Firstly no matter what hotkey is selected 'c' clears the F1 hotkey.
Secondly when 'c' is used in a name it clears the F1 hotkey (and does not appear in the name).
dfhack-0.44.12-r1-Linux-64-gcc-7Yeah, some stuff was added to "ui" in 0.44.12, and it's entirely in the wrong place - it appears to be related to the issue that broke squads in df-ai in 0.44.12. More details here: https://github.com/DFHack/dfhack/issues/1393 - obviously this is something we want to fix for r2.
tweak hotkey-clear is misbehaving (for me)
Firstly no matter what hotkey is selected 'c' clears the F1 hotkey.
Secondly when 'c' is used in a name it clears the F1 hotkey (and does not appear in the name).
I don't think there is a thread just for labormanager... but just have one small request.... that it be able to be disabled on a per-dwarf basis (by something simple like ignoring dwarfs that have the unused "Alchemist" labor set.)I feel like this may have been suggested before. https://github.com/DFHack/dfhack/issues/185 is a similar suggestion for autolabor - out of curiosity, does labormanager also skip dwarves in burrows? I haven't checked.
I'm sorry if this is a stupid question, i've tried searching for the answer but I haven't been able to find one. I have ran one or two of the 'deteriorate' scripts but i'm unsure whether these are permenantly stored once ran or if they need to be activated each time a game is loaded.They are not "permanently stored" (I mean, the script files themselves are permanently stored on disk in hack/scripts, but their enabled status is not stored in any way, like most tools'). You'll need to run them every time you load a save where you want them to be run. However, you can do this automatically by either putting the commands in onLoad.init in your DF folder (if you want them to apply to every save), or data/save/regionX/raw/onLoad.init if you want them run in individual save(s). Create those files as needed; more details at https://dfhack.readthedocs.io/en/latest/docs/Core.html#init-files. (Particularly if you go with a global .init file for this, you might need to use onMapLoad.init to avoid errors with those scripts in worldgen.)
I work strange hours so I have the habit of playing the game, saving it onto a memory stick by coping the region data in the save file, and then using that to continue the game when i'm on another computer; should I be running the scripts each time?
Is there a simple way to see which scripts are and are not running?
I don't think there is a thread just for labormanager... but just have one small request.... that it be able to be disabled on a per-dwarf basis (by something simple like ignoring dwarfs that have the unused "Alchemist" labor set.)Assigning a dwarf to a burrow (even if the burrow covers the entire embark) will cause that dwarf to be entirely ignored by labormanager (see https://github.com/DFHack/dfhack/blob/develop/plugins/labormanager/labormanager.cpp#L1333). I had no reason to remove that behavior (which I added to autolabor many many moons ago: https://github.com/ab9rf/dfhack/commit/78fc850ce20e14d7a37e44c18dc4486ee61796b4) when I forked autolabor to make labormanager.
I use :lua dfhack.timeout(N, "ticks", function () dfhack.run_command('fpause') end)Great, thanks! I wrapped that in a little script so I can type "step 10" for example and it seems to work, with two quirks: I have to manually unpause the game after issuing the command in order for it to then step forward N frames, and after it does so it prints "The game was forced to pause!" as if this were cause for concern. Is there any way to make my script automatically unpause the game after setting the pause timer, and to trigger that timer without the associated warning text?
Also something similar in a script (http://dwarffortresswiki.org/index.php/User:Fleeting_Frames#cposchange) for minecarts.
Is there a script to 'T'ravel through mountains?I'm not aware of one. It might be simple to do if there's a way to bypass the checks in the adventure mode UI, or it might be incredibly complicated if there isn't. The adventure mode UI hasn't been researched much.
Use "df.global.pause_state = true" ... Also, "df.global.pause_state = false"Thanks, this works perfectly!
<!-- CONFLICT vtable-address name='viewscreen_storesst' value='0x1e4c23c or 0x1e4b994 or 0xf694f8'/ -->
It should be something like <vtable-address name='viewscreen_storesst' value='0xSOMETHING'/>
and stderr.log should contain the appropriate line to replace it - please let us know what this is.
<vtable-address name='viewscreen_storesst' value='0xf694f8'/>
What does "apparently" mean? Does the search option show up in the stocks screen, and does the "e"xtended stocks screen work? If so, then they work, as far as I'm concerned.
Ok, then can you check the things I asked about (z->stocks->'s'earch and 'e'xtended view)? If those don't crash, then it's fine.
Is there a script to 'T'ravel through mountains?
utils=require('utils')
validArgs = utils.invert({
'reaction',
'skill',
'level'
})
args = utils.processArgs({...}, validArgs)
if not (args.skill and args.reaction and args.level) then qerror("Must have -level, -reaction and -skill arguments!") end
require('plugins.eventful').onReactionComplete[skillRequirement..args.reaction]=function(reaction,reaction_product,unit,input_items,input_reagents,output_items,call_native)
if dfhack.units.getEffectiveSkill(unit,df.job_skill[args.skill])<=tonumber(args.level) then
call_native.value=false
dfhack.gui.makeAnnouncement('CANCEL_JOB',{D_DISPLAY=true},unit.pos,dfhack.TranslateName(dfhack.units.getVisibleName(unit)..' cancels '..reaction.name..': not skilled enough.',COLOR_RED,true))
end
end
somevalue = 0
function blahblah()
-- Do something with the function
end
and call it from a script like-- Script stuff
dfhack.script_environment('functions').blahblah()
-- More script stuff
will the function blahblah know what the value of somevalue is?Absolutely. blahblah() always has access to its containing environment regardless of where it's called from.
Note that in the example you gave, re-running functions.lua (e.g. if you ran it at the command prompt) would reset somevalue to 0 ("somevalue = somevalue or 0" would avoid that). script_environment() usually won't reset that, but it might if the script file is modified. run_script() is what the DFHack console actually uses to run Lua scripts, I think, so it should always reset that.
EDIT: Looking through the currently written scripts I see there is full-heal which can heal and resurrect units (and it seems regrow limbs and such). Are there any scripts that replicate the animation of units and unit corpse pieces? I am going over some of the stuff I wrote which allows for more complex resurrections and reanimations, but it would be nice to have other working examples to go off of.If you mean in DFHack, no. We almost never make duplicate copies of tools just to remove features from one copy. If you want to modify full-heal to take more options to skip certain steps (I think there are some already), feel free, and feel free to submit a pull request as well.
EDIT2: Or other wound heal scripts that aren't quite so full? Particularly ones that heal but don't necessary regrow lost limbs.
I can see them with gm-editor here: "gui/gm-editor world.cultural_identities.all". Unfortunately, I can't seem to get LUA to recognize the same location, and all my attempts fail with "attempt to index a nil value".Can you specify where exactly you're using this and what you've attempted? If you're literally using "world.cultural_identities.all" in a Lua script ("Lua" isn't an acronym, by the way), that won't work - you'll need to replace "world" with "df.global.world". A few scripts, such as the Lua interpreter (run "lua") and gui/gm-editor search for undefined variables in other places, including fields in df.global (so the "df.global." part can be dropped), but in normal scripts that won't work.
I've seen Atkana's script on setting them, but the location used there doesn't seem to work either (df.cultural_identity.find(upers.cultural_identity).values[beliefId]).That looks like it should work to me. What exactly is the issue? Obviously "upers" needs to be defined as well, and I'm not sure what it's supposed to be (it's not an English word, at least).
Can anyone throw out a lua print statement that dumps out the contents of "world.cultural_identities.all[0].values"?(In case you're unaware, printall() might produce more useful output - it's also available as the "~" prefix in the Lua interpreter.)
Hi, I'm trying to retrieve the default belief values for a civilization, but I'm having a hard time determining how to get to them.
I can see them with gm-editor here: "gui/gm-editor world.cultural_identities.all". Unfortunately, I can't seem to get LUA to recognize the same location, and all my attempts fail with "attempt to index a nil value".
I've seen Atkana's script on setting them, but the location used there doesn't seem to work either (df.cultural_identity.find(upers.cultural_identity).values[beliefId]).
Can anyone throw out a lua print statement that dumps out the contents of "world.cultural_identities.all[0].values"?
for k, v in pairs(df.global.world.cultural_identities.all[0].values) do
print(k .. " = " .. v)
end
I used upers as a shorthand for unit personality because if I'm going to be too lazy to type out unit.status.current_soul.personality every time, I figured I might as well go full shorthand :PQuoteI've seen Atkana's script on setting them, but the location used there doesn't seem to work either (df.cultural_identity.find(upers.cultural_identity).values[beliefId]).That looks like it should work to me. What exactly is the issue? Obviously "upers" needs to be defined as well, and I'm not sure what it's supposed to be (it's not an English word, at least).
Minor nitpick, but that's also just an expression - there's no location in there.
function flymode()
local flyuser = df.global.world.units.active[0]
flyuser.enemy.unk_4406_1[4]=true -- dont know what these do
flyuser.enemy.unk_4406_1[10]=true --dont know what these do
flyuser.enemy.unk_4406_1[19]=true --this unlocks flymode
end
ok so broke down and figured out how to just Fly with out needing flier, so uhh now I guess the whole turning on ghostly flag being bugged is fixed with this.Code: [Select]function flymode()
ok so broke down and figured out how to just Fly with out needing flier, so uhh now I guess the whole turning on ghostly flag being bugged is fixed with this.
local flyuser = df.global.world.units.active[0]
flyuser.enemy.unk_4406_1[4]=true -- dont know what these do
flyuser.enemy.unk_4406_1[10]=true --dont know what these do
flyuser.enemy.unk_4406_1[19]=true --this unlocks flymode
end
also bonus silly things with testing with flying is the drag script causes the draggee to float up 1 tile if you drag them up there.
unit.enemy.unk_4406_1
corresponds to the creature token flags for the unit's caste, as found in df.creature_raw.find(unit.race).caste[unit.caste].flags
for _,unit in ipairs(df.global.world.units.all) do for k,v in ipairs(df.creature_raw.find(unit.race).caste[unit.caste].flags) do if unit.enemy.unk_4406_1[k]~=v then print(unit.id,k,v) end end end
I tested every unit on a map with this one-liner to corroborate this:welp I just saved in the air with a giant wagon train, pulling off some neverending story nonsense.Code: [Select]for _,unit in ipairs(df.global.world.units.all) do for k,v in ipairs(df.creature_raw.find(unit.race).caste[unit.caste].flags) do if unit.enemy.unk_4406_1[k]~=v then print(unit.id,k,v) end end end
Nothing printed at all, so that checks out.
I also checked if it saves with the world; it does not.
i forgot how to fix mousequery... i think it was just a fixed dfhack .plug.so somewhere on github though.Here's what you're referring to: http://www.bay12forums.com/smf/index.php?topic=126076.msg7830622#msg7830622
copypastinging the mousequery.plug.so from the 44.09 just doesnt work - saying it were written for another version of dfhack.This behavior has always been the case since before 2010. Loading plugins for a non-matching DFHack version is unsafe, so DFHack prevents it.
the only thing that doesnt work is the scrolling on screenborder when not in any dmode or lmode or qmode.Mousequery has a number of features, each of which you have to enable individually. Run "mousequery help" or maybe "help mousequery" for a list, then enable everything listed and see if that solves your issue.
it is that it only ignores the bottom and right screen border for scrolling. otherwise it works fine. that's the only thing weird about mousequery. manually enabling it helped if it wasnt working at all, but it does work, but oddly.i forgot how to fix mousequery... i think it was just a fixed dfhack .plug.so somewhere on github though.Here's what you're referring to: http://www.bay12forums.com/smf/index.php?topic=126076.msg7830622#msg7830622
It would be helpful if you could remember to link to what you're talking about instead of just randomly cross-posting in other threads. Few people in this thread would have any idea what issue you're referring to otherwise.
Also, DFHack itself has only ever distributed one mousequery plugin per release. We didn't put up a "fixed" build anywhere special - as far as we know, it's not a DFHack-caused issue. TWBT distributed its own for a while, but stoppedQuotecopypastinging the mousequery.plug.so from the 44.09 just doesnt work - saying it were written for another version of dfhack.This behavior has always been the case since before 2010. Loading plugins for a non-matching DFHack version is unsafe, so DFHack prevents it.Quotethe only thing that doesnt work is the scrolling on screenborder when not in any dmode or lmode or qmode.Mousequery has a number of features, each of which you have to enable individually. Run "mousequery help" or maybe "help mousequery" for a list, then enable everything listed and see if that solves your issue.
So, tangent to my post right above, figured I need to be able to simulate vanilla d-x behaviour, and that means erasing jobs and returning worker to No Job, even after they're taken.seemed to accomplish that objective, buuut when I was escaping back into the screen of viewing the now-erased job in gui/gm-editor, the game segfaulted.Spoiler: So, this (click to show/hide)
Predictable and expected with nil pointer.
...But I can't predict every common access. Would this cause an issue elsewhere (outside of using it while in, say, manipulator's screen)? I'm half expecting eventful to be unhappy if job is started and erased instead of finished, though I'd expect it might cause an issue with dfhack.jobs.removeWorker then too.
Is there a way to change abstract building internal extents in lua? dfhack.buildings can tell and iterate over building.room, telling me whether a tile is part of it, but attempting to do the same in lua is a failure (and the building.room.extent only has 1 lonely value).extent is a pointer, which points to the first element of the "extent" array in C, but you can index it (e.g. extents[1]) to access other elements. There's also the _displace() method, mentioned in https://dfhack.readthedocs.io/en/stable/docs/Lua%20API.html, which does the same thing. Note that you are responsible for determining the appropriate maximum index in this case, as you would be in C as well - an out-of-bounds index can cause undefined behavior/crashes.
Is there any way dfhack could extend manager orders to add item quality as a conditional? i.e. I can set an order to require "thrones < 10" or even "undecorated quartzite thrones < 10" but I'd love to be able to specify "masterful thrones < 10".had to do a workaround before, seperating the items in different stockpiles and selling the unwanted.
df.global.gview.view.child.child.squad_unk
as if I don't have this align with the new squad that's been added the game will crash. which means making a script that checks the list of how many squads there are and inserting them to the Gview squad pool, and also equally inserting a new squad_unk for each of them.
I have no idea if it works, but have you tried changing the DF settings to disable cave-ins, remove what you want to remove, save, exit, enable cave-ins, and start your save again? I generally set up my cave-ins to be triggered by a lever pull removing a support.
[code]
function z ()
local unit = dfhack.gui.getSelectedUnit (true)
local material = dfhack.matinfo.find ("PLANT:SINGLE-GRAIN_WHEAT:DRINK")
local preference = df.unit_preference:new ()
preference.type = df.unit_preference.T_type.LikeFood
preference.item_type = df.item_type.DRINK
preference.item_subtype = -1
preference.mattype = material.type
preference.matindex = material.index
preference.mat_state = df.matter_state.Solid -- Stupid for drinks, but everything seems to be specified as solid
preference.active = true
preference.prefstring_seed = 1 -- Or whatever
unit.status.current_soul.preferences:insert ("#", preference)
end
z ()
[/code]Also, the 75% ratio isn't checked for eggs; shown as I had 3 hens to something like 36 chicks at the time second set of eggs.
"enable autodump"
@Kat
I think this script might workCode: [Select][code]
[/code]
function z ()
local unit = dfhack.gui.getSelectedUnit (true)
local material = dfhack.matinfo.find ("PLANT:SINGLE-GRAIN_WHEAT:DRINK")
local preference = df.unit_preference:new ()
preference.type = df.unit_preference.T_type.LikeFood
preference.item_type = df.item_type.DRINK
preference.item_subtype = -1
preference.mattype = material.type
preference.matindex = material.index
preference.mat_state = df.matter_state.Solid -- Stupid for drinks, but everything seems to be specified as solid
preference.active = true
preference.prefstring_seed = 1 -- Or whatever
unit.status.current_soul.preferences:insert ("#", preference)
end
z ()
You'd have to replace "SINGLE-GRAIN_WHEAT" in the string on the third line with your plant name.
At least it added single-grain wheat beer to my test subject's preferences in addition to the pre-existing prickleberry one. Note that the unit to be subjected to the script has to be selected with 'v' or in the units list.
@taledan:
When did you slaughter? I suspect that poultry, once pregnant, can't get pregnant again until they stop being pregnant, which means you might have had the previous batch laid in later half of pregnancy.
I can provide saves for both turkey hens being pregnant and having laid eggs after first set hatched and of second clutch of turkey poults hatching with no turkey hens of gobblers alive (though as I've seen, that's not something to rely on).
That should remove the third issue, to my knowledge.There is also the far less arcane persist-table wrapper, which is AFAICT completely undocumented aside from the comments in its file in the lua folder for some reason, despite the fact that it is really useful.
Pretty expansive idea; someone other than me would have to pick it up, though. At least while I still have scripts I want to write more.
Yup, dfhack's lua can totally save data in game save (https://dfhack.readthedocs.io/en/latest/docs/Lua%20API.html#persistent-configuration-storage).
Looking at it (https://github.com/DFHack/dfhack/blob/master/library/lua/persist-table.lua), I'd guess the some reason is "All stored values MUST be strings. Numbers can be supported later but are not yet supported.".
Still, storing tables recursively? Assigning/retrieving things with less code? Could be pretty useful, and didn't know about it. (It's not useful now, though, since I already wrote what I was storing, but...Hm. Could make for, say, persistent context-script pairs without editing init for each one.)
I use persistent tables extensively in my scripts. A single fort that runs for a couple years could have thousands of them.
I am almost afraid to ask, but...how many persistent tables are getting used in your scripts, Rose? And how often are you reading from them?I use persistent tables extensively in my scripts. A single fort that runs for a couple years could have thousands of them.I've run profiling in Fortbent before I stopped using your class system and doing this caused something like 20% of the CPU time during processing of the game in a later fort and by itself was dropping the FPS below 100 in the early game, so I wouldn't recommend this too much. In fact, that's why I stopped using your class system; the heavy use of persist-table was causing massive slowdown.
...
I am almost afraid to ask, but...how many persistent tables are getting used in your scripts, Rose? And how often are you reading from them?I use persistent tables extensively in my scripts. A single fort that runs for a couple years could have thousands of them.I've run profiling in Fortbent before I stopped using your class system and doing this caused something like 20% of the CPU time during processing of the game in a later fort and by itself was dropping the FPS below 100 in the early game, so I wouldn't recommend this too much. In fact, that's why I stopped using your class system; the heavy use of persist-table was causing massive slowdown.
...
I ask because I have a TLCM randomizer script that keeps track of what creatures have had their appearance randomized via persist-table, and in a developed fort it could have as many as 3000 entries (3-4 records for the majority of civilized creatures that have been active at some stage). The persistent entries only get read once per save load cycle before a non-persistent table of checked creatures is filled, but those entries are still there, and now Putnam has me worried that I am unwittingly nuking a fort's late game FPS.
I probably would've stuck with roses stuff if I'd been able to get the JSON saving to reliably save with the game. I couldn't figure out a way to tell if quicksaving/autosaving was going on. The problem was more that every single table check was a persist-table check, IIRC.
could someone add some more options to the furniture planning mode?This was done in https://github.com/DFHack/dfhack/pull/1284. I don't think that's part of a release yet. Maybe we should make another release.
there's min quality, but not max quality
Also, is there a gui element (with the onInput() method) or another means of receiving keypresses that doesn't require the game being paused like a framed screen does?Nearly all screens will pause the game. You might be able to advance fortress mode by calling logic() on the dwarf mode screen, but there's usually a good reason for the game to be paused.
Nearly all screens will pause the game. You might be able to advance fortress mode by calling logic() on the dwarf mode screen, but there's usually a good reason for the game to be paused.
What are you doing, anyway? This sounds suspiciously like some kind of always-present UI element that you're trying to add with Lua. Lua isn't very well-suited for that (well, custom screens aren't suited for that, but they're basically the only way to do that in Lua). It's easy to break the game (e.g. by not handling the options/help/movies keys properly, or just leaving the screen around at inappropriate times). I would recommend writing a plugin if that's what you're trying to do, because you can modify existing screens in C++ (and can still write the rest in Lua, if you want).
I'm using DFHack 0.44.12-r1 from PESP 0.44.12-r03 and I see a "Max Quality" setting for building plan, which I've made good use of. But some other additions I'd find useful are a "Non-Decorated Only" (there's already Decorated Only, but sometimes I want to specifically avoid using that stuff), and/or even better, a way to set a minimum or maximum item value (i.e. to make sure the really good decorated stuff goes in the royal throne room, while the mediocre decorated stuff goes in the suite of the new baron who just inherited from some other site I don't care about).could someone add some more options to the furniture planning mode?This was done in https://github.com/DFHack/dfhack/pull/1284. I don't think that's part of a release yet. Maybe we should make another release.
there's min quality, but not max quality
oh, thanks. i haven't played with 0.44.12-r1 any further after seeing that mousequery edge wouldn't work properly. it still doesn't as i read twbt and dfhack have been merged and it seems the one that had the fixed mousequery was dropped in that process.I'm using DFHack 0.44.12-r1 from PESP 0.44.12-r03 and I see a "Max Quality" setting for building plan, which I've made good use of. But some other additions I'd find useful are a "Non-Decorated Only" (there's already Decorated Only, but sometimes I want to specifically avoid using that stuff), and/or even better, a way to set a minimum or maximum item value (i.e. to make sure the really good decorated stuff goes in the royal throne room, while the mediocre decorated stuff goes in the suite of the new baron who just inherited from some other site I don't care about).could someone add some more options to the furniture planning mode?This was done in https://github.com/DFHack/dfhack/pull/1284. I don't think that's part of a release yet. Maybe we should make another release.
there's min quality, but not max quality
welp time to cross off boats and movable constructions off the personal todo list, I guess that means building a mobile house is possible now.Nearly all screens will pause the game. You might be able to advance fortress mode by calling logic() on the dwarf mode screen, but there's usually a good reason for the game to be paused.
What are you doing, anyway? This sounds suspiciously like some kind of always-present UI element that you're trying to add with Lua. Lua isn't very well-suited for that (well, custom screens aren't suited for that, but they're basically the only way to do that in Lua). It's easy to break the game (e.g. by not handling the options/help/movies keys properly, or just leaving the screen around at inappropriate times). I would recommend writing a plugin if that's what you're trying to do, because you can modify existing screens in C++ (and can still write the rest in Lua, if you want).
I'm working on a construction-based boat for adventure mode, the UI element is just a workaround to receive input as I couldn't find any another way of getting it (my experience and ability being somewhat limited), I want the player to be able to control it in a semi-native fashion by using the arrow keys rather than with a menu or just reactions.
I didn't really want to post anything on the forums until I had something more substantial to show than this crude prototype, but here's an idea of what I'm trying to do:
-snip-
I really hope I don't have to look into making a plugin and there's another method, I've no experience with C++ and only a little experience with Java and lua.
Is there a way for a Lua script to tell if a female egglayer is currently busy sitting on eggs in a nestbox? "unit.job.currentjob" and "unit.actions" don't seem to show it. I can use dfhack.buildings.findAtTile(), :getType() and .claimed_by to check that the bird is standing on her own claimed nest box which is probably close enough, but I wondered if someone knows a more direct way to test for egg-sitting.From what I remember, this isn't an exact science in DF - egglayers can move away from the nestbox for a short time, and the eggs will be fine (unless that's changed). Maybe there's an egg-related timer somewhere in the unit that you can check. I don't know much off the top of my head here, unfortunately.
Edit: maybe a combination of building.claimed_by and eggs.flags.fertile (egg_flags.fertile? not sure) would work, unless you care about birds sitting on unfertilized eggs.I'm trying to work around this issue (http://bay12games.com/dwarves/mantisbt/view.php?id=10924) so the question is only whether the hen is sitting on eggs (and thus unable to mate again in time to lay her next clutch), I don't think it matters for my purpose whether the eggs are fertile or not. So I'll try it for now with just the building claim condition, I think that'll suffice.
Is there a way for a Lua script to tell if a female egglayer is currently busy sitting on eggs in a nestbox? "unit.job.currentjob" and "unit.actions" don't seem to show it. I can use dfhack.buildings.findAtTile(), :getType() and .claimed_by to check that the bird is standing on her own claimed nest box which is probably close enough, but I wondered if someone knows a more direct way to test for egg-sitting.
Has anyone experimented with manually creating social activities to coax a dwarf into, for example, praying or socializing to satisfy an unmet need? Or does anyone know what structure flags or values cause a dwarf's social activity to be urgent (shown in purple)?I've not ever really gone much into social stuff, but I know some things I could point you to that might help:
I've tried a few times to replicate the structures that I can see when a dwarf is engaged in a particular activity (i.e. praying), but I can't seem to make them stick; the dwarf just cancels the activity and finds something else to do, as if it were invalid or something. So I'm not sure if I've missed something or if the game just decides to cancel it and override with a higher priority activity (which could also be why the dwarf never meets that need on their own in the first place).
Does anyone have any knowledge to share about manipulating social activities or is this uncharted territory?
Hi, I'm slow.Hi,
I'm new. DFHack's pretty neat. Some of the documentation is bit poor though. :P
Anything specifically? Feel free to suggest improvements - I can't guarantee that I can improve docs for tools I'm unfamiliar with, but other people might.
"embark-skills points" and "points" are separate, but I think they work on the same screen, so it's possible that DFHack's layout for that screen is wrong (I don't remember checking that one). I'll investigate.
I'm pretty sure preferences are just stored in a vector, so I can't think of an obvious reason why adding "too many" would crash, or why "too many" would even be a possible state. Maybe the preferences it makes are malformed sometimes? In any case, that script is garbage (many hardcoded options for arbitrary preference combinations, lots of duplicated code, etc.), so it'd probably have to be rewritten some anyway.
Edit: you need to run "points" on the embark site-choosing screen (before the screen where you select items). That works in 0.44.12 for me. I can probably add support for the item-choosing screen too, but I can't support anything before a world is loaded, since the relevant field (df.global.world.worldgen.worldgen_parms.embark_points) is reset to the world-specific value when a world is loaded.
Edit: fixed "embark-skills points" as well. Its other options appeared to work fine.
the ones that might be relevant to social/break-related things being "TIME_SINCE_BREAK" and "ON_BREAK". In lua, these counters (and a lot more) can be found in the unit's status.misc_traits (provided they're active/whatever)Hm, I can try those, although I'm not sure they're used any more in the current game version; in my fort of 112 citizens, not a single one has a misc_trait with id 16 (TimeSinceBreak) or 17 (OnBreak). Also, the problem I'm having isn't just with dwarves aborting my forced social activity for an actual job (teal text); they'll take a few steps pathing toward my activity and then stop and switch to a different idle activity (i.e. stop walking to the temple and instead go to Individual Combat Drill or to the library to read). That's why I was hoping to figure out the criteria for Purple! idle activities, to maybe get them to stick with what I assigned and not switch to something else.
The strength of a need can be modified in the unit's status.personality.current_soul.personality.needs, by changing its focus level. I know that a value of 400 is what it's set to when the need is met, and I believe that the value lowers as they get less focused, into the negatives. The numbers listed on the wiki (http://dwarffortresswiki.org/index.php/DF2014:Need) are probably right for reference. You might be able to get dwarves having purple needs by just setting the focus value super low.Yes, those are the focus level values that I'm scanning for dwarves with severely unmet needs (for example I had a military dwarf with -200000 PrayOrMedidate, which I think is the lower limit, because he would always drill in his idle time and never pray).
Syndromes have some special triggers that allow their effects to activate based on the value of special counters (http://dwarffortresswiki.org/index.php/DF2014:Syndrome#Counter_Triggers), with the ones that might be relevant to social/break-related things being "TIME_SINCE_BREAK" and "ON_BREAK".Funny you should mention those ones in particular - I just checked a disassembly of version 0.44.12, and those two actually don't exist anymore. "MOUNTAIN" is also no longer a Counter, but instead has its own distinct field in unit_soul.personality.
Aw, that's a shame - I was getting some dumb ideas for syndromes I could make that use them :bSyndromes have some special triggers that allow their effects to activate based on the value of special counters (http://dwarffortresswiki.org/index.php/DF2014:Syndrome#Counter_Triggers), with the ones that might be relevant to social/break-related things being "TIME_SINCE_BREAK" and "ON_BREAK".Funny you should mention those ones in particular - I just checked a disassembly of version 0.44.12, and those two actually don't exist anymore. "MOUNTAIN" is also no longer a Counter, but instead has its own distinct field in unit_soul.personality.
this calls for a special case of a dwarven mood that gets triggered by being unable to worship their deity for too long - "craft religios symbol":Has anyone experimented with manually creating social activities to coax a dwarf into, for example, praying or socializing to satisfy an unmet need? Or does anyone know what structure flags or values cause a dwarf's social activity to be urgent (shown in purple)?I've not ever really gone much into social stuff, but I know some things I could point you to that might help:
I've tried a few times to replicate the structures that I can see when a dwarf is engaged in a particular activity (i.e. praying), but I can't seem to make them stick; the dwarf just cancels the activity and finds something else to do, as if it were invalid or something. So I'm not sure if I've missed something or if the game just decides to cancel it and override with a higher priority activity (which could also be why the dwarf never meets that need on their own in the first place).
Does anyone have any knowledge to share about manipulating social activities or is this uncharted territory?
- Syndromes have some special triggers that allow their effects to activate based on the value of special counters (http://dwarffortresswiki.org/index.php/DF2014:Syndrome#Counter_Triggers), with the ones that might be relevant to social/break-related things being "TIME_SINCE_BREAK" and "ON_BREAK". In lua, these counters (and a lot more) can be found in the unit's status.misc_traits (provided they're active/whatever). You may have to manipulate these to encourage them to take breaks and stay on said breaks. It looks like there's also some counters that haven't been mapped(?) - I noticed that one of my units had a misc_trait of ID 63 (I think), which according to gm-editor is nil. As a side note, I may have now found a hacky way using this to stop all my civs that don't even have access to clothing from complaining about not having any clothes to wear!
- The strength of a need can be modified in the unit's status.personality.current_soul.personality.needs, by changing its focus level. I know that a value of 400 is what it's set to when the need is met, and I believe that the value lowers as they get less focused, into the negatives. The numbers listed on the wiki (http://dwarffortresswiki.org/index.php/DF2014:Need) are probably right for reference. You might be able to get dwarves having purple needs by just setting the focus value super low. Bear in mind, also, that what needs units have varies from unit to unit. Apparently, I made a modtool for setting a unit's needs (https://github.com/Atkana/Dwarf-Fortress-Mods/blob/master/modtools/set-need.lua), don't know if I ever shared it anywhere :P
modtools/reaction-trigger -reactionName FOE_SPAWN_SPRITE -command [ modtools/create-unit -race FOE_ICE_SPRITE -setUnitToFort -name FOE_TUNDRA -location [ \\LOCATION ] -age 0 ]
I get the error 0.44.12\hack\lua\utils.lua:598: error: invalid arg: 5: race
Looking into it, it seems like it's treating the commands for modtools/create-unit as commands for modtools/reaction-trigger (-race happens to be in the position where a fifth arg would be) when checking for valid args. If I add "race" to reaction-trigger's valid args list, I then get an error for setUnitToFort not being valid.When I run DFHack with valgrind, I get a lot of libruby.so errors. Is that expected and is there a way to suppress them?libruby.so isn't something we wrote (it's from Ruby), and we don't control what it does. Valgrind probably has a way to disable warnings for a specific library.
I get the errorI think your syntax is ok, and the bracket looks like a normal bracket, not some funky Unicode one. Please post the full traceback, not just the one line - that will help identify whether this is originating from create-unit or reaction-trigger.Code: [Select]0.44.12\hack\lua\utils.lua:598: error: invalid arg: 5: race
I think your syntax is ok, and the bracket looks like a normal bracket, not some funky Unicode one. Please post the full traceback, not just the one line - that will help identify whether this is originating from create-unit or reaction-trigger.Haha, yeah sorry - I originally intended to post it all but couldn't figure out how to copy from the command prompt. I've got it now (unless there's a file everything's logged to and this is just a bit of it?)
...1\PERIDE~1.12-\Dwarf Fortress 0.44.12\hack\lua\utils.lua:598: error: invalid arg: 5: race
stack traceback:
[C]: in function 'error'
...1\PERIDE~1.12-\Dwarf Fortress 0.44.12\hack\lua\utils.lua:598: in function 'utils.processArgs'
...tress 0.44.12/hack/scripts/modtools/reaction-trigger.lua:226: in local 'script_code'
...\PERIDE~1.12-\Dwarf Fortress 0.44.12\hack\lua\dfhack.lua:680: in function 'dfhack.run_script_with_env'
(...tail calls...)
The exact line for reaction-trigger might be off by 8 because I just commented out my additions to the valid args table to get it erroring again :P The line that's mentioned in the error (line 226 for my commented-out-additions version) is the line local args = utils.processArgs({...}, validArgs)
libruby.so isn't something we wrote (it's from Ruby), and we don't control what it does. Valgrind probably has a way to disable warnings for a specific library.
{
ignore_rb_newobj_cond
Memcheck:Cond
...
fun:rb_newobj
}
{
ignore_rb_newobj_read8
Memcheck:Value8
...
fun:rb_newobj
}
{
ignore_ruby_xmalloc_cond
Memcheck:Cond
...
fun:ruby_xmalloc
}
{
ignore_ruby_xmalloc_read8
Memcheck:Value8
...
fun:ruby_xmalloc
}
==4507== Conditional jump or move depends on uninitialised value(s)
==4507== at 0x3E696CF3: mousequery_hook::interpose_fn_render() (mousequery.cpp:580)
==4507== by 0x3D31DC8C: autochop_hook::interpose_fn_render() (autochop.cpp:822)
==4507== by 0x6008A06: render_things() (graphics.cpp:537)
==4507== by 0x5FD6E64: enablerst::async_loop() (enabler.cpp:319)
==4507== by 0x5FD825E: call_loop(void*) (enabler.cpp:708)
==4507== by 0x5C872EB: ??? (in /usr/lib64/libSDL-1.2.so.0.11.4)
==4507== by 0x5CCA73E: ??? (in /usr/lib64/libSDL-1.2.so.0.11.4)
==4507== by 0x7B96593: start_thread (pthread_create.c:463)
==4507== by 0x7027E6E: clone (clone.S:95)
==4507==
==4507== Mismatched free() / delete / delete []
==4507== at 0x4C3078C: operator delete[](void*) (vg_replace_malloc.c:621)
==4507== by 0xB59DB3: ??? (in /home/clement/games/df/df_44_12_linux/libs/Dwarf_Fortress)
==4507== by 0xB5A060: ??? (in /home/clement/games/df/df_44_12_linux/libs/Dwarf_Fortress)
==4507== by 0x10AD3A1: ??? (in /home/clement/games/df/df_44_12_linux/libs/Dwarf_Fortress)
==4507== by 0x8CACAA: ??? (in /home/clement/games/df/df_44_12_linux/libs/Dwarf_Fortress)
==4507== by 0x9A3756: ??? (in /home/clement/games/df/df_44_12_linux/libs/Dwarf_Fortress)
==4507== by 0x600DCDF: interfacest::loop() (interface.cpp:863)
==4507== by 0x9CCDED: mainloop() (in /home/clement/games/df/df_44_12_linux/libs/Dwarf_Fortress)
==4507== by 0x5FD6F59: enablerst::async_loop() (enabler.cpp:337)
==4507== by 0x5FD825E: call_loop(void*) (enabler.cpp:708)
==4507== by 0x5C872EB: ??? (in /usr/lib64/libSDL-1.2.so.0.11.4)
==4507== by 0x5CCA73E: ??? (in /usr/lib64/libSDL-1.2.so.0.11.4)
==4507== Address 0x5f331440 is 0 bytes inside a block of size 2 alloc'd
==4507== at 0x4C2EBAB: malloc (vg_replace_malloc.c:299)
==4507== by 0x543FD22: df::historical_figure::historical_figure() (in /home/clement/games/df/df_44_12_linux/hack/libdfhack-debug.so)
==4507== by 0x5678200: DFHack::World::AddPersistentData(std::string const&) (World.cpp:270)
==4507== by 0x3D319294: initialize() (autochop.cpp:227)
==4507== by 0x3D319E00: plugin_onstatechange (autochop.cpp:919)
==4507== by 0x548B179: DFHack::Plugin::on_state_change(DFHack::color_ostream&, DFHack::state_change_event) (PluginManager.cpp:566)
==4507== by 0x548D393: DFHack::PluginManager::OnStateChange(DFHack::color_ostream&, DFHack::state_change_event) (PluginManager.cpp:981)
==4507== by 0x53A8F7C: DFHack::Core::onStateChange(DFHack::color_ostream&, DFHack::state_change_event) (Core.cpp:2283)
==4507== by 0x53A7940: DFHack::Core::doUpdate(DFHack::color_ostream&, bool) (Core.cpp:2015)
==4507== by 0x53A7B7D: DFHack::Core::Update() (Core.cpp:2071)
==4507== by 0x568318D: SDL_NumJoysticks (Hooks-linux.cpp:53)
==4507== by 0x5FD6FF2: enablerst::async_loop() (enabler.cpp:348)
Using my own debug build of "DFHack version 0.44.12-r1 (development build 0.44.12-r1-53-ge56cb2a2) on x86_64"
Fair. The first one looks like a mousequery bug. It sounds a little familiar, although I can't find an open PR fixing it, so maybe I'm mistaken. The second one looks like a mismatch between our historical_figure constructor and DF's destructor - maybe DF is dynamically allocating memory in the constructor? I've run into a likely-related issue where overriding operator new and operator delete crashes when freeing something allocated (I think?) in df::historical_figure::historical_figure(). Not really sure how to fix that one, so let me know if you uncover anything.libruby.so isn't something we wrote (it's from Ruby), and we don't control what it does. Valgrind probably has a way to disable warnings for a specific library.
It's just to be sure. You could have uninitialized data passed into the library and only used later.
Here are the suppressions, I came up with:
(snip)
Just to confirm, you haven't modified hack/lua/utils.lua at all, right?I think your syntax is ok, and the bracket looks like a normal bracket, not some funky Unicode one. Please post the full traceback, not just the one line - that will help identify whether this is originating from create-unit or reaction-trigger.Haha, yeah sorry - I originally intended to post it all but couldn't figure out how to copy from the command prompt. I've got it now (unless there's a file everything's logged to and this is just a bit of it?)Code: [Select]...1\PERIDE~1.12-\Dwarf Fortress 0.44.12\hack\lua\utils.lua:598: error: invalid arg: 5: race
The exact line for reaction-trigger might be off by 8 because I just commented out my additions to the valid args table to get it erroring again :P The line that's mentioned in the error (line 226 for my commented-out-additions version) is the line local args = utils.processArgs({...}, validArgs)
stack traceback:
[C]: in function 'error'
...1\PERIDE~1.12-\Dwarf Fortress 0.44.12\hack\lua\utils.lua:598: in function 'utils.processArgs'
...tress 0.44.12/hack/scripts/modtools/reaction-trigger.lua:226: in local 'script_code'
...\PERIDE~1.12-\Dwarf Fortress 0.44.12\hack\lua\dfhack.lua:680: in function 'dfhack.run_script_with_env'
(...tail calls...)
Just to confirm, you haven't modified hack/lua/utils.lua at all, right?Not to my knowledge, no. I've only opened it before to check the place it was erroring.
3dveins is giving me a "Discontinuous layer 1 at (73,86,120)" error. I'm guessing this might be because I have an ocean embark, but am not sure.
Is there any way to rectify this? Already tried fixveins but it didn't seem to do anything. I like using 3dveins.
Hello,The keybinding command does not support the mouse wheel, so that's not possible.
I'm having a bit of trouble with keybindings. I want to switch the mousewheel to execute
twbt tilesize 16 16
and
twbt tilesize 32 32
1. what should I input in dfhack.init to achieve this?
2. when i press CTRL+F1, a list of keybindings is brought up. There's Shift-O and Shift-P there, which exectute twbt tilesize smaller and bigger.dfhack.init (or a file it loads)
This should not happen because Shift-P opens petitions. Where can I disable this setting?
(http://www.truimagz.com/host/rumrusher/folder6/daynight-cycle-for-fortmode.gif)looks promising!
so kinda was wondering if it possible to get the advmode day night cycle working for fortmode?
what I got here is a gamemode set to 1 and Gametype set to 0.
@Roses: This script ought to carry you some of the way: https://github.com/PatrikLundell/scripts/blob/own_scripts/thoughts.lua (https://github.com/PatrikLundell/scripts/blob/own_scripts/thoughts.lua). Note that it's a web page with the script on it, not a link to a .lua file.
I don't care much about credits, apart from when people explicitly claim to have made stuff they've gotten from elsewhere. After all, the whole DFHack script thing is a process of building upon stuff others have made before (mine being no exception, of course). You're free to make use of it as is or take the parts you need, although it would probably be best if your script didn't end up with the same name, as that can lead to confusion down the line.@Roses: This script ought to carry you some of the way: https://github.com/PatrikLundell/scripts/blob/own_scripts/thoughts.lua (https://github.com/PatrikLundell/scripts/blob/own_scripts/thoughts.lua). Note that it's a web page with the script on it, not a link to a .lua file.
That's the exact script I remembered! Yay, I wasn't being silly this time. Do you mind if I use some/all of it (with credit of course)? Then I would just need to do something similar for attributes, appearance, and health and it would be perfect.
There's one bug with position.lua script, which manifests itself on the last day of the year.Reported on GitHub (https://github.com/DFHack/dfhack/issues/1413), thanks.
Hello,Within dfhack, one option I can think of is to have invisible lua screen overlaying game screen, set zoom speed to 0 in init, and then have the lua screen catch mousewheels and pass these commands to dfhack.
I'm having a bit of trouble with keybindings. I want to switch the mousewheel to execute
twbt tilesize 16 16
and
twbt tilesize 32 32
Are you sure you're using the correct scrolling keys? The list widgets support either the primary or secondary scroll keys, depending on how they're configured. Otherwise, there's not a lot I can do without code.
local gui = require 'gui'
local dialog = require 'gui.dialogs'
local widgets =require 'gui.widgets'
local guiScript = require 'gui.script'
local utils = require 'utils'
local split = utils.split_string
local persistTable = require 'persist-table'
local textC = COLOR_DARYGRAY
local cursorC = COLOR_LIGHTRED
local inactiveC = COLOR_CYAN
local views = {'main','detailedView','healthView','thoughtView','classView','featView','spellView'}
classSystem = false
featSystem = false
spellSystem = false
civSystem = false
ECreatureSystem = false
function center(str, length)
local string1 = str
local string2 = string.format("%"..tostring(math.floor((length-#string1)/2)).."s"..string1,"")
local string3 = string.format(string2.."%"..tostring(math.ceil((length-#string1)/2)).."s","")
return string3
end
function tchelper(first, rest)
return first:upper()..rest:lower()
end
function Set (list)
local set = {}
for _, l in ipairs(list) do set[l] = true end
return set
end
function getTargetFromScreens()
local my_trg
if dfhack.gui.getSelectedUnit(true) then
my_trg=dfhack.gui.getSelectedUnit(true)
else
qerror("No valid target found")
end
return my_trg
end
local guiFunctions = dfhack.script_environment('functions/gui')
DetailedUnitView = defclass(DetailedUnitView, gui.FramedScreen)
DetailedUnitView.ATTRS={
frame_style = gui.BOUNDARY_FRAME,
frame_title = "Detailed Unit Viewer",
}
function DetailedUnitView:init(args)
self.target = args.target
if persistTable.GlobalTable.roses then
systems = persistTable.GlobalTable.roses.Systems
if systems.Class == 'true' then classSystem = true end
if systems.Feat == 'true' then featSystem = true end
if systems.Spell == 'true' then spellSystem = true end
if systems.Civilization == 'true' then civSystem = true end
if systems.EnhancedCreature == 'true' then ECreatureSystem = true end
end
self.ClassSystem = classSystem
self.SpellSystem = spellSystem
self.FeatSystem = featSystem
self.CivSystem = civSystem
self.EnhancedSystem = ECreatureSystem
-- Bottom UI
self:addviews{
widgets.Panel{
view_id = 'bottomView',
frame = { b = 0, h = 1},
subviews = {
widgets.Label{
view_id = 'bottom_ui',
frame = { l = 0, t = 0},
text = 'filled by updateBottom()'
}
}
}
}
self.subviews.bottomView.visible = true -- Alwayes true
-- Create Frames
---- Main
self:addMainScreen() -- 3x3 - AX, AY, AZ, BX, BY, BZ, CX, CY, CZ
---- Sub Views
self:addDetailsScreen() -- 3x2 - D_ABX, D_ABY, D_AZ, D_BZ
self:addHealthScreen() -- 2x1 - H_AX, H_AY
self:addThoughtsScreen() -- 3x1 - T_AX, T_AY, T_AZ
self:addClassScreen() -- 2x2 - C_AX, C_BX, C_ABY
self:addFeatScreen() -- 2x2 - F_AX, F_BX, F_ABY
self:addSpellScreen() -- 2x2 - S_AX, S_BX, S_ABY
-- Fill Frames
self:fillMain()
self:fillDetails()
self:fillHealth()
self:fillThoughts()
if self.ClassSystem then self:fillClasses('All','List') end
if self.FeatSystem then self:fillFeats('All','List') end
if self.SpellSystem then self:fillSpells('All','List') end
-- Set Starting View
for _,view in pairs(self.subviews) do
view.visible = true
end
self:viewMain()
end
--= Screen Functions (create the screens)
function DetailedUnitView:addMainScreen()
---- Main
--[[ Positioning
Main:
| X | Y | Z |
--|--------------------|----------------|-------------------|
A | Base Information | Description | Attributes |
--|--------------------|----------------|-------------------|
B | Membership/Worship | Appearance | Skills |
--|--------------------|----------------|-------------------|
C | Class Information | Health | Stats/Resistances |
-------------------------------------------------------------
Bottom UI:
Details
]]
self:getPositioningMain()
self:addviews{
widgets.Panel{
view_id = 'main',
frame = { l = 0, r = 0 },
frame_inset = 1,
subviews = {
widgets.List{
view_id = 'AX',
frame = {l = self.AX.anchor.left, t = self.AX.anchor.top, w = self.X_width, h = self.AX.height},
},
widgets.List{
view_id = 'AY',
frame = {l = self.AY.anchor.left, t = self.AY.anchor.top, w = self.Y_width, h = self.AY.height},
},
widgets.List{
view_id = 'AZ',
frame = {l = self.AZ.anchor.left, t = self.AZ.anchor.top, w = self.Z_width, h = self.AZ.height},
},
widgets.List{
view_id = 'BX',
frame = {l = self.BX.anchor.left, t = self.BX.anchor.top, w = self.X_width, h = self.BX.height},
},
widgets.List{
view_id = 'BY',
frame = {l = self.BY.anchor.left, t = self.BY.anchor.top, w = self.Y_width, h = self.BY.height},
},
widgets.List{
view_id = 'BZ',
frame = {l = self.BZ.anchor.left, t = self.BZ.anchor.top, w = self.Z_width, h = self.BZ.height},
},
widgets.List{
view_id = 'CX',
frame = {l = self.CX.anchor.left, t = self.CX.anchor.top, w = self.X_width, h = self.CX.height},
},
widgets.List{
view_id = 'CY',
frame = {l = self.CY.anchor.left, t = self.CY.anchor.top, w = self.Y_width, h = self.CY.height},
},
widgets.List{
view_id = 'CZ',
frame = {l = self.CZ.anchor.left, t = self.CZ.anchor.top, w = self.Z_width, h = self.CZ.height},
},
}
}
}
end
function DetailedUnitView:addDetailsScreen()
------ Detailed Information
--[[
Detailed Information:
| X | Y | Z |
--|--------------|-------------|-------------|
A | | | Resistances |
--| Attributes | Skills |-------------|
B | | | Stats |
----------------------------------------------
Bottom UI:
Back
]]
self:getPositioningDetails()
self:addviews{
widgets.Panel{
view_id = 'detailedView',
frame = { l = 0, r = 0 },
frame_inset = 1,
subviews = {
widgets.List{
view_id = 'D_ABX',
frame = {l = self.D_ABX.anchor.left, t = self.D_ABX.anchor.top, w = self.D_X_width, h = self.D_ABX.height},
},
widgets.List{
view_id = 'D_ABY',
frame = {l = self.D_ABY.anchor.left, t = self.D_ABY.anchor.top, w = self.D_Y_width, h = self.D_ABY.height},
},
widgets.List{
view_id = 'D_AZ',
frame = {l = self.D_AZ.anchor.left, t = self.D_AZ.anchor.top, w = self.D_Z_width, h = self.D_AZ.height},
},
widgets.List{
view_id = 'D_BZ',
frame = {l = self.D_BZ.anchor.left, t = self.D_BZ.anchor.top, w = self.D_Z_width, h = self.D_BZ.height},
},
}
}
}
end
function DetailedUnitView:addHealthScreen()
------ Health
--[[
Health Information:
| X | Y |
--|--------------|-------------|
A | Health Stats | Syndromes |
--------------------------------
Bottom UI:
Back
]]
self:getPositioningHealth()
self:addviews{
widgets.Panel{
view_id = 'healthView',
frame = { l = 0, r = 0 },
frame_inset = 1,
subviews = {
widgets.List{
view_id = 'H_AX',
frame = {l = self.H_AX.anchor.left, t = self.H_AX.anchor.top, w = self.H_X_width, h = self.H_AX.height},
},
widgets.List{
view_id = 'H_AY',
frame = {l = self.H_AY.anchor.left, t = self.H_AY.anchor.top, w = self.H_Y_width, h = self.H_AY.height},
},
}
}
}
end
function DetailedUnitView:addThoughtsScreen()
------ Thoughts
--[[
Thoughts and Preferences:
| X | Y | Z |
--|----------|-------------|--------|
A | Thoughts | Preferences | Traits |
-------------------------------------
Bottom UI:
Back
]]
self:getPositioningThoughts()
self:addviews{
widgets.Panel{
view_id = 'thoughtView',
frame = { l = 0, r = 0 },
frame_inset = 1,
subviews = {
widgets.List{
view_id = 'T_AX',
frame = {l = self.T_AX.anchor.left, t = self.T_AX.anchor.top, w = self.T_X_width, h = self.T_AX.height},
},
widgets.List{
view_id = 'T_AY',
frame = {l = self.T_AY.anchor.left, t = self.T_AY.anchor.top, w = self.T_Y_width, h = self.T_AY.height},
},
widgets.List{
view_id = 'T_AZ',
frame = {l = self.T_AZ.anchor.left, t = self.T_AZ.anchor.top, w = self.T_Z_width, h = self.T_AZ.height},
},
}
}
}
end
function DetailedUnitView:addClassScreen()
------ Class Information
--[[
Classes:
| X | Y |
--|--------------|-------------|
A | Header | |
--|--------------| Details |
B | Class List | |
--------------------------------
Bottom UI:
Back
]]
self:getPositioningClasses()
self:addviews{
widgets.Panel{
view_id = 'classView',
frame = { l = 0, r = 0 },
frame_inset = 1,
subviews = {
widgets.List{
view_id = 'C_AX',
frame = {l = self.C_AX.anchor.left, t = self.C_AX.anchor.top, w = self.C_X_width, h = self.C_AX.height},
},
widgets.List{
view_id = 'C_BX',
frame = {l = self.C_BX.anchor.left, t = self.C_BX.anchor.top, w = self.C_X_width, h = self.C_BX.height},
on_select = self:callback('fillClasses'),
text_pen = textC,
cursor_pen = cursorC,
inactive_pen = inactiveC
},
widgets.List{
view_id = 'C_ABY',
frame = {l = self.C_ABY.anchor.left, t = self.C_ABY.anchor.top, w = self.C_Y_width, h = self.C_ABY.height},
},
}
}
}
end
function DetailedUnitView:addFeatScreen()
------ Feat Information
--[[
Feats:
| X | Y |
--|--------------|-------------|
A | Header | |
--|--------------| Details |
B | Feat List | |
--------------------------------
Bottom UI:
Back
]]
self:getPositioningFeats()
self:addviews{
widgets.Panel{
view_id = 'featView',
frame = { l = 0, r = 0 },
frame_inset = 1,
subviews = {
widgets.List{
view_id = 'F_AX',
frame = {l = self.F_AX.anchor.left, t = self.F_AX.anchor.top, w = self.F_X_width, h = self.F_AX.height},
},
widgets.List{
view_id = 'F_BX',
frame = {l = self.F_BX.anchor.left, t = self.F_BX.anchor.top, w = self.F_X_width, h = self.F_BX.height},
on_select = self:callback('fillFeats'),
text_pen = textC,
cursor_pen = cursorC,
},
widgets.List{
view_id = 'F_ABY',
frame = {l = self.F_ABY.anchor.left, t = self.F_ABY.anchor.top, w = self.F_Y_width, h = self.F_ABY.height},
},
}
}
}
end
function DetailedUnitView:addSpellScreen()
------ Spell Information
--[[
Spells:
| X | Y |
--|--------------|-------------|
A | Header | |
--|--------------| Details |
B | Spell List | |
--------------------------------
Bottom UI:
Back
]]
self:getPositioningSpells()
self:addviews{
widgets.Panel{
view_id = 'spellView',
frame = { l = 0, r = 0 },
frame_inset = 1,
subviews = {
widgets.List{
view_id = 'S_AX',
frame = {l = self.S_AX.anchor.left, t = self.S_AX.anchor.top, w = self.S_X_width, h = self.S_AX.height},
},
widgets.List{
view_id = 'S_BX',
frame = {l = self.S_BX.anchor.left, t = self.S_BX.anchor.top, w = self.S_X_width, h = self.S_BX.height},
on_select = self:callback('fillSpells'),
text_pen = textC,
cursor_pen = cursorC,
},
widgets.List{
view_id = 'S_ABY',
frame = {l = self.S_ABY.anchor.left, t = self.S_ABY.anchor.top, w = self.S_Y_width, h = self.S_ABY.height},
},
}
}
}
end
--= Positioning Functions (get the width, height, and anchor points for each screen)
function DetailedUnitView:getPositioningMain()
local AX = {anchor = {}, width = 40, height = 10}
local AY = {anchor = {}, width = 40, height = 10}
local AZ = {anchor = {}, width = 40, height = 10}
local BX = {anchor = {}, width = 40, height = 10}
local BY = {anchor = {}, width = 40, height = 10}
local BZ = {anchor = {}, width = 40, height = 10}
local CX = {anchor = {}, width = 40, height = 10}
local CY = {anchor = {}, width = 40, height = 10}
local CZ = {anchor = {}, width = 40, height = 10}
local X_width = math.max(AX.width,BX.width,CX.width)
local Y_width = math.max(AY.width,BY.width,CY.width)
local Z_width = math.max(AZ.width,BZ.width,CZ.width)
----
AX.anchor.top = 0
AY.anchor.top = 0
AZ.anchor.top = 0
AX.anchor.left = 0
AY.anchor.left = X_width + 4
AZ.anchor.left = X_width + Y_width + 8
BX.anchor.top = AX.height + 1
BY.anchor.top = AY.height + 1
BZ.anchor.top = AZ.height + 1
BX.anchor.left = 0
BY.anchor.left = X_width + 4
BZ.anchor.left = X_width + Y_width + 8
CX.anchor.top = AX.height + BX.height + 2
CY.anchor.top = AY.height + BY.height + 2
CZ.anchor.top = AZ.height + BZ.height + 2
CX.anchor.left = 0
CY.anchor.left = X_width + 4
CZ.anchor.left = X_width + Y_width + 8
----
self.AX = AX
self.AY = AY
self.AZ = AZ
self.BX = BX
self.BY = BY
self.BZ = BZ
self.CX = CX
self.CY = CY
self.CZ = CZ
self.X_width = X_width
self.Y_width = Y_width
self.Z_width = Z_width
end
function DetailedUnitView:getPositioningDetails()
local ABX = {anchor = {}, width = 40, height = 40}
local ABY = {anchor = {}, width = 40, height = 40}
local AZ = {anchor = {}, width = 40, height = 20}
local BZ = {anchor = {}, width = 40, height = 20}
local X_width = ABX.width
local Y_width = ABY.width
local Z_width = math.max(AZ.width,BZ.width)
----
ABX.anchor.top = 0
ABY.anchor.top = 0
ABX.anchor.left = 0
ABY.anchor.left = X_width + 4
AZ.anchor.top = 0
AZ.anchor.left = X_width + Y_width + 8
BZ.anchor.top = AZ.height + 1
BZ.anchor.left = X_width + Y_width + 8
----
self.D_ABX = ABX
self.D_ABY = ABY
self.D_AZ = AZ
self.D_BZ = BZ
self.D_X_width = X_width
self.D_Y_width = Y_width
self.D_Z_width = Z_width
end
function DetailedUnitView:getPositioningHealth()
local AX = {anchor = {}, width = 60, height = 40}
local AY = {anchor = {}, width = 60, height = 40}
local X_width = AX.width
local Y_width = AY.width
----
AX.anchor.top = 0
AY.anchor.top = 0
AX.anchor.left = 0
AY.anchor.left = X_width + 4
----
self.H_AX = AX
self.H_AY = AY
self.H_X_width = X_width
self.H_Y_width = Y_width
end
function DetailedUnitView:getPositioningThoughts()
local AX = {anchor = {}, width = 40, height = 40}
local AY = {anchor = {}, width = 40, height = 40}
local AZ = {anchor = {}, width = 40, height = 40}
local X_width = AX.width
local Y_width = AY.width
local Z_width = AZ.width
----
AX.anchor.top = 0
AY.anchor.top = 0
AZ.anchor.top = 0
AX.anchor.left = 0
AY.anchor.left = X_width + 4
AZ.anchor.left = X_width + Y_width + 8
----
self.T_AX = AX
self.T_AY = AY
self.T_AZ = AZ
self.T_X_width = X_width
self.T_Y_width = Y_width
self.T_Z_width = Z_width
end
function DetailedUnitView:getPositioningClasses()
local AX = {anchor = {}, width = 40, height = 3}
local BX = {anchor = {}, width = 40, height = 37}
local ABY = {anchor = {}, width = 80, height = 40}
local X_width = math.max(AX.width,BX.width)
local Y_width = ABY.width
----
AX.anchor.top = 0
AX.anchor.left = 0
BX.anchor.top = AX.height + 1
BX.anchor.left = 0
ABY.anchor.top = 0
ABY.anchor.left = X_width + 4
----
self.C_AX = AX
self.C_BX = BX
self.C_ABY = ABY
self.C_X_width = X_width
self.C_Y_width = Y_width
end
function DetailedUnitView:getPositioningFeats()
local AX = {anchor = {}, width = 40, height = 3}
local BX = {anchor = {}, width = 40, height = 37}
local ABY = {anchor = {}, width = 80, height = 40}
local X_width = math.max(AX.width,BX.width)
local Y_width = ABY.width
----
AX.anchor.top = 0
AX.anchor.left = 0
BX.anchor.top = AX.height + 1
BX.anchor.left = 0
ABY.anchor.top = 0
ABY.anchor.left = X_width + 4
----
self.F_AX = AX
self.F_BX = BX
self.F_ABY = ABY
self.F_X_width = X_width
self.F_Y_width = Y_width
end
function DetailedUnitView:getPositioningSpells()
local AX = {anchor = {}, width = 40, height = 3}
local BX = {anchor = {}, width = 40, height = 37}
local ABY = {anchor = {}, width = 80, height = 40}
local X_width = math.max(AX.width,BX.width)
local Y_width = ABY.width
----
AX.anchor.top = 0
AX.anchor.left = 0
BX.anchor.top = AX.height + 1
BX.anchor.left = 0
ABY.anchor.top = 0
ABY.anchor.left = X_width + 4
----
self.S_AX = AX
self.S_BX = BX
self.S_ABY = ABY
self.S_X_width = X_width
self.S_Y_width = Y_width
end
--= Filling Functions (call functions/gui to get the information to put on the screen)
function DetailedUnitView:fillMain()
local unit = self.target
local grid = {'AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ'}
local output = {}
for i,g in pairs(grid) do
output[g] = guiFunctions.getMainOutput(g, unit, self[g].width, self.ClassSystem)
self.subviews[g]:setChoices(output[g])
end
end
function DetailedUnitView:fillDetails()
local unit = self.target
local grid = {'D_ABX', 'D_ABY', 'D_AZ', 'D_BZ'}
local output = {}
for i,g in pairs(grid) do
output[g] = guiFunctions.getDetailsOutput(g, unit, self[g].width)
self.subviews[g]:setChoices(output[g])
end
end
function DetailedUnitView:fillHealth()
local unit = self.target
local grid = {'H_AX', 'H_AY'}
local output = {}
for i,g in pairs(grid) do
output[g] = guiFunctions.getHealthOutput(g, unit, self[g].width)
self.subviews[g]:setChoices(output[g])
end
end
function DetailedUnitView:fillThoughts()
local unit = self.target
local grid = {'T_AX', 'T_AY', 'T_AZ'}
local output = {}
for i,g in pairs(grid) do
output[g] = guiFunctions.getThoughtsOutput(g, unit, self[g].width)
self.subviews[g]:setChoices(output[g])
end
end
function DetailedUnitView:fillClasses(filter,details)
local unit = self.target
local output = {}
if details == nil then return end
if details and details ~= 'List' then
output = guiFunctions.getClassesOutput('C_ABY', unit, self.C_ABY.width, details)
self.subviews.C_ABY:setChoices(output)
else
local grid = {'C_AX','C_BX'}
local output = {}
for i,g in pairs(grid) do
output[g] = guiFunctions.getClassesOutput(g, unit, self[g].width, filter)
self.subviews[g]:setChoices(output[g])
end
end
end
function DetailedUnitView:fillFeats(filter,details)
local unit = self.target
local output = {}
if details == nil then return end
if details and details ~= 'List' then
output = guiFunctions.getFeatsOutput('F_ABY', unit, self.F_ABY.width, details)
self.subviews.F_ABY:setChoices(output)
else
local grid = {'F_AX','F_BX'}
for i,g in pairs(grid) do
output[g] = guiFunctions.getFeatsOutput(g, unit, self[g].width, filter)
self.subviews[g]:setChoices(output[g])
end
end
end
function DetailedUnitView:fillSpells(filter,details)
local unit = self.target
local output = {}
if details == nil then return end
if details and details ~= 'List' then
output = guiFunctions.getSpellsOutput('S_ABY', unit, self.S_ABY.width, details)
self.subviews.S_ABY:setChoices(output)
else
local grid = {'S_AX','S_BX'}
local output = {}
for i,g in pairs(grid) do
output[g] = guiFunctions.getSpellsOutput(g, unit, self[g].width, filter)
self.subviews[g]:setChoices(output[g])
end
end
end
--= Viewing Functions (change which screen is active and visible)
function DetailedUnitView:updateBottom(screen)
if screen == 'Main' then
text = {
{ key = 'CUSTOM_SHIFT_A', text = ': Details ', on_activate = self:callback('viewDetails') },
{ key = 'CUSTOM_SHIFT_H', text = ': Health ', on_activate = self:callback('viewHealth') },
{ key = 'CUSTOM_SHIFT_T', text = ': Thoughts ', on_activate = self:callback('viewThoughts') },
}
if self.ClassSystem then table.insert(text, {key = 'CUSTOM_SHIFT_C', text = ': Classes ', on_activate = self:callback('viewClasses')}) end
if self.ClassSystem then table.insert(text, {key = 'CUSTOM_SHIFT_F', text = ': Feats ', on_activate = self:callback('viewFeats') }) end
if self.ClassSystem then table.insert(text, {key = 'CUSTOM_SHIFT_S', text = ': Spells ', on_activate = self:callback('viewSpells') }) end
elseif screen == 'Details' then
text = {
{ text = 'ESC: Back '},
}
elseif screen == 'Health' then
text = {
{ text = 'ESC: Back '},
}
elseif screen == 'Thoughts' then
text = {
{ text = 'ESC: Back '},
}
elseif screen == 'Classes' then
text = {
{ key = 'CUSTOM_SHIFT_A', text = ': Show All Classes ', on_activate = function () self:fillClasses('All','List') end },
{ key = 'CUSTOM_SHIFT_C', text = ': Show Civilization Classes ', on_activate = function () self:fillClasses('Civ','List') end },
{ key = 'CUSTOM_SHIFT_K', text = ': Show Known Classes ', on_activate = function () self:fillClasses('Learned','List') end },
{ key = 'CUSTOM_SHIFT_V', text = ': Show Available Classes ', on_activate = function () self:fillClasses('Available','List') end },
{ text = 'ESC: Back'}
}
elseif screen == 'Feats' then
text = {
{ key = 'CUSTOM_SHIFT_A', text = ': Show All Feats ', on_activate = function () self:fillFeats('All','List') end },
{ key = 'CUSTOM_SHIFT_C', text = ': Show Class Feats ', on_activate = function () self:fillFeats('Class','List') end },
{ key = 'CUSTOM_SHIFT_K', text = ': Show Known Feats ', on_activate = function () self:fillFeats('Learned','List') end },
{ text = 'ESC: Back'}
}
elseif screen == 'Spells' then
text = {
{ key = 'CUSTOM_SHIFT_A', text = ': Show All Spells ', on_activate = function () self:fillSpells('All','List') end },
{ key = 'CUSTOM_SHIFT_C', text = ': Show Civilization Spells ', on_activate = function () self:fillSpells('Civ','List') end },
{ key = 'CUSTOM_SHIFT_K', text = ': Show Known Spells ', on_activate = function () self:fillSpells('Learned','List') end },
{ key = 'CUSTOM_SHIFT_L', text = ': Show Classes Spells ', on_activate = function () self:fillSpells('Class','List') end },
{ text = 'ESC: Back'}
}
end
self.subviews.bottom_ui:setText(text)
end
function DetailedUnitView:resetView()
for _,view in pairs(views) do
self.subviews[view].visible = false
self.subviews[view].active = false
end
--self.subviews.bottom_ui.visible = true
end
function DetailedUnitView:viewMain()
self:updateBottom('Main')
self:resetView()
self.subviews.main.visible = true
end
function DetailedUnitView:viewDetails()
self:updateBottom('Details')
self:resetView()
self.subviews.detailedView.visible = true
end
function DetailedUnitView:viewHealth()
self:updateBottom('Health')
self:resetView()
self.subviews.healthView.visible = true
end
function DetailedUnitView:viewThoughts()
self:updateBottom('Thoughts')
self:resetView()
self.subviews.thoughtView.visible = true
end
function DetailedUnitView:viewClasses()
self:updateBottom('Classes')
self:resetView()
self.subviews.classView.visible = true
self.subviews.classView.active = true
self.subviews.C_BX.active = true
self.subviews.C_BX:setSelected(2)
printall(self.subviews.classView.subviews.C_BX.frame)
end
function DetailedUnitView:viewFeats()
self:updateBottom('Feats')
self:resetView()
self.subviews.featView.visible = true
self.subviews.F_BX.active = true
end
function DetailedUnitView:viewSpells()
self:updateBottom('Spells')
self:resetView()
self.subviews.spellView.visible = true
self.subviews.S_BX.active = true
end
--= Base Functions
function DetailedUnitView:onInput(keys)
if keys.LEAVESCREEN then
if self.subviews.main.visible then
self:dismiss()
else
self:viewMain()
end
else
DetailedUnitView.super.onInput(self, keys)
end
end
function show_editor(trg)
local screen = DetailedUnitView{target=trg}
screen:show()
end
show_editor(getTargetFromScreens())
the ones that might be relevant to social/break-related things being "TIME_SINCE_BREAK" and "ON_BREAK". In lua, these counters (and a lot more) can be found in the unit's status.misc_traits (provided they're active/whatever)Hm, I can try those, although I'm not sure they're used any more in the current game version; in my fort of 112 citizens, not a single one has a misc_trait with id 16 (TimeSinceBreak) or 17 (OnBreak). Also, the problem I'm having isn't just with dwarves aborting my forced social activity for an actual job (teal text); they'll take a few steps pathing toward my activity and then stop and switch to a different idle activity (i.e. stop walking to the temple and instead go to Individual Combat Drill or to the library to read). That's why I was hoping to figure out the criteria for Purple! idle activities, to maybe get them to stick with what I assigned and not switch to something else.
@bloop_bleep: Firstly, why do you perform all of these narrowing type conversions, rather than keep the original types?
Is stonesense maintained by anybody? There seems to be an issue where constructed walls & floors are using the sprite for smoothed stone walls & floors, rather than the sprite for constructed walls.I always dread questions that start out like that, because the question usually contains "Stonesense is crashing" and the answer is usually "no". This is probably one of the most benign issues I've heard of with it. But unfortunately, we don't have anyone around who's familiar with Stonesense any more, and I don't really know how to fix that either. (Stonesense is pretty broken on macOS, and even more so on newer versions due to Stonesense's architectural choices, so I don't have a good way to develop it or test fixes.)
I mean, on principle, I don't want to tell you how to disable warnings, because you should fix the warnings instead. What types specifically are you trying to divide? What's the warning you're getting? Can you give an example (or all) of the code you're trying to compile?@bloop_bleep: Firstly, why do you perform all of these narrowing type conversions, rather than keep the original types?
Haha, believe me, I tried. It usually happens in the case where I’m dividing two values, and I can’t seem to be able to find the right combination of types such that the division is allowed and works as expected while also the end result matches the type desired by DF.
As for explicit typecasting, I might do that, but honestly sometimes I just can’t be bothered. Besides, it produces the same effect, and probably hurts readability.Yeah, I was about to suggest typecasting everything as an ugly solution.
If you know, is there any way to set compiler options globally (i.e. for the entire DFHack build)?Absolutely, but it requires recompiling literally everything.
Additionally, could I have the username of the person who wrote steam-engine.cpp, so I could message them.You can answer this with "git log" or on GitHub: go to https://github.com/DFHack/dfhack/blob/master/plugins/steam-engine.cpp, click "History", and go to the end. That takes me here (https://github.com/DFHack/dfhack/commits/master/plugins/steam-engine.cpp). (Angavrilov = ag on the forums, although he hasn't been active in a long time.)
I have two questions.the answer is in df.items.xml in df-structures (https://github.com/DFHack/df-structures/blob/0.44.12-r1/df.items.xml#L404-L406):
- df::item::addWear takes three arguments, an int16_t (which I assume is the amount of wear) and two other Booleans. What do these last two arguments mean? I ask because steam-engine.cpp seems to use both the (true, false) and (true, true) combinations, and neither of these seem to actually change the wear value when inspected with lua.
<vmethod ret-type='bool' name='addWear'>
<int32_t name='delta'/> <bool name='simple'/> <bool name='lose_masterwork'/>
</vmethod>
Vmethod parameter names don't get transferred to the generated headers like field names do, unfortunately.
return df::coord {
pos.x - ref.x,
pos.y - ref.y,
pos.z - ref.z
};
home/misha/df_linux/dfhack/plugins/moving-machines.cpp: In member function ‘df::coord MovingMachine::get_rel_pos(df::coord)’:
/home/misha/df_linux/dfhack/plugins/moving-machines.cpp:260:19: warning: narrowing conversion of ‘(((int)pos.df::coord::x) - ((int)((MovingMachine*)this)->MovingMachine::ref.df::coord::x))’ from ‘int’ to ‘uint16_t {aka short unsigned int}’ inside { } [-Wnarrowing]
pos.x - ref.x,
~~~~~~^~~~~~~
/home/misha/df_linux/dfhack/plugins/moving-machines.cpp:261:19: warning: narrowing conversion of ‘(((int)pos.df::coord::y) - ((int)((MovingMachine*)this)->MovingMachine::ref.df::coord::y))’ from ‘int’ to ‘uint16_t {aka short unsigned int}’ inside { } [-Wnarrowing]
pos.y - ref.y,
~~~~~~^~~~~~~
/home/misha/df_linux/dfhack/plugins/moving-machines.cpp:262:19: warning: narrowing conversion of ‘(((int)pos.df::coord::z) - ((int)((MovingMachine*)this)->MovingMachine::ref.df::coord::z))’ from ‘int’ to ‘uint16_t {aka short unsigned int}’ inside { } [-Wnarrowing]
pos.z - ref.z
~~~~~~^~~~~~~
Is stonesense maintained by anybody? There seems to be an issue where constructed walls & floors are using the sprite for smoothed stone walls & floors, rather than the sprite for constructed walls.
...
...
I found some explanations here (https://stackoverflow.com/questions/10047956/c-uint16-t-subtraction-behavior-in-gcc).
A third party tool to draw/view file XML images might be useful in this regard, unless other such applications already exist.So, maybe something like this (https://download.cnet.com/XML-Viewer/3000-7241_4-10223729.html) would be appropriate, unless I'm badly misreading the request here, let alone outright mistaken?
Why are your warnings mentioning uint16_t, if int16_t are used in df::coord? The reason it is cast to int is the same: C/C++ doesn't do operation on types smaller than int. But there maybe something more.
-Wno-narrowing disables the warnings for gcc, but it may hide warnings in other places where it would be useful.
- Children of the original intelligent pets i embark with are legal to change the labours of as long as my original trolls breed with each other, as for my function im creating a 'slave force' (just a profession name) of trolls that do jobs such as strong stand-in's for squad members, digging work and creating clothing & armor sized properly for other trolls when manipulated via labour manager with success
I assume you're talking about manipulator (u-l), not labormanager? If that's the case, here is the code (https://github.com/DFHack/dfhack/blob/0.44.12-r2/plugins/manipulator.cpp#L1154-L1170) determining which units can't be edited in manipulator. Other than that, I'm not sure what you're saying about r1 vs r2. Looking at the changes (https://github.com/DFHack/dfhack/compare/0.44.12-r1...0.44.12-r2), neither library/modules/Units.cpp or plugins/manipulator.cpp changed between 0.44.12-r1 and 0.44.12-r2.
labormanager:So obviously getting this straight, without touching the manipulator it should be a upgrade for a seperate plugin that i wasn't tapping into. Alright, all sweet.
stopped assigning labors to ineligible dwarves, pets, etc.
stopped assigning invalid labors
if (!ENUM_ATTR(profession, can_assign_labor, unit->profession))
cur->allowEdit = false;
could be the culprit since they arrived pre-specialised, so ill just gui set them to peasant or none and it should free them up, ill keep it in mind. Thanks again and nice work.
AlsoWhat profession specifically are they arriving with?Code: [Select]if (!ENUM_ATTR(profession, can_assign_labor, unit->profession))
could be the culprit since they arrived pre-specialised, so ill just gui set them to peasant or none and it should free them up, ill keep it in mind. Thanks again and nice work.
cur->allowEdit = false;
df.global.ui_advmode.message=""
EDIT 2: A different question now. How does add_subdirectory work? Does it just add all files in a certain directory to a plugin of the same name? Can I still set compiler flags?add_subdirectory is a CMake feature, not specific to DFHack at all. It looks for a CMakeLists.txt in the specified folder and includes it (with a few things like CMAKE_CURRENT_SOURCE_DIR changed accordingly). DFHack uses it in a lot of CMakeLists.txt files to include others - you'll see it a few times in the root CMakeLists.txt file too. In the case of plugins, the CMakeLists.txt in the subfolder typically creates a plugin of the same name, but it doesn't have to.
[DFHack]# lua
[lua]# ~dfhack.units.isOwnCiv(unit)
[lua]# ~unit.flags2.bits.visitor
(If the professions aren't ones I listed, the ENUM_ATTR check isn't relevant.)
Does DFHack provide a straightforward way to find out what method a particular address in memory belongs to? ... Is there a way I can find out what that address is in the executable?The first question to me sounds like "what does the function at address X do", which is harder to answer. First off, are you sure your profiler isn't just reporting a main loop of some kind?
EDIT3: Actually, apparently it's in PE format, because PE files also start with MZ. Wow, good job, Microsoft.I think the MZ prefix on PE files is done for backward compatibility. The MZ format was used by DOS, and if you try to open a Windows executable in DOS, it will show a message saying it needs to be run under Windows.
The only difference on Windows is that the offset isn't fixed across DF runs
According to Wikipedia (https://en.wikipedia.org/wiki/Portable_Executable#Relocations), the PE format generally includes absolute addresses, and if it's not loaded at its preferred address, the loader has to manually go through and change every address in the code before running. Yes, really.Yeah that usually happens for DLLs. You can not expect the code to be loaded in same location as there could be other dlls that want to be in that location (even without ASLR). So there is such thing as relocation table that list all addresses that need to be fixed when loading. You could compile code as PIC (https://en.wikipedia.org/wiki/Position-independent_code) however i have no idea what are the pros and cons of that.
I probably would've stuck with roses stuff if I'd been able to get the JSON saving to reliably save with the game. I couldn't figure out a way to tell if quicksaving/autosaving was going on. The problem was more that every single table check was a persist-table check, IIRC.
Straight up couldn't find a way to get it to save only when the game saves.
I found I had to load from the main world folder (not /data/save/current) even though I could save to either.Oh, sorry, I thought that part was obvious. The current folder gets cleared after saving, after its contents are copied into the region folder. Writing directly to the region folder will lead to incorrect behavior if DF quits without saving.
I found I had to load from the main world folder (not /data/save/current) even though I could save to either.That should actually be the other way around - when reading, you should try data/save/current first and then fall back to data/save/regionX, but when writing, you should always go to data/save/current.
If a crash happens "between the two" what? If a crash happens while saving, it should only affect files in the current folder - that's how DF is designed, to cut down on corruption. A crash when moving files into the region folder is exceptionally unlikely.
I think the issue would be if the game saves with a stale version of foo.dat in current.
Is it possible to use DFHack to remove engravings?You could use gui/liquids to spawn magma on them, then remove it a tick later. Not sure that works for walls, however.
I just noticed that all my main areas were engraved by low quality engravers, because I forgot to tick off stone detailing from the "peasant" squad that helped smoothed up the place. This a 5 year Fortress that I have meticulously micromanaged, and this really soured my playthrough.
@Romi: Note that you can achieve it without DFHack by manually designating each sub standard tile for smoothing again (which of course is a pain, but if you're hard into perfect, that's the way to do it). It would presumably be possible to write a script that designated all non masterworks engravings for smoothing, although I don't know the details. In both cases the dorfs would actually do the work themselves, so it wouldn't be "cheating".
Yes, sorry. I meant if the game saves and moves an out of date dat file then updates the dat file in the data/save/current directory but crashes before that can get moved to the data/save/regionX folder.If the game crashes, the updated .dat file shouldn't get copied to the regionX folder. That's how the current persistent API works as well.
For normal saves this isn't an issue since there is the ON_WORLD_UNLOAD thing, and for autosaves it shouldn't be an issue since they happen at specific times and I can always make sure to get the updated dat file in the /current directory in the correct order. But quicksaves aren't predictable. Luckily it is easy enough to hook into the quicksave script and just make sure that the dat file is updated in the correct order.Keep in mind that the quicksave script doesn't run on "normal" autosaves
Yes, sorry. I meant if the game saves and moves an out of date dat file then updates the dat file in the data/save/current directory but crashes before that can get moved to the data/save/regionX folder.If the game crashes, the updated .dat file shouldn't get copied to the regionX folder. That's how the current persistent API works as well.QuoteFor normal saves this isn't an issue since there is the ON_WORLD_UNLOAD thing, and for autosaves it shouldn't be an issue since they happen at specific times and I can always make sure to get the updated dat file in the /current directory in the correct order. But quicksaves aren't predictable. Luckily it is easy enough to hook into the quicksave script and just make sure that the dat file is updated in the correct order.Keep in mind that the quicksave script doesn't run on "normal" autosavesso you won't be able to detect those.edit: I guess you can use the fact that autosave times are fixed, yeah.
Again, I'm saying you should write to data/save/current/foo.dat whenever something changes, and you will be fine. You don't need to worry about detecting save events at all. Granted, if you need to write to disk a lot (as in multiple times a second), it would be better to detect saves, and writing only periodically could risk saving slightly stale data. As part of BenLubar's persistent JSON PR, I'll try to make sure scripts have a way to access the save events as well as plugins. But for non-intensive use, you don't have to detect save events.
We actually submitted a bug report to GitHub about that (it's because the scripts submodule uses a relative URL now, ../../DFHack/scripts). They know it's broken but it's presumably not a high-priority issue.
1. If I tie a save file function into the onUnload event should I save to current or the region save file?That is probably too late to access any world data. It might fire after DF copies the current folder to the region folder too. If you can't do it any earlier, you might have to save to the region folder, but even that name might not be accessible any more. I would experiment with saving something to current and see where that ends up.
2. For the GUI stuff is there a way to distinguish between the arrow keys, numpad keys, and numbers for the keys or are they all treated the same?You should be using DF's key IDs, which depend on the keybindings that are set. Use STANDARDSCROLL_UP/DOWN. when you're scrolling through a list and CURSOR_UP/etc. when you're moving a cursor (like the X on the map) around the screen. DF and DFHack don't have a way to distinguish between arrow keys and number keys once they've been translated to IDs.
3. Are unit or item ids every reused while playing? I'm guessing historical unit ids are unique, but several of my scripts assume unit ids are also unique.Don't know about this. They might potentially be reused in different forts in the same world, but I doubt it. Histfig IDs are unique (I'm pretty sure those are the IDs used in the unit-X.dat files in the save folders). If you're concerned, you could tack on the fort/site ID as well for whatever you're doing.
4. There are a lot of great functions available in dfhack.units and the other dfhack.BLANK stuff. If I was interested in porting some of my functions that I currently use that I think other people might find useful from my lua functions to built in functions how should I go about that? (I am familiar with writing C code so that isn't an issue)Files that need to be changed:
EDIT: Another question I forgot, is there an easy way to detect when custom view screens "turn on and off". My detailed unit viewer currently let's you access the gui/gm-editor for the unit directly for it, but if you change anything in the editor you have to exit out of the viewer and start it again. I was hoping to be able to automatically reload my viewer when exiting the editor but my tests with screen:onShow haven't been successfulLooking at https://github.com/DFHack/dfhack/blob/master/plugins/manipulator.cpp, I think the logic() vmethod gets called periodically when the screen is active (render() should too - logic() might be onIdle() in higher Lua APIs, but render() or onRender() or whatever you're using should work for your purpose). Your screen could set a flag when it opens the screen, and the next time logic() or render() are called, it could refresh itself and reset the flag. "do_refresh_names" is the flag manipulator uses. Does that help?
EDIT2: Do we know the speed change calculations for calculating gait speed based on strength/agility? I wrote a small script to calculate the kph value of a unit based on the gaits file in the raws but it doesn't takes into account and modifiers (the current dhack.units.computeMovementSpeed always returns 0)Not seeing anything in the XML files about it, sorry. Someone else might know.
--snip--
Is there a way to access the plant raw strings like we can access the creature raw strings? I figure the plant_raw.anon_1 is the list of strings similar to creature_raw.raws, but it's all userdata entries.Yup:
[lua]# ~df.reinterpret_cast('string',df.plant_raw.find(0).anon_1[0])
<string: 0x112e5b010>
value = [NAME:single-grain wheat]
Obviously don't use this in a script, but I'll add a name to the xml files.
Is there a way to access the plant raw strings like we can access the creature raw strings? I figure the plant_raw.anon_1 is the list of strings similar to creature_raw.raws, but it's all userdata entries.Yup:Code: [Select][lua]# ~df.reinterpret_cast('string',df.plant_raw.find(0).anon_1[0])
Obviously don't use this in a script, but I'll add a name to the xml files.
<string: 0x112e5b010>
value = [NAME:single-grain wheat]
a = Table.FOO or BAR
and if the table Table doesn't have the FOO entry I will get BAR. But I can't doa = itemRaw.adjective or ""
because some item raws don't have the adjective entry, so it will give me an error instead of just a blank string.
field_exists = pcall(function() return object.some_field end)
Something like this ought to be in utils.lua or exposed through the C++/Lua bridge...
To be clear, you don't mean actually crashing, right? Crashes aren't possible to prevent after the fact.
pcall takes a function to call (optionally with arguments) and returns a boolean representing whether an error occurred, followed by the function's return values or the error. So something like this should work:Code: [Select]field_exists = pcall(function() return object.some_field end)
Something like this ought to be in utils.lua or exposed through the C++/Lua bridge...
Worth noting that pcall will be significantly slower than handling individual types, if that's something you can do, so don't use the pcall approach in anything performance-intensive. I assume there are good reasons for not returning nil for nonexistent fields, although I wasn't around when that decision was made - it's probably better for those accesses to fail early instead of silently succeeding for typos and such.
b={}
for k,v in pairs(itemdef_XXX) do
b[k]=v
end
adjective = b.adjective or ""
Almost certainly slower if you're running the loop more than once. Maybe a table of types you know have the field you want, indexed by the type so you don't have to do a linear scan, would work and be cleaner than an if statement chain.
eventful.enableEvent(eventful.eventType.BUILDING, 10)
eventful.onBuildingCreatedDestroyed.buildingTrigger = function(buildingID)
print(buildingID)
building = df.building.find(buildingID)
if building then
checkBuildingCreated(buildingID)
else
checkBuildingDestroyed(buildingID)
end
end
eventful.onBuildingCreatedDestroyed.test = function(a) print('test',a) endon the command line. Then tried built and destroyed a couple of the default workshops (Leatherworker's and Kitchen) and the Soap Maker's custom workshop, each time it printed 1840249752 when I both created and destroyed the buildings (although in another world I tried this in it was printing a large negative number, so that isn't always the same).
Yeah, the eventType is 5, I've stripped everything else out of the script and just runQuoteeventful.onBuildingCreatedDestroyed.test = function(a) print('test',a) endon the command line. Then tried built and destroyed a couple of the default workshops (Leatherworker's and Kitchen) and the Soap Maker's custom workshop, each time it printed 1840249752 when I both created and destroyed the buildings (although in another world I tried this in it was printing a large negative number, so that isn't always the same).
Interesting. So it did used to work, at least sometimes. So I'm not crazy. Is there a way to get the id from the pointer? I suppose I can also just loop over all buildings and see what's new when a building gets created. Or I could link it to the on job completed eventfulI suggest using job complete. Or waiting for release with a fix. Also there is a way to get id from the pointer, but i suggest not to think about that because it's very close to summoning chtulian monstrosities from the computational multiverse. (also it would be broken after fix).
Is it possible to spawn in a wagon using DFhack?For a lark, I tried spawning one in arena with modtools/create-item. It spawned, but was scuttled a tick later. I doubt fort/adv mode behavior will be any different.
Interesting. So it did used to work, at least sometimes. So I'm not crazy. Is there a way to get the id from the pointer? I suppose I can also just loop over all buildings and see what's new when a building gets created. Or I could link it to the on job completed eventfulI suggest using job complete. Or waiting for release with a fix. Also there is a way to get id from the pointer, but i suggest not to think about that because it's very close to summoning chtulian monstrosities from the computational multiverse. (also it would be broken after fix).
Lastly - yes you could just do the same thing plugin does - personally i find all that "iterate over everything every N frames" very ugly and suggest leaving it in one place (i.e. the plugin). It's also a bit finicky to get right, however you probably have very similar systems in place.
modtools/create-item -creator *id* -material PLANT:GRASS_TAIL_PIG:THREAD -item TOOL:ITEM_TOOL_QUIRE
createitem TOOL:ITEM_TOOL_QUIRE PLANT:GRASS_TAIL_PIG:THREAD
This will select a valid unit ID automatically if one isn't selected.
What's the best way to use a global global table (one that is accessed by many different scripts fairly regularly for both reading and modifying)? Right now I'm just using the dfhack.script-environment to get the table, but I wasn't sure if that was the best wayThe "best practice" way to do it would probably be to refactor all your shared functionality into one or more lua files in dfhack's lua folder, and then require them in like you would with utils, plugins.eventful or syndrome-util.
dfhack.screen.getWindowSize()
is broken in the lua api? df::coord2d Screen::getWindowSize()
{
if (!gps) return df::coord2d(80, 25);
return df::coord2d(gps->dimx, gps->dimy);
}
dfhack.screen.getWindowSize()
will just return the x value (width), but if you print it it will show x and y in the console but there is no way to select either x or y.dfhack.screen.getWindowSize().x
dfhack.screen.getWindowSize().y
dfhack.screen.getWindowSize().X
dfhack.screen.getWindowSize().Y
dfhack.screen.getWindowSize()[0]
dfhack.screen.getWindowSize()[1]
...
Could it be thatCode: [Select]dfhack.screen.getWindowSize()
is broken in the lua api?
As per Screen.cpp Screen::getWindowSize() returns a coord2d, so it has a x and a y field. Reference:Code: [Select]df::coord2d Screen::getWindowSize()
{
if (!gps) return df::coord2d(80, 25);
return df::coord2d(gps->dimx, gps->dimy);
}
but lua treats it as a single number.Code: [Select]dfhack.screen.getWindowSize()
will just return the x value (width), but if you print it it will show x and y in the console but there is no way to select either x or y.Code: [Select]dfhack.screen.getWindowSize().x
dfhack.screen.getWindowSize().y
dfhack.screen.getWindowSize().X
dfhack.screen.getWindowSize().Y
dfhack.screen.getWindowSize()[0]
dfhack.screen.getWindowSize()[1]
...
all fail because of "attempt to index a number value" which makes sense because lua thinks getWindowSize returns a single number.
You sure it isn't just returning two values? x, y = dfhack.screen.getWindowSize()
:o You are right! Thanks. Now I feel dumb... I didn't knew multiple returns where possible in lua and the error didn't really help. I would have expected a different kind of error that somehow indicates that there are multiple values returned and not just taking the first one and working with that. That's what I'm used to from other languages. Thanks again for your help!
I've been experimenting with using Qt in DFHack plugins and it's mostly working (much better that I thought it would at first).
I pushed the code on this github (https://github.com/cvuchener/dfhack-qt). The qapplication plugin manages the main Qt thread and provides a few basic commands for configuration or diagnostics. Other plugins can call addTopLevelWidget from include/TopLevelWidget.h to create widgets. qapplication needs to be enabled before Qt widgets can be created.
The labors (https://github.com/cvuchener/dfhack-qt/tree/labors) branch contains a very basic DT-like plugin that can serve as a demo. If you want to try it you will need to have the default_gridviews.dtg (https://raw.githubusercontent.com/Dwarf-Therapist/Dwarf-Therapist/master/resources/default_gridviews.dtg) file from DT copied in DF root directory. Data is only accessed when the game is suspended (use the "suspend" and "resume" buttons in the tool bar).
I have a Qt-based library for dfhack clients I could publish too if you are interested.
Also note that the biome in the air may differ from the one at ground level, so any farming may support crops different from the ones expected (the air biome is the one of the world tile to the NW of the embark tile [and there's a "shear" bug that causes random 16*16 areas in the air to take on any of the up to 9 biomes of the embark world tile and the 8 surrounding ones]).
Ah, damn.Like Patrik said, I'm not sure that that will produce any different results, but what you could do is use dfhack to manually change the tile type of the ramps and floors to obsidian. Also time consuming, and if you are going to do it a lot I would suggest writing a script for it. But definitely doable
I guess I can try an alternative method, by creating magma then water to cast obsidian. It'll be very time consuming.
Thanks for the confirmation, though!
I fail to see how obsidianizing using magma+water would give a different result from spawning obsidian directly. The problem comes from mining out of a huge block, so spawn only walls (and build floors out of obsidian [blocks]). It's still more work to spawn parts rather than a block that's mined out, of course.I just assumed, for some reason, that casting obsidian that way would change the tile from air to obsidian.
Is there a function to find the center of the viewport that works with things like TWBT?Gui::getDwarfmodeViewDims() is modified by TWBT (specifically the map_* fields). I'm guessing you can use that to find the center of whatever you need.
I have a Qt-based library for dfhack clients I could publish too if you are interested.
Yes please!
By the way, with this Qt application you could recreate Dwarf Therapist entirely in DFHack, isn't?.
No need for .ini files, etc.
Just curious why you still keep using the original way
I have a Qt-based library for dfhack clients I could publish too if you are interested.
Yes please!
By the way, with this Qt application you could recreate Dwarf Therapist entirely in DFHack, isn't?.
No need for .ini files, etc.
Just curious why you still keep using the original way
Here it is. (https://github.com/cvuchener/dfhack-client-qt) I tried to write some documentation (in README and headers), not sure if it is clear. It is also missing pkg-config or cmake find_package stuff so you'll have to add the compile and link flags manually. There is nothing special though, simply add the usual include path (-I/install/path/include), lib path (-L/install/path/lib) and link flags (-ldfhack-client-qt).
It is not much tested and the API may need improvements. Don't hesitate to report issues or make suggestions or improve it yourself.
You just access the DF data directly from the plugin.
function Gigapurge()
local adv=df.global.world.units.active[0]
for k,v in pairs(df.global.world.units.active) do
if v.civ_id ~= adv.civ_id then
df.global.world.units.active:erase(k)
end
for ko,vp in pairs(df.global.world.units.all) do
if adv.id ~= vp.id then
df.global.world.units.all:erase(k)
end
end
end
end
Gigapurge()
I suspect you're playing with fire, Rumrusher. This is unpleasantly similar to the bugged DFHack clearing of "dead" (inactive in reality) units that just left them in limbo at map entry. I'd recommend you to take a very close look at what you're doingyeah this script is pretty much a big risk for being a solution for what I would pretty much avoid if it happen to me in adv mode, but was made to solve 'accidental influx of animal and wildlife filling up a camp.' which I noticed is caused by folks slow traveling around the camp edges repeatedly and ended up triggering the adv mode wildlife fill in in the campgrounds which ends up filling it up.
I don't know how the "pairs" operator handles iteration over lists that are modified in the process, but the normal way to purge things from dense lists (which these lists are) is to iterate over each index backwards. If you remove element i, that will move all elements beyond that one one step forward, and you'd then take a look at i - 1, which has not been affected except possibly of a "next" link, if one is present. If you were to do it forwards you'd have to refrain from increasing the index when you delete an element (and I suspect that's why you have to run it several times, as each deletion probably skips looking at the next element).
Note that I still advocate a great deal of caution and to analyze what's done. I haven't, so this warning is a gut feeling one.
OK, apologies if this is a dumb question, but google and readthedocs have failed me, so...According to the code (https://github.com/DFHack/dfhack/blob/c11f2b5ffa7434afca44e9ad44ea61d2f1aec37c/plugins/stocks.cpp#L471-L472) it's for improved items. It does seem like something that ought to be in a legend somewhere.
In the enhanced stocks view, some items have a blue asterix right before the quality marker. I have been unable to find anything that tells what this asterix is intended to indicate, nor can I find any pattern in which items have it and which items don't. It's driving me batty :).
So, what does the blue asterix indicate?
According to the code (https://github.com/DFHack/dfhack/blob/c11f2b5ffa7434afca44e9ad44ea61d2f1aec37c/plugins/stocks.cpp#L471-L472) it's for improved items. It does seem like something that ought to be in a legend somewhere.
this problem might relate to my mousequery problem, where it works fine for top and left mouse scrolling, but not the right and bottom border. i use 5:4 screen and 1280x960 windowed mode. so far none of the widescreen players confirmed if they get the same problem when they chose windowed and smaller res.I am getting the same mouse problem jaynixxo was having as well, it seems to be 87 by 43 tiles that the mouse will let me click on in full screen, so the problem goes away if I play very zoomed in.
I'm getting this problem as well, the mousequery only works inside a set area, if i zoom out the mouse doesn't scale to the now larger view area, doesn't matter if i'm windowed or fullscreen.
But in my case i also can't use right-click to pan around the map properly and it seems related to zoom level. At some levels i can only pan up or left and up, sometimes it pans up and down but not left or right.
Yeah, most of the time i can scroll left or up, but not right or down.
I play with 1920x1080 and the problem remains the same in windowed or fullscreen. And i just tried windowed with a low resolution (800x600) and the problem remains the same.
If i zoom the map all the way out, mousequery only works on a part of the top-left corner that is 87x40 tiles, which i designated on this image. The entire map is 192x192 tiles.Spoiler (click to show/hide)
I currently only access/modify DF data when a CoreSuspender is locked, but I wonder if this could be relaxed. Can any assumption be made on some pointers validity with regards to state change events (SC_WORLD_LOADED, SC_WORLD_UNLOADED, SC_MAP_LOADED, SC_MAP_UNLOADED, SC_PAUSED, SC_UNPAUSED, ...) for read-only access?
line 15: 80035 Segmentation fault: 11 DYLD_INSERT_LIBRARIES=./hack/libdfhack.dylib ./dwarfort.exe "$@"
Ah, yes. Well, looking at thisSpoiler (click to show/hide)
I'd say that yes, this definitely looks like the bug you mentioned. Funny that I didn't notice that earlier while playing with equipment. So this probably means that the fort is pretty much done?
Patrik/Roses, is it possible to identify the issue programmatically (i.e. not just by visual inspection)? A script to at least detect the issue would be somewhat useful, even if it's not recoverable at the moment.
All right. In that case it seems the best option is to mothball this fort in hopes that the bug can be fixed in some future version, and concentrate on more peaceful pursuits. Too bad, I had managed to steamroll over half the continent already before it became impossible to continue.
hmm kinda wonder if it possible to just create another fort in this save or is that world just doomedMy unsubstantiated guess is that a new fortress probably would be fine (until you raid), because the items you equip soldiers with are (supposed to) be items local to the fortress, so it's that information that gets corrupted, and the corruption is somehow caused by a bugged handling of what originally was fortress equipment held by raiding soldiers when they return, so if you never raid in a new fortress you probably would start from a blank slate. However, a quick embark with some embark points spent on armor would show if the equipment lists are corrupted from the start. That won't guarantee that corruption isn't hiding somewhere, of course.
What about just retiring then un-retiring? Would that help at all?I just tried it, and DF died on saving the retired fortress (both with and without DFHack)... so there's no save to unretire. It's only one world, though, but still an indication. Wouldn't exactly be surprise if DF is unable to package up the corrupted data.
@fortunawhisk: There's no lack of saves with the bug (including a couple of mine). Most of the crash reports for the current version on the bug tracker is caused by this bug. Someone with a tag I can't catch (ruisuif or something like that) went through the crashing saves and verified the cause in the majority of those (finding the corruption of the data and verifying the repeatable crashes went away of all militia was disbanded). Thus, there's not much point in more saves containing this bug.
Regarding the structures Roses mentioned, they seem to be used for squads, and I assume DF crashes when DF tries to place an unsuitable object in those structures. We probably need to get at the lists from which DF selects those items to DFHack around the issue.
so I guess if you want to raid in this buggy state you have to remove all items from your military uniform and send them out?No, the bug probably doesn't have to do with uniforms in the sense of a set of equipment, but rather with uniforms in the sense of the equipment allocated to militia in their capacity of militia. A uniform is just a template of selection criteria for items, and as far as I know these criteria get copied to the militia member when the uniform is allocated, so there's no difference in the militia squad info between a selection of a metal helmet and a user selection of a metal helmet for the head slot. Sure, civilian clothing and no shield nor weapons might not provoke the bug (we don't know for sure that civilian clothing doesn't corrupt things as well: after all, it also gets removed from the fortress and then gets returned to it when the buggers return), but their survivability isn't that great should it come to blows (but a squad that doesn't return probably doesn't generate corruption). If I was to gamble, I'd try to never load a save where any troops are outside of the embark and see if the corruption is caused by restoring offloaded data, as it might be that data that's retained in the item structure is fine, but I haven't heard of that being tested. The only safe action currently is never to raid.
Is there some way to truly erase trees? I tried using tiletypes to turn them into empty air and they reappear the next time plants are processed and "plants extirpate" seems to no longer be implemented.Fire a ballista at them.
They're kind of a problem when they're in edge connected water where I'd need to drop my dam to drain the lake.
Is there some way to truly erase trees? I tried using tiletypes to turn them into empty air and they reappear the next time plants are processed and "plants extirpate" seems to no longer be implemented.It sounds that your problem is cavern trees standing in the lake. My standard method to get rid of those is to fire at them with a ballista: A ballista arrow hitting a tree will disintegrate it completely. It's even possible to fire at tree stems under water if you've got a drain to protect the ballista operator. Note that any part of the tree can be hit for the tree to disappear, so most of the time you don't need to attack them from beneath.
They're kind of a problem when they're in edge connected water where I'd need to drop my dam to drain the lake.
Changing the tile doesn't help, as you've discovered, because the tree exists in the plants list as well (there's a bug involving repeated cave-ins due to maturing trees that were caved in originally, as DF apparently missed to remove those trees, so when growing they pop into existence and grow in the air, and then falls as they find there's no ground supporting them).That's really interesting, and quite pertinent to something I'm working on, couldn't you just remove the tree from the plants list to prevent this from happening?
Not sure where to ask this as I've scoured google for an answer..Looks like you found the pointers just fine to me! :)
I'm looking to cure only infection from my adventurer. I tried forking full-heal, but alas I can't seem to figure it out. I can get a list of wounds in unit.body.wounds, but they just look like memory addresses. Example:
<unit_wound: 0000025C0216BC10>
<unit_wound: 0000025C0216BD60>
<unit_wound: 0000025C0216C540>
<unit_wound: 0000025C0216BCF0>
<unit_wound: 0000025C0216BF90>
<unit_wound: 0000025C0216D420>
Any pointers?
I'm looking to cure only infection from my adventurer. I tried forking full-heal, but alas I can't seem to figure it out. I can get a list of wounds in unit.body.wounds, but they just look like memory addresses.
local unit = df.nemesis_record.find(df.global.ui_advmode.player_id).unit
unit.body.infection_level = 0
for _,wound in ipairs(unit.body.wounds) do
wound.flags.infection = false
end
eventType=invertTable{
[0]="TICK",
"JOB_INITIATED",
"JOB_COMPLETED",
"UNIT_DEATH",
"ITEM_CREATED",
"BUILDING",
"CONSTRUCTION",
"SYNDROME",
"INVASION",
"INVENTORY_CHANGE",
"REPORT",
"UNIT_ATTACK",
"UNLOAD",
"INTERACTION",
"EVENT_MAX"
}
Looks like an event being triggered at the beginning of each month is not available. There is an event called "TICK". I was thinking about using this event, but it looks "dangerous" and I found no other script using it so far. As I'm no LUA expert, I'm also unsure how to setup an event handler for this case.static const handler_t eventHandlers[] = {
NULL,
ev_mng_jobInitiated,
ev_mng_jobCompleted,
ev_mng_unitDeath,
ev_mng_itemCreate,
ev_mng_building,
ev_mng_construction,
ev_mng_syndrome,
ev_mng_invasion,
ev_mng_inventory,
ev_mng_report,
ev_mng_unitAttack,
ev_mng_unload,
ev_mng_interaction,
};
So eventHandlers[0] == NULL. Just discovered that LUA'sYou're looking for "dfhack.print", with "dfhack.println" adding a newline (which you didn't want).
io.stdout:write(...) gets written to stdout.log. To get something printed in the dfhack console window, you must use "print".
When using loops to generate output print is a problem, because each call appends a newline automatically. Is there another way to write something to the df console using the io.stream ... api?
I know, I can concatenate strings, but this is ugly.
You're looking for "dfhack.print", with "dfhack.println" adding a newline (which you didn't want).
I'd like to know if my Qt experiment works on macOS (I am worried by this (https://bugreports.qt.io/browse/QTBUG-7393)). But, since I cannot use macOS myself, I need help from a macOS user who knows how to compile DFHack plugins.
Download and compile the plugin from here (https://github.com/cvuchener/dfhack-qt/tree/master). You will need to install Qt (homebrew can help with that) and pass its installation directory to cmake with -DCMAKE_PREFIX_PATH (-DCMAKE_PREFIX_PATH=/usr/local/opt/qt for homebrew installation).
Try enabling the plugin ("enable qapplication"), then try commands like "qt-info" or "qt-log". Please tell me if windows appear and if DF survives it. Also check the content of "qt_log.txt" in DF directory (or the content of the log window if it works).
What do you mean by "exist"? Are you looking for the animals on the map, or animals that could appear on the map? Regular creatures or vermin? Is this before or after embarking?
Someone with a tag I can't catch (ruisuif or something like that)That would be me.
I expect those structures to exist only when viewedSeems to be true. I was able to put a debugger breakpoint on the function that populates equip lists, but i don't really know how that memory mapping thing is done, what can i do from there?
can someone finally fix the mousequery bug?Please link to a description of the bug. I've seen you mention it as if everyone knows about it several times, but I can't find a complete description of the problem you're having easily, and can't even attempt to fix it without one. The DFHack bug tracker (linked in the first post) would be the best place to report it in any case.
now with a 16:9 monitor, i can say the "mousequery edge" bug is not dependant on resolution.
it is well explained in its symptoms and exists ever since 44.9 (and there was a possible fix until 44.10, which got dropped)
all tilesets and mods move towards 44.12 and it's a pita (and most times impossible) making them work on 44.9.
added link to the working mousequery.plug.so https://www.dropbox.com/s/v3gjhco764p28uw/mousequery.plug.so?dl=0When lethosor asked for the dll/so/dylib, he meant that you should provide the one that matches your platform - DFHack uses .dll files for plugins on Windows, .so files on Linux, and .dylib files on MacOS.
could not find any .dll or whatever, but that might be because it's in the linuxLNP
e) Mephs lite + curses 800x600 font // Phoebus + curses 800x600 fontI'd appreciate it if you could test without TWBT and test with just the 800x600 font (i.e. set everything to use the 800x600 font, or maybe just all the same font - no separate text/map tiles). The potential issue with square tiles is that they take up more space than rectangular tiles, so fewer of them can fit on the screen. This could cause issues because the "20th tile from the top" could be close to the bottom of the screen in the map view, but not at all close in the menu view, if that makes sense.
f) TWBT always on testing turning it off, but i think i remember it not changing a thing.
When lethosor asked for the dll/so/dylib, he meant that you should provide the one that matches your platform - DFHack uses .dll files for plugins on Windows, .so files on Linux, and .dylib files on MacOS.Yup. I couldn't remember which platform you were using, so I included all the possible extensions.
Basic learner's question. Looking through lua script examples from DFHack, I'm confused by this point: when should a global variable be used instead of a local variable?By "local variables" do you mean ones declared inside a function, or ones declared with the "local" keyword anywhere (including outside a function)?
@Prismatic: Almost never.No, each script has its own completely separate environment where its globals live. Globals in one script are (almost) entirely separate from those in another. That's why reqscript() and dfhack.script_environment() exist. Granted, there are a few special ones that are shared between scripts (df, dfhack, and the built-in functions, and maybe something else I'm forgetting), but scripts cannot create their own globals that are shared across all scripts.
Most cases of global variables being used are probably bugs. A global variable clutters the Lua global variable space. <snip - this was a good point, see above>. Also note that you'd probably want to use a very specific variable name for that case to avoid two scripts using the same one accidentally.
Q: Can you make a Guest that is 'ready to leave' a permanent resident of the fortress? maybe with tweak makeown?"tweak makeown" predates visitors/guests by many years, so I wouldn't expect it to work, although I don't think I've tried. It's definitely something I've wanted to work, though.
in the description of makeown, it says to "select the creature", is that using 'k' or 'v'?
thanks for the help and thanks for all the work on dfhack!
PS: It is a wild boar woman fighter
Pvt Pirate: sorry, I should have been nicer about that.so with render mode "standard" and curses 800x600 it works.e) Mephs lite + curses 800x600 font // Phoebus + curses 800x600 fontI'd appreciate it if you could test without TWBT and test with just the 800x600 font (i.e. set everything to use the 800x600 font, or maybe just all the same font - no separate text/map tiles). The potential issue with square tiles is that they take up more space than rectangular tiles, so fewer of them can fit on the screen. This could cause issues because the "20th tile from the top" could be close to the bottom of the screen in the map view, but not at all close in the menu view, if that makes sense.
f) TWBT always on testing turning it off, but i think i remember it not changing a thing.
It's also possible that TWBT is interfering with mousequery even when the tilesets are all the same size, which is why I suggest disabling it entirely too.
so with render mode "standard" and curses 800x600 it works.Ok, good to know! I ought to be able to figure out why it's not working now.
now how do i get it working with TWBT and Meph's?The "20" was just an example of how the number of rows in different parts of the screen can be different with TWBT; that number doesn't actually appear anywhere in mousequery.
i mean if it works perfectly fine with a 16x16 set, why shouldn't we be able to make it work with a 32x32 too. (edit: doesn't work with twbt and 16x16 either)
is it necessary to define the scrolling border as the 20th tile or could one adjust that number to fit the tileset?
You can grab a build of mousequery from my PR (https://github.com/DFHack/dfhack/pull/1440) at https://files.dfhack.org/pr1440-1/dfhack-0.44.12-r2-pr1440-1-Linux-64-gcc-7/hack/plugins/ (for 64-bit Linux, GCC 7). Let me know if it fixes the issue.thanks :) trying it.
If anyone wants a different build I can put it up there too.
Would you mind testing with smaller map tiles than text tiles (zooming out in the map view should do the trick). I'm not sure how it'll handle that case.
IT WOOORKS! :D
I NEED KNOW SIZE OF CHAIN!I'm not quite sure what you're looking for or why you need it. Can you explain what you need it for?
I need chain size for weapon made from one trap component and one chain. I know trap component size, but not know chain size.Would you mind testing with smaller map tiles than text tiles (zooming out in the map view should do the trick). I'm not sure how it'll handle that case.
IT WOOORKS! :DI NEED KNOW SIZE OF CHAIN!I'm not quite sure what you're looking for or why you need it. Can you explain what you need it for?
My understanding is that sizes of objects aren't necessarily fixed, although maybe chains are all the same size. If they have a size, it should be tracked somewhere, so looking for it through DFHack should be possible (I doubt anyone can tell you off the top of their head).
I need chain size for weapon made from one trap component and one chain. I know trap component size, but not know chain size.I NEED KNOW SIZE OF CHAIN!I'm not quite sure what you're looking for or why you need it. Can you explain what you need it for?
My understanding is that sizes of objects aren't necessarily fixed, although maybe chains are all the same size. If they have a size, it should be tracked somewhere, so looking for it through DFHack should be possible (I doubt anyone can tell you off the top of their head).
This will be ogre's weapon, so chain may be long.I need chain size for weapon made from one trap component and one chain. I know trap component size, but not know chain size.I NEED KNOW SIZE OF CHAIN!I'm not quite sure what you're looking for or why you need it. Can you explain what you need it for?
My understanding is that sizes of objects aren't necessarily fixed, although maybe chains are all the same size. If they have a size, it should be tracked somewhere, so looking for it through DFHack should be possible (I doubt anyone can tell you off the top of their head).
There are nicer ways to ask. As far as I can tell chains don't have a size, at least not in the traditional sense (they have no item subtype so there is no [SIZE] token. However, if we look at a trap component made of iron and a chain made of iron and compare their weight we can guess a size. An enormous iron corkscrew weighs 12.56 and has a size of 160, an iron chain has a weight of 39.25, therefore, if SIZE is a unit of volume, the chain would have a size of 500. As a comparison, an iron two-handed sword has a weight of 7.65, which would be a SIZE of 97.45, compared to it's listed size of 90. So obviously it's not exact, and for some reason it's huge, but that's the closest you can get. Of course you could also just use the size of whip as an approximation for a weapon chain, since a weapon made with a chain will probably cut off some of the chain anyway.
A chain inside a well can stretch from the surface down to the depths of hell, potentially, so....
i already zoomed in and out and scrolling worked fine (meph32lite+curses800x600)Would you mind testing with smaller map tiles than text tiles (zooming out in the map view should do the trick). I'm not sure how it'll handle that case.
IT WOOORKS! :D
i already zoomed in and out and scrolling worked fine (meph32lite+curses800x600)Basically, mousequery was always checking the cursor position against the window size in text tiles to determine when to scroll the map. TWBT modifies the cursor position that gets reported when you're hovering over the map so that it reports map tile coordinates instead. This meant that when the map tiles are larger than the text tiles, the cursor position in the lower/right areas of the map would never get large enough to trigger mousequery's scrolling. Thanks for the help diagnosing it - the fix is merged now, so it'll be in the next DFHack release.
i don't know what you changed axactly, but it does the trick.
i'll also tell my friend who had the same problem.i already zoomed in and out and scrolling worked fine (meph32lite+curses800x600)Basically, mousequery was always checking the cursor position against the window size in text tiles to determine when to scroll the map. TWBT modifies the cursor position that gets reported when you're hovering over the map so that it reports map tile coordinates instead. This meant that when the map tiles are larger than the text tiles, the cursor position in the lower/right areas of the map would never get large enough to trigger mousequery's scrolling. Thanks for the help diagnosing it - the fix is merged now, so it'll be in the next DFHack release.
i don't know what you changed axactly, but it does the trick.
can we have a plugin that causes monthly saves? like triggering quicksave at the beginning of each month (except those where seasonal save does)?
the seasonal autosaves aren't enough when the game crashes too often and too unpredictable and i tend to forget to quicksave often enough.
repeat -name monthly_save -time 1 -timeUnits months -command [ quicksave ]
It wouldn't skip seasonal saves, though. (Not sure if that causes a problem.)repeat -name ten_min_save -time 60000 -timeUnits frames -command [ quicksave ]
Assuming a max FPS cap of 100. Not sure what happens if you're in a menu when it activates. Maybe it just fails.Project "C:\Users\Putnam\Documents\Projects\C++\dfhack\build\win64\VC2015\CMakeFiles\3.14.0\VCTargetsPath.vcxproj" on node 1 (default targets).
C:\Users\Putnam\Documents\Projects\C++\dfhack\build\win64\VC2015\CMakeFiles\3.14.0\VCTargetsPath.vcxproj(14,2): error MSB4019: The imported project "C:\Microsoft.Cpp.Default.props" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
Done Building Project "C:\Users\Putnam\Documents\Projects\C++\dfhack\build\win64\VC2015\CMakeFiles\3.14.0\VCTargetsPath.vcxproj" (default targets) -- FAILED.
Build FAILED.
"C:\Users\Putnam\Documents\Projects\C++\dfhack\build\win64\VC2015\CMakeFiles\3.14.0\VCTargetsPath.vcxproj" (default target) (1) ->
C:\Users\Putnam\Documents\Projects\C++\dfhack\build\win64\VC2015\CMakeFiles\3.14.0\VCTargetsPath.vcxproj(14,2): error MSB4019: The imported project "C:\Microsoft.Cpp.Default.props" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.15
using your first script really helps alot! it saves on every 15th day of the month (can't interfere with seasonal saves) and that somehow seems to make fps smoother after saving.can we have a plugin that causes monthly saves? like triggering quicksave at the beginning of each month (except those where seasonal save does)?
the seasonal autosaves aren't enough when the game crashes too often and too unpredictable and i tend to forget to quicksave often enough.
Maybe something like this added to the "onLoad" init file would suffice:Code: [Select]repeat -name monthly_save -time 1 -timeUnits months -command [ quicksave ]
It wouldn't skip seasonal saves, though. (Not sure if that causes a problem.)
Or if you want it to save every 10 mins, even while paused, this might work:Code: [Select]repeat -name ten_min_save -time 60000 -timeUnits frames -command [ quicksave ]
Assuming a max FPS cap of 100. Not sure what happens if you're in a menu when it activates. Maybe it just fails.
You could cancel it while going AFK using "repeat -cancel ten_min_save". You'd have to re-enter the main command to reactivate it, or save&quit then reload.
If you want anything more complex, you'd need a script/plugin.
I have never once in my life managed to successfully compile DFHack. Right now I can't even get past the CMake step:Code: [Select]Project "C:\Users\Putnam\Documents\Projects\C++\dfhack\build\win64\VC2015\CMakeFiles\3.14.0\VCTargetsPath.vcxproj" on node 1 (default targets).
C:\Users\Putnam\Documents\Projects\C++\dfhack\build\win64\VC2015\CMakeFiles\3.14.0\VCTargetsPath.vcxproj(14,2): error MSB4019: The imported project "C:\Microsoft.Cpp.Default.props" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
Done Building Project "C:\Users\Putnam\Documents\Projects\C++\dfhack\build\win64\VC2015\CMakeFiles\3.14.0\VCTargetsPath.vcxproj" (default targets) -- FAILED.
Build FAILED.
"C:\Users\Putnam\Documents\Projects\C++\dfhack\build\win64\VC2015\CMakeFiles\3.14.0\VCTargetsPath.vcxproj" (default target) (1) ->
C:\Users\Putnam\Documents\Projects\C++\dfhack\build\win64\VC2015\CMakeFiles\3.14.0\VCTargetsPath.vcxproj(14,2): error MSB4019: The imported project "C:\Microsoft.Cpp.Default.props" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.15
Can't find anything about these parts, based on my complete failure to ever compile a C++ project in my entire life I'd guess it's cause I somehow never manage to install MSVC correctly (because I have two hard drives? Because I'm a fuckup? Could be anything). Googling it reveals that there's some ways to fix it by explicitly including stuff in the command line, but I don't really like doing hammer-swing break-shit changes like that for my own personal use to git projects.
How make dwarf and other sapient corpses butcherable and usable?
for k,unit in ipairs(df.global.world.units.all) do
unit.name.first_name = 'Testing'
end
for k = 0,#df.global.world.units.all-1 do
local unit = df.global.world.units.all[k]
unit.name.first_name = 'Testing'
end
I'd like to know if my Qt experiment works on macOS (I am worried by this (https://bugreports.qt.io/browse/QTBUG-7393)). But, since I cannot use macOS myself, I need help from a macOS user who knows how to compile DFHack plugins.
Download and compile the plugin from here (https://github.com/cvuchener/dfhack-qt/tree/master). You will need to install Qt (homebrew can help with that) and pass its installation directory to cmake with -DCMAKE_PREFIX_PATH (-DCMAKE_PREFIX_PATH=/usr/local/opt/qt for homebrew installation).
Try enabling the plugin ("enable qapplication"), then try commands like "qt-info" or "qt-log". Please tell me if windows appear and if DF survives it. Also check the content of "qt_log.txt" in DF directory (or the content of the log window if it works).
I hope that I'm just missing something, but I believe this to be true: Running "reveal" then saving and exiting the game does not allow un-revealing the map when that save is re-opened.That is correct. The previous state is only saved in memory. "revflood" should be helpful in reproducing the unrevealed state for the most part, though.
Can DFhack automatically forbid fertile eggs? I need this for test egg-laying civ.You can write a DFHack Lua script to forbid fertile eggs, but it would probably cost you in FPS, because I don't think there's any trigger for the laying of eggs, so you'd have to use a script that makes a callback at fixed intervals. If those intervals are too long, eggs may be laid and collected in between script callbacks.
I believe all of that information has been mapped, which means it should be possible to use gui/gm-editor to modify it. I don't know if there are any scripts specifically for it, though.I don't know where to find it.
It's probably related to the body parts defined by the raws in some way. I haven't mucked around in this area so I don't know any details.I believe all of that information has been mapped, which means it should be possible to use gui/gm-editor to modify it. I don't know if there are any scripts specifically for it, though.I don't know where to find it.
I use gm-editor, find appearance - bp-modifier and then I see numbers.
0 98
1 95
2 92...
What do they mean?
Is there a way to edit, modify appearance with DFHACK? I mean those descritions: "He is tall/short. His hair is curvy/straight" and so on.I made a modification to gui/gm-unit a while back for editing appearances which can be found in my DF dumping repo (https://github.com/Atkana/Dwarf-Fortress-Mods#gm-newnit) :p
Using a variable within the script combined with the SC_VIEWSCREEN_CHANGED argument to the dfhack.onStateChange callback
Is there any way to convert a tile into a specific soil type? I embarked in a super specific location thinking it would have sand but apparently the "sand" was just sandy loam and completely worthless.Experimenting, it turns out to be surprisingly easy to do with the Biome Manipulator http://www.bay12forums.com/smf/index.php?topic=164658.0 (http://www.bay12forums.com/smf/index.php?topic=164658.0). Start the fortress, invoke the Biome Manipulator and change ("morph") the offending geo biome layer from sandy loam to the kind of sand you want. Note that you should probably save and reload DF to make sure DF does recalculate things properly, and I'd also make a backup of the save before manipulating it to have something to return back to if things go horribly wrong. Also note that your embark may have multiple (geo) biomes or the geo biome of a neighboring world tile, so you may need to search around the neighborhood to find the right geo biome to manipulate.
Though builder (http://www.bay12forums.com/smf/index.php?topic=167487.0) doesn't place soils in drop-down box, you can place individual tiles with it. However, the lack of placement may be very well justified by how sand mineral walls are not acceptable options for sand collection, and changing tiletype to soil changes material to layer material.I've taken a look at "builder", and it seems it creates walls from veins/inclusions, and that DF doesn't actually reference the veins and inclusions in the geo biome for the actual data, but rather creates a local list for the tile block to draw from (and presumably this list was drawn from the geo biome when DF generated the block). There's nothing except script selection logic that blocks the creations of soil "vein" walls, and it was possible to create a "milk of lime" vein floor with the script. Changing a floor tile to be a red sand vein floor (after removing the script part that blocks selection of soils) results in a floor tile that's not suitable for sand collection nor farm plot placement, presumably because it's not actually a natural floor.
Haven't checked whether soil mineral walls are acceptable for plant growth, but I suspect not.
I don't think you can change single tiles, because it seems the tile itself doesn't specify the material, but rather references the layer that does that (and, I presume, vein and inclusion to use, when appropriate).This is correct. The "tiletypes" feature I was thinking of creates new veins on the fly to resolve this (but I don't know if it works for soil).
I was wrong about veins and inclusions, as indicated by my previous post, as they're local to the map block, allowing for the creation of new ones (as you said). Given my brief fiddling with "builder", I see no reason why you can't create veins of soil, when you can create them out of milk of lime and refined metals: it ought to work with any inorganic material. However, soil vein floors didn't work for farming or sand extraction, as I mentioned, so there's little reason to do so. I'd expect "grass" won't grow of such soil either, although I didn't attempt to test that (the test bed save I used doesn't have any anywhere, anyway).I don't think you can change single tiles, because it seems the tile itself doesn't specify the material, but rather references the layer that does that (and, I presume, vein and inclusion to use, when appropriate).This is correct. The "tiletypes" feature I was thinking of creates new veins on the fly to resolve this (but I don't know if it works for soil).
A test build with a fix (hopefully) for a stonesense wagon-related crash is up at https://github.com/DFHack/stonesense/issues/54#issuecomment-510273737 if anyone here is interested in testing that.
well here's a script for implanting an adventurer's genes into an egg for reasons I can't grasp be warn it's probably for the best to change the mother genes to father genes if you don't want the game to crash trying to grasp the horror's true formSpoiler: egg splicing script (click to show/hide)
oh and probably should poke around their genes after birth as some times their colors could hit the 1000 to 6000 range which I guess is probably why the game crashes on looking at them.
oh yeah forgot this script was made for adv mode, and that the person is holding no items but the egg itself, this probably wouldn't work universally in fort mode either.well here's a script for implanting an adventurer's genes into an egg for reasons I can't grasp be warn it's probably for the best to change the mother genes to father genes if you don't want the game to crash trying to grasp the horror's true formSpoiler: egg splicing script (click to show/hide)
oh and probably should poke around their genes after birth as some times their colors could hit the 1000 to 6000 range which I guess is probably why the game crashes on looking at them.
Hmmm.
Shouldn't you check that the item type that Invcheck return is a item_egg?.
My unit has a inventory item of type 1, but is a item_weapon, not a item_egg, so no mother_genes, etc.
Edit Edit: I also figured out that unk_1, unk_2, and unk_3 in df.global.ui_advmode are the last x, y, and z coordinates of the adventurer's army (set/updated when the travel screen is opened, or you move on the travel screen)... then I discovered this was already known because it was used in questport Q.Q
I want to report a bug in Dfhack (mousequery thing, to be precise). Even when I set mousequery edge disable, it still scrolls on edge, particularly after clicking on a tile and moving around.Does it only scroll when you click near the edge of the screen? If so, that's probably intentional - that's how DF's cursor works when moved with the arrow keys too.
Noticed I can change what dwarfmonitor's dwarfmode viewscreen widgets display by editing the dwarfmonitor.lua (in 43.03 in case it matters).Do you mean dwarfmonitor.json? That's the file you're intended to edit (should be in the dfhack-config folder).
The usefulness has unexpected limitation, though - it seems like neither require of dwarfmonitor nor script_environment of another script won't share the environment when I call it again from a separate :lua line, and using persistent configuration storage (after world has loaded) freezes the game (while getting/setting them by getting an exact copy by require and :lua works).I believe "reload('plugins.dwarfmonitor')" in Lua will reload the config and the lua file properly (although you shouldn't have to edit the lua file ordinarily).
Editing json and reloading dwarfmonitor still works, though, but is there perhaps a better way to share values between the the plugin and another lua script already available?
No, I mean hack/lua/plugins/dwarfmonitor.lua. (While I did notice one can use the cursor widget to display some text at some point with it, it's somewhat limited.)The cursor widget should always be displaying text at the same point, but you can choose where various widgets display by editing dwarfmonitor.json. I'm kind of confused - what exactly are you trying to do by editing the lua file?
I noticed dfhack.screen natives can be edited inside its render functions and work. Well, paintString at least.Editing things in dfhack.screen is generally not a good idea because it will affect all scripts.
I want to pass data to thus modified dwarfmonitor from another script during runtime (rather than putting all code inside dwarfmonitor before start).What data are you trying to pass to dwarfmonitor?
Though the reload command is useful tip if that isn't possible and rewriting the dwarfmonitor.json during gameplay is the simplest way.
Issue with partial rewriting is, of course, departing from DF's "keep all in memory" paradigm, which could cause problems when dwarfmonitor gets passed something to something newly created and process is killed.
Ah, the "its" in the context of second quote was dwarfmonitor('s renders), not dfhack.screen's. Additional widgets is pretty much right (editing just json will not display invalid widget types, so have to edit the .lua file - but not like one can include code in .json by its lonesome anyway.)(I thought you meant you were editing dfhack.screen itself, not really from somewhere in particular)
Ideally, I'd want to pass lua table(s), but any data might work - the issue is passing data without modifying files on disk, so that if DF is killed the next start will begin in same state as previous (which could be accomplished via cleanup on startup), and so that one doesn't need to worry about excessive disk access (which couldn't be accomplished when passing data through .json or the like).I'm still kind of confused about what exactly you're doing. Can you explain? Are you trying to display some data that a separate script is generating in a widget? Is there a reason you can't do that in the widget itself? (Passing it in memory is definitely better than going through disk - at the very least, reqscript() / script_environment() should work from dwarfmonitor.lua.)
"Are you trying to display some data that a separate script is generating in a widget?" Yes, exactly.They are! (https://github.com/DFHack/dfhack/blob/0.44.12-r2/plugins/dwarfmonitor.cpp#L2027) Yeah, that might cause some issues. That's almost certainly why dfhack.timeout and persistent storage aren't available, as you mentioned - widgets haven't needed those, and running Lua code from DF's render() as the widgets do can sometimes break in weird ways, which I think is why I isolated it by using a separate Lua state.
Tried script_environment on a dummy script (also tried reqscript now; same result) and dwarfmoniter alike, adding/editing variables in during runtime, didn't work even with disable-enable or by editing variables from console before enabling dwarfmonitor for first time in current process. It looks like the environment dwarfmonitor generates and the environment console generates are different. (Adding something to BASE_G obviously failed too; wildly speculating might be that they're on separate C lua states).
After a bit of curious scanning, I couldn't find in dfhack.lua any way a script can get run without having a reference to its `env` stored in dfhack.lua's local `scripts` table which records when scripts where last run, their `env` (a reference to their local meta-table (?)) and other things. It looks as though env references have to be working in order for the module system to be working (by sharing script state through them)
Since it seems all handled within dfhack.lua, some logging about the `scripts` tables updates might reveal why its not sharing scripts `env` as expected.
modtools/reaction-trigger -reactionName SUMMON_CRUNDLE_MALE -command [ modtools/create-unit -race CRUNDLE -caste MALE -setUnitToFort -location [ \\LOCATION ] -age 1 ]
When trying to run the scripts "adv-max-skills" or "make-legendary" I just get the error message "Error loading script"
Is the scripts old and need an update to work right again?
Image to show what I did...
https://gyazo.com/bd4e3048fc3f4ce2e839b925d751457d (https://gyazo.com/bd4e3048fc3f4ce2e839b925d751457d)
(I don't know how to "upload" images to this site, so i use Gyazo... sorry for that mild inconvenience)
EDIT: Forgot to mention that i use Meph's Tileset x32 v5.4 found here: http://www.bay12forums.com/smf/index.php?topic=161047.0 (http://www.bay12forums.com/smf/index.php?topic=161047.0)
The Tileset comes with it's own launcher and DFHack and all... But i'm sure you guys know that by now :P
Quick question: The use of this create-unit line sometimes crashes the game, sometimes it works. Any idea why?Code: [Select]modtools/reaction-trigger -reactionName SUMMON_CRUNDLE_MALE -command [ modtools/create-unit -race CRUNDLE -caste MALE -setUnitToFort -location [ \\LOCATION ] -age 1 ]
I tried searching through the thread, but found not much, besides this post: http://www.bay12forums.com/smf/index.php?topic=164123.msg8003175;topicseen#msg8003175
Ah i see, just tried as of typing this.When trying to run the scripts "adv-max-skills" or "make-legendary" I just get the error message "Error loading script"
Is the scripts old and need an update to work right again?
Image to show what I did...
https://gyazo.com/bd4e3048fc3f4ce2e839b925d751457d (https://gyazo.com/bd4e3048fc3f4ce2e839b925d751457d)
(I don't know how to "upload" images to this site, so i use Gyazo... sorry for that mild inconvenience)
EDIT: Forgot to mention that i use Meph's Tileset x32 v5.4 found here: http://www.bay12forums.com/smf/index.php?topic=161047.0 (http://www.bay12forums.com/smf/index.php?topic=161047.0)
The Tileset comes with it's own launcher and DFHack and all... But i'm sure you guys know that by now :P
You don't need to write "script" before scriptname. It's enough to write "adv-max-skills" and "make-legendary" and it automagically figures out you mean you want to run script named "adv-max-skills.lua" (or .rb or etc...)
Not on that as such, no.
Though maybe worth consideration when doing this: When I looked sharing things with dwarfmonitor off df. (as dm doesn't have persistent storage module loaded), I found I could use positions listed in df.global.ui.main.fortress_entity.positions.own, which might provide roughly 16 strings, numbers and unknown number of booleans. i.e. I could have dwarfmonitor pull data from there, alter the data from lua console, and have the results reflected in dwarmonitor's display.
(I haven't actually written anything for that yet, leaving it off for when I do need it and only implementing "simpler" widgets for now.)
I recall putnam mentioned too intense usage of persist-table can drag the game down; if that's down to the size of historical unit vector this could perhaps help streamline it a little bit.
Worth noting that https://github.com/DFHack/dfhack/pull/1402 was merged and will be in the next release - specifically, it changes the underlying persistent storage API from using histfigs to using global structures that get saved/loaded to/from JSON files as needed, much like you mentioned.
However, both the new and previous implementations should be relatively fast, since all changes are made in memory. Any slowness you're observing with persist-table is likely due to issues specific to persist-table (my understanding is that it has to do some weird and slow things to store tables across multiple histfigs) or due to you making a LOT of writes (which the new JSON-based implementation wouldn't address either). Granted, with the new API from that PR, persist-table could be made a lot faster, if I'm right about why it's slow right now, but migrating old data would take a decent amount of effort and I haven't really looked into it.
I'm not sure what you mean by closure based. There is only one class implementation in dfhack (i.e. defclass) and it uses table based single inheritance model.Worth noting that https://github.com/DFHack/dfhack/pull/1402 was merged and will be in the next release - specifically, it changes the underlying persistent storage API from using histfigs to using global structures that get saved/loaded to/from JSON files as needed, much like you mentioned.
However, both the new and previous implementations should be relatively fast, since all changes are made in memory. Any slowness you're observing with persist-table is likely due to issues specific to persist-table (my understanding is that it has to do some weird and slow things to store tables across multiple histfigs) or due to you making a LOT of writes (which the new JSON-based implementation wouldn't address either). Granted, with the new API from that PR, persist-table could be made a lot faster, if I'm right about why it's slow right now, but migrating old data would take a decent amount of effort and I haven't really looked into it.
Interesting. For now I will stick with my json work around that just stores everything in global arrays and then writes it out every so often to the data/save/current directory. I'll check out the new API in the next release and see if I can tie it in to what I am doing.
A new question though, I have been using lua for a different project and have gotten more comfortable with some of the more advanced features that I have completely ignored for my dfhack work. My question is, is there a preferred method for dfhack scripts for class constructions (e.g. table-based vs closure-based)? I've noticed most scripts seem to use the table-based method but have been trying to decide which one to use. They each have their advantages/disadvantages, but if one is more preferred for this then I'll gladly just use that.
I'm not sure what you mean by closure based. There is only one class implementation in dfhack (i.e. defclass) and it uses table based single inheritance model.
Requesting/suggesting an adjustment to exportlegends commands: that they look in DF root for a folder called 'legends'. If not found, then they go through the ISO standard user dialogue of "legends does not exist. Would you like to create it (Y/N)?" 'cause I'm rather fed up with, say, exportlegends info dumping to root like it does, and mess-making as a result.Some of the files that get dumped into the main DF folder are put there by vanilla DF's legends exporter - it's not something that DFHack controls. Having exportlegends move those afterwards is probably doable, though, and has been suggested before.
Some of the files that get dumped into the main DF folder are put there by vanilla DF's legends exporter - it's not something that DFHack controls. Having exportlegends move those afterwards is probably doable, though, and has been suggested before.
A quick search brings up Unbutcherable sentient workaround (http://www.bay12forums.com/smf/index.php?topic=165414.msg7553808#msg7553808) for the latter.Thank you. Whats about milking sentients?
I guess it may be theoretically possible, but nobody has put it into practice yet as far as I know.So I request this script.
I guess it may be theoretically possible, butnobody has put it into practice yet as far as I know.everyone is scared of how it will be weaponized.
[DFHack]# :lua local bld = dfhack.gui.getSelectedBuilding() printall(bld.room) print(dfhack.buildings.countExtentTiles(bld.room,0))
extents = nil
x = -390889336
y = 0
width = 0
height = 0
0
The impossibly incorrect values, mainly. Here's an example forThe large negative value is most likely an uninitialized value, not anything meaningful.31x1 farm plot:Code: [Select][DFHack]# :lua local bld = dfhack.gui.getSelectedBuilding() printall(bld.room) print(dfhack.buildings.countExtentTiles(bld.room,0))
extents = nil
x = -390889336
y = 0
width = 0
height = 0
0
[DFHack]# startdwarf 10
E: Encoding::ConverterNotFoundError: code converter not found (ASCII-8BIT to UTF-8)
eval:2:in `load'
eval:2:in `block in <main>'
eval:2:in `catch'
eval:2:in `<main>'
E: Encoding::ConverterNotFoundError: code converter not found (ASCII-8BIT to UTF-8)
eval:1:in `require'
eval:1:in `<main>'
E: NoMethodError: undefined method `onstatechange' for DFHack:Module
eval:1:in `<main>'
E: NoMethodError: undefined method `onstatechange' for DFHack:Module
eval:1:in `<main>'
DFHack is ready. Have a nice day!
DFHack version 0.44.12-r2 (release) on x86_64
Type in '?' or 'help' for general help, 'ls' to see all commands.
E: NoMethodError: undefined method `onstatechange' for DFHack:Module
eval:1:in `<main>'
E: NoMethodError: undefined method `onstatechange' for DFHack:Module
eval:1:in `<main>'
[DFHack]#
A quick question about DF structures documentation. In the Enum type definition section (https://dfhack.readthedocs.io/en/stable/library/xml/SYNTAX.html#enum-type-definition), it is written "Every attribute must be declared at the top level of the enum". Isn't it supposed to be "at the top of the enum" instead (or more precisely before any enum-item element)? Mentioning the top level in this context feels strange and all enum attributes being defined before use makes it easier to parse.
Yeah, there are letters of the Swedish variety in the old address, but not in the new. That could be it!Yeah, the Ruby plugin has a somewhat sketchy way of loading scripts, which has broken with single quotes (https://github.com/DFHack/dfhack/issues/1146) before. However, that should have been fixed as of this change (https://github.com/DFHack/dfhack/commit/08656a3ca7d433c43e052920836e650880c18b90).
lua print(dfhack.internal.getScriptPaths())
lua print(dfhack.filesystem.getcwd())
(feel free to remove any personal info from this one)
lua print(dfhack.internal.GetScriptPaths()[1])
(the original command just gave a table, and [DFHack]# lua print(dfhack.internal.getScriptPaths())
table: 0000026AE1010560
[DFHack]# lua print(dfhack.filesystem.getcwd())
D:\Gustav\detsomfallitfrÕntrõden\÷vrigt\DF\df_44_12_win
[DFHack]# lua print(dfhack.filesystem.getcwd())
D:\...\DF\┼\─\Í\df_44_12_win
dfhack.run_command("modtools/force -eventType NightCreature")
Request plugin that make sapients milkable in fortress mode when goblin ethics.http://www.bay12forums.com/smf/index.php?topic=121451.0
I don't know if it matters for me to respond as well, but three data points is better than two right?Sorry, I wanted printall, not print! (I didn't actually test it first). This is the one I'm more concerned about, really, so if you can double-check, that would be great.
My original, faulty path was "D:\Gustav\detsomfallitfrånträden\övrigt\DF\df_44_12_win". As you can see I manage to score all three Swedish letters in there. In alphabetic order, even. Go me.
Running the commands you asked:Code: [Select][DFHack]# lua print(dfhack.internal.getScriptPaths())
table: 0000026AE1010560
Yeah, the letters changing is weird. As Clement pointed out, it's probably something to do with the DFHack console using a different encoding (I think it uses CP437 like DF on Windows?). It's probably not the cause of the issue you were having, but it could be a related issue.Code: [Select][DFHack]# lua print(dfhack.filesystem.getcwd())
D:\Gustav\detsomfallitfrÕntrõden\÷vrigt\DF\df_44_12_win
Õ appears to be an O with a ~ over it.
å=Õ
ä=õ
ö=÷
I don't if you want to know what symbols they become, just continuing off of Lundell's musings, but anyway to get all the capital letters as well I moved the DF map to "D:\...\DF\Å\Ä\Ö\df_44_12_winCode: [Select][DFHack]# lua print(dfhack.filesystem.getcwd())
D:\...\DF\┼\─\Í\df_44_12_win
Å=┼
Ä=─
Ö=Í
I tested it on Mac too.
path:
Applications\Älgstubben\Bäverdammen\Järvträsket\Lazy Mac Pack v0.44.12 dfhack-r2/Dwarf Fortress 0.44.12/
I can confirm that LNP won´t launch when there are common Swedish words in the path.
Changing "Games" to "Gämes" in my path to DF causes the LNP launcher to fail to launch.I should clarify - I don't care at all about LNPs; I'm just interested in the DFHack issue (which is that running Ruby scripts from these paths doesn't work). I do have macOS and Linux systems available, but if you can test running just DFHack (without going through PyLNP) from a path with special characters, that would be great. PyLNP issues like the one you described should go here: http://www.bay12forums.com/smf/index.php?topic=140808.0
Note that I'm unable to copy/paste from the DFHack windowYeah, Windows is a bit weird here, but you should be able to do it by right-clicking on the title bar of the DFHack console.
This command isn't working for me:Does running "modtools/force -eventType NightCreature" in the DFHack console do what you expect?Code: [Select]dfhack.run_command("modtools/force -eventType NightCreature")
The script is running (as the print command works), but nothing is happening for that line.
I am trying to get a random werecreature to spawn in fortress mode. Am I missing something in that code?
[DFHack]# lua printall(dfhack.internal.getScriptPaths())
1 = D:\Gustav\detsomfallitfrÕntrõden\÷vrigt\DF\df_44_12_win/raw/scripts
2 = D:\Gustav\detsomfallitfrÕntrõden\÷vrigt\DF\df_44_12_win/hack/scripts
Is this what you meant?Yup! To explain further: these two folders are where DFHack will look for scripts. To work around some issues with special characters, it will remove the DF path from the beginning, but it has to match exactly (which it should, but if it doesn't, that could explain the issue you saw). However, it looks like this does match the DF path you posted earlier, so that rules out the specific problem I was thinking of:Code: [Select][DFHack]# lua printall(dfhack.internal.getScriptPaths())
1 = D:\Gustav\detsomfallitfrÕntrõden\÷vrigt\DF\df_44_12_win/raw/scripts
2 = D:\Gustav\detsomfallitfrÕntrõden\÷vrigt\DF\df_44_12_win/hack/scripts
Code: [Select][DFHack]# lua print(dfhack.filesystem.getcwd())
D:\Gustav\detsomfallitfrÕntrõden\÷vrigt\DF\df_44_12_win
I think it uses CP437 like DF on Windows?
Changing the windows console encoding is the easy fix, but you won't be able to display both paths (or error strings or whatever windows gives you) and df strings correctly. The best solution is to use unicode everywhere, but it will require more work.When using the command scriver has issues with ("startdwarf 10")? I had no problems with DFHack itself with the funny path: DF started just fine when invoked directly, and the commands lethosor provided worked without issues. I assume it's scripts written in Ruby that get into trouble.
Running dfhack on linux in a directory containing "éçà" does not generate any error (ruby or other).
I think it uses CP437 like DF on Windows?On my system (french locale), it uses CP850 (it matches scriver case too). It is a DOS encoding, I guess it is there for compatibility with old console applications.
In the short term (before Toady expands the character set with the Premium arc), it sounds like it would be appropriate for the DF console to use the same code page as DF does (CP437)
Interesting - my understanding was that it was supposed to use CP437 on Windows by default (at least, I have never seen complaints that printing CP437-encoded names from DF with accented characters to the console displays the wrong characters on Windows, while I've seen several complaints about the same issue on Linux/macOS). Can you double-check whether print(dfhack.TranslateName(some_unit.name)) displays the right characters in the Windows console?
When using the command scriver has issues with ("startdwarf 10")? I had no problems with DFHack itself with the funny path: DF started just fine when invoked directly, and the commands lethosor provided worked without issues. I assume it's scripts written in Ruby that get into trouble.I did not test that, on Windows starting DF is enough to get the error messages. I'll try that later.
You may just have been lucky because non-ASCII characters used in DF names are compatible between CP437 and CP850. Or CP437 may be the default for the US locale. I wonder what happens with other locales using different alphabets (like cyrillic, arabic, ...).Oh, that's probably it, actually!
I thought there was a conversion with the DF2CONSOLE function, but apparently it is only for Linux.It's defined to "do the right thing" on all platforms, which is specifically a no-op on Windows. Seems that we might need to adjust that. Thanks for the insight.
When using the command scriver has issues with ("startdwarf 10")? I had no problems with DFHack itself with the funny path: DF started just fine when invoked directly, and the commands lethosor provided worked without issues. I assume it's scripts written in Ruby that get into trouble.
repeat -time 1 -timeUnits frames -command [ ":lua if df.global.ui.main.autosave_request and not already_saved then dfhack.persistent.save({key='testname',value='hi'}) print('autosave request') already_saved = true end" ] -name autodetect
to save persistent data before save goes through in one test, i.e. the testname is set after kill+restart. Not sure if it is useful or always happens, but an idea.Warning: Plugin RemoteFortressReader compiled for DFHack 0.44.12-r2-80-gc793b101, running DFHack 0.44.12-r2-0-g315852a2
Another thing - need to teleport adventurer to world coords. How can i do it? teleport.lua work only inside one global tile for me.what you want is an old script made by warmist that used waypoints for teleporting for a selected destination but from what I remember it hasn't been updated.
okey, i found a solution - questport works fine. But i can't make it teleport for choosen numbers coords on global map, not by q-menu. Can somebody help please?
if I were to be submitting some scripts to the repo sometime soon should I still keep the current value in, or pre-emptively change them?Changing the names of existing enum items is always painful for this reason. I've fixed it in df-structures, and it doesn't look like any of our code uses this name. For the purposes of something that you want included in the next DFHack release, I would use the new name - but this means that you'll have to change it to use the old name for 0.44.12-r2. I'll try my best to get a DFHack release out soon-ish, but that hasn't happened for several months now...
I need plugin that make sentient creatures (example trolls) milkable.You've posted this 3 times in this thread and created your own thread, and I'm thinking maybe nobody's interested.
I will stop only when someone make this.I need plugin that make sentient creatures (example trolls) milkable.You've posted this 3 times in this thread and created your own thread, and I'm thinking maybe nobody's interested.
If you don't stop pestering people with unreasonable demands to satisfy your whims for modded games with limited to no utility for anyone else someone will probably request that you're banned from the forum. The only thing you achieve by your repeated posting all over the place is to annoy people. Asking once (nicely, as is the English custom, as opposed to the brutal directness of your supposed native language) is fine. No response means no interest/no relevant knowledge.I will stop only when someone make this.I need plugin that make sentient creatures (example trolls) milkable.You've posted this 3 times in this thread and created your own thread, and I'm thinking maybe nobody's interested.
like with dfhack's item spawning you can produce unlimited amounts of liquid milk in any container you want and probably attach the milk origins to any person, though I think no one here probably too keen on wanting to use dfhack to force sapient folks into milking factories... when one could just craft milk via modding... that said kinda wonder how the whole process of milking works in adventure mode to some what improve on the advfort stuff.In advfort - haven't tested but general idea is this: create just enough df stuff that it's job handling logic takes over and then if player presses "pass time" it controls your char as if it's one of the fort mode dwarves (well more like your units ) and does all the complicated stuff. And that fails in many ways (e.g. plant gathering... have no idea why it does not work). Sometimes you need to setup a lot more stuff than other times (e.g. fill out various references)...
Yeah, that's much easier - Eventmanager (https://dfhack.readthedocs.io/en/stable/docs/Lua%20API.html#events-from-eventmanager) would let you fill in milk from creature into bucket when they complete a job to milk themselves, provided they're milkable. (That takes a bucket and produces that same bucket.)So I need only reaction raw code? Or also need script?
There might have been a way to ensure only suitable creatures can take the job without cancellations; not sure. But you could cancel milk self when the unit can't be milked due not being milkable or being milked too recently.
(Though you can't assign same creature as the milker and the milkee in vanilla job; they'll cancel the job due being in custody.)
You need both, but script would be over ten times simpler to do than before.Can you make new script and reaction?
- Dorfs that have had a strange mood are immune to insanity. I had one that was tantruming and had maximum stress, but once I managed to get the finicky bugger to pick up a trinket of a desired material (wouldn't take anything else hauled), a slow trek towards recovery started. Unfortunately, the fortress was cut short by raid corruption when the stress level had improved from -100000 to -70000 (which is still extremely bad). I believe I also did other things apart from the crucial trinket acquisition to improve the condition of this starting 7 dorf, though.
Is there a data structure somewhere in the unit that contains a list of desired trinkets and their material(s)? A 'I want to acquire a ring, preferably dolomite' entry, if you will?That would be the unit's preferences (that list you can see where it explains materials, creatures, art forms, etc. that they like). The preferences are stored in the unit's unit.status.current_soul.preferences.
Hm, while we're speaking of structures anyway, any idea what's the minimum change to convert 43.03 units.xml unit from struct-type to class-type?
It just spits errors at me (such as unit has no .name) if I just change the type as in the commit that changed it and add the three virtual methods, and don't want to paste wholesale since that'll break plugins relying on old relationship compounds.However, this might be something else. What specific commit are you trying to compile? What changes are you looking at? Links and error messages would help (pastebin is fine if they're long).
so during the first time advfort broke and noticing it doesn't really break on npcs I modified it to be a 'companion' fort script that just sends the jobs over to them. which while works does mean I have to pass a lot of time and probably could just re-write it to just send them job commands or something.like with dfhack's item spawning you can produce unlimited amounts of liquid milk in any container you want and probably attach the milk origins to any person, though I think no one here probably too keen on wanting to use dfhack to force sapient folks into milking factories... when one could just craft milk via modding... that said kinda wonder how the whole process of milking works in adventure mode to some what improve on the advfort stuff.In advfort - haven't tested but general idea is this: create just enough df stuff that it's job handling logic takes over and then if player presses "pass time" it controls your char as if it's one of the fort mode dwarves (well more like your units ) and does all the complicated stuff. And that fails in many ways (e.g. plant gathering... have no idea why it does not work). Sometimes you need to setup a lot more stuff than other times (e.g. fill out various references)...
Anyway my newest attempt at "job creation utils" is here but it looks very bad already (https://github.com/warmist/df_multiplay/blob/master/jobs.lua) :< need to revisit all this job thing (for my undead doing the dirty jobs for you mod)
Yes. And wait, I think I got confused with a different error, it does compile but segfaults when looking at unit and using gui/gm-editor (confirmed this by compile, check, revert, check again).Oh, if it's crashing when you're reading "name", that's a different problem. Since name is the first field, struct-type vs. class-type is probably what's causing it. You have to use struct-type in 0.43.03 or class-type in 0.43.05; flipping those will cause crashes.
Of course, given can't include the vmethods anyway the point is moot.
This could work (based on me being able to make unfertilized eggs hatch in a tick and Rumrusher's similar experiments with vermin eggs): Have a job that takes an egg, and outputs that same egg.
Then add onJobCompleted hook that gets the output item (egg), then sets egg's incubation_counter to 100800 (or 100799, both work) and .egg_flags fertile to true.
I guess you might and could add a initialization hook to make sure the job takes long time to complete (though thirst/hunger/sleep prevents you normally from using whole three months it'd take normally).
(PS: Also noticed it's possible to add some tags to offspring on egg item, such as marking them for butchering the moment they're born. Neat!)
This could work (based on me being able to make unfertilized eggs hatch in a tick and Rumrusher's similar experiments with vermin eggs): Have a job that takes an egg, and outputs that same egg.
Then add onJobCompleted hook that gets the output item (egg), then sets egg's incubation_counter to 100800 (or 100799, both work) and .egg_flags fertile to true.
I guess you might and could add a initialization hook to make sure the job takes long time to complete (though thirst/hunger/sleep prevents you normally from using whole three months it'd take normally).
(PS: Also noticed it's possible to add some tags to offspring on egg item, such as marking them for butchering the moment they're born. Neat!)
You can go one step forward and the job would only add the egg to the building (and maybe disable jobs on that building?) and then speed up the egg incubation with periodic kicks to the timer. Though not sure what will happen if egg inside the building hatches :D
Is it possible to add a new folder search location to DFHack so that it will looks for scripts in raw/blah/ in addition to raw/scripts/?Yes: search for "addScriptPath" in https://dfhack.readthedocs.io/en/stable/docs/Lua%20API.html
Is it possible to add a new folder search location to DFHack so that it will looks for scripts in raw/blah/ in addition to raw/scripts/?Yes: search for "addScriptPath" in https://dfhack.readthedocs.io/en/stable/docs/Lua%20API.html
dfhack-config/script-paths.txt is a more permanent place to set that up, if you prefer.
Using DFHack to change the "dead_dwarf" flag on the skeleton in a52's save causes the corpse to be laid to rest in the coffin...
ITEM = {}
ITEM.__index = ITEM
setmetatable(ITEM, {
__call = function (cls, ...)
local self = setmetatable({},cls)
self:_init(...)
return self
end,
})
function ITEM:_init(item)
if tonumber(item) then item = df.item.find(tonumber(item)) end
self.id = item.id
end
function ITEM:getRandomAttack()
local item = df.item.find(self.id)
local rand = dfhack.random.new()
local weights = {}
weights[0] = 0
local n = 0
for _,attacks in pairs(item.subtype.attacks) do
if attacks.edged then x = 100 else x = 1 end
n = n + 1
weights[n] = weights[n-1] + x
end
local pick = rand:random(weights[n])
for i = 1,n do
if pick >= weights[i-1] and pick < weights[i] then attack = i-1 break end
end
if not attack then attack = n end
return item.subtype.attacks[attack]
end
Is it possible to add faux virtual methods to objects with lua. For instance if you have an item (say from item = df.item.find(#)) you can do item:getSubtype(), but I'd like to be able to be able to do something like item:getRandomAttack().
I have it working so far where I create a new metatable that has all the functions I want, but I can't seem to get inheritance to work for df structures. Below is a small excerpt of the ITEM metatable I am using at the moment (it's called by just doing item = ITEM(#))
<...>
ITEM = defclass(ITEM)
function ITEM:__index(key)
if rawget(self,key) then return rawget(self,key) end
if rawget(ITEM,key) then return rawget(ITEM,key) end
local item = df.item.find(self.id)
return item[key]
end
function ITEM:init(item)
--??
if tonumber(item) then item = df.item.find(tonumber(item)) end
self.id = item.id
end
function ITEM:getRandomAttack()
local item = df.item.find(self.id)
local rand = dfhack.random.new()
local weights = {}
weights[0] = 0
local n = 0
for _,attacks in pairs(item.subtype.attacks) do
if attacks.edged then x = 100 else x = 1 end
n = n + 1
weights[n] = weights[n-1] + x
end
local pick = rand:random(weights[n])
for i = 1,n do
if pick >= weights[i-1] and pick < weights[i] then attack = i-1 break end
end
if not attack then attack = n end
return item.subtype.attacks[attack]
end
local it=ITEM(0)
printall(it:getRandomAttack())
local D=df.global.world.units.active[0]
local Xi=df.global.cursor.x
local Yi=df.global.cursor.y
local Zi=df.global.cursor.z
for k,v in pairs(D.actions) do
if v.type == 1 then
v.data.attack.attack_velocity=910000009
v.data.attack.attack_accuracy=100
v.data.attack.timer1=1
v.data.attack.timer2=0
end
if v.type == 11 then
v.data.dodge.timer= 0
v.data.dodge.x2=Xi
v.data.dodge.y2=Yi
v.data.dodge.z2=Zi
end
end
Is it possible to add faux virtual methods to objects with lua. For instance if you have an item (say from item = df.item.find(#)) you can do item:getSubtype(), but I'd like to be able to be able to do something like item:getRandomAttack().
I have it working so far where I create a new metatable that has all the functions I want, but I can't seem to get inheritance to work for df structures. Below is a small excerpt of the ITEM metatable I am using at the moment (it's called by just doing item = ITEM(#))
<...>
This seems to work, but i'm pretty sure there is still a better way:
well worked on a script on what if you could dodge farther distances which in turn became what if you can just dodge and attack in a way that kills the opponent setting you up to take on the next person.
<snip>
the result turn out to be dodge teleporting next to someone doesn't give you your turn back and you're just placing yourself in position for an attack, also teleporting behind someone would lead to the person doing a 180 and hit you if they have an attack primed.
This seems to work, but i'm pretty sure there is still a better way:One suggestion to improve efficiency: set "self._item = df.item.find(id)" in the constructor (init()) and then look up fields on that in __index() (i.e. replace the last two lines with "return self._item[key]").Code: [Select]ITEM = defclass(ITEM)
function ITEM:__index(key)
if rawget(self,key) then return rawget(self,key) end
if rawget(ITEM,key) then return rawget(ITEM,key) end
local item = df.item.find(self.id)
return item[key]
end
This seems to work, but i'm pretty sure there is still a better way:One suggestion to improve efficiency: set "self._item = df.item.find(id)" in the constructor (init()) and then look up fields on that in __index() (i.e. replace the last two lines with "return self._item[key]").Code: [Select]ITEM = defclass(ITEM)
function ITEM:__index(key)
if rawget(self,key) then return rawget(self,key) end
if rawget(ITEM,key) then return rawget(ITEM,key) end
local item = df.item.find(self.id)
return item[key]
end
Of course, this will crash if the underlying item is ever deleted from DF, so be aware of that (I'm not sure what the use case is; if you're accessing item fields infrequently, then calling find() every time should be fine).
I'm trying to install dfhack 0.34.11-r3 with dwarf fortress 34.11 on an old old old IBM thinkpad that will only support 32-bit ubuntu mate and i cannot get it to run. The main goal for me is to try and get dwarf therapist or something similar working. I've gone through ever thing i can find online to fix it, please, send help!!
I'm trying to run DF 0.44.12 with DFhack on my phone using Exagear. It crashes whenever I try to load a world (Arena loads fine, though). It doesn't crash when I copy the installation to my computer, which probably means it's a problem with Exagear or something.I would try removing or renaming "onLoad.init-example" and see if that helps.
--snip--
Invoking: warn-starving
I'm trying to install dfhack 0.34.11-r3 with dwarf fortress 34.11 on an old old old IBM thinkpad that will only support 32-bit ubuntu mate and i cannot get it to run. The main goal for me is to try and get dwarf therapist or something similar working. I've gone through ever thing i can find online to fix it, please, send help!!If the suggestions above don't help, we'll need more details to help you. For example: what instructions have you followed? What files did you download (links would help) and where did you put them?
I'm trying to track down and kill the world's last werebeast. It's not in its lair though, and I can't find it in the surrounding wilderness. Is there a command I can use to locate it?Not as far as I know. You could probably find it through lua (or gui/gm-editor) but I'm not familiar enough with adventurer mode to know exactly where to look.
I'm looking to generate a list of positions based on certain criteria when the fortress map is loaded (e.g. all surface positions, all cavern positions, etc...). Is there a more efficient way to go about it than just brute forcing through each tile? I can get all the surface tiles fairly quickly by starting at the top in a corner, going down in z until I go underground, and then starting from that z in the next tile over and iterating through all the x,y pairs that way. But getting all cavern positions would require going through potentially millions of tiles, which I'd rather avoid if possible.Not that I know of. That's why things like "reveal" are implemented in C++.
I'm looking to generate a list of positions based on certain criteria when the fortress map is loaded (e.g. all surface positions, all cavern positions, etc...). Is there a more efficient way to go about it than just brute forcing through each tile? I can get all the surface tiles fairly quickly by starting at the top in a corner, going down in z until I go underground, and then starting from that z in the next tile over and iterating through all the x,y pairs that way. But getting all cavern positions would require going through potentially millions of tiles, which I'd rather avoid if possible.
Layer typically indicates whether it's Surface, Cavern 1, Cavern 2, Cavern 3, Magma Sea or none of those (if it's a specific feature, such as e.g. a volcano/magma pipe, or a candy spike, etc.). However, that's not useful for Roses' purposes, as it doesn't specify the exact extents of the corresponding Layer feature (this is probably generated from seeds when the in-game tiles are generated).I'm looking to generate a list of positions based on certain criteria when the fortress map is loaded (e.g. all surface positions, all cavern positions, etc...). Is there a more efficient way to go about it than just brute forcing through each tile? I can get all the surface tiles fairly quickly by starting at the top in a corner, going down in z until I go underground, and then starting from that z in the next tile over and iterating through all the x,y pairs that way. But getting all cavern positions would require going through potentially millions of tiles, which I'd rather avoid if possible.
There's some data structures that might be relevant:
df.global.features.map_features
- .layer, although I'm not sure what the number means.
:
I'm running DF on an OSX system, and it was installed via the Lazy Mac Pack. I can't seem to access the GUI version of Workflow, nor find documentation for the hot key in the DFHack docs. Was this feature removed?
I'm running DF on an OSX system, and it was installed via the Lazy Mac Pack. I can't seem to access the GUI version of Workflow, nor find documentation for the hot key in the DFHack docs. Was this feature removed?Note that this shortcut was removed for a few DFHack versions. Make sure you're using a recent DF version - some Mac packs are still on 0.44.05, but the most recent DF version is 0.44.12. (I think the workflow shortcut should be enabled in all versions of DFHack for 0.44, though - if it's not working for you, see Bumber's advice.)
Totally different question: Are there any more robust collections of stockpile settings for DFHack/stocksettings? One that's been updated to include all the new items from the last year or two?More robust than what? I couldn't find an existing collection on the wiki, and I'm not aware of one that comes with DFHack.
I'm running DF on an OSX system, and it was installed via the Lazy Mac Pack. I can't seem to access the GUI version of Workflow, nor find documentation for the hot key in the DFHack docs. Was this feature removed?Note that this shortcut was removed for a few DFHack versions. Make sure you're using a recent DF version - some Mac packs are still on 0.44.05, but the most recent DF version is 0.44.12. (I think the workflow shortcut should be enabled in all versions of DFHack for 0.44, though - if it's not working for you, see Bumber's advice.)
Totally different question: Are there any more robust collections of stockpile settings for DFHack/stocksettings? One that's been updated to include all the new items from the last year or two?More robust than what? I couldn't find an existing collection on the wiki, and I'm not aware of one that comes with DFHack.
The LNP and LMP both come with a Gems stockpile setting group (Rough Ornamental. Rough Rare, Rough Semiprecious), a Magma Safe group (different Stone and Ore settings), and a MechGuides group.
So since I'm going to go through the trouble of making [Workshop]_Input stockpiles for everything, how do I submit those for inclusion into df-hack?
I feel like a true DFHack implementation would involve adding those options to the GUI, which is the opposite quantity of work than I want to generate. :)Well, they'd show up in the existing list of profiles in the GUI, wouldn't they? I think that's good enough - yeah, making more complicated options would take some work.
The stockpile files I've created since my last post are industry based rather than workshop based, so you can, for example, keep one Quern & stockpile adjacent to your Dyers Workshop and another one next to the Kitchen.Ah, so those would be somewhat harder to generate programmatically than I was thinking. As long as they're compatible with vanilla, I'm fine with bundling them with DFHack (although it's possible that some mods would break compatibility with them). LNP authors would probably also be willing to include them if you reached out (most of those threads are in the DF General Discussion forum).
I've successfully opened the stockpile files in a text editor, so unless my understanding of what Smultron is capable of doing is deeply outdated, they're text files.I double-checked a .dfstock file I have laying around from 0.43.05 (it's using protobuf's default format, I think, and I don't think the plugin has changed its format since then). It's a "binary" file in the sense that it has a lot of non-printable and non-ASCII characters, but it also uses ASCII for a lot of stuff. So you'd probably be able to read these files in a text editor and get an idea of what they're doing, but editing them would probably break them. I don't know how Git would handle this exactly, but the (basic) file I have isn't very big, so I'm not too concerned about that anymore.
I tested it on Mac too.
path:
Applications\Älgstubben\Bäverdammen\Järvträsket\Lazy Mac Pack v0.44.12 dfhack-r2/Dwarf Fortress 0.44.12/
I can confirm that LNP won´t launch when there are common Swedish words in the path.
I tested it on Mac too.
path:
Applications\Älgstubben\Bäverdammen\Järvträsket\Lazy Mac Pack v0.44.12 dfhack-r2/Dwarf Fortress 0.44.12/
I can confirm that LNP won´t launch when there are common Swedish words in the path.
This may point to lack of unicode path support. Try it with other national characters (cyrillic, Polish letters, japanese moonrunes...) - if they don't work either we'll know the answer. I've seen similar stuff happen wit other software as well, particularly an old version of ToonBoom (not sure if it still happens in the new versions) - these programs simply don't get unicode in paths and they try to interpret two byte unicode sequences as two ascii characters, then freak out when they encounter characters that shouldn't be there (such as control codes belonging in the 0x00-0x1F range).
//edit: @lethosor the earlier discussion suggested that it happens outside of PyLNP as well and if python's unicode handling is so terrible, isn't it a sign it would be a good idea to rewrite PyLNP in another, better language or at least upgrade the code to Python 3?That bug is specific to the Ruby plugin. Also, paths are being printed incorrectly in the console on Windows, but (at least from my understanding) other features like plugins and Lua scripts worked.
How do I run a DFHack script with a syndrome?That seems like a very incomplete question. I, at least, fails to understand what the question actually is:
I'm trying to track down and kill the world's last werebeast. It's not in its lair though, and I can't find it in the surrounding wilderness. Is there a command I can use to locate it?Well there is now :)
Whoops! I should clarify. I am asking how to run a script in general (anything in the scripts folder) upon the triggering of a syndrome.How do I run a DFHack script with a syndrome?That seems like a very incomplete question. I, at least, fails to understand what the question actually is:
- Is the question one of how to run scripts in general (but then, where does the syndrome fit in)?
- Is it a question of how to use a particular, but unspecified, script?
- Is it about how to write scripts that involve syndromes in unspecified ways?
To have a decent chance to get a useful answer you probably have provide enough info.
Whoops! I should clarify. I am asking how to run a script in general (anything in the scripts folder) upon the triggering of a syndrome.
modtools/syndrome-trigger -syndrome SYN_NAME -command [ script ]
You could place this in an onLoad.init file in your raw folder to have it run automatically whenever the game is loaded.
Hello all.Well if nobody's commenting on this I'd at least like to mention that I'd like a copy of that script if you have it available somewhere :P
I've made a script, which will locate historical figures, and I want to hear whatever there is any interest in having it added to DFHack.
[snip]
Sulter provided it here: http://www.bay12forums.com/smf/index.php?topic=175307.0 (http://www.bay12forums.com/smf/index.php?topic=175307.0)Hello all.Well if nobody's commenting on this I'd at least like to mention that I'd like a copy of that script if you have it available somewhere :P
I've made a script, which will locate historical figures, and I want to hear whatever there is any interest in having it added to DFHack.
[snip]
Could you add an option of "just save" to ESC menu that would save the game, but then return to the fort/adventure (i.e. saving without quitting)?The default dfhack.init lets you quicksave with Alt+Ctrl+S.
And you can type "quicksave" into the DFHack console as well.Could you add an option of "just save" to ESC menu that would save the game, but then return to the fort/adventure (i.e. saving without quitting)?The default dfhack.init lets you quicksave with Alt+Ctrl+S.
And you can type "quicksave" into the DFHack console as well.Could you add an option of "just save" to ESC menu that would save the game, but then return to the fort/adventure (i.e. saving without quitting)?The default dfhack.init lets you quicksave with Alt+Ctrl+S.
Something completely different: I've failed to find where DF stores info about what growths each plant sports (leaves, fruit, etc.). The plant_raw contains info of what it CAN have (and when), but shrubs without usable structure parts can have their growths picked while leaving the structure part behind (e.g. bitter melon vines left behind when gathering the leaves and the melons), and fruit pickers climb ladders to pick fruit from different parts of trees, so the info obviously resides somewhere. The logical place (at least according to my logic) would be a reference in the plant structure (the one in the vector that's used to hold info of every plant on the map), but it has no specific info on shrubs, and only generic info for trees (branches, twigs, roots). Can someone point me in the right direction, or hasn't that info been mapped yet?
It has been far too long, but we finally have a new release up for 0.44.12!
https://github.com/DFHack/dfhack/releases/tag/0.44.12-r3
Thanks to everyone who helped out with this, including the 12 (!) new contributors.
Looking at Toady's recent posts about a new DF release coming soon, this will probably be the last release for 0.44.12, and the last stable release for a while.
1. I am digging the assign-X scripts, I have something very similar in my set of scripts, but I would obviously prefer to use the ones in the default package. My only question is speed. Is there much (if any) overhead in calling one of the assign-X scripts using the dfhack.script_environment() system?Nope, it's actually the same system that running Lua scripts uses under the hood. I'd recommend reqscript() instead because it does some extra safety checks (and it's shorter) but either should work. As long as you only import the script once (i.e. not every time you call a function), it shouldn't be noticeable.
2. Could we get some information on the new Persistence module and how it works? I switched from using the persistent tables to a global table that gets saved to a JSON file whenever the game saves and loads from the JSON file whenever the game loads (I was doing a lot with them and it was faster to go through global tables than the old persistent tables). It seems like the new system is using JSON files, but I am just wondering how it all works.From Lua it shouldn't be any different. It'll be stored differently under the hood, though. C++ plugins have access to a bit of extra stuff, but I expect persist-table and whatever else is available to Lua to continue to work normally. The functions in the World module are now just wrappers around the Persistence module.
3. I'll probably have a bunch of questions about the persistence stuff, are there any scripts currently included in the package that use the new system (or is it the same syntax as the old one, with ._children and such)?
I've got trouble trying to use the set-orientation command. If anybody could help, I'd be real grateful.Make sure to begin the arguments with a -, so in this example set-orientation -view. I don't think I mentioned the necessity for doing that in any of the scripts I added, and this is one where I neglected to include an example usage so that's understandable. It's kinda the standard formatting for dfhack script arguments, but I don't know how many scripts actually use it over their own methods :b
Here's the error log
Invoking: set-orientation view
C:\Users\Me\Downloads\df_44_12_win\hack\lua\utils.lua:610: error parsing arg 1: view
stack traceback:
[C]: in function 'error'
C:\Users\Me\Downloads\df_44_12_win\hack\lua\utils.lua:610: in function 'utils.processArgs'
...\Downloads\df_44_12_win/hack/scripts/set-orientation.lua:189: in global 'main'
...\Downloads\df_44_12_win/hack/scripts/set-orientation.lua:265: in local 'script_code'
C:\Users\Me\Downloads\df_44_12_win\hack\lua\dfhack.lua:680: in function 'dfhack.run_script_with_env'
(...tail calls...)
I've got trouble trying to use the set-orientation command. If anybody could help, I'd be real grateful.Make sure to begin the arguments with a -, so in this example set-orientation -view. I don't think I mentioned the necessity for doing that in any of the scripts I added, and this is one where I neglected to include an example usage so that's understandable. It's kinda the standard formatting for dfhack script arguments, but I don't know how many scripts actually use it over their own methods :b
Here's the error log
Invoking: set-orientation view
C:\Users\Me\Downloads\df_44_12_win\hack\lua\utils.lua:610: error parsing arg 1: view
stack traceback:
[C]: in function 'error'
C:\Users\Me\Downloads\df_44_12_win\hack\lua\utils.lua:610: in function 'utils.processArgs'
...\Downloads\df_44_12_win/hack/scripts/set-orientation.lua:189: in global 'main'
...\Downloads\df_44_12_win/hack/scripts/set-orientation.lua:265: in local 'script_code'
C:\Users\Me\Downloads\df_44_12_win\hack\lua\dfhack.lua:680: in function 'dfhack.run_script_with_env'
(...tail calls...)
Can confirm, I'd be happy to ship these :DThe stockpile files I've created since my last post are industry based rather than workshop based, ...LNP authors would probably also be willing to include them if you reached out (most of those threads are in the DF General Discussion forum).
if not dfhack_flags.module then
main(...)
end
Just wanted to make sure I understand this, if a script gets called from the command line or with run_script/run_command it will call main, if it gets access with reqscript() it will not call main, correct?
Couple questions;Looks like it:
1. Does the defclass() support overloading operators? I know with "normal" metatables you can put functions for things like __add, __eq, etc... Just making sure that I can still do that.
[lua]# x = defclass()
[lua]# function x:__add() print('adding') end
[lua]# print(x() + 2)
adding
nil
2. I'm trying to decide how to organize my scripts and functions collection and am wondering what some of my options might be. Currently I am adding expanding on some of the current df-structures objects by using classes. As an example, I have a UNIT class which adds functions like UNIT:makeProjectile(vx,vy,vz) which turns the unit into a projectile. Now for the question, my current thoughts for organization are 1) Putting the defclass and all the functions associated with that class in their own file, or 2) put the defclass for each of the different classes in a single file with descriptions of each class, and then put the functions for the class in a seperate file, 3) group classes and functions into categories and create files for each category. Is there a "best" way to handle this in terms of performance (I'm guessing #3)? If they are all roughly equal I was thinking of using #2 because it would be the easiest to read and create help files for. Is there another way that I'm not thinking of?I wouldn't split up the defclass and function definitions - defclass is really just one line. Personally, I would go with #1, unless you have a lot of classes, and then #3 might be a bit faster. One disadvantage of #2 is that if you forget to include the file that defines all the functions, you'll just end up with a class that does nothing.
3. I noticed in some of the scripts in the repository there is the lineYup! It's the same as the "moduleMode" check you might see in some scripts, but that's deprecated.Code: [Select]if not dfhack_flags.module then
Just wanted to make sure I understand this, if a script gets called from the command line or with run_script/run_command it will call main, if it gets access with reqscript() it will not call main, correct?
main(...)
end
The issue is that MSVC and GCC C++ ABIs are incompatible (there is no standard C++ ABI). It is not about posix/win32 translation. You are stuck with whatever compiler Toady uses.
The issue is that MSVC and GCC C++ ABIs are incompatible (there is no standard C++ ABI). It is not about posix/win32 translation. You are stuck with whatever compiler Toady uses.My understanding was that it was mainly a runtime library issue, specifically the memory layouts of std::string/std::vector and the fact that DF's heap is managed by the VC++ 2015 runtime library (which MingW does not use).
-- Backwards compatibility detection
--
local river_type_updated = false
if true then -- To get the temporary variable's context expire
local river = df.world_river:new()
for i, k in pairs (river) do
if i == "flow" then
river_type_updated = true
break
end
end
river:delete ()
end
The method I've used to detect if a field is present (to determine if the DF version is the current one or an older one) is to iterate over the fields with "pairs". If it's present I'll eventually get a match.Code: [Select]-- Backwards compatibility detection
--
local river_type_updated = false
if true then -- To get the temporary variable's context expire
local river = df.world_river:new()
for i, k in pairs (river) do
if i == "flow" then
river_type_updated = true
break
end
end
river:delete ()
end
function getFlag(dfvalue, key)
-- Utility function for safely requesting info from df data
if not dfvalue or not key then return nil end --pairs crash prevention
flagtypetable = flagtypetable or {} --memoization so it doesn't iterate through the dfvalue if we've already checked that type of value for given flag
if flagtypetable[dfvalue._type] and flagtypetable[dfvalue._type][key] then return dfvalue[key] end
if flagtypetable[dfvalue._type] and flagtypetable[dfvalue._type][key]==false then return nil end
if not flagtypetable[dfvalue._type] then flagtypetable[dfvalue._type] = {} end
for akey, avalue in pairs(dfvalue) do
if akey == key then
flagtypetable[dfvalue._type][key] = true
return dfvalue[akey]
end
end
flagtypetable[dfvalue._type][key] = false
end
what key binding do people mostly use for toggling gui/gm-editor? it doesn't seem to have a default?Typing "gui/gm-editor" into the console window. Most of the time I need a parameter to open the structure I want to look at anyway.
blueprint 47 47 30 nfort
digfort nfort-dig.csv
Faults toblueprint 48 48 30 nfort
digfort nfort-dig.csv
#dig
, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,#
from the csv into the variable max_y when clearly it should not. (see point 3 final part)if x < 0 or y < 0 or x+max_x >= map.x_count or y+max_y >= map.y_count
should be insteadif x < 0 or y < 0 or x+max_x > map.x_count or y+max_y > map.y_count
because we could only have an problem if the dimensions of the blueprint are bigger than those of the map, never if they are the same. Even when the blueprint dimensions are bigger than those of the map; maybe a better implementation than raising an exception before any work is done would be to stamp the part of the blueprint that effectively can be fit into the map and print a warning about the number of rows and/or lines not stamped to inform the user. More work done before trapping, more debug aware and in the worst case, the user can always delete the designations if they were not what he intended. max_x = x if x > max_x and not t.empty?
test correctly for semantic empty cells but max_y = y if y > max_y
does not take into account the semantic empty lines that we have identified that exist (example: #dig, ...), we must remember the fact that any border of the map (in X or Y) is by definition undesignable hence semantically empty in any case with respect to designations, that is, in any map the TOP and BOTTOM lines of the map are undesignable... Hope this helps to identify the issue.# designate an area based on a '.csv' plan
=begin
digfort
=======
A script to designate an area for digging according to a plan in csv format.
This script, inspired from quickfort, can designate an area for digging.
Your plan should be stored in a .csv file like this::
# this is a comment
d;d;u;d;d;skip this tile;d
d;d;d;i
Available tile shapes are named after the 'dig' menu shortcuts:
``d`` for dig, ``u`` for upstairs, ``j`` downstairs, ``i`` updown,
``h`` channel, ``r`` upward ramp, ``x`` remove designation.
Unrecognized characters are ignored (eg the 'skip this tile' in the sample).
Empty lines and data after a ``#`` are ignored as comments.
To skip a row in your design, use a single ``;``.
One comment in the file may contain the phrase ``start(3,5)``. It is interpreted
as an offset for the pattern: instead of starting at the cursor, it will start
3 tiles left and 5 tiles up from the cursor.
additionally a comment can have a < for a rise in z level and a > for drop in z.
The script takes the plan filename, starting from the root df folder (where
``Dwarf Fortress.exe`` is found).
=end
fname = $script_args[0].to_s
if not $script_args[0] then
puts " Usage: digfort <plan filename>"
throw :script_finished
end
if not fname[-4..-1] == ".csv" then
puts " The plan file must be in .csv format."
throw :script_finished
end
if not File.file?(fname) then
puts " The specified file does not exist."
throw :script_finished
end
planfile = File.read(fname)
if df.cursor.x == -30000
puts "place the game cursor to the top-left corner of the design and retry"
throw :script_finished
end
offset = [0, 0]
tiles = []
max_x = 0
max_y = 0
y = 0
planfile.each_line { |l|
if l =~ /#.*start\s*\(\s*(-?\d+)\s*[,;]\s*(-?\d+)/
raise "Error: multiple start() comments" if offset != [0, 0]
offset = [$1.to_i, $2.to_i]
end
if l.chomp == '#<'
l = '<'
y = 0
end
if l.chomp == '#>'
l = '>'
y = 0
end
l = l.chomp.sub(/#.*/, '')
next if l == ''
x = 0
tiles << l.split(/[;,]/).map { |t|
t = t.strip
x = x + 1
max_x = x if x > max_x and not t.empty?
(t[0] == '"') ? t[1..-2] : t
}
y = y + 1
max_y = y if y > max_y
}
x = df.cursor.x - offset[0]
y = df.cursor.y - offset[1]
z = df.cursor.z
starty = y - 1
map = df.world.map
if x < 0 or y < 0 or x+max_x >= map.x_count or y+max_y >= map.y_count
max_x = max_x + x + 1
max_y = max_y + y + 1
raise "Position would designate outside map limits. Selected limits are from (#{x+1}, #{y+1}) to (#{max_x},#{max_y})"
end
tiles.each { |line|
next if line.empty? or line == ['']
line.each { |tile|
if tile.empty?
x += 1
next
end
t = df.map_tile_at(x, y, z)
s = t.shape_basic
case tile
when 'd'; t.dig(:Default) if s == :Wall
when 'u'; t.dig(:UpStair) if s == :Wall
when 'j'; t.dig(:DownStair) if s == :Wall or s == :Floor
when 'i'; t.dig(:UpDownStair) if s == :Wall
when 'h'; t.dig(:Channel) if s == :Wall or s == :Floor
when 'r'; t.dig(:Ramp) if s == :Wall
when 'x'; t.dig(:No)
when '<'; y=starty; z += 1
when '>'; y=starty; z -= 1
end
x += 1
}
x = df.cursor.x - offset[0]
y += 1
}
puts ' done'
not sure if this general question should go here or somewhere else.I figured I would try here because its based off information gathered with a script. I couldn't find an understandable answer. My question is, when I use "gui/unit-info-viewer" how do i interpret the size value? examples:
Puppy 'she appears to be about 1199:725 cubic decimeters in size
giant crab 'she appears to be about 545:988 cubic decimeters in size
and how does these numbers coincide with the size page on the wiki?
Possible bug in dfhack r3 in digfort exporting a multilevel nanofortCurious, have you tried the version from 0.44.12-r2? digfort did change a bit between the two versions (to fix a similar issue) and I'm wondering if the fix broke your case.
2-) The lineI'm pretty sure the code as-is is correct - most things in DF, including the map, are 0-indexed, so while x_count is the number of things in the X dimension, the valid things range only from 0 to X-1.Code: [Select]if x < 0 or y < 0 or x+max_x >= map.x_count or y+max_y >= map.y_count
should be insteadCode: [Select]if x < 0 or y < 0 or x+max_x > map.x_count or y+max_y > map.y_count
because we could only have an problem if the dimensions of the blueprint are bigger than those of the map, never if they are the same.
3-) The lineThis does sound like it could be an issue to me. Does your fixed script work properly for you? (I wasn't sure if you meant your version didn't work at all, or if you weren't sure that it always worked.)Code: [Select]max_x = x if x > max_x and not t.empty?
test correctly for semantic empty cells butCode: [Select]max_y = y if y > max_y
does not take into account the semantic empty lines that we have identified that exist (example: #dig, ...), we must remember the fact that any border of the map (in X or Y) is by definition undesignable hence semantically empty in any case with respect to designations, that is, in any map the TOP and BOTTOM lines of the map are undesignable... Hope this helps to identify the issue.
paint hidden 1
I'm pretty sure revflood worked on constructions in past versions.revflood hasn't worked for me in ages. It's been broken for several versions now at least. I can't imagine what use it would be, if it didn't work on constructed walls, that's like it's only purpose. heh
It worked for me just now in 0.47.02, and I don't recall seeing it broken before.I'm pretty sure revflood worked on constructions in past versions.revflood hasn't worked for me in ages. It's been broken for several versions now at least. I can't imagine what use it would be, if it didn't work on constructed walls, that's like it's only purpose. heh
It worked for me just now in 0.47.02, and I don't recall seeing it broken before.I'm pretty sure revflood worked on constructions in past versions.revflood hasn't worked for me in ages. It's been broken for several versions now at least. I can't imagine what use it would be, if it didn't work on constructed walls, that's like it's only purpose. heh
That said, its purpose is to replicate the vanilla DF reveal logic (e.g. when reclaiming a site), which ignores constructions last I checked, so it's working as intended. Its logic hasn't changed much since 2014, but maybe DF's has - if someone would like to test reclaiming a site with constructions, feel free to let us know if DF's behavior has changed.
Interesting, didn't know that. Can't comment about the intentions or logic of revflood.A faster option might be using unrevealdesignated (http://www.bay12forums.com/smf/index.php?topic=168607.0) for same thing.
A slightly labourious way to hide those tiles is to use the 'hidden' flag in tiletypes, from within tiletypesCode: [Select]paint hidden 1
Then run over each tile or brush area you want to hide. It can be combined with other commands if you want. paint hidden 0 should be the reverse (reveal) tile command from memory.
Hi,
just wanted so say "thanks for a great tool" and ask ... if there could be released some kind like a "minimal alpha" for 0.47.
Due to the new religious stuff my forts keep producing "fun" ... because of dwarfes not able to pray to one god or another god.
I have a population of 19 Dwarfs and a total of about 74(!) deities (not kidding). Dwarfes pray to 5 to 10 gods, and I can't keep up with providing temples to avoid tantrums.
Being able to at least run "remove stress" would be of great help :)
That's what the three messages right above yours are about, an early alpha for 0.47.02. I don't know if remove-stress is tested yet.
They're for 0.47.02 - they use the last Git tag, which makes sense for nightly builds once a DFHack version has been released for a specific DFHack version, but doesn't make as much sense before then :( (but any other DFHack build at this point would also report being a change after "0.44.12-r3", unless manually changed)
... aaaand i just went looking through the dfhack tags to see what your naming scheme is, and it looks like you've already done exactly this (https://github.com/DFHack/dfhack/releases/tag/0.47.02-alpha0), carry on :DYup :) We haven't triggered a build yet since making that tag, though, so the most recent one on BuildMaster still reports "0.44.12-r3" at the moment. Hopefully that will change soon.
Is there a way to download a win64 build already compiled? I only find the previous version.If you're looking for a test 0.47.02 build, see the 6 posts above yours (noting that it might still be labeled "0.44.12-r3" per this post, but if the build number starts with at least "2002", it should work for 0.47.02). Also note that these builds are very experimental and will probably crash.
The PRELOAD_LIB line probably works, though - you can verify that the library is in memory with "devel/lsmem".
Does anyone know what's happening with TwbT? The repo hasn't updated in almost a year, which makes me nervous :-\Related: https://github.com/mifki/df-twbt/pull/94#issuecomment-586006257
I just downloaded and installed 200213003 (0.47.02-alpha0-27-gf7f7bd7c?) on Windows 10 x86_64 and the `fix/loyaltycascade` command is returning "no loyalty cascade found".As far as I can understand it is because whatever is causing the mess isn't a loyalty cascade, or at least not the type the script tries to fix. To me it looks like other odd kinds of hiddenly hostile behavior that has been reported for 0.47.X, as well as the older mess triggered by a sneaking artifact seeker group member getting into a fight while other members are inside the fortress.
http://dffd.bay12games.com/file.php?id=14798
... automated builds available at https://buildmaster.lubar.me/applications/3/overview for anyone who would like to test those ...
... Does your fixed script work properly for you? (I wasn't sure if you meant your version didn't work at all, or if you weren't sure that it always worked.)
# designate an area based on a '.csv' plan
=begin
digfort
=======
A script to designate an area for digging according to a plan in csv format.
This script, inspired from quickfort, can designate an area for digging.
Your plan should be stored in a .csv file like this::
# this is a comment
d;d;u;d;d;skip this tile;d
d;d;d;i
Available tile shapes are named after the 'dig' menu shortcuts:
``d`` for dig, ``u`` for upstairs, ``j`` downstairs, ``i`` updown,
``h`` channel, ``r`` upward ramp, ``x`` remove designation.
Unrecognized characters are ignored (eg the 'skip this tile' in the sample).
Empty lines and data after a ``#`` are ignored as comments.
To skip a row in your design, use a single ``;``.
One comment in the file may contain the phrase ``start(3,5)``. It is interpreted
as an offset for the pattern: instead of starting at the cursor, it will start
3 tiles left and 5 tiles up from the cursor.
additionally a comment can have a < for a rise in z level and a > for drop in z.
The script takes the plan filename, starting from the root df folder (where
``Dwarf Fortress.exe`` is found).
=end
fname = $script_args[0].to_s
map = df.world.map
Xsentinel = map.x_count - 1
Ysentinel = map.y_count - 1
Xstamp = false
Ystamp = false
if not $script_args[0] then
puts " Usage: digfort <plan filename>"
throw :script_finished
end
if not fname[-4..-1] == ".csv" then
puts " The plan file must be in .csv format."
throw :script_finished
end
if not File.file?(fname) then
puts " The specified file does not exist."
throw :script_finished
end
planfile = File.read(fname)
if df.cursor.x == -30000
puts "place the game cursor to the top-left corner of the design and retry"
throw :script_finished
end
offset = [0, 0]
tiles = []
max_x = 0
max_y = 0
y = 0
planfile.each_line { |l|
if l =~ /#.*start\s*\(\s*(-?\d+)\s*[,;]\s*(-?\d+)/
raise "Error: multiple start() comments" if offset != [0, 0]
offset = [$1.to_i, $2.to_i]
end
if l.chomp == '#<'
l = '<'
y = 0
end
if l.chomp == '#>'
l = '>'
y = 0
end
l = l.chomp.sub(/#.*/, '')
next if l == ''
x = 0
tiles << l.split(/[;,]/).map { |t|
t = t.strip
if x < Xsentinel then x += 1 else x = Xsentinel ; Xstamp = true end
max_x = x if x > max_x and not t.empty?
(t[0] == '"') ? t[1..-2] : t
}
if y < Ysentinel then y += 1 else y = Ysentinel ; Xstamp = true end
}
x = df.cursor.x - offset[0]
y = df.cursor.y - offset[1]
z = df.cursor.z
starty = y - 1
if x < 0 or y < 0 or x+max_x >= map.x_count or y+max_y >= map.y_count
max_x = max_x + x + 1
max_y = max_y + y + 1
raise "Position would designate outside map limits. Selected limits are from (#{x+1}, #{y+1}) to (#{max_x},#{max_y})"
end
tiles.each { |line|
next if line.empty? or line == ['']
line.each { |tile|
if tile.empty?
if x < Xsentinel then x += 1 else x = Xsentinel ; Xstamp = true end
next
end
t = df.map_tile_at(x, y, z)
s = t.shape_basic
case tile
when 'd'; t.dig(:Default) if s == :Wall
when 'u'; t.dig(:UpStair) if s == :Wall
when 'j'; t.dig(:DownStair) if s == :Wall or s == :Floor
when 'i'; t.dig(:UpDownStair) if s == :Wall
when 'h'; t.dig(:Channel) if s == :Wall or s == :Floor
when 'r'; t.dig(:Ramp) if s == :Wall
when 'x'; t.dig(:No)
when '<'; y=starty; z += 1
when '>'; y=starty; z -= 1
end
if x < Xsentinel then x += 1 else x = Xsentinel ; Xstamp = true end
}
x = df.cursor.x - offset[0]
if y < Ysentinel then y += 1 else y = Ysentinel ; Ystamp = true end
}
if Xstamp or Ystamp then
puts 'Warning: ' + (Xstamp ? 'the columns': '') + (Xstamp and Ystamp ? ' and ': '') + (Ystamp ? 'the rows' : '') + ' of the blueprint that overflow the designable area of the Dwarf Fortress embark have been ignored'
puts ' if your blueprint X or Y dimension is close to that of the embark map, this warning is probably a false positive due to faulty logic in this script'
end
puts ' done'
... maybe a better implementation than raising an exception before any work is done would be to stamp the part of the blueprint that effectively can be fit into the map and print a warning about the number of rows and/or lines not stamped to inform the user. More work done before trapping, more debug aware and in the worst case, the user can always delete the designations if they were not what he intended...The modified script as of now just work eagerly before trapping and has some new nice features:
Well at the time you asked, it do not even existed. So the modified script that works (for me at least) with DF 47.02 plus the most recent dfhack automatic build.Thanks! I'll put this up in a GitHub issue so it doesn't get lost.Code: (digfort.rb) [Select]# designate an area based on a '.csv' plan
=begin
digfort
=======
A script to designate an area for digging according to a plan in csv format.
This script, inspired from quickfort, can designate an area for digging.
Your plan should be stored in a .csv file like this::
# this is a comment
d;d;u;d;d;skip this tile;d
d;d;d;i
Available tile shapes are named after the 'dig' menu shortcuts:
``d`` for dig, ``u`` for upstairs, ``j`` downstairs, ``i`` updown,
``h`` channel, ``r`` upward ramp, ``x`` remove designation.
Unrecognized characters are ignored (eg the 'skip this tile' in the sample).
Empty lines and data after a ``#`` are ignored as comments.
To skip a row in your design, use a single ``;``.
One comment in the file may contain the phrase ``start(3,5)``. It is interpreted
as an offset for the pattern: instead of starting at the cursor, it will start
3 tiles left and 5 tiles up from the cursor.
additionally a comment can have a < for a rise in z level and a > for drop in z.
The script takes the plan filename, starting from the root df folder (where
``Dwarf Fortress.exe`` is found).
=end
fname = $script_args[0].to_s
map = df.world.map
Xsentinel = map.x_count - 1
Ysentinel = map.y_count - 1
Xstamp = false
Ystamp = false
if not $script_args[0] then
puts " Usage: digfort <plan filename>"
throw :script_finished
end
if not fname[-4..-1] == ".csv" then
puts " The plan file must be in .csv format."
throw :script_finished
end
if not File.file?(fname) then
puts " The specified file does not exist."
throw :script_finished
end
planfile = File.read(fname)
if df.cursor.x == -30000
puts "place the game cursor to the top-left corner of the design and retry"
throw :script_finished
end
offset = [0, 0]
tiles = []
max_x = 0
max_y = 0
y = 0
planfile.each_line { |l|
if l =~ /#.*start\s*\(\s*(-?\d+)\s*[,;]\s*(-?\d+)/
raise "Error: multiple start() comments" if offset != [0, 0]
offset = [$1.to_i, $2.to_i]
end
if l.chomp == '#<'
l = '<'
y = 0
end
if l.chomp == '#>'
l = '>'
y = 0
end
l = l.chomp.sub(/#.*/, '')
next if l == ''
x = 0
tiles << l.split(/[;,]/).map { |t|
t = t.strip
if x < Xsentinel then x += 1 else x = Xsentinel ; Xstamp = true end
max_x = x if x > max_x and not t.empty?
(t[0] == '"') ? t[1..-2] : t
}
if y < Ysentinel then y += 1 else y = Ysentinel ; Xstamp = true end
}
x = df.cursor.x - offset[0]
y = df.cursor.y - offset[1]
z = df.cursor.z
starty = y - 1
if x < 0 or y < 0 or x+max_x >= map.x_count or y+max_y >= map.y_count
max_x = max_x + x + 1
max_y = max_y + y + 1
raise "Position would designate outside map limits. Selected limits are from (#{x+1}, #{y+1}) to (#{max_x},#{max_y})"
end
tiles.each { |line|
next if line.empty? or line == ['']
line.each { |tile|
if tile.empty?
if x < Xsentinel then x += 1 else x = Xsentinel ; Xstamp = true end
next
end
t = df.map_tile_at(x, y, z)
s = t.shape_basic
case tile
when 'd'; t.dig(:Default) if s == :Wall
when 'u'; t.dig(:UpStair) if s == :Wall
when 'j'; t.dig(:DownStair) if s == :Wall or s == :Floor
when 'i'; t.dig(:UpDownStair) if s == :Wall
when 'h'; t.dig(:Channel) if s == :Wall or s == :Floor
when 'r'; t.dig(:Ramp) if s == :Wall
when 'x'; t.dig(:No)
when '<'; y=starty; z += 1
when '>'; y=starty; z -= 1
end
if x < Xsentinel then x += 1 else x = Xsentinel ; Xstamp = true end
}
x = df.cursor.x - offset[0]
if y < Ysentinel then y += 1 else y = Ysentinel ; Ystamp = true end
}
if Xstamp or Ystamp then
puts 'Warning: ' + (Xstamp ? 'the columns': '') + (Xstamp and Ystamp ? ' and ': '') + (Ystamp ? 'the rows' : '') + ' of the blueprint that overflow the designable area of the Dwarf Fortress embark have been ignored'
puts ' if your blueprint X or Y dimension is close to that of the embark map, this warning is probably a false positive due to faulty logic in this script'
end
puts ' done'
I'm guessing "Rejected" just means that it wasn't chosen as a release, and "Active" means that it hasn't been rejected yet."Rejected" means there was an error or the build was cancelled. Some rejected builds still have some downloads available, e.g. https://buildmaster.lubar.me/applications/3/builds/build?buildId=6879 - that's because the Linux builds using GCC 4.8 are currently failing, but everything else is working. Ziusudra's link is useful, but it'll exclude newer builds in this case (this isn't typical, though).
Thanks! I'll put this up in a GitHub issue so it doesn't get lost.Thank you! (https://github.com/DFHack/dfhack/issues/1498)
The latest release (as of a few hours ago, I guess) crashes immediately on launching DF. The develop branch that was released in January did too. Not sure what changed, but something is interfering with the game launching properly. I'm using the 32-bit version of DF and Hack, for reference.We need version numbers of both DF and DFHack to diagnose anything. (I'm also not sure what you mean by "the develop branch" - we released one official build for 0.44.12 in January, and several unstable nightly builds for 0.47.)
What's a usable plugin to play "mutliplayer" (hotseat screen sharing) these days? Web Fortress hasn't been updated since 2015 and DFTerm3 since 2014. (DFRemote only works on iOS.) Have the structures changed significantly enough since then that it would be nontrivial to port either to .47.xx?
There already is feedback for dfhack/tiletypes aquifer on github issues tracker.
Due to not being a developer and not having a github account, my additional info here:
tiletypes aquifer does not work as expected.
The aquifer flag can be set. Obviously the aquifer information exists somehow, because revealing the map and pressing "d" (dig) shows a blue/blinking aquifer area where it was set by tiletypes. But ... dwarfes digging into the aquifer area do not start water flooding the digged tunnel.
I tried this before on a "granite" level. Set tiletypes to range 7/7/1, material soil, shape wall, special normal, aquifer 1. And replaced 7/7 granite by soil aquifer.
Former channeling into the aquifer area created a soil downward slope ... and water. Currently it produces a granite downward slope ... and no water.
It looks like the aquifer and material information is lost, when a tile becomes "digged in/out".
Are you using 0.47? Aquifers changed significantly in that version, from my understanding, but we haven't made any changes to support that, so any aquifer-related stuff probably won't work well.
There's one for Embark-Tools somehow causes the native DF aquifer UI string to go AWOL, but it has nothing to do with manipulation.
How make reaction with product made of local material of worker who do this reaction?
Like if serpent man doing this reaction, product made of serpent's man venom, if spider man do this reaction, product made of spider man venom.How make reaction with product made of local material of worker who do this reaction?
What do you mean by "local material of worker"?
Determining if the pthread_create exist failed with the following output:
Change Dir: /home/changemewtf/dfhack/build/CMakeFiles/CMakeTmp
Run Build Command:"/usr/bin/ninja" "cmTC_57f82"
[1/2] Building C object CMakeFiles/cmTC_57f82.dir/CheckSymbolExists.c.o
[2/2] Linking C executable cmTC_57f82
FAILED: cmTC_57f82
: && /usr/bin/cc -fvisibility=hidden -mtune=generic -m64 -mno-avx -rdynamic CMakeFiles/cmTC_57f82.dir/CheckSymbolExists.c.o -o cmTC_57f82 && :
CMakeFiles/cmTC_57f82.dir/CheckSymbolExists.c.o: In function `main':
CheckSymbolExists.c:(.text+0x1b): undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
File /home/changemewtf/dfhack/build/CMakeFiles/CMakeTmp/CheckSymbolExists.c:
/* */
#include <pthread.h>
int main(int argc, char** argv)
{
(void)argv;
#ifndef pthread_create
return ((int*)(&pthread_create))[argc];
#else
(void)argc;
return 0;
#endif
}
I'm trying to build the current alpha so I can try to build There Will Be Text (https://github.com/mifki/df-twbt) so I can try out the GemSet (https://github.com/DFgraphics/GemSet) graphics pack and currently striking out.If all you want is a build for 0.47.03, check https://dfhack.org/builds (see a page or two back for more information). If you do want to build it yourself, though:
I see from the DFHack docs (https://docs.dfhack.org/en/stable/docs/Compile.html) pthread is specifically mentioned, and I made sure to compile the latest stable version of cmake from source. But I still got the pthread errors.Did you install libpthread, though? And did the one you installed match the architecture you're building DFHack for? (e.g. CMake won't pick up on a 32-bit libpthread if you're building 64-bit DFHack.)
Is there possibly something wrong with the autogems plugin? It does not appear to be working. Gems are available and there are dwarves assigned to gem cutting, and yet it isn't creating tasks at the workshops. Build ID is 200218004.You're sure the plugin is enabled, right? Is it showing up in the "o": standing orders menu?
Is there possibly something wrong with the autogems plugin? It does not appear to be working. Gems are available and there are dwarves assigned to gem cutting, and yet it isn't creating tasks at the workshops. Build ID is 200218004.You're sure the plugin is enabled, right? Is it showing up in the "o": standing orders menu?
Is it possible to determine whether a script has been called directly from the DFHack console as opposed to onLoad.init?I would guess:
dfhack.is_interactive()
Quick note - the "feature" script seems to be broken for me. feature list doesn't return anything, and feature magma tells me that it "Could not find a magma-bearing feature." Map doesn't have a magma pool, so its just the sea, however normally the caverns/etc would be listed as well.Not being familiar with the script, but running "feature list" on the same save in 0.44.12 and 0.47.03 gives the same result for me (9 different ones), and "feature magma" resulted in "Enabled magma furnaces" on both (there are already working magma furnaces, though). However, the DF structures I'm using were downloaded 10 or so hours ago, and so may be newer than the ones you used.
Happy to provide logs/etc - not experiencing any other issues though. Arch 64 bit :)
if dfhack.gui.getCurFocus() ~= 'setupadventure' then
qerror('Must be called on adventure mode setup screen')
end
local adv = dfhack.gui.getCurViewscreen().party_members -- hint:df.viewscreen_setupadventurest
for Pm, Ad in pairs(adv) do
for k in pairs(Ad.skills) do
Ad.skills[k] = df.skill_rating.Legendary5
end
for k in pairs(Ad.physical_levels) do
Ad.physical_levels[k] = df.adventurer_attribute_level.Superior
end
for k in pairs(Ad.mental_levels) do
Ad.mental_levels[k] = df.adventurer_attribute_level.Superior
end
end
How resurrect my dead adventurer?
local fullHeal = reqscript('full-heal')
if df.global.gamemode ~= df.game_mode.ADVENTURE then
qerror("This script can only be used in adventure mode!")
end
local adventurer = df.nemesis_record.find(df.global.ui_advmode.player_id).unit
if not adventurer or not adventurer.flags2.killed then
qerror("Your adventurer hasn't died yet!")
end
fullHeal.heal(adventurer, true)
df.global.ui_advmode.player_control_state = 1
Quick note - the "feature" script seems to be broken for me. feature list doesn't return anything, and feature magma tells me that it "Could not find a magma-bearing feature." Map doesn't have a magma pool, so its just the sea, however normally the caverns/etc would be listed as well.Not being familiar with the script, but running "feature list" on the same save in 0.44.12 and 0.47.03 gives the same result for me (9 different ones), and "feature magma" resulted in "Enabled magma furnaces" on both (there are already working magma furnaces, though). However, the DF structures I'm using were downloaded 10 or so hours ago, and so may be newer than the ones you used.
Happy to provide logs/etc - not experiencing any other issues though. Arch 64 bit :)
Did you install libpthread, though? And did the one you installed match the architecture you're building DFHack for? (e.g. CMake won't pick up on a 32-bit libpthread if you're building 64-bit DFHack.)
libevent-pthreads-2.1-6/bionic,now 2.1.8-stable-4build1 amd64 [installed]
libpthread-stubs0-dev/bionic,now 0.3-4 amd64 [installed]
Technical question: Is there a way to check if a pointer is valid in Lua, i.e. if a df structure pointer address is within the range allocated to DF?
Thanks!How resurrect my dead adventurer?Code: [Select]local fullHeal = reqscript('full-heal')
if df.global.gamemode ~= df.game_mode.ADVENTURE then
qerror("This script can only be used in adventure mode!")
end
local adventurer = df.nemesis_record.find(df.global.ui_advmode.player_id).unit
if not adventurer or not adventurer.flags2.killed then
qerror("Your adventurer hasn't died yet!")
end
fullHeal.heal(adventurer, true)
df.global.ui_advmode.player_control_state = 1
Copy-paste the above into a text editor and save it as a .lua file (name it something like resurrect-adv.lua) in the hack\scripts folder. Then run it by entering the file's name in the DFHack console after you've been presented with the "You are deceased" message.
eventful=require('plugins.eventful')
eventful.onReport.print_report_text = function(id) local report=df.report.find(id); print(id, report.text); end
eventful.enableEvent(eventful.eventType.REPORT, 1)
df.report.get_vector():erase(idx)
dfhack.gui.getViewscreenByType(df.viewscreen_announcelistst, 0).reports:erase(idx)
So, what I'm asking is,I think world.status.display_timer (https://github.com/DFHack/df-structures/blob/master/df.world.xml#L997) is what you want - setting it to 0 should clear the bottom of the screen.
1. is there an easy way to prevent reports from appearing on announcements screen (like set some flag inside the eventful.onReport or something);
2. lacking that, is there a way to access that `getViewscreenByType(df.viewscreen_announcelistst, 0).reports` when the screen isn't loaded;No, that's a copy that is made specifically for the screen when the screen is opened, and is destroyed when the screen is closed. Modifying it won't affect the global list of reports.
3. and lacking that, is it possible to execute a script when the announcements screen opens?You could use an onStateChange handler - I think that would probably be the easiest way.
local dlg=require("gui.dialogs")
local testdia=df.global.ui_advmode.conversation
testdia.choices:insert("#",{new=df.ui_advmode.T_conversation.T_choices,})
for nu,choi in pairs(df.global.ui_advmode.conversation.choices) do
if choi.choice==nil then
choi.choice=df.talk_choice:new()
testdia.choices[nu].title:insert("#",df.new("string"))
testdia.choices[nu].keywords:insert("#",df.new("string"))
testdia.choices[nu].choice.type=247
testdia.choices[nu].choice.unk.anon_2=testdia.activity_event[0].participants[1].histfig_id
testdia.choices[nu].title[0].value=("Gift someone themselves")
testdia.choices[nu].keywords[0].value=("Gift")
end
for nod,mof in pairs(df.global.ui_advmode.conversation.page_bottom_choices) do
if nod == 1 then
testdia.page_bottom_choices[nod]=nu
elseif nod == 0 then
testdia.page_bottom_choices[nod]=nu
elseif nod == 1 and 0 then
break
end
end
end
unretire-anyone seems to start the people you unretire naked and without any items, only with any artifacts they hold. Is there any way to fix that? e.g by giving them clothing and items appropriate to their civ?hmm have you tested this with any historical figure that shown up before? or just random ones? this might be the 'historical figure not having a unit body thus when the game gives them one it's only holding what they were told to hold during world gen, vs seeing them at a site which the game loads them into the site themed outfits.
Could you please make a pull request with this fix so it doesn't get lost? I hadn't even seen any reports of issues with that script.for anyone who want to use questport again in the new dfhackSpoiler: 'questport fix' (click to show/hide)
well sent an issue with the fix, so that at least it's there.Could you please make a pull request with this fix so it doesn't get lost? I hadn't even seen any reports of issues with that script.for anyone who want to use questport again in the new dfhackSpoiler: 'questport fix' (click to show/hide)
Is there a way to get the soul information of a selected histfig in legends viewer? I can write lua scripts but I can't find the variables needed.hmm it should be in the info section of their data, some how the historical figure info section which holds data on if they read books and reputation and relationship towards others also holds their personality which ends up being inserted into the unit's soul.
(I'm trying to scan the personality and values of historical figures to see if there's a reason behind the things they do... The better to mod secrets and entities to manipulate unit behavior.)
EDIT: Never mind, figured it out.
3. and lacking that, is it possible to execute a script when the announcements screen opens?You could use an onStateChange handler - I think that would probably be the easiest way.
Is a script as I described even possible?so far give one could make a camp(slap a civ to said camp) and retire there the idea of moving into a lair would require scrubbing the restrictions on the site then adding a civ to it.
I have a question concerning the nature of DFHack - are the variable names of objects created by Toady, or does the DFHack team have to actually figure out what they do and name them that way?I don't think there's a single answer to the question:
Is there any way of getting a list of all historical events related to a historical figure? Like what is shown on the legends screen?Mostly, I would say. Either you'd have to scour the historical info for entries that refer to the historical figure, and that means you'll have to know the fields where HF Ids are stored, which relies on DFHack mapping of the fields (and a lot of processing of the entries to get the rest of the info), or use the alternative approach that is to scour the exported legends info (which e.g. Legends Viewer uses, to great effect). Neither method probably provides the full picture, however, as Toady doesn't export everything in the entries, and DFHack hasn't mapped everything (in the ideal world, where DFHack structures has mapped everything, you'd be able to extract more than what Legends Mode tells you).
Is there any way of getting a list of all historical events related to a historical figure? Like what is shown on the legends screen?Mostly, I would say. Either you'd have to scour the historical info for entries that refer to the historical figure, and that means you'll have to know the fields where HF Ids are stored, which relies on DFHack mapping of the fields (and a lot of processing of the entries to get the rest of the info), or use the alternative approach that is to scour the exported legends info (which e.g. Legends Viewer uses, to great effect). Neither method probably provides the full picture, however, as Toady doesn't export everything in the entries, and DFHack hasn't mapped everything (in the ideal world, where DFHack structures has mapped everything, you'd be able to extract more than what Legends Mode tells you).
A bit late, but we have an official pre-release, 0.47.03-beta1, up now: https://github.com/DFHack/dfhack/releases/tag/0.47.03-beta1 (just in time for 0.47.04, probably)
1. Make the current weather get logged 3 seconds before the current season. This way with the big "official" pack you start to hear the relaxing weather sounds a few seconds before the seasonal music start, and more importantly with the "toikkus" pack, you don't hear "The weather has cleared" and "It is now spring" talking over each other.It looks to me like this freezes DF for 3 seconds whenever the weather (or season?) changes. That's not a great user experience and is bound to lead to bug reports.
2. Second suggestion: Don't log "The weather has cleared." or log a shortened version of it. The "official" pack doesn't have any sound or music to play when then weather has cleared. And for the "toikkus" pack, it doesn't really make sense to hear "The weather has cleared" announced when loading the save. But maybe someday someone will want to make some special clear-weather music that can load when the save loads. So maybe change the logged message from "The weather has cleared." to "weather has cleared." This way sound pack makers can choose to either target all weather cleared messages or target all weather cleared messages except the one that gets logged when the save file is first loaded.My opinion on the matter: there are multiple sound utilities (SoundSense, SoundCenSe, SoundSense-RS, and maybe others). Just because one doesn't support a message doesn't mean we should remove that message for other utilities as well. And I'm really not sure what removing "The" accomplishes - both messages look equally easy to parse to me, and by changing a message, we risk breaking compatibility with existing utilities for no clear benefit. (A similar argument could be made for weather - the script's job is to log events when they occur, and sound utilities could fairly easily add a delay before certain sounds if they want to.)
@Rumrusher and IndigoFenix: If there is a history event list for each hist fig it hasn't been mapped, as far as I can see.
It's not surprising that a lot of stuff hasn't been mapped yet, as it depends on people doing that, which, obviously, tends to be in areas they like to poke around in, or at least report what the find missing as issues on github (with as much info as possibly, to get the poor sod who may well be going to try to map something he's not familiar with a reasonable chance of doing a good job).
My opinion on the matter: there are multiple sound utilities (SoundSense, SoundCenSe, SoundSense-RS, and maybe others). Just because one doesn't support a message doesn't mean we should remove that message for other utilities as well. And I'm really not sure what removing "The" accomplishes - both messages look equally easy to parse to me, and by changing a message, we risk breaking compatibility with existing utilities for no clear benefit. (A similar argument could be made for weather - the script's job is to log events when they occur, and sound utilities could fairly easily add a delay before certain sounds if they want to.)
Oh, that makes more sense - it should be easy enough to not print weather information when a game is actually loaded.
if (not snowing and not raining) then --msg("The weather has cleared.")
elseif raining then msg("It has started raining.")
elseif snowing then msg("A snow storm has come.")
end
It is a bug if this has occurred naturally, as descriptions shouldn't be cut short.
A guess is that a guild hall or temple might have been removed, in which case DF might have failed to find the referenced "building". If you can check what entity 4746 is you might find a religion or guild.
Edit: Didn't manage to provoke any text for 260-262 when starting with a guildhall thought.
- There's been a lot of mapping of the "shape" of structures, i.e. introduction of new, unidentified, fields to align structures so that fields previously known again contain the known contents, as well as new unidentified fields at the end of structures.
- Similarly, a lot of enum values have been located, and a fair number identified.
- A number of new fields and structures have been (partially) identified.
- There's been a fair bit of at least identification of new derived types, although the identification of the contents may be lagging behind.
If you've got an interest in a particular area, such as e.g. syndromes, you ought to be better suited than most to help in the identification of the fields within it. Things that those active in field identification are interested in ought to be more likely to be identified than things peripheral to their interests. The syndrome and interaction complex is a particularly messy area that I, personally, avoid unless I need to deal with it.
I'm pretty sure I've built an altar outdoors when testing out graphics for the new items. I didn't realize an indoor requirement existed.Certain furniture (namely beds, tables, and chairs) are coded to not allow designating to build outside in fort mode. Most other furniture can be - and all can in adventure mode, as far as I know - it's just some weird restriction on a certain few :c
Hello, is there an ETA for the 47.04 version?No. It's done when it's done.
local utils = require('utils')
for i,item in ipairs(df.global.world.items.all) do
if not item.flags.rotten and not item.flags.dump and not item.flags.forbid and not item.flags.construction then
if item:getMaterial() == df.builtin_mats.INORGANIC and item:getType() == df.item_type.BAR then
print(utils.getItemDescription(item,0))
end
end
end
if item.flags.owned and not item.flags.rotten and not item.flags.dump and not item.flags.forbid and not item.flags.construction then
item:getMaterial() == df.builtin_mats.INORGANIC
df.inorganic_raw.find(matIndex).material.state_name.Solid
to get the material name for an inorganic item, if I have the materialIndex.I saw that there should be a flag "item.flags.owned". If I add this flag to the other flag checks, NO item will be printed. As I'm currently seeing items in dark yellow and red, there should always be printing something.
...
It seems, this flag is false for all items. Is this a bug? Does this flag represent something else? Is there another way to check the "owned" status? (I'm using DF 0.43.03 with DFHack 0.47.03-beta1).
Look at the item flags to try to figure out which ones you need.That's part of the general question above. Where should I look up such an information?
[DFHack]# lua
Type quit to exit interactive lua interpreter.
Shortcuts:
'= foo' => '_1,_2,... = foo'
'! foo' => 'print(foo)'
'~ foo' => 'printall(foo)'
'^ foo' => 'printall_recurse(foo)'
'@ foo' => 'printall_ipairs(foo)'
All of these save the first result as '_'.
[lua]# ^ df.global.world.items.all[0]
gives me lots of info, especially interestingflags = <item_flags: 00000206A2E739C0>
artifact = true
artifact_mood = true
flags2 = <item_flags2: 00000206A2E739C4>
[lua]# @ df.item_flags
Thanks for your response.That's the part of the "try to figure out" above. You've got the names that somebody assigned at some time, which typically was done without writing a description of how that was done, although in some cases you may have comments in the XML files. From there you'll guess at the detailed meaning, and perform experiments to see if you've got it correctly.
So it seems there is no item flag that has the information whether the item is owned by the fort (as being indicated by the stockpile color red) and I must check other states that might block access.Look at the item flags to try to figure out which ones you need.That's part of the general question above. Where should I look up such an information?
Thanks for your response.That's the part of the "try to figure out" above. You've got the names that somebody assigned at some time, which typically was done without writing a description of how that was done, although in some cases you may have comments in the XML files. From there you'll guess at the detailed meaning, and perform experiments to see if you've got it correctly.
So it seems there is no item flag that has the information whether the item is owned by the fort (as being indicated by the stockpile color red) and I must check other states that might block access.Look at the item flags to try to figure out which ones you need.That's part of the general question above. Where should I look up such an information?
For instance, if all webs are red, then you can assume the spider_web flag is one criterion of several that can cause the items to be colored red. If only some are red, then look at one uncollected web (e.g. in the cavern) and one collected web (in an inventory or workshop) and try to see which flags differ.
I use gui/gm-editor to look at things in DF.
I recently (with 0.47 release) got interest in writing dfhack scripts (in Lua), an was facing same problems you do now.
The built-in `lua` REPL is actually a great help. F.e.
...
This was enough for me for the beginning, and then I switched to a full-text search for keywords in dfhack sources.
I use gui/gm-editor to look at things in DF.
Thanks for your replies, Erendir, PatrikLundell, Roses. I've already done some small scripts by experimenting, reading scripts of others. Sometimes I had the feeling I was running against a wall, because after a whole evening of trial and error, I hardly got 20-30 lines of working code. So the question was: did I overlook something? This should be easier. But it seems, I need to develop more skill in researching the available code base.I recently (with 0.47 release) got interest in writing dfhack scripts (in Lua), an was facing same problems you do now.
The built-in `lua` REPL is actually a great help. F.e.
...
This was enough for me for the beginning, and then I switched to a full-text search for keywords in dfhack sources.
The LUA REPL looks promising, I will surely give it a try. I already tried to use the dfhack sources, but I was not able to make much use of that information in LUA so far. One of my approaches was to find the namespace the LUA interpreter is using to resolve the symbols being used in the LUA code. Any pointers here? Is this idea a dead end?I use gui/gm-editor to look at things in DF.
Already did that for stuff related to creatures. But it seems, it is not really usefull for general world items. Am I overlooking something here, too?
...
<bitfield-type type-name='item_flags'>
<flag-bit name='on_ground' comment='Item on ground'/>
<flag-bit name='in_job' comment='Item currently being used in a job'/>
<flag-bit name='hostile' comment='Item owned by hostile'/>
<flag-bit name='in_inventory' comment='Item in a creature, workshop or container inventory'/>
<flag-bit name='removed' comment='completely invisible and with no position'/>
<flag-bit name='in_building' comment='Part of a building (including mechanisms, bodies in coffins)'/>
<flag-bit name='container' comment='Set on anything that contains or contained items?'/>
<flag-bit name='dead_dwarf' comment='Dwarfs dead body or body part'/>
<flag-bit name='rotten' comment='Rotten food'/>
<flag-bit name='spider_web' comment='Thread in spider web'/>
<flag-bit name='construction' comment='Material used in construction'/>
<flag-bit name='encased' comment='Item encased in ice or obsidian'/>
<flag-bit name='unk12' comment='unknown, unseen'/>
<flag-bit name='murder' comment='Implies murder - used in fell moods'/>
<flag-bit name='foreign' comment='Item is imported'/>
<flag-bit name='trader' comment='Item ownwed by trader'/>
<flag-bit name='owned' comment='Item is owned by a dwarf'/>
<flag-bit name='garbage_collect' comment='Marked for deallocation by DF it seems'/>
<flag-bit name='artifact' comment='Artifact'/>
<flag-bit name='forbid' comment='Forbidden item'/>
<flag-bit name='already_uncategorized' comment='unknown, unseen'/>
<flag-bit name='dump' comment='Designated for dumping'/>
<flag-bit name='on_fire' comment='Indicates if item is on fire, Will Set Item On Fire if Set!'/>
<flag-bit name='melt' comment='Designated for melting, if applicable'/>
<flag-bit name='hidden' comment='Hidden item'/>
<flag-bit name='in_chest' comment='Stored in chest/part of well?'/>
<flag-bit name='use_recorded' comment='transient in unit.used_items update'/>
<flag-bit name='artifact_mood' comment='created by mood/named existing item'/>
<flag-bit name='temps_computed' comment='melting/boiling/ignite/etc. points'/>
<flag-bit name='weight_computed'/>
<flag-bit name='unk30' comment='unknown, unseen'/>
<flag-bit name='from_worldgen' comment='created by underground critters?'/>
</bitfield-type>
...
...
<enum-type type-name='item_type' base-type='int16_t'>
<enum-attr name='caption'/>
<enum-attr name='is_rawable' type-name='bool'/>
<enum-attr name='is_stackable' type-name='bool'/>
<enum-attr name='is_caste_mat' type-name='bool'
comment='instead of material, uses a creature/caste pair'/>
<enum-attr name='classname'/>
<enum-item name='NONE' value='-1'/>
<enum-item name='BAR' comment='Bars, such as metal, fuel, or soap.'>
<item-attr name='caption' value='bars'/>
<item-attr name='classname' value='item_barst'/>
</enum-item>
<enum-item name='SMALLGEM' comment='Cut gemstones usable in jewelers workshop'>
<item-attr name='caption' value='cut gem'/>
<item-attr name='classname' value='item_smallgemst'/>
</enum-item>
<enum-item name='BLOCKS' comment='Blocks of any kind.'>
<item-attr name='caption' value='blocks'/>
<item-attr name='classname' value='item_blocksst'/>
</enum-item>
<enum-item name='ROUGH' comment='Rough gemstones.'>
<item-attr name='caption' value='rough gem'/>
<item-attr name='classname' value='item_roughst'/>
</enum-item>
<enum-item name='BOULDER' comment='Raw mined stone.'>
<item-attr name='caption' value='boulder'/>
<item-attr name='classname' value='item_boulderst'/>
</enum-item>
<enum-item name='WOOD' comment='Wooden logs.'>
<item-attr name='caption' value='logs'/>
<item-attr name='classname' value='item_woodst'/>
</enum-item>
...
The df structures xml files are the actual variable name spaces being used by the dfhack lua, for instance, df.items.xml ...
gui/gm-editor can show the info about items just the same way it can show info about creatures. The difference is that you'd have to have the right viewing mode in DF, so for a creature it would be 'v', while for an item it would be 'k', and I think workshops show different info with 'k' and 't'.
artifact_mood: I assume this means in_job, but specifically for an artifact.
Another helpful hint. I was using it mostly from the units list, so I never had the idea to try it with other viewing modes.
The df structures xml files are the actual variable name spaces being used by the dfhack lua, for instance, df.items.xml ...
I did a git clone of dfhack and found nothing. Until I discovered a few minutes ago that there is another repo, which contains the xml files you mentioned:
https://github.com/DFHack/df-structures
It looks, like this is the key!
git clone --recursive https://github.com/DFHack/dfhack
`recursive` is the important part.So it seems there is no item flag that has the information whether the item is owned by the fort (as being indicated by the stockpile color red) and I must check other states that might block access.
So it seems there is no item flag that has the information whether the item is owned by the fort (as being indicated by the stockpile color red) and I must check other states that might block access.
In this case, you are correct - the criteria for "foreign" items in the stocks screen showing up Red is that they are stored in the inventory of a unit who is not a member of your fortress. Determining this involves looking at item.general_refs and following them until you get to the owner, then using a DFHack builtin function to tell you if that unit is a citizen.
not item.flags.rotten and not item.flags.dump and not item.flags.forbid and not item.flags.construction and not item.flags.trader and not item.flags.spider_web
this Dwarfexplorer looks very promising. Unfortunately, the v1.2 release's `DwarfExplorer_0.44.07_Win64.zip` doesn't include the `qapplication.plug.dll`, and v1.1's is compiled against dfhack 0.44.12-r3 and wouldn't work with 0.47.03-beta1.
local utils = require('utils')
for i,item in ipairs(df.global.world.items.all) do
if item.flags.rotten and item.flags.on_ground and item.flags.trader then
for k = #item.general_refs-1, 0, -1 do
if (item.general_refs[k].entity_id == 244) then -- human civ
print(utils.getItemDescription(item,0)..' '..tostring(item:getStackSize()))
item.flags.dump = true
item.flags.forbid = false
item.flags.trader = false
item.flags.foreign = false
item.general_refs:erase(k)
end
end
end
end
Is there a way to "revive" the wagons (toggling the "killed" flag is not sufficient).
Is there a way to "revive" the wagons (toggling the "killed" flag is not sufficient).
"full-heal" command?
if unit.flags2.killed and not unit.flags3.scuttle then -- scuttle appears to be applicable to just wagons, which probably shouldn't be resurrected
full-heal currently does not work with wagons. There is a check, that prevents this (for whatever reason).
I removed the check and tried the full-heal. I don't know if it really succeeded, but it did not fix the "stuck caravan" problem.
There is a dfhack script, that should fix merchant stuck when entering the map. I tried it and it reported "4 wagons being removed", but the caravan is still stuck.
I removed the check and tried the full-heal. I don't know if it really succeeded, but it did not fix the "stuck caravan" problem.
I don't know whether resurrecting wagons is going to solve your problem, but removing the scuttle check should have been sufficient. Did you remember to add -r in addition to selecting the target unit?
What version are you running? As far as I can tell, "fix/stuck-merchants.lua" only works on merchants which are stuck outside the local map, and "fix/merchants.lua" supposedly allows humans to make trade agreements; neither is relevant in this case.
local utils = require('utils')
function mark_dumped(aitem)
aitem.flags.dump = true
aitem.flags.forbid = false
aitem.flags.trader = false
aitem.flags.foreign = false
end
for i,item in ipairs(df.global.world.items.all) do
-- item.flags.rotten and
if item.flags.on_ground and item.flags.trader and not item.flags.container then
if #item.general_refs>0 and (item.general_refs[0].entity_id == 244) then
print(utils.getItemDescription(item,0)..' '..tostring(item:getStackSize()))
mark_dumped(item)
item.general_refs:erase(0)
end
end
if item.flags.on_ground and item.flags.trader and item.flags.container then
if #item.general_refs>0 and (item.general_refs[0].entity_id == 244) then
print(utils.getItemDescription(item,0)..' '..tostring(item:getStackSize()))
for k = #item.general_refs-1 , 0, -1 do
if item.general_refs[k]._type == df.general_ref_contains_itemst then
local item_id = item.general_refs[k].item_id
local cont_item = df.item.find(item_id)
print(' ',utils.getItemDescription(cont_item,0)..' '..tostring(cont_item:getStackSize()))
mark_dumped(cont_item)
end
item.general_refs:erase(0)
mark_dumped(item)
end
end
end
end
-- remove all items stuck in depot
local depot_items = df.global.world.buildings.other.TRADE_DEPOT[0].contained_items
for k = #depot_items-1 , 0, -1 do
local item = depot_items[k].item
print(tostring(item))
print(utils.getItemDescription(item,0))
if item.flags.trader then
mark_dumped(item)
depot_items:erase(k)
end
end
./hack/libs/liballegro.so.5.0: file too short
Can't load plugin stonesense
This is just a heads up about something I noticed while digging for what is my real question -So Stonesense. (No it's not crashing. ;))
Using dfhack-0.47.04-alpha0-200308001-Linux-64-gcc-7 (although its been this way for quite a while) I noticed that what are presumably meant to be symlinks to the appropriate libraries are unpacked as plain text files on my system, this explains the messages in the stderr.logCode: [Select]./hack/libs/liballegro.so.5.0: file too short
This is just a heads up about something I noticed while digging for what is my real question -
Can't load plugin stonesense
How do I disable stonesense from attempting to load when I start dfhack?
I couldn't find anything in the inits or startup scripts but it is possible that I missed something...
How automatically butcher sentient corpses?You can't in vanilla. Ask in the mod section. There's a bunch of people there obsessed with that issue, so they may or may not know.
I do not understand how to compile/build the latest release from the github. I have even downloaded the main folder and all of the programs necessary to perform the task, but I fail to comprehend what needs to be done.Does https://docs.dfhack.org/en/stable/docs/Compile.html help? What OS are you building on? If you have any specific questions about it, we'd be happy to answer them. It should build on all supported OSes, as far as I know.
I've personally never managed to get that working on windows 10 64-bit but I figure that's because I've already messed up my setup's C++ environment in a variety of strange ways.Microsoft has changed things with MSVC since the documentation for DFHack were written, but I eventually managed to get the installation to work and DFHack to build on a new computer (requiring quite a few attempts to get all the parts needed installed. Windows 7 support in particular is required, but the errors resulting from not having it give no clue whatsoever about that being the cause. For MSVC 2017 I ended up with an everything and the kitchen sink installation rather than trying to guess what's needed, and I think Windows 7 support still needed to be added).
Out of curiosity, is there a line of code that I could remove within the Full-Heal.lua script that will remove the resetting of syndromes applied? (meaning, so when its used, syndromes such as secrets wont be removed upon being used on the character). and Yeah im aware that counts towards all syndromes.I took a skim through the full-heal script then did a quick arena test to be sure, and there doesn't seem to be anything in that script that touches syndromes at all.
Is it intended that eventful's onUnload triggers every time a site is offloaded in adventure mode? The documentation doesn't actually say what it does, but I have only ever seen it used as if it is only meant to run on unloading the entire world (i.e. when the user saves and quits to main menu or ends their run with that fort/adventurer). As such, a lot of dfhack scripts break as soon as you travel through the travel screen, since moving even a single tile unloads the current site. This happens on the current 0.47.03-beta1 version, but I set up an older version of df/dfhack and confirmed that this behavior was also present in 0.44.12.
<...>
I took a look at what it does edit to see if any of it links to syndromes indirectly, and it looks like wounds are created for syndromes. What might be happening is that some syndrome effects reference either just the wound or both the wound and the syndrome, and deleting the wound breaks those effects. However, I tried a couple of tests and full heal only deleted those wounds for a single tick - they were back again in the wounds vector the next tick, and stuff like nausea was applied properly despite being momentarily removed by full-heal. I wouldn't be surprised if the recreation of syndrome related wounds and the correct reapplication of the associated effects wasn't universal, though.
If that is what is creating your problem, then the fix for full-heal would require skipping the deletion of wounds that have associated syndromes. This may potentially lead to undefined behavior when it comes to things like werecreature bites, however, since as far as I know wounds created by those would have both a syndrome and "conventional" wound info (or it might be fine, who knows).
Afaik it was always that way. In the source (not eventful as it wraps (among other stuff) eventmanager module) here (https://github.com/DFHack/dfhack/blob/develop/library/modules/EventManager.cpp#L210) it's called more explicit: SC_MAP_UNLOADED.
<...>Afaik it was always that way. In the source (not eventful as it wraps (among other stuff) eventmanager module) here (https://github.com/DFHack/dfhack/blob/develop/library/modules/EventManager.cpp#L210) it's called more explicit: SC_MAP_UNLOADED.
Why is SC_MAP_UNLOADED used as opposed to SC_WORLD_UNLOADED? It seems to me that the latter would surely make more sense in the context of adventure mode, with no difference in fort mode functionality (I think).
modtools/item-trigger breaks, for starters. All the set item triggers get cleared on site offloading.<...>Afaik it was always that way. In the source (not eventful as it wraps (among other stuff) eventmanager module) here (https://github.com/DFHack/dfhack/blob/develop/library/modules/EventManager.cpp#L210) it's called more explicit: SC_MAP_UNLOADED.
Why is SC_MAP_UNLOADED used as opposed to SC_WORLD_UNLOADED? It seems to me that the latter would surely make more sense in the context of adventure mode, with no difference in fort mode functionality (I think).
Both have their uses. However most stuff is developed without adventure mode in mind. I.e. what scripts are braking? Why are they running in adventure mode in first place?
...
The issue is rather easy to solve; just add an "if not wound.curse" check and clear any actual injury data instead of deleting the wound outright in that case.Would that play nicely with syndromes that do stuff on a body part by body part basis? Like, say, bruising or blistering whatever body parts the syndrome's material comes in contact with?
I believe all of those scripts were originally just made with fort mode in mind (Expwnent was the author for most of them IIRC). It probably would be a good idea to update them though so that they don't break in adventure modemodtools/item-trigger breaks, for starters. All the set item triggers get cleared on site offloading.<...>Afaik it was always that way. In the source (not eventful as it wraps (among other stuff) eventmanager module) here (https://github.com/DFHack/dfhack/blob/develop/library/modules/EventManager.cpp#L210) it's called more explicit: SC_MAP_UNLOADED.
Why is SC_MAP_UNLOADED used as opposed to SC_WORLD_UNLOADED? It seems to me that the latter would surely make more sense in the context of adventure mode, with no difference in fort mode functionality (I think).
Both have their uses. However most stuff is developed without adventure mode in mind. I.e. what scripts are braking? Why are they running in adventure mode in first place?
...
The scripts interaction-trigger, random-trigger, syndrome-trigger, projectile-trigger and outside-only (all modtools scripts) use the same style of clearing triggers using onUpload, and thus have the same problem.
modtools/reaction-trigger and modtools/reaction-product-trigger are exceptions solely because they currently do not support adventure mode at all. If support was added (AFAICT just handling reactions/jobs without associated buildings), then they would have the same problem.
modtools/invader-item-destroyer also has the same problem, but frankly I have no idea how adventure mode armies/invasions work so I can't say whether this even should be running at all in adventure mode. EDIT: I suppose the same goes for outside-only, should it even run in adv mode?
These are all the scripts that use onUpload, according to the github search I did. Every script that touches onUpload uses it as if it is synonymous with leaving a world, and as such every one of those scripts that could apply to adventure mode breaks as soon as a site is offloaded.
EDIT:Sort of. Syndromes are notoriously tricky in their application and removal, although removal usually is easier. You will typically just get a single wound with a single syndrome, but for each instance of the syndrome you can get a different wound for a different body part. I'm not sure if syndrome-util handles all of that (I know it was previously not working for any syndromes that needed to function through wounds, both for application and removal), but you should be able to handle removal fairly easily with a lua scriptThe issue is rather easy to solve; just add an "if not wound.curse" check and clear any actual injury data instead of deleting the wound outright in that case.Would that play nicely with syndromes that do stuff on a body part by body part basis? Like, say, bruising or blistering whatever body parts the syndrome's material comes in contact with?
Does anybody know of a nightly build with a working exportlegends command, that gives functional XML files for Legends Viewer? I had it working fine a little over a week ago, but all the more recent builds seem to be unable to generate the files correctly. I don't know which build it was that I got it to work on, so I guess I'm just hoping that somebody else still has a build where it works and can give me the number for it.If you have problems, please report them. Nobody's going to fix problems unless they're reported or accidentally detected by someone who actually goes about fixing the issues.
Does anybody know of a nightly build with a working exportlegends command, that gives functional XML files for Legends Viewer? I had it working fine a little over a week ago, but all the more recent builds seem to be unable to generate the files correctly. I don't know which build it was that I got it to work on, so I guess I'm just hoping that somebody else still has a build where it works and can give me the number for it.If you have problems, please report them. Nobody's going to fix problems unless they're reported or accidentally detected by someone who actually goes about fixing the issues.
Having said that, I made a pull request 12 hour ago or so for an updated version that fixes the things I encountered. Due to the way DF is organized, it won't appear in a "nightly build" until it's been approved and then "imported" into the main DFHack repository. However, it should be possible for you to get that version off of Github to see if it works for your problems. The good thing with scripts is that they don't need to be compiled, so you don't need the rest of the infrastructure. The bad thing with scripts is that they're not compiled, so there is no compiler available to flag things that don't compile (any longer).
...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:228: Cannot read field abstract_building_templest.deity: not found.
stack traceback:
[C]: in metamethod '__index'
...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:228: in global 'export_more_legends_xml'
...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:826: in global 'export_legends_info'
...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:879: in local 'script_code'
...\DF4704W64VettlingrPack\df_47_04_win\hack\lua\dfhack.lua:680: in function 'dfhack.run_script_with_env'
(...tail calls...)
*ahem* I wonder if, perhaps, a codeblock would also suffice as a substitute? Like so:Yes - in fact, text is usually better, in case we need to copy something from it. (It's a bit annoying to copy from the console on Windows - one way to do it is by right-clicking in the title bar, or maybe in the console too, depending on your Windows version.)Code: [Select]...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:228: Cannot read field abstract_building_templest.deity: not found.
stack traceback:
[C]: in metamethod '__index'
...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:228: in global 'export_more_legends_xml'
...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:826: in global 'export_legends_info'
...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:879: in local 'script_code'
...\DF4704W64VettlingrPack\df_47_04_win\hack\lua\dfhack.lua:680: in function 'dfhack.run_script_with_env'
(...tail calls...)
How automatically butcher sentient corpses?This doesn't do it automatically (http://www.bay12forums.com/smf/index.php?topic=165414.msg7553808#msg7553808), but does make them butcherable as normal. This thread (http://www.bay12forums.com/smf/index.php?topic=78108) could be helpful for figuring out how to script making it happen automatically.
Was referring to Lewa263's musing, actually, but thanks. However, the problem then becomes either relying on Github (unsure if its to spec like you've stated) or digging for direct download links in the nightlies (worse than useless so far, it's a freaking warren).*ahem* I wonder if, perhaps, a codeblock would also suffice as a substitute? Like so:Yes - in fact, text is usually better, in case we need to copy something from it. (It's a bit annoying to copy from the console on Windows - one way to do it is by right-clicking in the title bar, or maybe in the console too, depending on your Windows version.)Code: [Select]...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:228: Cannot read field abstract_building_templest.deity: not found.
stack traceback:
[C]: in metamethod '__index'
...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:228: in global 'export_more_legends_xml'
...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:826: in global 'export_legends_info'
...ettlingrPack\df_47_04_win/hack/scripts/exportlegends.lua:879: in local 'script_code'
...\DF4704W64VettlingrPack\df_47_04_win\hack\lua\dfhack.lua:680: in function 'dfhack.run_script_with_env'
(...tail calls...)
Also, is this a separate bug report? I'm not sure, but I'll double-check that part of the script to see if it's up-to-date.
Edit: your error appears to be related to something accessing the "deity" field, which the current version doesn't do - my advice (if you're experiencing the issue) is to try upgrading.
Most scripts work only with the versions compatible with the part of the code they're supposed to work with, and there's no reason at all for scripts distributed with DFHack to explicitly support other versions (i.e. add additional scripting to handle variations). The DF structures have been in a bit of flux since 0.47.04 due to introduction of new background functionality to handle "tagged unions" better, naming essentially unnamed fields ("unknown1b", for instance), and renaming of a few fields with misleading/no longer correct names. This means that grabbing a script newer than the code base you're using will work only if the structures it was written for are included in your older code. A script fixed today for code you grabbed yesterday will probably work, but if the code was grabbed a week ago it depends. Due to DFHack being spread over 3 repositories, updates will be out of phase from time to time, as you need to have the structures in place when scripts using them are updated, and the scripts then have to be imported into the main repository (together with the structures, of course).
...op\dwarfplug\df_47_04_win/hack/scripts/exportlegends.lua:348: Cannot read field historical_entity.unknown1b: not found.
stack traceback:
[C]: in metamethod '__index'
...op\dwarfplug\df_47_04_win/hack/scripts/exportlegends.lua:348: in global 'export_more_legends_xml'
...op\dwarfplug\df_47_04_win/hack/scripts/exportlegends.lua:906: in global 'export_legends_info'
...op\dwarfplug\df_47_04_win/hack/scripts/exportlegends.lua:959: in local 'script_code'
...kwell\Desktop\dwarfplug\df_47_04_win\hack\lua\dfhack.lua:680: in function 'dfhack.run_script_with_env'
(...tail calls...)
...at first, I thought it was something to do with the 'histfig culling' protocol being active versus otherwise, apparently not. I'm not familiar with that - is it an option in the adventure mode dialog menu or somewhere else? We can certainly take a look, although it might require additional research into adventure mode that we haven't done yet.
Ah! I've investigated that screen before, and that should be easier. Do you happen to have a save where that screen is available? Getting to that point could be a bit time-consuming.
Ah! I've investigated that screen before, and that should be easier. Do you happen to have a save where that screen is available? Getting to that point could be a bit time-consuming.http://dffd.bay12games.com/file.php?id=14968
http://dffd.bay12games.com/file.php?id=14968Heh, I have this one downloaded already! (Thanks Nilsolm too)
I'm hoping to put up a "beta" release soon - we'd still like some help testing scripts on Windows (e.g. I'm unsure if feature and modtools/create-unit work yet) if anyone is able.
It might be 2-3 days still - I got wrapped up in other things, and plan on merging in some more PRs before putting it out. I don't expect many breaking changes between .03 and .04, although I haven't written a changelog yet (which is honestly one of the more time-consuming parts).
Dumb question but: I'm trying to use the changelayer script, but dfhack doesn't recognize it. The error message suggests I try loading the script (though dfhack doesn't recognize load...) - but its irrelevent as I don't see any script file with that name in the appropriate directory. Anyone know where I could get a copy of the appropriate file, and what the alternative to LOAD is?
DFhack is not something I've messed with in a long, long time. Sorry for the dumb question.
Edit: This is going to sound even crazier, but though I made *100% sure* I wasn't typing changelayer wrong....its now recognizing the command, but claiming there's no such material. "changelayer marble" is all I'm trying to do.
Dumb question but: I'm trying to use the changelayer script, but dfhack doesn't recognize it. The error message suggests I try loading the script (though dfhack doesn't recognize load...) - but its irrelevent as I don't see any script file with that name in the appropriate directory. Anyone know where I could get a copy of the appropriate file, and what the alternative to LOAD is?
DFhack is not something I've messed with in a long, long time. Sorry for the dumb question.
Edit: This is going to sound even crazier, but though I made *100% sure* I wasn't typing changelayer wrong....its now recognizing the command, but claiming there's no such material. "changelayer marble" is all I'm trying to do.
Dumb question but: I'm trying to use the changelayer script, but dfhack doesn't recognize it. The error message suggests I try loading the script (though dfhack doesn't recognize load...) - but its irrelevent as I don't see any script file with that name in the appropriate directory. Anyone know where I could get a copy of the appropriate file, and what the alternative to LOAD is?Just curious, what exactly was the error message (if you still have it)? Since changelayer is a plugin, it does need to be loaded in order to work - but DFHack loads all plugins it can find on startup, so I'm not really sure why it wouldn't have been loaded.
...
It worked! Thank you, Warmist....
Use one commit before (as the job re-rename is still not in dfhack). Also I suggest replacing all "local function" to just "function" as it breaks some stuff...
Wait, what? I'm pretty sure I updated structures, and I tested that code snippet to make sure it worked....
Use one commit before (as the job re-rename is still not in dfhack). Also I suggest replacing all "local function" to just "function" as it breaks some stuff...
Not sure if this is of interest to people (or maybe it is already known), but in the unit counters unit.counters, the think_counter, controls how fast a unit can shoot a crossbow. Testing in arena, as soon as my dwarf shot the crossbow, think_counter went to 80, if I then manually set it back to 0 the dwarf fired again immediately. It should be possible then to modify firing rates by checking if a projectile has been fired and then setting the units think_counter to a specific level.A while back (2 years ago, now?), a few people including myself did some dfhackery on this, and there are at least a couple of scripts floating around (including the one in my mod) that will change that counter on the fly to simulate faster firing weapons.
Is there a utility for exporting entities for use between worlds? I've wanted something like that since day 1.No, and I don't see how one could be made with a reasonable effort. It might be possible to export the properties of a civ and interrupt world gen as soon as the first civ of that entity_raw is placed and replace the properties of it with those collected, but I don't see any point in doing so. You could probably achieve essentially the same thing by defining an entity_raw with rigidly fixed parameters and specify that there should only be one of these.
I meant in reference to historical figures, namely my self-insert garystu adventurer.Is there a utility for exporting entities for use between worlds? I've wanted something like that since day 1.This assumes "entities" is used in the sense it's used in the DFHack structures.
If you're interested, go for it! plugins/search.cpp is the place to look - there are a bunch of existing implementations for other screens there already.
Wait, what? I'm pretty sure I updated structures, and I tested that code snippet to make sure it worked....
Use one commit before (as the job re-rename is still not in dfhack). Also I suggest replacing all "local function" to just "function" as it breaks some stuff...
Jan: You will have to upgrade DFHack, though - just upgrading the script won't work in this case.
Warmist: could you fix the function declarations when you get a chance? Thanks.
As with other things in DF, hist figs have ties to other hist figs, sites, entities (civs, etc.), and history, and the items they carry likewise have ties. It would presumably be possible to create a new character in a new word and change it to get the stats of the "exported" one, as well as items that are technically similar to those used originally, but all links will be severed. The transplanted character would either have no history at all if DFHacked in from scratch, or would inherit the history of the character "grafted" onto.I meant in reference to historical figures, namely my self-insert garystu adventurer.Is there a utility for exporting entities for use between worlds? I've wanted something like that since day 1.This assumes "entities" is used in the sense it's used in the DFHack structures.
Sorry that i've didn't do it earlier. Just for future reference, if we actually want BenLubar's changes for lua linting (if i understood correctly) then somebody (prob me) needs forward declare functions that are used later.Yeah, I was in a hurry and remembered this last-minute before putting up 0.47.04-beta1, but I suspect it's just the function definition order that needs adjusting. Removing "local" was the quickest fix. (We might be able to silence that particular luacheck warning for advfort, too, if that's easier.)
I was looking at soundsense and discovered there is a soundsense.lua file providing extra information to the gamelog, like repeating season or weather information on SC_WORLD_LOADED. And then I realized there is the extra-gamelog.lua that includes almost all of the features from soundsense.lua, but not all of them.Looking at that script's history (https://github.com/DFHack/scripts/commits/master/modtools/extra-gamelog.lua), I'm not seeing anything that removed it. It was combined (https://github.com/DFHack/scripts/commit/ce514b45f5112d92f0f513cceab14b02fd74b8c2) from soundsense-season and log-region, both of which only logged what their names imply.
Is there a reason the part about skills was omitted? (like, "<dwarf_name> is now very rusty <job_name>.")
Is there a DFHack which supports DF 0.47.04 ? I got an error about an unknown DF version.
(For reference, you can copy text from the console on Windows by right-clicking in the console or in the title bar. That makes it possible for other people to search and find these posts more easily - a screenshot isn't searchable.)Oh me, oh my!! Sorry about that!
Oh me, oh my!! Sorry about that!No worries; I'd already copied the text from the earlier post above.
Here follows the code as was present in console itself:
-snip-
About your sugestion on special characters and move the game to a different folder, yeah it totally works. I was using a "ã" letter to name the folder the game was in and when I renamed it the dfhack was back on trail.
Does siege-engine work with ballistae or catapults only? Can I put ballistae on a tower above ground and have it shoot down into a target area?It appears to work with a ballista in 0.47.04 for me (at least, gui/siege-engine recognizes and rotates the ballista properly; I didn't try actually firing anything).
Just to help us out, are you using 64-bit or 32-bit DF+DFHack? I think the Ruby versions bundled with those are different.
I've managed to cobble together a script that lists all guildhall and temple petitions in fortress mode. I've tested it on a few saves I had strewn about on my hard drive and it seems to work reliably.
You should generally use named enum values instead of magic numbers (1, 2, 11, etc). In this case, I had to look in df.entities.xml (https://github.com/DFHack/df-structures/blob/96a8df43d1c4508800c209701ce39ca972579e3b/df.entities.xml#L1451-L1460) to find the definition of the agreement_details_data_location type. The "type" field's type is abstract_building_type (https://github.com/DFHack/df-structures/blob/96a8df43d1c4508800c209701ce39ca972579e3b/df.world-site.xml#L37-L51), so in Lua, 2 = df.abstract_building_type.TEMPLE and 11 = df.abstract_building_type.GUILDHALL. gui/gm-editor will also show a human-friendly list if you can navigate to this structure in it.Code: [Select]function get_location_type(agr)
loctype = agr.details[0].data.Location.type
return loctype
end
function get_location_name(agr)
if get_location_type(agr) == 2 and get_location_tier(agr) == 1 then
return "temple"
...
elseif get_location_type(agr) == 11 and get_location_tier(agr) == 2 then
return "grand guildhall"
end
Code: [Select]function is_satisfied(agr)
satisfied = agr.anon_3.convicted_accepted
return satisfied
end
function is_denied(agr)
denied = agr.anon_3.petition_not_accepted
return denied
end
Code: [Select]for agr, _ in pairs(allagreements) do
if allagreements[agr].details[0].data.Location.site == playerfortid then
if get_location_type(allagreements[agr]) == 2 then
table.insert(templeagreementids, agr)
elseif get_location_type(allagreements[agr]) == 11 then
table.insert(guildhallagreementids, agr)
end
end
end
I report about some bugs in beta1. When I play testing fort, I spawn two harpies. They was asexual, so I try to change their orientation to "marry females", but multiple using of script has no effect - in DT harpies still was asexual, also not work current orientation showing by command. I try catsplosion to harpies without marring, but this cause crash. What go wrong? For elven visitors and spawned trolls catsplosion work normal, babies from it has no fathers.
female.pregnancy_caste = 1
The script for some reason assumes that a caste ID of 1 is valid for all creatures instead of checking for it. Harpies only have one caste with an ID of 0. On testing, changing the value from 1 to 0 before running this on a harpy allowed it to give birth without crashing the game.
Thanks! Now catsplosion works fine. Maybe, harpy orientation bug needs some RAW solving.I report about some bugs in beta1. When I play testing fort, I spawn two harpies. They was asexual, so I try to change their orientation to "marry females", but multiple using of script has no effect - in DT harpies still was asexual, also not work current orientation showing by command. I try catsplosion to harpies without marring, but this cause crash. What go wrong? For elven visitors and spawned trolls catsplosion work normal, babies from it has no fathers.
Providing the exact script commands used would be beneficial.
I suspect that the orientation problem is due to harpies being a female-only species. Units created using modtools/create-unit have their orientation set by the game, so if this isn't happening for female-only creatures it's likely a bug in the game itself, not DFHack.
I successfully set the "marry female" orientation of a spawned harpy via "set-orientation -female 2" with the target unit selected.
The catsplosion crash appears to be due to following on line 81:Code: [Select]female.pregnancy_caste = 1
The script for some reason assumes that a caste ID of 1 is valid for all creatures instead of checking for it. Harpies only have one caste with an ID of 0. On testing, changing the value from 1 to 0 before running this on a harpy allowed it to give birth without crashing the game.
on somewhat related note: do we know enough to add a petition? maybe that would sidestep all this hackery and just make df do the work?Looks like agreement is the relevant struct, and there are some unknown fields in it and in agreement_party, so it may or may not be possible to create working petitions.
Using rejuvenate successfully changes dwarves to 20 in fortress mode; however, retiring the fort and checking the dwarves in Legends Mode will state they were born in their original birth date. Reclaiming the fort will show they are all, however, still 20 as I left them.My guess is that this is because the script is only changing unit.birth_year, and legends mode is checking the corresponding historical figure instead of the unit (since units don't really exist in legends mode). Maybe it should be changing the histfig age too.
Rejuvenate on Adventurer Mode will appear to work on a never-before-retired character, giving the message that <name> is 20 and will live at least a hundred years, but retiring them will show they were born at some other completely different time, and attempting to use rejuvenate on a previously retired character, who thus already has an age set in legends, will just crash the game.Ideally it wouldn't be able to cause a crash, so I'm also curious. What happens if you run the script with "-dry-run" in this case? (I'm wondering if it's crashing when actually setting the age or at some point before.)
Why? I realize this function wasn't intended for Adventurer Mode, I am curious though.
Does the supposed birth date actually matter? If I remove the birth date change and make rejuvenate only use unit.old_year = current_year + 100, would that work fine? It'd take a lot more time investment for me to check this part myself.From comments in df-structures (https://github.com/DFHack/df-structures/blob/9bf18dfc6647e1ff51deddac9da042baa1655987/df.units.xml#L655), old_year isn't when the unit was born - it may be when they die, although it's unclear if this was ever confirmed.
local args = {...}
local count = 0
if args[1] == 'help' then
print(help)
return
elseif args[1] == 'all' or args[1] == '-all' then
for _, item in ipairs(df.global.world.items.all) do
if item.wear > 0 then --hint:df.item_actual
item:setWear(0)
count = count + 1
end
end
else
for i, arg in ipairs(args) do
local item_id = tonumber(arg)
if item_id then
local item = df.item.find(item_id)
if item then
item:setWear(0)
count = count + 1
else
dfhack.printerr('remove-wear: could not find item: ' .. item_id)
end
else
qerror('Invalid item ID: ' .. arg)
end
end
end
print('remove-wear: removed wear from '..count..' objects')
elseif args[1] == 'equipment' or args[1] == '-equipment' then
for _, item in ipairs(df.global.world.items.all) do
if item.wear > 0 then --hint:df.item_actual -- Here?????
item:setWear(0)
count = count + 1
end
end
if item._type == df.item_weaponst or item:isArmorNotClothing() then
if (auto threaditem = virtual_cast<item_threadst>(item)) {
// do stuff with threaditem
}
Heads up: the current dev build (https://dfhack.org/builds/) is likely to be one of the last before we release 0.47.04-r1 (the last couple things on the r1 to-do list (https://github.com/orgs/DFHack/projects/8) probably don't need to be addressed urgently). I don't know exactly when this build will be readybecause the one before it in the queue got stuck, but hopefully within the next12 hourshour or so. (Update: BenLubar fixed the stuck build)
Anyway, we haven't gotten a lot of reports of urgent issues in 0.47.04-beta1 - hopefully that means there aren't any left to fix, but if you've been encountering any issues with the beta build(s), please let us know!
Do cage traps work differently when placed with Advfort? I've tried to capture a dragon with a cage trap by setting one up in his lair and luring him into it, but the trap didn't work. Later tried the same with a random mountain goat and got the same result. I'm using the 0.47.04 version of DFHack.
Heads up: the current dev build (https://dfhack.org/builds/) is likely to be one of the last before we release 0.47.04-r1 (the last couple things on the r1 to-do list (https://github.com/orgs/DFHack/projects/8) probably don't need to be addressed urgently). I don't know exactly when this build will be readybecause the one before it in the queue got stuck, but hopefully within the next12 hourshour or so. (Update: BenLubar fixed the stuck build)
Anyway, we haven't gotten a lot of reports of urgent issues in 0.47.04-beta1 - hopefully that means there aren't any left to fix, but if you've been encountering any issues with the beta build(s), please let us know!
I see, thanks! Is there any way i could re-enable them?Do cage traps work differently when placed with Advfort? I've tried to capture a dragon with a cage trap by setting one up in his lair and luring him into it, but the trap didn't work. Later tried the same with a random mountain goat and got the same result. I'm using the 0.47.04 version of DFHack.
Cage traps are disabled in adventure mode; \data\init\d_init.txt contains a note by Toady regarding this matter. This was presumably done as getting the player character caught in a cage trap used to give rise to buggy behaviour in earlier versions; escaping cages or otherwise acting from within them hasn't been implemented yet.
It doesn't look like automaterial is working in 0.47.04-beta1. Enable shows the plugin is on, but the last selected material doesn't end up at the top of the list.I was unable to reproduce the issue just now (in 0.47.04-beta1-93-g4dce9f20) when building constructed walls. Were you building another type of construction?
Who are authors of SPATTER_ADD and steam engine? I have idea of stuff like this, but maybe even harder to code (or easier, I dunno).From GitHub (note that the most recent commits are at the top):
I see, thanks! Is there any way i could re-enable them?That's hardcoded functionality (i.e. not in the raws), so the only way I can think of (that doesn't involve reimplementing traps entirely in DFHack, which is hard) would be to override building vmethods. These (https://github.com/DFHack/df-structures/blob/951975f89920fa665675be42f91daa0183dab4e3/df.buildings.xml#L459-L465) might be relevant, judging by their names, but I have no idea what they do or if they're trap-related. So unless we're lucky, it's probably not feasible.
Shame. Still, gonna see if i can do somenthing with theese. Thanks.I see, thanks! Is there any way i could re-enable them?That's hardcoded functionality (i.e. not in the raws), so the only way I can think of (that doesn't involve reimplementing traps entirely in DFHack, which is hard) would be to override building vmethods. These (https://github.com/DFHack/df-structures/blob/951975f89920fa665675be42f91daa0183dab4e3/df.buildings.xml#L459-L465) might be relevant, judging by their names, but I have no idea what they do or if they're trap-related. So unless we're lucky, it's probably not feasible.
Using LNP r03 (which uses beta1) I've seen no problems with constructions. It appears to be working as normal for me (mostly floors and walls, but I believe I've seen it work with ramps as well).It doesn't look like automaterial is working in 0.47.04-beta1. Enable shows the plugin is on, but the last selected material doesn't end up at the top of the list.I was unable to reproduce the issue just now (in 0.47.04-beta1-93-g4dce9f20) when building constructed walls. Were you building another type of construction?
:
It doesn't look like automaterial is working in 0.47.04-beta1. Enable shows the plugin is on, but the last selected material doesn't end up at the top of the list.I was unable to reproduce the issue just now (in 0.47.04-beta1-93-g4dce9f20) when building constructed walls. Were you building another type of construction?
Bridges.I'm pretty sure it only supports constructions (things like bridges and roads that aren't in the b-C menu are separate building types). At least, the docs (https://docs.dfhack.org/en/stable/docs/Plugins.html?highlight=automaterial#automaterial) indicate that, and there are a couple checks (https://github.com/DFHack/dfhack/blob/02c118335f3f399ea41f9c0129cd13e840abc7c7/plugins/automaterial.cpp#L109-L124) in the plugin that only allow constructions. I feel like support for more buildings types has been suggested before, and we could look into it.
Can confirm it's working all right for walls.
Only Angavrilov can code some mod-usable plugins or not?It doesn't look like automaterial is working in 0.47.04-beta1. Enable shows the plugin is on, but the last selected material doesn't end up at the top of the list.I was unable to reproduce the issue just now (in 0.47.04-beta1-93-g4dce9f20) when building constructed walls. Were you building another type of construction?Who are authors of SPATTER_ADD and steam engine? I have idea of stuff like this, but maybe even harder to code (or easier, I dunno).From GitHub (note that the most recent commits are at the top):
https://github.com/DFHack/dfhack/commits/develop/plugins/add-spatter.cpp
https://github.com/DFHack/dfhack/commits/develop/plugins/steam-engine.cpp
Angavrilov (ag on the forums) isn't really active these days, though.I see, thanks! Is there any way i could re-enable them?That's hardcoded functionality (i.e. not in the raws), so the only way I can think of (that doesn't involve reimplementing traps entirely in DFHack, which is hard) would be to override building vmethods. These (https://github.com/DFHack/df-structures/blob/951975f89920fa665675be42f91daa0183dab4e3/df.buildings.xml#L459-L465) might be relevant, judging by their names, but I have no idea what they do or if they're trap-related. So unless we're lucky, it's probably not feasible.
Only Angavrilov can code some mod-usable plugins or not?It's open source, so anyone can submit changes. My point is that the original author isn't very active, so sending him feature requests probably isn't what you want. If you post your suggestions at https://github.com/dfhack/dfhack/issues or on the forums, someone might be willing to implement them or direct you towards resources you could use to implement them.
I need some fully new plugins. DONOR plugin about reactions that give as product, example, blood or tears of worker (and only workers who have this material, example tears, will do this reaction - for prevent bugs). Second plugin will read CREATURE:CASTE from item PET (maybe, VERMIN or even REMAINS) and transform it into live creature, for re-using bugged purring maggots.Only Angavrilov can code some mod-usable plugins or not?It's open source, so anyone can submit changes. My point is that the original author isn't very active, so sending him feature requests probably isn't what you want. If you post your suggestions at https://github.com/dfhack/dfhack/issues or on the forums, someone might be willing to implement them or direct you towards resources you could use to implement them.
<...>
I need some fully new plugins. DONOR plugin about reactions that give as product, example, blood or tears of worker (and only workers who have this material, example tears, will do this reaction - for prevent bugs). Second plugin will read CREATURE:CASTE from item PET (maybe, VERMIN or even REMAINS) and transform it into live creature, for re-using bugged purring maggots.
I don't understand differences between script and plugin, what of them may be started by reaction? Can you write this scripts for me, please?<...>
I need some fully new plugins. DONOR plugin about reactions that give as product, example, blood or tears of worker (and only workers who have this material, example tears, will do this reaction - for prevent bugs). Second plugin will read CREATURE:CASTE from item PET (maybe, VERMIN or even REMAINS) and transform it into live creature, for re-using bugged purring maggots.
IMHO this does not need plugins and can be done in script. Plugins usually have more work involved and are only used when you need high performance (e.g. doing work on most of tiles of map) or impossible to do in scripts.
This being said: first idea is possible in scripts (with second part impossible at all - AFAIK we cannot limit workers for a job), second one is also a simple script.
@Iä! RIACTOR!: A script is interpreted text (e.g. Lua), while a plugin is code that has to be compiled with the same compiler using exactly the same DFHack code base as the DFHack version you're using it with. Thus, a script is portable and works across versions as long as the particular pieces of information it uses hasn't changed, while a plugin has to be recompiled for every new version and has to be provided for each DFHack version (OS/bitness, etc.) separately. The advantage of the more cumbersome plugin is that it runs a lot faster than interpreted text, which can matter when heavy work is done or it is run frequently. A positive side effect of the plugins compiler dependence is that the compiler will point out where the code no longer works because the underlying DFHack structure has changed (i.e. it will fail to compile), while a script will only detect such changes when it happens to try to interpret such lines of the script.Can script work "automatic" like SPATTER_ADD, without starting by command for every playing session?
There are a few things that are available from code that isn't available to scripts, but very much can be done with scripts.
Can script work "automatic" like SPATTER_ADD, without starting by command for every playing session?Not in the same way as add-spatter, but it's possible for a script to be run once and register handlers that run when the game state changes. The easiest way to ensure that it gets run would be to add it to dfhack.init, and there are already several plugins enabled in dfhack.init too, so it would feel about the same to a user.
Could you make this scripts for me? Please!Can script work "automatic" like SPATTER_ADD, without starting by command for every playing session?Not in the same way as add-spatter, but it's possible for a script to be run once and register handlers that run when the game state changes. The easiest way to ensure that it gets run would be to add it to dfhack.init, and there are already several plugins enabled in dfhack.init too, so it would feel about the same to a user.
Could anybody tell me how (if it is possible) to teleport a bunch of stuff from the bottom of a waterfall onto dry ground? Preferably something that can be used with multiple items at once.
Designate them for dumping, move your cursor to where you want them, then use autodump.
Note that this teleports everything marked for dumping to the given location, so either make sure you don't have (too many) other things marked for dumping, select a target location that's somewhat suitable for all the things marked, or mark the items you actually wanted elsewhere for dumping again and autodump them to a new location (you may have to unforbid them first).Could anybody tell me how (if it is possible) to teleport a bunch of stuff from the bottom of a waterfall onto dry ground? Preferably something that can be used with multiple items at once.
Designate them for dumping, move your cursor to where you want them, then use autodump.
In case you're still interested, "updateAction" is the vmethod that handles traps being triggered, but the class involved (building_trapst) handles all of the different trap types, including levers, pressure plates, and track stops. You might be able to temporarily set gamemode=0 for the duration of the vmethod if it's the correct subtype, but that might also cause the trap to malfunction (e.g. from trying to invoke Fortress-specific behavior in Adventurer mode).Shame. Still, gonna see if i can do somenthing with theese. Thanks.I see, thanks! Is there any way i could re-enable them?That's hardcoded functionality (i.e. not in the raws), so the only way I can think of (that doesn't involve reimplementing traps entirely in DFHack, which is hard) would be to override building vmethods. These (https://github.com/DFHack/df-structures/blob/951975f89920fa665675be42f91daa0183dab4e3/df.buildings.xml#L459-L465) might be relevant, judging by their names, but I have no idea what they do or if they're trap-related. So unless we're lucky, it's probably not feasible.
Tho i'm not that good with modding DF so i doubt i'm gonna manage to get them working.
How make adding syndrome of regeneration and body parts regrowing for blizzard men from any contact with water? Water is hardcoded, so standard way of adding syndromes is not our way. Adding new material will not work too, I need regeneration from rain, rivers and snow.
-- Add a syndrome to an inbuilt material.
-- Author: Atomic Chicken
local utils = require 'utils'
local validArgs = utils.invert({
'material',
'syndrome',
})
local args = utils.processArgs({...}, validArgs)
if not args.material then
qerror('Material not specified!')
end
if not args.syndrome then
qerror('Syndrome not specified!')
end
addedSynMats = addedSynMats or {}
local matInfo = dfhack.matinfo.find(args.material)
if not matInfo then
qerror('Material not found: ' .. args.material)
end
if matInfo.mode ~= 'builtin' then
qerror('This script should only be used with inbuilt materials.')
end
local material = matInfo.material
local foundSyn = false
for _, syndrome in ipairs(df.global.world.raws.syndromes.all) do
if syndrome.syn_name == args.syndrome then
material.syndrome:insert('#', syndrome)
if syndrome.flags.SYN_INJECTED then
material.flags.ENTER_BLOOD = true -- reverses on reload
end
if not addedSynMats[args.material] then
addedSynMats[args.material] = true
end
foundSyn = true
break
end
end
if not foundSyn then
qerror('Syndrome not found: ' .. args.syndrome)
end
-- Clear added syndromes when unloading; prevents crashes when reloading
dfhack.onStateChange.InbuiltMatSynMonitor = function(event)
if event == SC_WORLD_UNLOADED then
for k,_ in pairs(addedSynMats) do
local material = dfhack.matinfo.find(k).material
material.syndrome:resize(0)
addedSynMats[k] = nil
end
end
end
inbuilt-material-syndrome -material WATER -syndrome YOUR_SYN_NAME
Other inbuilt materials (https://dwarffortresswiki.org/index.php/DF2014:Hardcoded_material), such as "VOMIT", may also be specified. The syndrome needs to have been defined elsewhere.
I believe you can use dfhack.units.setNickname(<unit>, <nickname>) inside a lua script to change a unit's nickname, if that's what you were looking for.
use df.global.world.units.active to get a list of all active units (units that are currently loaded) and df.global.world.units.all to get a list of all units recorded in the world.
The DF structures are found under "df.global", with most of the interesting things found under "df.global.world". These structures can be explored using e.g. gui/gm-editor, or by reading the XML files (in the df structures project on github) mapping them. Documenting DF's internal structures would be a massive undertaking: it's hard enough to try to figure out what things are and describe them in XML form, and a fair bit of that is tentative. The way I got started on getting some kind of understanding of how the structures hang together was to read scripts that do something in the vicinity of what I'm looking for, use gui/gm-editor to view the corresponding structures, trying to understand the scripts, and build from there.use df.global.world.units.active to get a list of all active units (units that are currently loaded) and df.global.world.units.all to get a list of all units recorded in the world.
ah, that's exactly the type of thing i'm looking for! where did you find out about these? they aren't referenced in the docs at all.
The DF structures are found under "df.global", with most of the interesting things found under "df.global.world". These structures can be explored using e.g. gui/gm-editor, or by reading the XML files (in the df structures project on github) mapping them.
While df.global.world.units.active includes all active units, it is not restricted to your citizens, but includes every active unit, be it buzzard, goblin invader, dog, visitor, underworld denizen, or citizen. Thus, you'd need to filter the units to locate the ones you actually want. Also, I think the dead units are in that list as well.
The DF structures are found under "df.global", with most of the interesting things found under "df.global.world". These structures can be explored using e.g. gui/gm-editor, or by reading the XML files (in the df structures project on github) mapping them.
..."world.units.active" doesn't seem to be defined anywhere in the df.global xml, though? am i looking in the wrong place? https://github.com/DFHack/df-structures/blob/a7dae6083d295821afe2ba91073d9b7547395b45/df.globals.xml
local in_file = io.open (dfhack.getDFPath ().."\\data\\init\\exported_map.txt", "r")
i think i have something with this script - most of the parts i could test worked, but i'm getting an error when i try the full thing, so i can't be sure...
where should i be putting names.txt so my script can find it? /hack doesn't seem to work, and neither does hack/scripts.
@Su:
Line from one of my scripts:Code: [Select]local in_file = io.open (dfhack.getDFPath ().."\\data\\init\\exported_map.txt", "r")
I think that provides you with the basic info you need.
Also, you probably want unit.name.nickname ~= "", as the == is never met due explicitly avoiding putting "" into names table.To clarify, this only applies to the first occurrence - the second is intentionally checking for units without nicknames.
createitem [Item token] [Material] [Quantity (optional)]
Is it possible to create fruits with the createitem thing?Here (https://github.com/DFHack/dfhack/blob/d0c030c3da62b88d97a05c417fce3d86e80fa7de/library/modules/Items.cpp#L108-L121) is the list of item subtypes that createitem supports internally. PLANT and PLANT_GROWTH aren't listed, so you can't specify (item) subtypes for them.
The wiki says:Code: [Select]createitem [Item token] [Material] [Quantity (optional)]
My best guess was:
createitem PLANT_GROWTH:FRUIT PLANT_MAT:BILBERRY:FRUIT 5
It doesn't seem to recognize "PLANT_GROWTH:FRUIT" as an Item token, probably because it has a subtype. If you just put in PLANT_GROWTH as the item token it does create "a stack of 5", but it doesn't appear to be a stack of 5 anythings in particular.
[lua]# ~df.item_type[item:getType()]
PLANT_GROWTH
[lua]# ~item:getSubtype()
2
[lua]# ~dfhack.matinfo.decode(item.mat_type, item.mat_index)
<material 423:86 PLANT:BILBERRY:FRUIT>
plant = <plant_raw: 0x7fffcd96f330>
mode = plant
type = 423
subtype = 4
index = 86
material = <material: 0x7fffcd973230>
[lua]# ~df.item_type[item:getType()]
PLANT_GROWTH
[lua]# ~item:getSubtype()
-1
[lua]# ~dfhack.matinfo.decode(item.mat_type, item.mat_index)
<material 423:86 PLANT:BILBERRY:FRUIT>
plant = <plant_raw: 0x7fffcd96f330>
mode = plant
type = 423
subtype = 4
index = 86
material = <material: 0x7fffcd973230>
It still has wrong icon because it still has wrong GROWTH_PRINT index (-1 with createitem). Setting it to 0 gives correct image.Thanks, I opened a report here (https://github.com/DFHack/df-structures/issues/403). For reference, pressing the "y" key in a code view on GitHub will give you a permalink - linking to a specific line on the master branch could end up pointing to the wrong place in the future as changes are made.Spoiler: "From two pages back" (click to show/hide)
[17/123] Building CXX object plugins/l.../labormanager.dir/joblabormapper.cpp.o
FAILED: plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o
ccache /usr/bin/c++ -DHAVE_CUCHAR -DLINUX_BUILD -DLUA_BUILD_AS_DLL -DPROTOBUF_USE_DLLS -D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_CXX11_ABI=0 -D_LINUX -Dlabormanager_EXPORTS -I../depends/protobuf -I../depends/lua/include -I../depends/md5 -I../depends/tinyxml -I../depends/tthread -I../depends/clsocket/src -I../library/include -I../library/proto -I../plugins/proto -I../library/depends/xgetopt -fvisibility=hidden -mtune=generic -m32 -march=i686 -Wl,-z,defs -O3 -DNDEBUG -fPIC -std=c++11 -MD -MT plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o -MF plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o.d -o plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o -c ../plugins/labormanager/joblabormapper.cpp
../plugins/labormanager/joblabormapper.cpp: In constructor ‘JobLaborMapper::JobLaborMapper()’:
../plugins/labormanager/joblabormapper.cpp:900:38: error: ‘unk_fake_no_job’ is not a member of ‘df::enums::job_type::job_type’
job_to_labor_table[df::job_type::unk_fake_no_job] = jlf_no_labor; // added for 47.04 - see #1561
^~~~~~~~~~~~~~~
../plugins/labormanager/joblabormapper.cpp:901:38: error: ‘InterrogateSubject’ is not a member of ‘df::enums::job_type::job_type’
job_to_labor_table[df::job_type::InterrogateSubject] = jlf_no_labor; // added for 47.04 - see #1561
^~~~~~~~~~~~~~~~~~
../plugins/labormanager/joblabormapper.cpp:902:38: error: ‘unk_fake_no_activity’ is not a member of ‘df::enums::job_type::job_type’
job_to_labor_table[df::job_type::unk_fake_no_activity] = jlf_no_labor; // added for 47.04 - see #1561
^~~~~~~~~~~~~~~~~~~~
[23/123] Building CXX object library/CMakeFiles/dfhack.dir/LuaApi.cpp.o
ninja: build stopped: subcommand failed.
CMake Warning at depends/jsoncpp-sub/src/lib_json/CMakeLists.txt:36 (MESSAGE):
Locale functionality is not supported
Does anyone know if the thing that marks eggs as fertile or infertile when you examine the nest box is part of DFHack, or just a part of vanilla DF? I'm asking because I'd like to know the difference between "Infertile" and "N. Fertile". I've searched both the DF Wiki, and the DFHack documentation on readthedocs.org, and neither even mentions this feature at all.
Does anyone know if the thing that marks eggs as fertile or infertile when you examine the nest box is part of DFHack, or just a part of vanilla DF? I'm asking because I'd like to know the difference between "Infertile" and "N. Fertile". I've searched both the DF Wiki, and the DFHack documentation on readthedocs.org, and neither even mentions this feature at all.
https://github.com/DFHack/dfhack/blob/develop/plugins/tweak/tweaks/eggs-fertile.h
I don't use nestboxes, but if I'm understanding the code correctly, the nestbox uses the term "Eggs infertile" and the eggs themselves use the term "N.Fert".
Yeah, I just looked at the code you linked to and it appears that the only difference is if you use 't' or 'q' to look at the nest box. It'd be less confusing if someone would change it to say the same thing (e.g. "Infertile") in both cases.The reason I did that is simple: "infertile" doesn't fit in the 't' menu (at least not for some eggs). The colors also serve as a bit of an indicator ("fertile" and "fert" are both green, "infertile" and "N. fert" are both red) but I understand that color alone isn't always enough to distinguish between them.
Edit: I just used git pull to download the latest changes on the stable branch and tried building again. It gave me this error message:You probably need to run "git submodule update", or "git pull" from within the library/xml folder. See https://docs.dfhack.org/en/stable/docs/Compile.html for more details, specifically the note in this section (https://docs.dfhack.org/en/stable/docs/Compile.html#how-to-get-the-code).Code: [Select][17/123] Building CXX object plugins/l.../labormanager.dir/joblabormapper.cpp.o
FAILED: plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o
ccache /usr/bin/c++ -DHAVE_CUCHAR -DLINUX_BUILD -DLUA_BUILD_AS_DLL -DPROTOBUF_USE_DLLS -D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_CXX11_ABI=0 -D_LINUX -Dlabormanager_EXPORTS -I../depends/protobuf -I../depends/lua/include -I../depends/md5 -I../depends/tinyxml -I../depends/tthread -I../depends/clsocket/src -I../library/include -I../library/proto -I../plugins/proto -I../library/depends/xgetopt -fvisibility=hidden -mtune=generic -m32 -march=i686 -Wl,-z,defs -O3 -DNDEBUG -fPIC -std=c++11 -MD -MT plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o -MF plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o.d -o plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o -c ../plugins/labormanager/joblabormapper.cpp
../plugins/labormanager/joblabormapper.cpp: In constructor ‘JobLaborMapper::JobLaborMapper()’:
../plugins/labormanager/joblabormapper.cpp:900:38: error: ‘unk_fake_no_job’ is not a member of ‘df::enums::job_type::job_type’
job_to_labor_table[df::job_type::unk_fake_no_job] = jlf_no_labor; // added for 47.04 - see #1561
^~~~~~~~~~~~~~~
../plugins/labormanager/joblabormapper.cpp:901:38: error: ‘InterrogateSubject’ is not a member of ‘df::enums::job_type::job_type’
job_to_labor_table[df::job_type::InterrogateSubject] = jlf_no_labor; // added for 47.04 - see #1561
^~~~~~~~~~~~~~~~~~
../plugins/labormanager/joblabormapper.cpp:902:38: error: ‘unk_fake_no_activity’ is not a member of ‘df::enums::job_type::job_type’
job_to_labor_table[df::job_type::unk_fake_no_activity] = jlf_no_labor; // added for 47.04 - see #1561
^~~~~~~~~~~~~~~~~~~~
[23/123] Building CXX object library/CMakeFiles/dfhack.dir/LuaApi.cpp.o
ninja: build stopped: subcommand failed.
Ah, here it is:Looking at the file in question, that should just be a warning, and the build should be able to continue despite that. That said, what compiler are you using? We only support MSVC 2015 on Windows (which is a hard requirement) and GCC 4.8+ on other platforms, and I don't recall seeing this particular warning before, but maybe it's something system-specific, not compiler-specific.Code: [Select]CMake Warning at depends/jsoncpp-sub/src/lib_json/CMakeLists.txt:36 (MESSAGE):
Locale functionality is not supported
Yeah, that makes sense. Thanks for the link to the docs. Interesting that the search engine in the page couldn't pull that up...Yeah, I just looked at the code you linked to and it appears that the only difference is if you use 't' or 'q' to look at the nest box. It'd be less confusing if someone would change it to say the same thing (e.g. "Infertile") in both cases.The reason I did that is simple: "infertile" doesn't fit in the 't' menu (at least not for some eggs). The colors also serve as a bit of an indicator ("fertile" and "fert" are both green, "infertile" and "N. fert" are both red) but I understand that color alone isn't always enough to distinguish between them.
The tweak docs are here (https://docs.dfhack.org/en/stable/docs/Plugins.html?highlight=eggs-fertile#tweak), by the way.
Edit: I just used git pull to download the latest changes on the stable branch and tried building again. It gave me this error message:You probably need to run "git submodule update", or "git pull" from within the library/xml folder. See https://docs.dfhack.org/en/stable/docs/Compile.html for more details, specifically the note in this section (https://docs.dfhack.org/en/stable/docs/Compile.html#how-to-get-the-code).Code: [Select][17/123] Building CXX object plugins/l.../labormanager.dir/joblabormapper.cpp.o
FAILED: plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o
ccache /usr/bin/c++ -DHAVE_CUCHAR -DLINUX_BUILD -DLUA_BUILD_AS_DLL -DPROTOBUF_USE_DLLS -D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_CXX11_ABI=0 -D_LINUX -Dlabormanager_EXPORTS -I../depends/protobuf -I../depends/lua/include -I../depends/md5 -I../depends/tinyxml -I../depends/tthread -I../depends/clsocket/src -I../library/include -I../library/proto -I../plugins/proto -I../library/depends/xgetopt -fvisibility=hidden -mtune=generic -m32 -march=i686 -Wl,-z,defs -O3 -DNDEBUG -fPIC -std=c++11 -MD -MT plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o -MF plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o.d -o plugins/labormanager/CMakeFiles/labormanager.dir/joblabormapper.cpp.o -c ../plugins/labormanager/joblabormapper.cpp
../plugins/labormanager/joblabormapper.cpp: In constructor ‘JobLaborMapper::JobLaborMapper()’:
../plugins/labormanager/joblabormapper.cpp:900:38: error: ‘unk_fake_no_job’ is not a member of ‘df::enums::job_type::job_type’
job_to_labor_table[df::job_type::unk_fake_no_job] = jlf_no_labor; // added for 47.04 - see #1561
^~~~~~~~~~~~~~~
../plugins/labormanager/joblabormapper.cpp:901:38: error: ‘InterrogateSubject’ is not a member of ‘df::enums::job_type::job_type’
job_to_labor_table[df::job_type::InterrogateSubject] = jlf_no_labor; // added for 47.04 - see #1561
^~~~~~~~~~~~~~~~~~
../plugins/labormanager/joblabormapper.cpp:902:38: error: ‘unk_fake_no_activity’ is not a member of ‘df::enums::job_type::job_type’
job_to_labor_table[df::job_type::unk_fake_no_activity] = jlf_no_labor; // added for 47.04 - see #1561
^~~~~~~~~~~~~~~~~~~~
[23/123] Building CXX object library/CMakeFiles/dfhack.dir/LuaApi.cpp.o
ninja: build stopped: subcommand failed.
(Also, which branch is "the stable branch"? We recently switched the default branch to "develop", because that's what most people who build from source typically want, but the most recent stable release, currently 0.47.04-r1, is on the "master" branch.)Yeah, I'm using the default branch as that is what the compile doc said was the stable branch. There is a possibility that I did the original cloning before you made the change. How long ago did you change the default branch to the development branch?
QuoteAh, here it is:Looking at the file in question, that should just be a warning, and the build should be able to continue despite that. That said, what compiler are you using? We only support MSVC 2015 on Windows (which is a hard requirement) and GCC 4.8+ on other platforms, and I don't recall seeing this particular warning before, but maybe it's something system-specific, not compiler-specific.Code: [Select]CMake Warning at depends/jsoncpp-sub/src/lib_json/CMakeLists.txt:36 (MESSAGE):
Locale functionality is not supported
Submodule path 'depends/clsocket': checked out '6a9153d053a250be34996b3fd86ac1166c3e17cb'
Submodule path 'library/xml': checked out '4053321b202a29f667d64d824ba8339ec1b1df4f'
error: The following untracked working tree files would be overwritten by checkout:
agui/include/Agui/ActionEvent.hpp
agui/include/Agui/ActionListener.hpp
agui/include/Agui/Agui.hpp
agui/include/Agui/Backends/Allegro5/Allegro5.hpp
agui/include/Agui/Backends/Allegro5/Allegro5CursorProvider.hpp
agui/include/Agui/Backends/Allegro5/Allegro5Font.hpp
agui/include/Agui/Backends/Allegro5/Allegro5FontLoader.hpp
agui/include/Agui/Backends/Allegro5/Allegro5Graphics.hpp
agui/include/Agui/Backends/Allegro5/Allegro5Image.hpp
agui/include/Agui/Backends/Allegro5/Allegro5ImageLoader.hpp
agui/include/Agui/Backends/Allegro5/Allegro5Input.hpp
agui/include/Agui/BaseTypes.hpp
agui/include/Agui/BlinkingEvent.hpp
agui/include/Agui/BorderLayout.hpp
agui/include/Agui/Clipboard/Clipboard.hpp
agui/include/Agui/Clipboard/OSXClipboard.hpp
agui/include/Agui/Clipboard/WinClipboard.hpp
agui/include/Agui/Color.hpp
agui/include/Agui/CursorProvider.hpp
agui/include/Agui/Dimension.hpp
agui/include/Agui/EmptyWidget.hpp
agui/include/Agui/Enumerations.hpp
agui/include/Agui/EventArgs.hpp
agui/include/Agui/FlowLayout.hpp
agui/include/Agui/FocusListener.hpp
agui/include/Agui/FocusManager.hpp
agui/include/Agui/Font.hpp
agui/include/Agui/FontLoader.hpp
agui/include/Agui/Graphics.hpp
agui/include/Agui/GridLayout.hpp
agui/include/Agui/Gui.hpp
agui/include/Agui/Image.hpp
agui/include/Agui/ImageLoader.hpp
agui/include/Agui/Input.hpp
agui/include/Agui/KeyboardListener.hpp
agui/include/Agui/Layout.hpp
agui/include/Agui/MouseListener.hpp
agui/include/Agui/Platform.hpp
agui/include/Agui/Point.hpp
agui/include/Agui/Rectangle.hpp
agui/include/Agui/ResizableBorderLayout.hpp
agui/include/Agui/ResizableText.hpp
agui/include/Agui/SelectionListener.hpp
agui/include/Agui/TableLayout.hpp
agui/include/Agui/TopContainer.hpp
agui/include/Agui/Transform.hpp
agui/include/Agui/UTF8.hpp
agui/include/Agui/Widget.hpp
agui/include/Agui/WidgetListener.hpp
agui/include/Agui/Widgets/Button/Button.hpp
agui/include/Agui/Widgets/Button/ButtonGroup.hpp
agui/include/Agui/Widgets/Button/ButtonListener.hpp
agui/include/Agui/Widgets/CheckBox/CheckBox.hpp
agui/include/Agui/Widgets/CheckBox/CheckBoxListener.hpp
agui/include/Agui/Widgets/DropDown/DropDown.hpp
agui/include/Agui/Widgets/DropDown/DropDownListener.hpp
agui/include/Agui/Widgets/Frame/Frame.hpp
agui/include/Agui/Widgets/Frame/FrameListener.hpp
agui/include/Agui/Widgets/ImageWidget/ImageWidget.hpp
agui/include/Agui/Widgets/Label/Label.hpp
agui/include/Agui/Widgets/Label/LabelListener.hpp
agui/include/Agui/Widgets/ListBox/ListBox.hpp
agui/include/Agui/Widgets/ListBox/ListBoxListener.hpp
agui/include/Agui/Widgets/PopUp/PopUpMenu.hpp
agui/include/Agui/Widgets/PopUp/PopUpMenuItem.hpp
agui/include/Agui/Widgets/RadioButton/RadioButton.hpp
agui/include/Agui/Widgets/RadioButton/RadioButtonGroup.hpp
agui/include/Agui/Widgets/RadioButton/RadioButtonListener.hpp
agui/include/Agui/Widgets/ScrollBar/HScrollBar.hpp
agui/include/Agui/Widgets/ScrollBar/HScrollBarListener.hpp
agui/include/Agui/Widgets/ScrollBar/VScrollBar.hpp
agui/include/Agui/Widgets/ScrollBar/VScrollBarListener.hpp
agui/include/Agui/Widgets/ScrollPane/ScrollPane.hpp
agui/include/Agui/Widgets/Slider/Slider.hpp
agui/include/Agui/Widgets/Slider/SliderListener.hpp
agui/include/Agui/Widgets/Tab/Tab.hpp
agui/include/Agui/Widgets/Tab/TabbedPane.hpp
agui/include/Agui/Widgets/Tab/TabbedPaneListener.hpp
agui/include/Agui/Widgets/TextBox/ExtendedTextBox.hpp
agui/include/Agui/Widgets/TextBox/TextBox.hpp
agui/include/Agui/Widgets/TextBox/TextBoxListener.hpp
agui/include/Agui/Widgets/TextField/TextField.hpp
agui/include/Agui/Widgets/TextField/TextFieldListener.hpp
agui/include/Agui/Widgets/ToolTip/ToolTip.hpp
Please move or remove them before you switch branches.
Aborting
Submodule path 'plugins/stonesense': checked out '4fdb2be54365442b8abea86f21746795f83fbdc2'
Submodule path 'scripts': checked out '790e2efbe9d987d8678375ee634991fe4c324f1d'
Unable to checkout 'fbbf9e46458e41707c27f2a4438452a579490db1' in submodule path 'plugins/isoworld'
Yeah, that makes sense. Thanks for the link to the docs. Interesting that the search engine in the page couldn't pull that up...I kind of cheated and searched for "eggs-fertile" because I knew what it was, but even then, it didn't show up in the first couple results. Searching for just "fertile" pulls up the plugins page as expected, but result #5 out of 5, and quotes the nestboxes plugin. The Sphinx search engine isn't always the best (but also neither are our docs).
Yeah, I'm using the default branch as that is what the compile doc said was the stable branch. There is a possibility that I did the original cloning before you made the change. How long ago did you change the default branch to the development branch?Maybe in March? I don't really know. That particular part of the docs was last updated in 2015, and I realized as soon as I posted that it was probably the source of the confusion. https://docs.dfhack.org/en/latest/docs/Compile.html#how-to-get-the-code should be up-to-date now (see my comment about the URL earlier in this post) - does that help? I'm happy to tweak it further.
Well I ran git submodule update from the dfhack directory. It compiled just fine. I did have to edit the DFHack script to include a reference to libz.so.1, though I had to do that when I originally installed dfhack too (also had to make a similar change to ./df in order to get that to work).The DFHack script you're referring to is the "dfhack" launcher in the DF folder, right? You should also be able to set the PRELOAD_LIB environment variable to do that (instead of editing the launcher), and you can set that in ~/.dfhackrc or /path/to/df/.dfhackrc. (Yet another mostly-undocumented feature that we should address...)
Still says I'm using the beta1, though for some reason.
Still says I'm using the beta1, though for some reason.Not sure on this. Things you could try: running "git describe --tags" in the source folder, or running "install-info" from the DFHack console.
Edit2:I'm assuming you set BUILD_ISOWORLD to ON? I've probably never run into this because I've never built Isoworld locally before. Does it still work? (I honestly don't know; it defaults to OFF in plugins/CMakeLists.txt, and I don't think we distribute it currently.)
Just tried running git checkout master and then git submodule update from the dfhack directory.
git submodule update gave me this error message:Code: [Select]Submodule path 'depends/clsocket': checked out '6a9153d053a250be34996b3fd86ac1166c3e17cb'
Any Ideas. IIRC, I've had problems with Agui before. It's not available for debian based systems when using the package manager, and I'm not sure how to install libraries by hand. There used to be some instructions on how to get Agui, Isoworld, and Stonesense working, but they stopped working in newer versions. Usually, I just set ccmake to not compile Isoworld and Stonesense.
Submodule path 'library/xml': checked out '4053321b202a29f667d64d824ba8339ec1b1df4f'
error: The following untracked working tree files would be overwritten by checkout:
agui/include/Agui/ActionEvent.hpp
agui/include/Agui/ActionListener.hpp
agui/include/Agui/Agui.hpp
-snip-
Apparently parts of our docs are very outdated. I've started to update some of them, but it will be a while before they're all done. I wish I had gotten to the Compile doc sooner; I can imagine parts of it have confused people recently. Some updates are at https://docs.dfhack.org/en/latest/docs/Compile.html if anyone would like to provide feedback (note that "latest" on ReadTheDocs corresponds to "develop" on GitHub, and "stable" = "master"). I may try to backport some of these changes to the master/stable version before the next release, if a lot of people are looking there for instructions too.Yeah, I just took a look at that doc and it still says that you have to run git checkout develop in order to get the development branch (implying that the default branch is the stable branch).
I cloned it after that. I'm probably on the development branch then...QuoteYeah, I'm using the default branch as that is what the compile doc said was the stable branch. There is a possibility that I did the original cloning before you made the change. How long ago did you change the default branch to the development branch?Maybe in March? I don't really know. That particular part of the docs was last updated in 2015, and I realized as soon as I posted that it was probably the source of the confusion. https://docs.dfhack.org/en/latest/docs/Compile.html#how-to-get-the-code should be up-to-date now (see my comment about the URL earlier in this post) - does that help? I'm happy to tweak it further.
How would I go about doing that? just make a file called ".dfhackrc" and include a line that says "PRELOAD_LIB=libz.so.1"?QuoteWell I ran git submodule update from the dfhack directory. It compiled just fine. I did have to edit the DFHack script to include a reference to libz.so.1, though I had to do that when I originally installed dfhack too (also had to make a similar change to ./df in order to get that to work).The DFHack script you're referring to is the "dfhack" launcher in the DF folder, right? You should also be able to set the PRELOAD_LIB environment variable to do that (instead of editing the launcher), and you can set that in ~/.dfhackrc or /path/to/df/.dfhackrc. (Yet another mostly-undocumented feature that we should address...)
Still says I'm using the beta1, though for some reason.
Still says I'm using the beta1, though for some reason.Not sure on this. Things you could try: running "git describe --tags" in the source folder, or running "install-info" from the DFHack console.
DFHack 0.47.04-r1 on linux/x86
Version information:
DF version: v0.47.04 linux32
DFHack version: 0.47.04-r1
DFHack release: r1
DFHack build ID:
Compiled DF version: 0.47.04
Git description: 0.47.04-beta1-122-g0ffafbde
Git commit: 0ffafbde298cc84003ebec0de75013d6baa547e9
Git XML commit: efc5c2610bd0345e9102279be0a9f154e5862050
Git XML expected commit: efc5c2610bd0345e9102279be0a9f154e5862050
Git XML match: true
Is release: false
Is prerelease: false
Is world loaded: false
Is map loaded: false
Output of "devel/save-version":
devel/save-version: no world loaded
Output of "plug":
Name State Cmds Enabled
3dveins loaded 1 n/a
RemoteFortressReader loaded 2 enabled
add-spatter loaded 0 disabled
autochop loaded 1 enabled
autoclothing loaded 1 disabled
autodump loaded 3 enabled
autofarm loaded 1 disabled
autogems loaded 1 enabled
autohauler loaded 1 disabled
autolabor loaded 1 disabled
automaterial loaded 0 enabled
automelt loaded 1 enabled
autotrade loaded 0 enabled
blueprint loaded 1 n/a
building-hacks loaded 0 n/a
buildingplan loaded 1 enabled
burrows loaded 1 disabled
changeitem loaded 1 n/a
changelayer loaded 1 n/a
changevein loaded 1 n/a
cleanconst loaded 1 n/a
cleaners loaded 2 n/a
cleanowned loaded 1 n/a
command-prompt loaded 1 n/a
confirm loaded 1 enabled
createitem loaded 1 n/a
cursecheck loaded 1 n/a
cxxrandom loaded 0 n/a
debug loaded 1 n/a
deramp loaded 1 n/a
dig loaded 7 n/a
digFlood loaded 1 disabled
diggingInvaders loaded 1 disabled
dwarfmonitor loaded 1 enabled
dwarfvet loaded 1 disabled
embark-assistant loaded 1 enabled
embark-tools loaded 1 enabled
eventful loaded 0 n/a
fastdwarf loaded 1 disabled
filltraffic loaded 4 n/a
fix-armory loaded 1 disabled
fix-unit-occupancy loaded 1 disabled
fixveins loaded 1 n/a
flows loaded 1 n/a
follow loaded 1 disabled
forceequip loaded 1 n/a
fortplan loaded 1 disabled
generated-creature-renamer loaded 2 disabled
getplants loaded 1 n/a
hotkeys loaded 1 n/a
infiniteSky loaded 1 disabled
isoworldremote loaded 0 n/a
jobutils loaded 3 n/a
labormanager loaded 1 disabled
lair loaded 1 n/a
liquids loaded 2 n/a
luasocket loaded 0 n/a
manipulator loaded 0 enabled
map-render loaded 0 n/a
misery loaded 1 disabled
mode loaded 1 n/a
mousequery loaded 1 enabled
nestboxes loaded 1 disabled
orders loaded 1 n/a
pathable loaded 0 n/a
petcapRemover loaded 1 disabled
plants loaded 1 n/a
power-meter loaded 0 disabled
probe loaded 3 n/a
prospector loaded 1 n/a
regrass loaded 1 n/a
rename loaded 1 disabled
rendermax loaded 1 n/a
resume loaded 1 enabled
reveal loaded 6 disabled
ruby loaded 2 enabled
search loaded 0 enabled
seedwatch loaded 1 disabled
showmood loaded 1 n/a
siege-engine loaded 0 disabled
sort loaded 2 n/a
steam-engine loaded 0 disabled
stockflow loaded 1 disabled
stockpiles loaded 3 enabled
stocks loaded 1 enabled
strangemood loaded 1 n/a
tailor loaded 1 disabled
tiletypes loaded 4 n/a
title-folder loaded 0 disabled
title-version loaded 0 enabled
trackstop loaded 0 enabled
tubefill loaded 1 n/a
tweak loaded 1 enabled
workNow loaded 1 n/a
workflow loaded 2 disabled
zone loaded 3 enabled
Tweak log entries:
loading plugin tweak
loaded plugin tweak; DFHack build 0.47.04-beta1-122-g0ffafbde
Invoking: tweak stable-cursor
Enabled tweak stable-cursor (stable_cursor_hook::feed)
Invoking: tweak advmode-contained
Enabled tweak advmode-contained (advmode_contained_hook::feed)
Invoking: tweak fast-trade
Enabled tweak fast-trade (fast_trade_assign_hook::feed)
Enabled tweak fast-trade (fast_trade_select_hook::feed)
Invoking: tweak military-stable-assign
Enabled tweak military-stable-assign (military_assign_hook::feed)
Invoking: tweak military-color-assigned
Enabled tweak military-color-assigned (military_assign_hook::render)
Invoking: tweak craft-age-wear
Enabled tweak craft-age-wear (craft_age_wear_hook::ageItem)
Invoking: tweak farm-plot-select
Enabled tweak farm-plot-select (farm_select_hook::feed)
Enabled tweak farm-plot-select (farm_select_hook::render)
Invoking: tweak import-priority-category
Enabled tweak import-priority-category (takerequest_hook::feed)
Enabled tweak import-priority-category (takerequest_hook::render)
Invoking: tweak condition-material
Enabled tweak condition-material (condition_material_hook::feed)
Invoking: tweak hotkey-clear
Enabled tweak hotkey-clear (hotkey_clear_hook::feed)
Enabled tweak hotkey-clear (hotkey_clear_hook::render)
Invoking: tweak embark-profile-name
Enabled tweak embark-profile-name (embark_profile_name_hook::feed)
Invoking: tweak block-labors # Prevents labors that can't be used from being toggled
Enabled tweak block-labors (block_labors_hook::feed)
Enabled tweak block-labors (block_labors_hook::render)
Invoking: tweak burrow-name-cancel
Enabled tweak burrow-name-cancel (burrow_name_cancel_hook::feed)
Invoking: tweak cage-butcher
Enabled tweak cage-butcher (cage_butcher_hook::feed)
Enabled tweak cage-butcher (cage_butcher_hook::render)
Invoking: tweak civ-view-agreement
Enabled tweak civ-view-agreement (civ_agreement_view_hook::render)
Invoking: tweak eggs-fertile
Enabled tweak eggs-fertile (egg_fertile_hook::render)
Invoking: tweak fps-min
Enabled tweak fps-min (fps_min_hook)
Invoking: tweak hide-priority
Enabled tweak hide-priority (hide_priority_hook::feed)
Enabled tweak hide-priority (hide_priority_hook::render)
Invoking: tweak kitchen-prefs-all
Enabled tweak kitchen-prefs-all (kitchen_prefs_all_hook::feed)
Enabled tweak kitchen-prefs-all (kitchen_prefs_all_hook::render)
Invoking: tweak kitchen-prefs-empty
Enabled tweak kitchen-prefs-empty (kitchen_prefs_empty_hook::render)
Invoking: tweak max-wheelbarrow
Enabled tweak max-wheelbarrow (max_wheelbarrow_hook::render)
Enabled tweak max-wheelbarrow (max_wheelbarrow_hook::feed)
Invoking: tweak shift-8-scroll
Enabled tweak shift-8-scroll (shift_8_scroll_hook::feed)
Invoking: tweak stone-status-all
Enabled tweak stone-status-all (stone_status_all_hook::feed)
Enabled tweak stone-status-all (stone_status_all_hook::render)
Invoking: tweak title-start-rename
Enabled tweak title-start-rename (title_start_rename_hook::feed)
Enabled tweak title-start-rename (title_start_rename_hook::render)
Invoking: tweak tradereq-pet-gender
Enabled tweak tradereq-pet-gender (pet_gender_hook::render)
I have BUILD_ISOWORLD set to OFF.QuoteEdit2:I'm assuming you set BUILD_ISOWORLD to ON? I've probably never run into this because I've never built Isoworld locally before. Does it still work? (I honestly don't know; it defaults to OFF in plugins/CMakeLists.txt, and I don't think we distribute it currently.)
Just tried running git checkout master and then git submodule update from the dfhack directory.
git submodule update gave me this error message:Code: [Select]Submodule path 'depends/clsocket': checked out '6a9153d053a250be34996b3fd86ac1166c3e17cb'
Any Ideas. IIRC, I've had problems with Agui before. It's not available for debian based systems when using the package manager, and I'm not sure how to install libraries by hand. There used to be some instructions on how to get Agui, Isoworld, and Stonesense working, but they stopped working in newer versions. Usually, I just set ccmake to not compile Isoworld and Stonesense.
Submodule path 'library/xml': checked out '4053321b202a29f667d64d824ba8339ec1b1df4f'
error: The following untracked working tree files would be overwritten by checkout:
agui/include/Agui/ActionEvent.hpp
agui/include/Agui/ActionListener.hpp
agui/include/Agui/Agui.hpp
-snip-
Did you do anything special to get a copy of isoworld besides cloning DFHack? I'm mostly confused because at least the first one, agui/include/Agui/ActionEvent.hpp, exists in plugins/isoworld on my end, and Git seems to know about them.Actually, it looks like these files were only added to the repo in January 2020 (they were probably ignored before then) - is your clone earlier than that? Those files are pretty easy to replace, assuming you haven't changed them at all, so you could just try adding "--force" to the "git submodule update" command and see if that works.
Submodule path 'depends/clsocket': checked out '6a9153d053a250be34996b3fd86ac1166c3e17cb'
Submodule path 'depends/jsoncpp-sub': checked out 'ddabf50f72cf369bf652a95c4d9fe31a1865a781'
Submodule path 'library/xml': checked out '4053321b202a29f667d64d824ba8339ec1b1df4f'
warning: unable to rmdir 'agui': Directory not empty
warning: unable to rmdir 'allegro': Directory not empty
Submodule path 'plugins/isoworld': checked out 'fbbf9e46458e41707c27f2a4438452a579490db1'
Submodule path 'plugins/stonesense': checked out '4fdb2be54365442b8abea86f21746795f83fbdc2'
Submodule path 'scripts': checked out '790e2efbe9d987d8678375ee634991fe4c324f1d'
CMake Warning (dev) at CMakeLists.txt:376 (find_package):
Policy CMP0074 is not set: find_package uses <PackageName>_ROOT variables.
Run "cmake --help-policy CMP0074" for policy details. Use the cmake_policy
command to set the policy and suppress this warning.
CMake variable ZLIB_ROOT is set to:
/usr/lib/i386-linux-gnu
For compatibility, CMake is ignoring the variable.
This warning is for project developers. Use -Wno-dev to suppress it.
CMake Warning at depends/jsoncpp-sub/src/lib_json/CMakeLists.txt:36 (MESSAGE):
Locale functionality is not supported
Loading bindings from data/init/interface.txt
New window size: 800x300
Font size: 10x12
Resizing grid to 80x25
Resizing font to 10x12
Resetting textures
./dfhack: line 83: 15504 Segmentation fault setarch "$setarch_arch" -R env LD_PRELOAD="$PRELOAD_LIB" ./libs/Dwarf_Fortress "$@"
Loading bindings from data/init/interface.txt
New window size: 800x300
Font size: 10x12
Resizing grid to 80x25
Resizing font to 10x12
Resetting textures
./dfhack: line 83: 15577 Segmentation fault setarch "$setarch_arch" -R env LD_PRELOAD="$PRELOAD_LIB libz.so.1" ./libs/Dwarf_Fortress "$@"
Yeah, I just took a look at that doc and it still says that you have to run git checkout develop in order to get the development branch (implying that the default branch is the stable branch).Where are you seeing that? The link I posted is a different version of the docs:
This will check out the code on the default branch of the GitHub repo, currently develop, which may be unstable. If you want code for the latest stable release, you can check out the master branch instead:It did change recently, so if your browser has it cached, you could try hard-refreshing (e.g. ctrl-shift-r in Chrome) or clearing your cache.
git checkout master
git submodule update
How would I go about doing that? just make a file called ".dfhackrc" and include a line that says "PRELOAD_LIB=libz.so.1"?Yeah, that should do it. The launcher just sources .dfhackrc, so you shouldn't even need "export" (like you would in a shell, for instance).
git describe --tags gives me
0.44.12-r3
install-info gives me:Code: [Select]DFHack 0.47.04-r1 on linux/x86
Version information:
DF version: v0.47.04 linux32
DFHack version: 0.47.04-r1
DFHack release: r1
DFHack build ID:
Compiled DF version: 0.47.04
Git description: 0.47.04-beta1-122-g0ffafbde
Git commit: 0ffafbde298cc84003ebec0de75013d6baa547e9
Git XML commit: efc5c2610bd0345e9102279be0a9f154e5862050
Git XML expected commit: efc5c2610bd0345e9102279be0a9f154e5862050
Git XML match: true
Is release: false
Is prerelease: false
Is world loaded: false
Is map loaded: false
I have BUILD_ISOWORLD set to OFF.I think your best bet is clearing the entire plugins/isoworld folder and trying again. Turns out that my earlier guess about the files being added earlier was wrong - in this commit (https://github.com/dfhack/isoworld/commit/0b483bae30c0d8b4b148d82fe594ae262286e979), they were actually changed to submodules, and that tends to lead to confusing errors like this when switching between branches (since in one revision, the files are part of the DFHack repo, and in the other, the files exist but the DFHack repo isn't tracking them). You might also need some extra options to get isoworld's submodules to update (which I should add to the docs...): "git submodule update --init --recursive"
Running ccmake .. and choosing [c]onfigure resulted in this:This one is weird. I made a fix here (https://github.com/dfhack/dfhack/commit/d3a007489) specifically for this issue, and it should be on both develop and master by now. What CMake version are you using?Code: [Select]CMake Warning (dev) at CMakeLists.txt:376 (find_package):
Policy CMP0074 is not set: find_package uses <PackageName>_ROOT variables.
Run "cmake --help-policy CMP0074" for policy details. Use the cmake_policy
command to set the policy and suppress this warning.
CMake variable ZLIB_ROOT is set to:
/usr/lib/i386-linux-gnu
For compatibility, CMake is ignoring the variable.
This warning is for project developers. Use -Wno-dev to suppress it.
Ok. It looks like I got it to work.Cool! Sometimes submodule changes are hard to resolve without just starting with a fresh clone of DFHack. (By the way, in case anyone tries to copy that, step 5 should just be "git checkout master".)
What I did was to:
1. rm -rf the dfhack directory
2. git clone --recursive https://github.com/DFHack/dfhack
3. cd dfhack
5. git clone checkout master
So I wanted to give my starting dwarves some musician skills, and triedI was able to reproduce the issue with the command you gave in 0.47.04-r1 (64-bit Linux). There were close to 150 (!) new fields added to that screen in 0.47, so it's entirely possible that one is wrong and broke the script. I've opened an issue here (https://github.com/DFHack/dfhack/issues/1572). In the future, it would be helpful to specify the DFHack version and OS you're using to help us figure out how to reproduce it.
'embark-skills points 20'
but nothing happened.
is embark-skills not working in the new version?
Could the issue not stem from 'points' being used as a script of it's own?No, definitely not - once DFHack starts running a script, it doesn't get it confused with another script. (Scripts can run other scripts, but this one doesn't.)
What happens if you change the 'points' command of the embark-skills to 'pts' ?
Are there any scripts for cheating in adventurer equipment points? A cursory search revealed nothing.last I mess around with that the game crashed, chances are the best you can get is max skill points but I think the one currently made is set up for solo adventurers and not adv parties so you end up with 1.
[DFHack]# deathcause
E: NoMethodError: undefined method `item' for #<DFHack::UiLookList_TItems:0x00017950e15ee0>
C:/DOWNLO~1/PERIDE~2.04-/Dwarf Fortress 0.47.04/hack/ruby/item.rb:25:in `item_find'
hack/scripts/deathcause.rb:44:in `<top (required)>'
eval:2:in `load'
eval:2:in `block in <main>'
eval:2:in `catch'
eval:2:in `<main>'
I found an unexpected error when trying to use "k" to select a body part for the deathcause.rb script in v47.04-r05 (Win10 64bit). Other methods of selecting a target seem to work fine (Unit list, stock screen, etc).You're right that it was a change we made in 0.47. I've already fixed it, although the fix isn't part of a release yet: https://github.com/DFHack/dfhack/issues/1563. You could probably install a development build with the fix if you want.Code: [Select][DFHack]# deathcause
E: NoMethodError: undefined method `item' for #<DFHack::UiLookList_TItems:0x00017950e15ee0>
C:/DOWNLO~1/PERIDE~2.04-/Dwarf Fortress 0.47.04/hack/ruby/item.rb:25:in `item_find'
hack/scripts/deathcause.rb:44:in `<top (required)>'
eval:2:in `load'
eval:2:in `block in <main>'
eval:2:in `catch'
eval:2:in `<main>'
After digging a bit, this looks like it might be caused by a difference in the way the UiLookList class is set up in ruby-autogen-win.rb for 47.04 vs 44.12? I'm not familiar with ruby at all, but I was able to get the script to work for historical creature parts by just pasting the field definition(?) for :item from an older version outside the :data field block it currently lives in. I wasn't able to get the :item version that lives in the :data block to work by just moving it. Looking at non-historical creature parts with the modified ruby-autogen-win.rb caused errors and occasional crashes.
Is it possible to call map.rb's map_tile_at(x,y,z) from a lua script, or do I have to implement my own?not sure what is map.rb's but there is this module (https://docs.dfhack.org/en/stable/docs/Lua%20API.html#maps-module) and tile is usually collection of type+flags+occupancy(+ some other stuff that is harder to explain). Most of the things can be done with
local ttype=dfhack.maps.getTileType(x,y,z)
local des,occ=dfhack.maps.getTileFlags(x,y,z)
not sure what is map.rb's but there is this module (https://docs.dfhack.org/en/stable/docs/Lua%20API.html#maps-module) and tile is usually collection of type+flags+occupancy(+ some other stuff that is harder to explain). Most of the things can be done withhttps://github.com/DFHack/dfhack/blob/master/plugins/ruby/map.rbCode: [Select]local ttype=dfhack.maps.getTileType(x,y,z)
local des,occ=dfhack.maps.getTileFlags(x,y,z)
<path to perl>\perl\vendor\lib\auto\XML\LibXML
The LNP and LMP both come with a Gems stockpile setting group (Rough Ornamental. Rough Rare, Rough Semiprecious), a Magma Safe group (different Stone and Ore settings), and a MechGuides group.
So since I'm going to go through the trouble of making [Workshop]_Input stockpiles for everything, how do I submit those for inclusion into df-hack?
Submitting a pull request on GitHub is probably easiest for us. However, this case is a bit tricky because we currently don't distribute any stockpile profiles with DFHack, so there's not really a place in the repository to put them (but we can figure out how to address that and make sure they get bundled properly). They're also binary files, from what I remember, which tend not to play very well with Git - if they're large, we might just distribute them separately.
As a side thought, if it's possible to generate these profiles programmatically, it might be more maintainable to have the plugin generate them (although built-in workshops are hardcoded, and reactions for custom workshops can be pretty complicated... so your solution is probably easiest for now).
Oh, hey. I set up those By-Industry custom stockpiles and then never did anything with them. Almost overwrote them when I downloaded the latest versions.We still don't really have a place for them. I'd post them in the LNP thread(s) for now, since they're better equipped to distribute that sort of thing (similar to embark profiles, etc).
A) Are you still interested in trying to find some way to distribute these custom stockpiles with dfhack, or do you think it would be simpler to submit these to the LNP and LMacP folks, since they already have folders for such things?
B) If yes, uh....where on Github? I've never submitted a pull request via Github before.
Did anyone ever sort out the bug with the building planner? The one where your dwarves build your furniture directly out of bars of various materials? Mine are doing it right now with iron bars. I'll place the ghost furniture icon and a while later a dwarf with an inventory of iron bars comes up and builds it. I have on Masterful cabinets of any material selected to build with none in my stocks. It even does this when I specifically designate stone as a material.This is the first time I've ever heard of this issue, so as far as I'm concerned, it was never reported to us (at least through GitHub, here, or another official channel - have you seen reports of it elsewhere?). The plugin doesn't seem to have changed recently (https://github.com/DFHack/dfhack/commits/develop/plugins/buildingplan.cpp) either. I'll see if I can reproduce it on my end. Any idea if this is new to 0.47, or was it happening earlier?
Did anyone ever sort out the bug with the building planner? The one where your dwarves build your furniture directly out of bars of various materials? Mine are doing it right now with iron bars. I'll place the ghost furniture icon and a while later a dwarf with an inventory of iron bars comes up and builds it. I have on Masterful cabinets of any material selected to build with none in my stocks. It even does this when I specifically designate stone as a material.This is the first time I've ever heard of this issue, so as far as I'm concerned, it was never reported to us (at least through GitHub, here, or another official channel - have you seen reports of it elsewhere?). The plugin doesn't seem to have changed recently (https://github.com/DFHack/dfhack/commits/develop/plugins/buildingplan.cpp) either. I'll see if I can reproduce it on my end. Any idea if this is new to 0.47, or was it happening earlier?
Right, sorry I should have said all the technical stuff :)
I'm using the LNP (PeridexisErrant's Starter Pack 0.43.05-r03) which has DF 43.05 along with DFHack 0.43.05-r1 (thread: http://www.bay12forums.com/smf/index.php?topic=126076.0)
To be clear, I have the min quality set to ordinary and filter set to *any. I have say 4 beds prebuilt. I place 1 bed down in planning mode and a dwarf hauls a bar of coke over.
I don't think this happens on a new game. I feel like I did something at some point that triggered this behaviour because it reliably does this as soon as I load up the save (even if I already have the furniture constructed). But when I load an earlier save, it doesn't happen and dwarfs will wait until I construct a bed even if there is coke bars available.
I tried forbidding all my coke bars from the stocks screen. When they are forbidden, dwarfs properly use normal constructed beds. After unforbidding, it seems this has fixed the behaviour... Very strange!!! I'll post again if anything interesting happens...
Hello, wasn't sure where to put this and I haven't see anyone talk about this, but can someone explain why doors, tables and I guess other furniture are built with coke when they are put down in planning mode?
By coke, I mean they haul bars of coke to the location and then create a door or table from scratch! I can deconstruct it and I get bars of coke back... I've never seen this behaviour before.
Planning mode was working normally a while ago where it would wait until I'd build the funiture item and then a dwarf would go haul the constructed item to the spot and install it. But suddenly (and I'm not sure exactly when as 'coke bar' furniture looks like gabbro) any planned furniture immediately gets made out of coke bars... and it's always coke bars. Any ideas?
oh, i think i've seen this before!
iirc, "iron bars" are the default object (first one listed in the raws?) that gets used if somehow no other object was provided. (maybe, back in 2017, the first one was coke?)
i believe it's not that the script is "constructing the cabinet on the spot out of iron bars", but rather, something has gotten corrupted, and instead of collecting a cabinet object from the stockpile, the dwarf has "collected" some nonsense unitialised object that's being interpreted as iron bars
i haven't used plannng mode in a while, but i _think_ maybe you can reproduce this on demand by placing some furniture in planning mode, specifying objects you don't have yet (so that it creates a suspended job, which will be unsuspended by the script once the desired object exists), and then going into the jobs screen and unsuspending the job yourself -- some dwarf will execute the job, with whatever placeholder object it had been originally created with...
the simple answer might be "well, don't do that then". but maybe there's a new bug that has surfaced and creates similar weirdness, when the player hasn't manually unsuspended an unready planning-mode job? idk
maybe Stargazer can chime in as to whether they recently unsuspended some jobs just before this happened
if Table.SubTable then
for k,v in pairs(Table.SubTable) do
...
end
end
for k,v in pairs(Table.SubTable or {}) do
...
end
Exactly - I would be more concerned about optimizing the loop body, since that's presumably run more often.
Regarding stonesense.. Is the expectation that it's currently supposed to be working, in -r1? Or is that a future revision goal?
I've tried a default/brand-new install of both 0.47.04 + dfhack 0.47.04-r1, and attempting to launch stonesense crashes df and dfhack.
There's a few exceptions/errors thrown, but if they're all known, I won't bother pursuing it..
last lines of stonesense.log (with [VERBOSE_LOGGING:YES]):
Reading xml stonesense\items\greiger items\Grei_items.xml...
New image: stonesense\items\greiger items\Grei_items.png
last lines of stderr.log:
Reading xml stonesense\buildings\Shop.xml...
stonesense\buildings\Shop.xml: <building
possibly relevant errors in stderr.log:(?)
libpng warning: iCCP: known incorrect sRGB profile
libpng warning: iCCP: cHRM chunk does not match sRGB
(many of each)
tt = dfhack.maps.getTileBlock(x,y,z).tiletype[x%16][y%16]
tt_info = df.tiletype.attrs[tt]
print(tt)
print(tt_info.shape..','..tt_info.material..','..tt_info.special)
How do I use them as the enums that show up in "df-structures/df.tile-types.xml", etc.? There's matinfo (https://docs.dfhack.org/en/0.47.04-r1/docs/Lua%20API.html#material-info-lookup) for materials, but I'm not sure how to use that. (tile-material.lua (https://github.com/DFHack/dfhack/blob/master/library/lua/tile-material.lua) seems to have some additional stuff on materials.) dfhack.println (df.tiletype [dfhack.maps.getTileBlock(x,y,z).tiletype[x%16][y%16]])
should print the tile type enum value. You're essentially going one step too far to try to get attributes for the enum value.Code: [Select]dfhack.println (df.tiletype [dfhack.maps.getTileBlock(x,y,z).tiletype[x%16][y%16]])
should print the tile type enum value. You're essentially going one step too far to try to get attributes for the enum value.
Regarding stonesense.. Is the expectation that it's currently supposed to be working, in -r1? Or is that a future revision goal?For reference, here (https://github.com/DFHack/stonesense/issues/68) is the issue. It's been around for a while, but Stonesense is large, complicated, and has few active devs (so thank you Rose for the fix!) so I didn't think it was worth holding off 0.47.04-r1 indefinitely for a fix. In retrospect, I probably should have made that more clear in the release notes. Our release notes are semi-generated now and really just focus on changes since the last release(s), not release-specific notes, but this is something we could improve.
I've tried a default/brand-new install of both 0.47.04 + dfhack 0.47.04-r1, and attempting to launch stonesense crashes df and dfhack.
Lua does short-circuit and/or, for what it's worth, so it's about as inexpensive as it can get:Exactly - I would be more concerned about optimizing the loop body, since that's presumably run more often.
More just wanted to make sure that the x = y or z syntax wasn't stupidly expensive (it's not in the other languages I code in, so I didn't have a good handle on it). That being said, I should have just run a little test case (which I just did) with a million calls there was no appreciably difference in run times (when the SubTable was present the second was slightly faster, when the SubTable was not present the first was slightly faster, but within a few percent of eachother each time I ran)
> 5 or f()
5
> nil or f()
stdin:1: attempt to call a nil value (global 'f')
stack traceback:
stdin:1: in main chunk
[C]: in ?
Also, what's the difference between "dfhack.maps.getTileBlock(x,y,z)" and "dfhack.maps.ensureTileBlock(x,y,z)"?From docs:
dfhack.maps.ensureTileBlock(coords), or ensureTileBlock(x,y,z)I'd have to dig into the source here to give you a better answer: https://github.com/DFHack/dfhack/blob/f20446534bb7f39425e102bd70daec46e328004f/library/modules/Maps.cpp#L181-L223
Like getTileBlock, but if the block is not allocated, try creating it.
Regarding enums, if you can navigate in gui/gm-editor to the structure containing the fields you're interested in, editing it will give you a popup containing a list of enum items as well as the enum type name.Can I do that with tiles? All I get is "No valid target found". I've just been using "printall" in lua to examine the fields, then doing another printall if there's another structure.
Regarding enums, if you can navigate in gui/gm-editor to the structure containing the fields you're interested in, editing it will give you a popup containing a list of enum items as well as the enum type name.Can I do that with tiles? All I get is "No valid target found". I've just been using "printall" in lua to examine the fields, then doing another printall if there's another structure.
Now I've just got to figure out how to get the base material of a tile so I can determine if a construction is built in air. It shows up in probe.cpp as "mc.baseTiletypeAt(cursor)", where "mc" is a MapCache object. The "original_tile" field of a "df.global.world.constructions" entry just gives a soil layer, even in air.
You would have to pass in the expression you're looking at as an argument to gui/gm-editor ("gui/gm-editor world.something.tile"). It doesn't support selecting tiles in the GUI because there's no single "tile object" that it could display (some tile attributes are located in different structures).Regarding enums, if you can navigate in gui/gm-editor to the structure containing the fields you're interested in, editing it will give you a popup containing a list of enum items as well as the enum type name.Can I do that with tiles? All I get is "No valid target found". I've just been using "printall" in lua to examine the fields, then doing another printall if there's another structure.
Please report bugs in the GitHub issue tracker (http://github.com/DFHack/dfhack/issues).
@Rumrusher: That kind of research is probably better noted as an issue on Github (https://github.com/DFHack/df-structures/issues (https://github.com/DFHack/df-structures/issues)), as it's likely to get buried over time in this thread (although a mention here as well is useful if you think it's of general interest too).figure once I map out most of the stuff I'll send in a issue report on my findings.
Very cool.It won't be a script, but a compiled plugin that overrides DF's internal behavior when performing any custom reactions.
Just to check I understand you though, I would want to modify my reaction to produce a single glove/gauntlet/etc instead of two, because your script handles the duplication?
Why doesn't dfhack.TranslateName() capitalize non-ascii letters? For example, it outputs äs Nêcikled, instead of Äs Nêcikled?The general, simple answer is that to do that the code will have to know which character codes are letters and which are not, as well as what the opposite case character code of each letter code is. For the ASCII subset there are simple rules for both, but I'd expect CP437 (if the number is correct) letters to be all over the place, which would require someone to write a translation table for codes that are deemed to be letters.
Half a year, sounds optimistic. :DWhy doesn't dfhack.TranslateName() capitalize non-ascii letters? For example, it outputs äs Nêcikled, instead of Äs Nêcikled?The general, simple answer is that to do that the code will have to know which character codes are letters and which are not, as well as what the opposite case character code of each letter code is. For the ASCII subset there are simple rules for both, but I'd expect CP437 (if the number is correct) letters to be all over the place, which would require someone to write a translation table for codes that are deemed to be letters.
It can also be noted that character coding will change with the Premium release (I've seen no indication of how, only that the 8 bit restriction is intended to be replaced), but a changed implementation would still probably be valid for half a year.
Edit: Also, I'm not sure there's a consensus about what upper case versions of lower case letters is between languages. I believe English has a tendency to drop a trema over an 'ï' for instance resulting in 'I' rather than 'Ï'.Well, at least in this case the game itself shows the first letter of that name as a capital one.
Is there any scripts or other ways to edit biome/region parameters of an already running fortress?You can change it, but whether it will take effect I don't know. The Biome Manipulator http://www.bay12forums.com/smf/index.php?topic=164658.msg7495705#msg7495705 (http://www.bay12forums.com/smf/index.php?topic=164658.msg7495705#msg7495705) allows you to change things on a world tile level, but it's really intended to be used before embark, and I haven't tried it after.
I've finally found a decent site after a week of world-genning, but it rains EVERY SINGLE DAY there.
It's in your site's entity_links. There's a link to the government of the site (i.e. the entity, not the site), and flags.local_market is set. Land for holding links have to go from an entity to a site, and I believe those are between the civ and the site, not the site government and a site (with a possible exception for necro towers). In my case I have an economic link to a gobbo conquered (formerly human) site, but no land for holding claim on it from the (dead) civ. The civ has claims only on the (lost) capital and my fortress.Thanks! I had trouble finding the site data apart from df.global.world.world_data.active_site[0], but df.world_site.find() helped with that. I also noticed that there's a site_link that matches my site's id in the entity of the linked economy, and removing that seems to get rid of the message on the civilizations view, but I'll also remove the entity_link from my site to be safer.
I believe it's a bug that nobility for land holding sites other than your own nevertheless demand to be given noble quarters at your site. You really should have to satisfy your own nobles only (if nobility over other sites are keen on noble treatment they should go to their sites to demand it...).
-- show and sever economic links, by xzaxza
local help = [====[
economiclinks
======================
Displays the economic links of a site, or optionally severs them.
Usage:
economiclinks [sever] [site_id]
Both options are optional. If absent, site_id defaults to the current site.
Examples:
Running `economiclinks` without arguments displays the economic links of the current site.
Running `economiclinks 7` displays the economic links of the site with id 7.
Running `economiclinks sever` severs the economic links of the current site.
Running `economiclinks sever 7` severs the economic links of the site with id 7.
]====]
function fullname(item) --lifted from modtools/extra-gamelog
return dfhack.TranslateName(item.name)..' ('..dfhack.TranslateName(item.name ,true)..')'
end
function CountLinks(site_id)
if df.world_site.find(site_id) == nil then
print "Error: site not found."
return nil
end
local site = df.world_site.find(site_id)
local local_markets = {}
for k,v in pairs(site.entity_links) do
if v.flags.local_market then
local_markets[#local_markets+1] = v.entity_id
end
end
return #local_markets
end
function ShowLinks(site_id)
if df.world_site.find(site_id) == nil then
print "Error: site not found."
return
end
local site = df.world_site.find(site_id)
local local_markets = {}
print(dfhack.df2console('Site '..site_id..': '..fullname(site)..' at coordinates ('..site.pos.x..','..site.pos.y..')'))
for k,v in pairs(site.entity_links) do
if v.flags.local_market then
local_markets[#local_markets+1] = v.entity_id
end
end
if #local_markets > 0 then
print(dfhack.df2console('The site has the following economic links:'))
for k,v in pairs(local_markets) do
tmp_ent = df.historical_entity.find(v)
if tmp_ent ~= nil then
print(dfhack.df2console(' Entity '..v..', '..fullname(tmp_ent)))
else
print(' Entity '..v..', <unknown entity>')
end
end
else
print(dfhack.df2console('The site has no economic links.'))
end
end
function SeverLinks(site_id)
if df.world_site.find(site_id) == nil then
print "Error: site not found."
return
end
local site = df.world_site.find(site_id)
local tmp_ent
local tmp_ent_id
for k,v in pairs(site.entity_links) do
tmp_size1 = #site.entity_links
if v.flags.local_market then
tmp_ent_id = v.entity_id
tmp_ent = df.historical_entity.find(tmp_ent_id)
for k2,v2 in pairs(tmp_ent.site_links) do
if v2.target == site_id and v2.flags.local_market then
tmp_ent.site_links:erase(k2)
break
end
end
site.entity_links:erase(k)
return
end
end
end
-- main script
local opt = ...
local args = {...}
local site_id = df.global.ui.site_id
local link_count
if opt and opt ~= "" then
if opt=="sever" then
if tonumber(args[2]) then
site_id = math.floor(tonumber(args[2]))
end
link_count = CountLinks(site_id)
for i = 1,link_count,1 do
SeverLinks(site_id)
end
return
elseif tonumber(opt) then
site_id = math.floor(tonumber(opt))
ShowLinks(site_id)
return
else
print(help)
end
else
ShowLinks(site_id)
end
I've been poking around trying to learn DFHack for some tile-designation manipulation, and since there's no documentation on the why-
Is there documentation of, or does anyone know the purpose of the crazy coordinate system used for map tiles? At first I figured "block.map_pos" was the proper coordinates but they increment oddly instead of reflecting the real coords.
Digging through other scripts reveals a crazy 2D array in some of the fields such "block.designation", which I can't figure out the purpose or function of. I've managed to get something working by copying what other people have done, but I'd sure love to know what this structure's for.
(https://cdn.discordapp.com/attachments/173908541088858113/724716402014945431/unknown.png)
(https://cdn.discordapp.com/attachments/173908541088858113/724716402014945431/unknown.png)Map blocks are 16x16x1 tiles. Looks to me like getTileBlock returns the block that a tile is in, and the map_pos field is the coordinates of one corner of that block. Some attributes are stored in 16x16 arrays in each block and some are stored at the block level. It is a little complicated.
map_pos field is the coordinates of one corner of that block.
The way it's all organized made it confusing. And having the chunks be named "blocks".We don't know what they're actually called, because the actual structure name isn't exposed anywhere within the executable - we know the names of all of the C++ classes that have virtual methods (e.g. "building_civzonest", "feature_init_deep_special_tubest", "world_construction_wallst") thanks to RTTI (https://en.wikipedia.org/wiki/Run-time_type_information), but for everything else we have to guess what it's called based on how it's used.
dfhack.gui.getCurViewscreen().dwarf_info[0].name.has_name
is false, and all the other name fields are also empty, revealing that the dwarves have no name at all. But they're there on the screen! Does anyone know where these specific names are stored before they're applied to the dwarves? I've dug through the structure files for the setupdwarfgame screen but can't find anything that leads to the mysterious intermediate names, if they're accessible at all.
df.global.world.units.all is populated by 7 creatures at that point, and the first one had a name of "kogsak" when I checked. Thus, I'm fairly sure that's where you've got your dorfs. When you enter the embark carefully screen the dorfs have been generated, with names and all.
(Dwarf Therapist can display the dorfs as well, at that point).
df.global.world.units.all is populated by 7 creatures at that point, and the first one had a name of "kogsak" when I checked. Thus, I'm fairly sure that's where you've got your dorfs. When you enter the embark carefully screen the dorfs have been generated, with names and all.
(Dwarf Therapist can display the dorfs as well, at that point).
Actually df.global.world.units.all has more than 7 creatures. I had to change Dwarf Therapist to use the view screen instead since the more units in the world were activated. But I use viewscreen_setupdwarfgamest.units instead viewscreen_setupdwarfgamest.dwarf_info.
Edit: Well their first name is there at least, but that's good enough!If you want a human-readable version of a language_name object, you'll want dfhack.TranslateName(some_unit.name) (in Lua, anyway). Also worth mentioning: TranslateName returns a CP437-encoded string, so if you're printing these names somewhere, you should use df2utf or df2console as appropriate: https://docs.dfhack.org/en/stable/docs/Lua%20API.html#c-function-wrappers
Plants:
Creatures and vermin:
While looking at the world map, I am currently trying to get an overview of the creatures available at the embark, so I can chose a location that has a certain species for my fort.I wouldn't be surprised if the structure region-pops uses isn't populated until you embark as DF has no use for it until then. You can use the Biome Manipulator http://www.bay12forums.com/smf/index.php?topic=164658.msg7495705#msg7495705 (http://www.bay12forums.com/smf/index.php?topic=164658.msg7495705#msg7495705), although it's a bit more cumbersome, as you have to manually identify all regions your planned embark has, and then locate a "parent" world tile for each of those regions (basically embark world tile some of the 8 surrounding ones). On the other hand you can add desired creatures with it.
In the past, I think I used the "region-pops" command in DFHack for this. However, "region-pops list" now gives me only:QuotePlants:
Creatures and vermin:
There is nothing shown. What am I doing wrong? Can I only use this after embark?
@Rumrusheri just got done with repeatedly milking a sheep, and realizing no you can't just toggle the milkable enemy caste flag on folks
But will you fulfill the late DerMeister's dreams of a plugin for milkable ogresses (http://www.bay12forums.com/smf/index.php?topic=174898.0)?
gui/gm-editor df.global.world.nemesis.all[milker nemesis id here].unit.job.current_job.general_refs
- this here is where you can edit the job of the unit hopefully you wrote a script that inserts a milking job for them so that they go do this but if you haven't you probably need to insert "general_ref_unit_milkeest" in the job's general ref section after the general ref of the building and worker. other wise you just edit the job's milkee unit id before the worker grabs the default milkee-
gui/gm-editor df.global.world.raws.creatures.all[insert milkees creature id here].caste[the caste id of the milkee here].extracts
-and here is where you need to fill out which material do you want to have this creature extracting if you already have a creature with raw mods set for this you can skip this step for others uhh you need to edit the materials of the creature's raws to have a liquid that you can produce... or this just grabs any raws and makes it work regardless either way this is needed to produce stuff or else the stuff above will be getting nothing-
You certainly could fairly easily combine a few things and create a script that allows you to extract blood, tears, pus, etc... You could even have it set up like the gui/create-item script where you select an extractor, an extractee, and the material to extract. You could also turn it into a more automated thing by creating reactions for extracting blood and such, and then tie the script into those reactions so that they functions similarly to how the milking reaction goes.How make script and reactions for this script?
You certainly could fairly easily combine a few things and create a script that allows you to extract blood, tears, pus, etc... You could even have it set up like the gui/create-item script where you select an extractor, an extractee, and the material to extract. You could also turn it into a more automated thing by creating reactions for extracting blood and such, and then tie the script into those reactions so that they functions similarly to how the milking reaction goes.How make script and reactions for this script?
the work I did mostly discovered you could probably set the extract material to anything of that creature's material, though this does mean you could end up with milked flesh.You certainly could fairly easily combine a few things and create a script that allows you to extract blood, tears, pus, etc... You could even have it set up like the gui/create-item script where you select an extractor, an extractee, and the material to extract. You could also turn it into a more automated thing by creating reactions for extracting blood and such, and then tie the script into those reactions so that they functions similarly to how the milking reaction goes.How make script and reactions for this script?
You would need to combine the work that Rumrusher did with a heavily modified create-item script. You could also use the reaction-trigger hooks to set up a script, that would probably be more work, but also more configurable.
If you were asking if someone would make the script for you, probably not, as it is probably not of a lot of interest to most of the people that routinely write scripts. But it would be a great exercise for you to learn how to write scripts.
so far the only result I got from this is getting a designated campsite inhabitant to drag one of my adventurers off their mount to the farmer's workshop but unable to finish the job so they are just continuing dragging this adventurer around.
Is possible to make genderless creature pregnant by DFhack? How modify 'Catsplosion' script for this? Any way to make genderless creatures (snail man) breed automatic?You could try changing this line (https://github.com/DFHack/scripts/blob/0.47.04-r1/catsplosion.lua#L52) to
table.insert(females[id], unit)
That would add all units to the "females" list, which is used to attempt to trigger pregnancies later. I'm not sure what will happen, though - it might work, do nothing, crash, or something else (so save first!).
This does not cause crash. And even create pregnancies, as DFhack told me. But creatures don't give birth, if they are not females or if they lay eggs (I don't try with nestboxes).Is possible to make genderless creature pregnant by DFhack? How modify 'Catsplosion' script for this? Any way to make genderless creatures (snail man) breed automatic?You could try changing this line (https://github.com/DFHack/scripts/blob/0.47.04-r1/catsplosion.lua#L52) toCode: [Select]table.insert(females[id], unit)
That would add all units to the "females" list, which is used to attempt to trigger pregnancies later. I'm not sure what will happen, though - it might work, do nothing, crash, or something else (so save first!).
<snip>
<snip>
If we can unretire any histfig, why there is no way to embark as necromancer governments?because adv mode is set up in a way that grabs a historical figure from the poll of histfigs that are flagged for adv mode the script uses the 'unretire function of adventure mode to unretire anyone'
Just add SITE_CONTROLLABLE to necromancer tower entity. Is this too uneasy for scripting?If we can unretire any histfig, why there is no way to embark as necromancer governments?because adv mode is set up in a way that grabs a historical figure from the poll of histfigs that are flagged for adv mode the script uses the 'unretire function of adventure mode to unretire anyone'
it's probably alot more work to figure out how DF does fort embarks to manipulate that to control who shows up.
dfHack createunit REPTILE_MAN Male, works and produces the 'r'. dfHack gui/gm-unit on r, edits it, fine. However, once game is unpaused, the entire game crashes to the desktop.
dfHack add-thought on a tamed war elephant, crashes the game to desktop, if he is chained and cannot find a bed to sleep on. Must use dfHack tame and gui/gm-unit to tame and set war animal flags, for this crashing to occur.
...
Nothing occurs with that facet command on a unit.
If we can unretire any histfig, why there is no way to embark as necromancer governments?
Just add SITE_CONTROLLABLE to necromancer tower entity. Is this too uneasy for scripting?
I can embark as amphibian men or goblins even by using just RAW changing, no DFhack. The only exception is generated civs of vault guardians. So I want to test your script.dfHack createunit REPTILE_MAN Male, works and produces the 'r'. dfHack gui/gm-unit on r, edits it, fine. However, once game is unpaused, the entire game crashes to the desktop.
dfHack add-thought on a tamed war elephant, crashes the game to desktop, if he is chained and cannot find a bed to sleep on. Must use dfHack tame and gui/gm-unit to tame and set war animal flags, for this crashing to occur.
...
Nothing occurs with that facet command on a unit.
Hi knutor, please provide the full commands that you used in the above scenarios to help identify the cause. In addition, try to describe exactly what you did with gui/gm-unit.
...why is an elephant trying to sleep on a bed?If we can unretire any histfig, why there is no way to embark as necromancer governments?
I had messed around with fortress mode to a similar effect whilst I was writing the 'unretire-anyone' script. It was definitely possible to embark under a normally unavailable parent civilisation using the same method (i.e. by adding the desired entity to the viewscreen selection). The game gave me starting units of the correct race, with the fort/group naming language and equipment/pet options appropriately corresponding to the selected entity. This worked even for the underground civilisations like amphibian men. However, there was some quirky behaviour such as an inability to assign noble positions and units refusing to pick up axes for woodcutting jobs (even when using a goblin civilisation). I'm not familiar with what it is that modders do to make non-dwarf races playable, but I assume that those sorts of changes would be required to make it work properly.
Embarking as a specific necromancer group isn't technically possible as you create a new entity (a site government) upon embarking (unless unretiring a site); the necromancer group would probably be listed as your parent civilisation, but I have no clue how it'd work out. Also, necromancer groups aren't civilisation type entities, but site governments; this may or may not cause issues. In addition, your embark party wouldn't contain necromancers or undead units without additonal hacking, and any opposed-to-life units would still be hostile to living units. I'm also not sure whether the regular reanimated units are capable of performing jobs.
I had also attempted to directly unretire small non-player sites such as necromancer towers at one point, but never got it to work well.Just add SITE_CONTROLLABLE to necromancer tower entity. Is this too uneasy for scripting?
It doesn't work like this.
so far in my experience with making the necro site as one of the civs you can start as in fort mode is that the game treats them like any other entity and they don't get any special bonusesNo changes? But why they act so different in worldgen?
shoot one could say they pull from the main entity they originally come from so a dwarf necro tower will follow dwarf laws and ethics and the human tower will follow human ones and so on.
the big kicker is with the experiment stuff is the way the game made them hardcoded is tied to the historical figure Id number and that makes it so you need to get lucky on the right historical figure number for your experiment raw name or some weird plan like 'post editing the animal tokens so it matches with the experiments the necromancers make.' would ever work.
It was the last msg before crash. Elephant was unable to find a place to rest. I may have added fat to him. 999,999. Hard to remember.dfHack createunit REPTILE_MAN Male, works and produces the 'r'. dfHack gui/gm-unit on r, edits it, fine. However, once game is unpaused, the entire game crashes to the desktop.
dfHack add-thought on a tamed war elephant, crashes the game to desktop, if he is chained and cannot find a bed to sleep on. Must use dfHack tame and gui/gm-unit to tame and set war animal flags, for this crashing to occur.
Hi knutor, please provide the full commands that you used in the above scenarios to help identify the cause. In addition, try to describe exactly what you did with gui/gm-unit.
...why is an elephant trying to sleep on bed?
well that's because experiments are like angels where they are generated After world gen and all the sites been made.so far in my experience with making the necro site as one of the civs you can start as in fort mode is that the game treats them like any other entity and they don't get any special bonusesNo changes? But why they act so different in worldgen?
shoot one could say they pull from the main entity they originally come from so a dwarf necro tower will follow dwarf laws and ethics and the human tower will follow human ones and so on.
the big kicker is with the experiment stuff is the way the game made them hardcoded is tied to the historical figure Id number and that makes it so you need to get lucky on the right historical figure number for your experiment raw name or some weird plan like 'post editing the animal tokens so it matches with the experiments the necromancers make.' would ever work.
I can search world.dat for experiment Id full name. Then I put them to ALWAYS_PRESENT pets. Only issue: I cannot unpack world.dat from already generated world. And cannot force necromancers to make experiments in worldgen. Even with modded creature who can do experiments directly in civ - I don't know how make them creating more experiments.
If I need necromancers in my fort, I can just raid for books and create ny own fort of necromancers only. But I want experiments, not necromancers.well that's because experiments are like angels where they are generated After world gen and all the sites been made.so far in my experience with making the necro site as one of the civs you can start as in fort mode is that the game treats them like any other entity and they don't get any special bonusesNo changes? But why they act so different in worldgen?
shoot one could say they pull from the main entity they originally come from so a dwarf necro tower will follow dwarf laws and ethics and the human tower will follow human ones and so on.
the big kicker is with the experiment stuff is the way the game made them hardcoded is tied to the historical figure Id number and that makes it so you need to get lucky on the right historical figure number for your experiment raw name or some weird plan like 'post editing the animal tokens so it matches with the experiments the necromancers make.' would ever work.
I can search world.dat for experiment Id full name. Then I put them to ALWAYS_PRESENT pets. Only issue: I cannot unpack world.dat from already generated world. And cannot force necromancers to make experiments in worldgen. Even with modded creature who can do experiments directly in civ - I don't know how make them creating more experiments.
so a necromancer legit creating a new creature species the reason they act so differently in world gen is because the historical figure the necromancer is doing stuff than the Site.
like the necromancer has access to a new feature toady added which was scheme and plot.
but like even if you get through all these hoops to get a bunch of necromancers showing up to your fort you still have this one big issue of their resurrection/reanimation is tied to combat, and undead zombies oppose to living causes them to be hostile to everyone in the fort including their own necromancers
I would probably say the experiments are at this case toady making them one part night trolls where they are historical figures and angels where their raw name and existance is co-dependent to the historical figure that makes them that you would have a harder time getting one.If I need necromancers in my fort, I can just raid for books and create ny own fort of necromancers only. But I want experiments, not necromancers.well that's because experiments are like angels where they are generated After world gen and all the sites been made.so far in my experience with making the necro site as one of the civs you can start as in fort mode is that the game treats them like any other entity and they don't get any special bonusesNo changes? But why they act so different in worldgen?
shoot one could say they pull from the main entity they originally come from so a dwarf necro tower will follow dwarf laws and ethics and the human tower will follow human ones and so on.
the big kicker is with the experiment stuff is the way the game made them hardcoded is tied to the historical figure Id number and that makes it so you need to get lucky on the right historical figure number for your experiment raw name or some weird plan like 'post editing the animal tokens so it matches with the experiments the necromancers make.' would ever work.
I can search world.dat for experiment Id full name. Then I put them to ALWAYS_PRESENT pets. Only issue: I cannot unpack world.dat from already generated world. And cannot force necromancers to make experiments in worldgen. Even with modded creature who can do experiments directly in civ - I don't know how make them creating more experiments.
so a necromancer legit creating a new creature species the reason they act so differently in world gen is because the historical figure the necromancer is doing stuff than the Site.
like the necromancer has access to a new feature toady added which was scheme and plot.
but like even if you get through all these hoops to get a bunch of necromancers showing up to your fort you still have this one big issue of their resurrection/reanimation is tied to combat, and undead zombies oppose to living causes them to be hostile to everyone in the fort including their own necromancers
dfhack dont support 32bit anymore right? if it still support it, can someone add the link for me to download? or the original zip already include it? i cant go into github for some reason, never try dfhack before though.Afaik we support 32bits but github is currently down. I would recommend patience as github should sort it out soon.
Afaik we support 32bits but github is currently down. I would recommend patience as github should sort it out soon.
It's up for me. There's 32bit versions at the bottom of this page: https://github.com/DFHack/dfhack/releases/tag/0.47.04-r1
Or 32bit testing builds available through: https://dfhack.org/builds/
Is it possible to write data to a file using a script or plugin? Purpose is to save fort tiles as a .csv (or other text file format.)Yes, on both accounts. Exportmap exports data and tweakmap reads it, allows modification, and writes it http://www.bay12forums.com/smf/index.php?topic=161188.msg7228166#msg7228166 (http://www.bay12forums.com/smf/index.php?topic=161188.msg7228166#msg7228166). Embark Assistant can also write a search profile file and read it back.
Is it possible to write data to a file using a script or plugin? Purpose is to save fort tiles as a .csv (or other text file format.)All languages that DFHack uses (C++, Lua, Ruby) have native support for reading and writing files - there's nothing DFHack-specific about it, and DFHack hasn't disabled it, so the language documentation would be the place to look for details (or existing examples like PatrikLundell's).
However, this question made me think of the project that Myk is taking on to make a Quickfort-like utility:
http://www.bay12forums.com/smf/index.php?topic=176889.0
https://github.com/DFHack/dfhack/issues/499
It might be worth looking at these to make sure you're not duplicating effort, or to see if you might be able to help out.
Is there some way to determine the lowest valid z-level of a map block? Or do we just have to check if there's a map block at each given location?I think you just have to check whether each block is allocated or not. (For reference, there are also some sky blocks at higher z-levels that aren't allocated by default until you build in them.)
For example, on one embark the eerie glowing pits were located on z=1. On another, they were on z=6, with no valid map blocks from z=1 to z=5.
Thanks. One last question:
In many lua scripts I see the lines "--@module = true" and "if not dfhack_flags.module then". What is the point of these?
Lua has "modules" which are like script-library. We (i.e. dfhack team) has extended this functionality to scripts. Thus a script can be used as a module but it needs to know if it should just run the functionality (e.g. teleport a unit to cursor) or give the functions (e.g. if you are making a building or a spell that teleports creatures). More info here (https://docs.dfhack.org/en/stable/docs/Lua%20API.html#scripts)
Lua has "modules" which are like script-library. We (i.e. dfhack team) has extended this functionality to scripts. Thus a script can be used as a module but it needs to know if it should just run the functionality (e.g. teleport a unit to cursor) or give the functions (e.g. if you are making a building or a spell that teleports creatures). More info here (https://docs.dfhack.org/en/stable/docs/Lua%20API.html#scripts)
Still a bit unclear to me. Does this mean that putting these lines allows the function definitions to be used by other scripts without executing the main function? Similar to Python's if __name__ == "__main__"?
Thanks. One last question:
In many lua scripts I see the lines "--@module = true" and "if not dfhack_flags.module then". What is the point of these?
local fullHeal = reqscript('full-heal')
...
fullHeal.heal(adventurer, true)
if not dfhack_flags.module then
print("foo")
end
Prevents "foo" from being printed during a reqscript call.if dfhack_flags.module then
return
end
print("foo")
An alternative way to do the above.You can also transform male creatures to a female caste during the pregnancy, though I think that ruins the "monogendered" criterion.Transformation heal all injures. And satyrs, blendecs, grimelings, blizzard men has no female caste at all. So, can you test my idea? I am very bad with lua coding.
...
To me teleport works like marching orders, for the squad soldiers.
...
Fastdwarf teleport does not bring along the visiting mercs.Looks like there's a check (https://github.com/DFHack/dfhack/blob/0.47.04-r1/plugins/fastdwarf.cpp#L54) to skip non-citizens. That part was last changed in 2012, well before mercenaries and other visitors were added, so presumably it was meant to exclude livestock, invaders, etc.. I'm not sure what a better check would be - maybe Units::isOwnGroup()?
Ive a hu mercenary in shock squad, 4. S4 is filled to 10 souls. It was ordered to kill something in caverns. 9 soldiers teleported to thing in cavern. All the dwarfs minus the mercenary.
Crossing fingers. To me teleport works like marching orders, for the squad soldiers. I hope someone out there in forumland, can fix this.
Probably reported in one of 1xx's of dfHack pages, but in the event it hasn't been reported.A quick search of the DFHack issue tracker (https://github.com/DFHack/dfhack/issues) turned up https://github.com/DFHack/dfhack/issues/664, which is a similar issue. Unclear if the reporter there wanted it to apply to visitors or some other group of units, though.
The modified script as of now just work eagerly before trapping and has some new nice features:It has been a while, but I finally got around to this. It turns out the map boundary check was off by one, so it thought any blueprint that touched the bottom or right edge of the map went off the map. I fixed that, and added a "force" option to allow drawing partial blueprints instead of failing. I hope this is what you were aiming to do with your changes - if not, feel free to let me know.
- it stamps the safe part of blueprints that are same size than the embark. (with some more tinkering and 4 additional params partial stamping should be in the reachable zone)
- it warns when some lines and/or rows of the blueprint could had been ignored because the dimensions of the blueprint are bigger than the design-able zone of the embark.
- well formed multilevel nanoforts of 48x48 or 47x47 tiles faultily rejected by the previous script over an 1x1 embark are now possible.
Some things that are on my list include:Here's some remote API documentation (forgot to mention it a week or so ago when I put it up): https://docs.dfhack.org/en/latest/docs/Remote.html
- Documenting the RPC (remote) interface - https://github.com/DFHack/dfhack/issues/1574 has some information, and BenLubar put together a description of the format (https://github.com/DFHack/dfhack/issues/1574#issuecomment-637916990), so this mostly just involves integrating this into the docs
Buggy Reproduction for cleanowned.Thanks for the detailed instructions, but I don't have a save that has most of the things needed to reproduce it. Can you upload a save to make it easier for us to reproduce? http://dffd.bay12games.com/ is one site that takes DF-related files, but somewhere else would also work. (In case you're unfamiliar with the process, we would only need a compressed copy of the data/save/regionX folder you're using.)
It is done all in one pause, in any fort with a public tavern, with an entertainer guest with an owned instrument.
I've noticed that lua scripts use `` in their help text instead of ". Is there a reason for this convention?It produces inline code blocks in ReStructuredText, which is what our documentation system uses.
Question about the onReactionComplete eventful trigger. IIRC it only triggers when a product is actually created, is that still true? Also, if using both onReactionComplete and onJobCompleted will the always trigger in the same order, or will it be somewhat random which one triggers first?
Question about the onReactionComplete eventful trigger. IIRC it only triggers when a product is actually created, is that still true? Also, if using both onReactionComplete and onJobCompleted will the always trigger in the same order, or will it be somewhat random which one triggers first?
Could be wrong but onReactionComplete(and the ing) is actually badly named (mea culpa). Should be along the lines "reaction product create". It triggers when reaction is creating a product. IIRC it does trigger more than once when it's doing reaction with multiple products and other strange stuff. onJobCompleted on the other hand is way different - it uses the eventmanager system where it periodically scans the jobs and sees if they changed (in this case got completed). Not sure on the specs of it (is it 100% accurate) there are some notes about false positives though.
...\Dwarf Fortress\DF 0.47/hack/scripts/names.lua:67: Cannot read field viewscreen_setupadventurest.adventurer: not found.
stack traceback:
[C]: in metamethod '__index'
...\Dwarf Fortress\DF 0.47/hack/scripts/names.lua:67: in local 'fun'
...\Dwarf Fortress\DF 0.47\hack\lua\class.lua:98: in upvalue 'invoke_after_rec'
...\Dwarf Fortress\DF 0.47\hack\lua\class.lua:127: in global 'namescr'
...\Dwarf Fortress\DF 0.47/hack/scripts/names.lua:89: in local 'script_code'
...\Dwarf Fortress\DF 0.47\hack\lua\dfhack.lua:680: in function 'dfhack.run_script_with_env'
(...tail calls...)
Reporting an issue with the "names.lua" script. Attempting to use it to rename a unit fails with the following error message:Thanks for the report! I've figured out a fix, which will be in the next release.Code: [Select]...\Dwarf Fortress\DF 0.47/hack/scripts/names.lua:67: Cannot read field viewscreen_setupadventurest.adventurer: not found.
stack traceback:
[C]: in metamethod '__index'
...\Dwarf Fortress\DF 0.47/hack/scripts/names.lua:67: in local 'fun'
...\Dwarf Fortress\DF 0.47\hack\lua\class.lua:98: in upvalue 'invoke_after_rec'
...\Dwarf Fortress\DF 0.47\hack\lua\class.lua:127: in global 'namescr'
...\Dwarf Fortress\DF 0.47/hack/scripts/names.lua:89: in local 'script_code'
...\Dwarf Fortress\DF 0.47\hack\lua\dfhack.lua:680: in function 'dfhack.run_script_with_env'
(...tail calls...)
I've been using project boards (https://github.com/orgs/DFHack/projects/) on GitHub for a while to organize releases. They primarily have two benefits: keeping track of things that are part of a release (fixed issues, merged PRs), and keeping track of things that need to be addressed before the next release. The latter category has historically included lots of features, some of which can be time-consuming to get around to. Going forward, I think I would like to make smaller but more-frequent releases with whatever happens to be ready at the time (and maybe delaying for some important bugfixes). These would still follow the current versioning convention ("0.47.04-r3", "0.47.04-r4", etc.).Regarding stonesense.. Is the expectation that it's currently supposed to be working, in -r1? Or is that a future revision goal?For reference, here (https://github.com/DFHack/stonesense/issues/68) is the issue. It's been around for a while, but Stonesense is large, complicated, and has few active devs (so thank you Rose for the fix!) so I didn't think it was worth holding off 0.47.04-r1 indefinitely for a fix. In retrospect, I probably should have made that more clear in the release notes. Our release notes are semi-generated now and really just focus on changes since the last release(s), not release-specific notes, but this is something we could improve.
I've tried a default/brand-new install of both 0.47.04 + dfhack 0.47.04-r1, and attempting to launch stonesense crashes df and dfhack.
At the same time, there is a weird and nasty bug (https://github.com/DFHack/dfhack/issues/1576) on develop (well, hopefully just this one) that would definitely hold up an r2. Maybe we should start making smaller and more frequent "point" releases (like r1.1) with fixes like these, now that I think about it.
case SOIL:
{
auto &biome = mblock->biomeInfoAt(pos);
rv.mat_index = biome.layer_stone[mblock->layerIndexAt(pos)];
if (getGroundType(rv.mat_index) == G_STONE)
{
int idx = biome.default_soil;
if (idx >= 0)
rv.mat_index = idx;
}
break;
}
case STONE:
{
auto &biome = mblock->biomeInfoAt(pos);
rv.mat_index = biome.layer_stone[mblock->layerIndexAt(pos)];
if (getGroundType(rv.mat_index) == G_SOIL)
{
int idx = biome.default_stone;
if (idx >= 0)
rv.mat_index = idx;
}
break;
}
Some questions about the lua APINot sure, but it seems as it's for twbt and similar stuff. It reports it for current screen?
1. dfhack.gui.getDepthAt(x, y) - This states that it "Returns the distance from the z-level of the tile at map coordinates (x, y) to the closest ground z-level below". I'm not really sure what it is tying to say, since it doesn't take a z level as an argument.
2. dfhack.job.getGeneralRef(job, type) - By type, does it mean something like "df.general_ref_building_holderst"Nope it's df.general_ref_type enum (https://github.com/DFHack/scripts/blob/aed35fdefa7a7818e99c5a6ed9a0c878458cdb7c/devel/nuke-items.lua#L15)
3. dfhack.items.moveToBuilding(item,building[,use_mode[,force_in_building]) - When using force_in_building, should use_mode = 2? Is force_in_building temporary?Don't know this one. But only use_mode=0 or use_mode=2 supported and you can use it with use_mode==0 force_in_building. Does not help that both are my scripts :< (https://github.com/DFHack/scripts/search?q=moveToBuilding&unscoped_q=moveToBuilding). The lever one creates a lever with real mechanisms.
How do I check the type of a block_events struct in Lua? I want the one of type df.block_square_event_type.frozen_liquid (if it exists for the block.)
I see the line <vmethod ret-type='block_square_event_type' name='getType'/> in df/structures, but I'm sure if I can call that in Lua.
As for onJobCompleted, looking at reaction-trigger it says that the frequency for the event needs to be set to 0 so that it ignores cancelled jobs. What does a frequency of 0 mean? I was under the impression that the frequency for the eventful stuff was just how ever many ticks in between the check (e.g. item-trigger has a 5 for INVENTORY_CHANGE, so every 5 ticks it checks for inventory changes), so is 0 a special case?
You can just do "event:getType()" as lua supports vmethods (even if it does not enumerate them when printing members).
1. dfhack.gui.getDepthAt(x, y) - This states that it "Returns the distance from the z-level of the tile at map coordinates (x, y) to the closest ground z-level below". I'm not really sure what it is tying to say, since it doesn't take a z level as an argument.Like Warmist said, it's for TWBT. Maybe the docs should read "distance in z-levels" or something. Basically, it's supposed to take in x and y map coordinates, and calculate the number of z-levels you would need to move down to reach a solid tile. This only happens when TWBT or another plugin is enabled that implements this behavior, though. The default behavior (https://github.com/DFHack/dfhack/blob/0.47.04-r2/library/modules/Gui.cpp#L1834) is to always return 0.
3. dfhack.items.moveToBuilding(item,building[,use_mode[,force_in_building]) - When using force_in_building, should use_mode = 2? Is force_in_building temporary?force_in_building is only used here (https://github.com/DFHack/dfhack/blob/0.47.04-r2/library/modules/Items.cpp#L1008). It looks to me like the opposite of what you said - if you want the item to be "in" the building, and don't set use_mode = 2, you'll need to set force_in_building = true. I'm not very familiar with why this behavior makes sense, though.
4. dfhack.buildings.checkFreeTiles(pos,size[,extents,change_extents,allow_occupied]) - What is the format for size?From its definition (https://github.com/DFHack/dfhack/blob/0.47.04-r2/library/modules/Buildings.cpp#L575), it takes a df::coord2d. Not sure off the top of my head whether there's a shorthand, but passing in a table with "x" and "y" keys set might work.
How do I check the type of a block_events struct in Lua? I want the one of type df.block_square_event_type.frozen_liquid (if it exists for the block.)A couple other ways, in addition to what Warmist mentioned:
I see the line <vmethod ret-type='block_square_event_type' name='getType'/> in df/structures, but I'm sure if I can call that in Lua.
Is there a proper way to do this, or should I just check for the existence of a "tiles" array (which only the frozen_liquid type seems to have?)
df.block_square_event_frozen_liquidst:is_instance(event)
event._type == df.block_square_event_frozen_liquidst
Found something odd in MapCache.cpp: (https://github.com/DFHack/dfhack/blob/f2b0f012c9895aa3fd743ad50fab9e6fb78b479d/library/modules/MapCache.cpp#L855)At first glance, I think it's intentional. In the soil case, it looks up the material at the specified position. If it happens to actually be stone, instead of soil, then it tries to look up the default soil type, and uses that for the return value instead if it's available.
<snip>
The checks for G_SOIL and G_STONE seem to be swapped. Is this a mistake or not?
Are you seeing behavior that would suggest that this is incorrect, or did this just look surprising from a read-through?
Not quite the place to do this, I know, but could the self-closing tag stuff (https://github.com/DFHack/dfhack/releases/tag/0.47.04-r2) be related to some of the problems (http://www.bay12forums.com/smf/index.php?topic=154617.375) that have arisen, or not? *Legends Viewer hasn't been updated to parse things that export differently from before. The tool can handle a certain amount of things it can't recognize (according to my experience), but when the amount is too large it goes belly up. Changing types from boolean to tag present/absent is one of the things the tools does not recognize (it can be noted that Toady uses "self closing tags" in his standard exports, so the export-legends changes were made to use the same logic while also cutting down on the amount of useless fluff exported [all of those tags that were previously False are now simply not present]).
(*Exibit 2. (https://www.reddit.com/r/dwarffortress/comments/i9bh6c/biweekly_df_questions_thread/g1e1438/))
Haha, yeah I had a lot of things on my plate the last few month.Not quite the place to do this, I know, but could the self-closing tag stuff (https://github.com/DFHack/dfhack/releases/tag/0.47.04-r2) be related to some of the problems (http://www.bay12forums.com/smf/index.php?topic=154617.375) that have arisen, or not? *Legends Viewer hasn't been updated to parse things that export differently from before. The tool can handle a certain amount of things it can't recognize (according to my experience), but when the amount is too large it goes belly up. Changing types from boolean to tag present/absent is one of the things the tools does not recognize (it can be noted that Toady uses "self closing tags" in his standard exports, so the export-legends changes were made to use the same logic while also cutting down on the amount of useless fluff exported [all of those tags that were previously False are now simply not present]).
(*Exibit 2. (https://www.reddit.com/r/dwarffortress/comments/i9bh6c/biweekly_df_questions_thread/g1e1438/))
That change is in addition to things that is exported differently because the mapping of DF structures has progressed.
As far as I understand, Legends Viewer is a bit on the back burner because Real Life is making demands.
I noticed that dfhack has a new "unretire-anyone" script. It sounds pretty crazy, since you could unretire necromancers or werebeast or something. Has anybody experimented with it?
you could probably get deities if you give them a nemesis id which just fully gives them a unit when they load in so with a few pokes and prods you could probably play as any historical figure in the game. which from what I can tell form testing legit gives them a body based off their historical figure descriptions.I noticed that dfhack has a new "unretire-anyone" script. It sounds pretty crazy, since you could unretire necromancers or werebeast or something. Has anybody experimented with it?
I'm the author of that particular script, so I'd say I've experimented with it quite a bit. It enables you to play adventure mode as any living (or undead) historical figure with the exception of deities (as they currently lack physical manifestations). This includes necromancers, werewolves, fortress citizens, random villagers, nobles, important animals, forgotten beasts, various other monstrosities, and anyone else mentioned in Legends mode (as well as a number of unlisted individuals who wouldn't have done anything noteworthy yet). People will interact with you as appropriate, and you should have access to whatever powers and knowledge the figure possesses (as well as their limitations; it turns out that cats aren't very good at opening doors). That said, be aware that this won't give you a whole lot of additional functionality beyond that which the game normally provides; playing as a monarch, for example, won't enable you to do anything especially significant with your status at present.
Thanks all for the answers to my previous questions, I've got a couple new one now. Does ``dfhack.job.removeWorker(job,timeout)`` add a timeout to the worker stopping them from picking up the job again, or does it put the cooldown on the job, stopping all units from picking up the job again?It calls setJobCooldown (https://github.com/DFHack/dfhack/blob/0.47.04-r2/library/modules/Job.cpp#L277) internally, which either creates (https://github.com/DFHack/dfhack/blob/0.47.04-r2/library/modules/Job.cpp#L289-L292) or updates (https://github.com/DFHack/dfhack/blob/0.47.04-r2/library/modules/Job.cpp#L296-L297) an entry in job_claim_suppress. Looks like it prevents the unit from picking up a job at that workshop for the specified cooldown period. From the API docs (https://docs.dfhack.org/en/0.47.04-r2/docs/Lua%20API.html#job-module), I can see why it wouldn't be clear that removeWorker uses the same logic, so I'll update that.
Thanks all for the answers to my previous questions, I've got a couple new one now. Does ``dfhack.job.removeWorker(job,timeout)`` add a timeout to the worker stopping them from picking up the job again, or does it put the cooldown on the job, stopping all units from picking up the job again?It calls setJobCooldown (https://github.com/DFHack/dfhack/blob/0.47.04-r2/library/modules/Job.cpp#L277) internally, which either creates (https://github.com/DFHack/dfhack/blob/0.47.04-r2/library/modules/Job.cpp#L289-L292) or updates (https://github.com/DFHack/dfhack/blob/0.47.04-r2/library/modules/Job.cpp#L296-L297) an entry in job_claim_suppress. Looks like it prevents the unit from picking up a job at that workshop for the specified cooldown period. From the API docs (https://docs.dfhack.org/en/0.47.04-r2/docs/Lua%20API.html#job-module), I can see why it wouldn't be clear that removeWorker uses the same logic, so I'll update that.
(By the way, you can use [tt] on the forums to get preformatted text, like this)
I cannot for the life of me, get the exportlegends.lua to work properly on linux. It exports everything properly right up until we get to legends_plus xml in which it gets an error, saying simply "exportlegends: Could not move into the save folder." I'm guessing its something involving the feature of moving the legends into a folder instead of just plopping it in the dwarf fortress directory. I wish I could give more of an error report, but this is all I get when I export legends:Spoiler (click to show/hide)
:lua print(dfhack.getDFPath())
:lua print(dfhack.filesystem.chdir(dfhack.getDFPath()))
The first should print the path to DF - you don't have to include its output if it contains anything personal, but try creating a file in that folder and see if you're able to.My best guess is that this has something to do with permissions - can you run these two commands?Code: [Select]:lua print(dfhack.getDFPath())
The first should print the path to DF - you don't have to include its output if it contains anything personal, but try creating a file in that folder and see if you're able to.
:lua print(dfhack.filesystem.chdir(dfhack.getDFPath()))
The second should print either "true" or "false", and I'd like to know what it says.
The strange thing is that the code that's failing in this case has already run once, before the "Exporting: World map/gen info" message, so I'm not sure why it would fail when run a second time.
Some other things to check would be whether you can create a new file in the "legends-region1-00150-01-01" folder, and whether you have any free disk space on whatever disk that folder is on.
You can also pass a directory as the second argument to exportlegends, so as a possible workaround, "exportlegends info ." should avoid changing directories at all. I'd still be interested in getting to the bottom of this, if you don't mind. I've been testing on Linux and have yet to reproduce this issue (with either "info" or "custom").
How exactly can I pass a directory as the second argument to exportlegends? It's currently very late over here and my mind is a blank on how to do that. lol
exportlegends info .
(the dot at the end is the second argument):lua print(dfhack.filesystem.chdir("legends-region1-00150-01-01"))
:lua print(dfhack.filesystem.chdir(dfhack.getDFPath()))
You could run them a couple times, and each command should print "true", as long as you run them in this order. You should end with the last one to be safe - if you don't, you'll leave DF in a directory it doesn't expect, which will probably cause it to crash. (Maybe do this without a save loaded... it shouldn't make a difference, anyway.)For more troubleshooting, can you try these commands?Code: [Select]:lua print(dfhack.filesystem.chdir("legends-region1-00150-01-01"))
:lua print(dfhack.filesystem.chdir(dfhack.getDFPath()))
I gave an example, although it may have been hard to read with the quotes. Here it is again but with code formatting:Code: [Select]exportlegends info .
(the dot at the end is the second argument)
Weird. Even when you run them more than once? I have no idea why exportlegends could be failing, let alone reliably.For more troubleshooting, can you try these commands?Code: [Select]:lua print(dfhack.filesystem.chdir("legends-region1-00150-01-01"))
:lua print(dfhack.filesystem.chdir(dfhack.getDFPath()))
Both came out as true!
This actually gave me an error list! Here it is:This one also doesn't make sense, although it's an unrelated error. abstract_building_templest has a deity_type field in both DFHack 0.47.04-r1 and r2 (I checked the release builds of both).Spoiler (click to show/hide)
:lua print(df.abstract_building_templest:new().deity_type)
This one also doesn't make sense, although it's an unrelated error. abstract_building_templest has a deity_type field in both DFHack 0.47.04-r1 and r2 (I checked the release builds of both).
How did you obtain DFHack? What version of DFHack are you using (run "help" if you don't know)?
Can you try running the following command (it should print -1)?Code: [Select]:lua print(df.abstract_building_templest:new().deity_type)
My Bitdefender keeps deleting my dfhack-run.exe saying that it contains a Trojan.GenericKD.34432417...Should I worry?
My Bitdefender keeps deleting my dfhack-run.exe saying that it contains a Trojan.GenericKD.34432417...Should I worry?Probably not, although if you're not downloading from https://github.com/dfhack/dfhack/releases, you could download a copy from there and check that its dfhack-run.exe matches yours.
Does that mean you could have a DFHack console running on one computer, working with Dwarf Fortress running on another computer?Sort of, although it wouldn't exactly be the DFHack console - dfhack-run needs to be run from a system command prompt, only runs one command at a time, and doesn't support interactive commands (like tiletypes, lua, liquids, etc. with no arguments).
Ah, yeah, all of those issues were related to things that changed between alpha0 and r2, so no need to troubleshoot further if it works in r2. No worries.Both the one from LNP and from your link get flagged.My Bitdefender keeps deleting my dfhack-run.exe saying that it contains a Trojan.GenericKD.34432417...Should I worry?Probably not, although if you're not downloading from https://github.com/dfhack/dfhack/releases, you could download a copy from there and check that its dfhack-run.exe matches yours.
It's interesting that dfhack-run.exe would be flagged, of all things. All it does is allow you to run commands by connecting to DFHack over the local network. You don't strictly need it for DFHack to work, unless you want that feature, so it's up to you whether you decide to keep it.
The one from GitHub was built automatically, so I'm pretty confident that it hasn't been tampered with. But use your best judgement. If it helps, this isn't the first false positive we've had reported, but the majority of users seem to not run into any. (I was suggesting that you check that the file you have is identical to the one from GitHub, not just whether both of them get flagged.)Admittedly I'm not entirely sure how to check that, but out of curiousity I downloaded the github zip on my two old laptops, one with Bitdefender, another with Windows Defender and they both flagged it.
It's not too surprising, since DFHack does do everything a virus does. In a sense it is a literal trojan horse: it impersonates another piece of code (SDL.dll) for its own nefarious purposes (modifying working memory... of a video game, to be fair)Right, the surprising part is that (only?) dfhack-run was flagged, which doesn't do that.
Admittedly I'm not entirely sure how to check that, but out of curiousity I downloaded the github zip on my two old laptops, one with Bitdefender, another with Windows Defender and they both flagged it.You could check the hash of both files or use some file-comparison tool.
I first downloaded LNP last Sunday and everything was fine until yesterday.
It's not too surprising, since DFHack does do everything a virus does. In a sense it is a literal trojan horse: it impersonates another piece of code (SDL.dll) for its own nefarious purposes (modifying working memory... of a video game, to be fair)Right, the surprising part is that (only?) dfhack-run was flagged, which doesn't do that.Admittedly I'm not entirely sure how to check that, but out of curiousity I downloaded the github zip on my two old laptops, one with Bitdefender, another with Windows Defender and they both flagged it.You could check the hash of both files or use some file-comparison tool.
I first downloaded LNP last Sunday and everything was fine until yesterday.
I suspect that your anti-virus definitions got updated recently and triggered this, which can happen pretty often depending on the software you're using. Redownloading like you did is probably the best way to ensure that something didn't actually change the file itself.
Hey there folks. Just curious if there is a way to utilize the buildingplan plug so that every time I close DF it saves the parameters. Every time I open the game I have to reset all the build-able items and their materials, etc.That's currently not supported, at least as far as I know (most plugins don't support this). It may be worth putting up a feature request for it on the issue tracker (https://github.com/dfhack/dfhack/issues) if it would be a good feature to have. I'd be happy to help anyone interested in working on this, although I may not have a lot of time for it myself in the near future. There have been some (sort of) recent persistent storage changes that might make things like this easier, although part of it also depends on how complicated the buildingplan settings are to save/load.
Another/additional option for persisting buildingplan filters could be to use json-based export/import like the orders plugin. Not quite as automatic as persistent data (unless we auto-export on save and auto-import on load), but more useful for copying across forts.
I do have some dev tasks for buildingplan on my short list for quickfort integration:If filter persistence is still unimplemented when I'm done with that, I could look into implementing it.
- respect item filters that are passed in from the api
- support all teh buildings (starting with constructions)
Might be a while, though. There might be a raft of bug reports for quickfort when it goes out in the next release that I'll need to focus on.
Another/additional option for persisting buildingplan filters could be to use json-based export/import like the orders plugin. Not quite as automatic as persistent data (unless we auto-export on save and auto-import on load), but more useful for copying across forts.
I do have some dev tasks for buildingplan on my short list for quickfort integration:If filter persistence is still unimplemented when I'm done with that, I could look into implementing it.
- respect item filters that are passed in from the api
- support all teh buildings (starting with constructions)
Might be a while, though. There might be a raft of bug reports for quickfort when it goes out in the next release that I'll need to focus on.
It's fairly easy to auto-save/auto-load json files using the import/export stuff. Depending on how the data for the plugin is stored it may only be a few lines of code.
It's fairly easy to auto-save/auto-load json files using the import/export stuff. Depending on how the data for the plugin is stored it may only be a few lines of code.
It's fairly easy to auto-save/auto-load json files using the import/export stuff. Depending on how the data for the plugin is stored it may only be a few lines of code.
Are you talking about Lua-side persistent tables? Unfortunately it's not nearly as trivial in C++, because C++ types can't be serialized by default - plugins have to serialize their own data manually, which is why a lot of them don't. For example, here (https://github.com/DFHack/dfhack/blob/8728056674c7249f1a15f80c62e3ded16507bb56/plugins/autochop.cpp#L196-L231) is what autochop does to save/load its config. Granted, it still uses the old histfig-based system, which is now a wrapper around the new JSON-based system - the new system would result in code that's a bit more readable, but just as verbose.
hey uh sorry to sound like a n00b but i was trying to install dfhack to go with the most recent version of the game instead of downloading the LNP since the most recent DF version is updated when i do a system update and i didn't see a point in downloading another copy via the pack. i think i installed it properly, but when i run it it only gives me the builtin commands and no scripts or plugins.Did you install dfhack manually? Or did you use an AUR package (https://aur.archlinux.org/packages/?O=0&K=dfhack)?the stderr.logSpoiler (click to show/hide)
i'm running arch linux (which i didn't set up my brother did, he's more computer savvy than i am)
i installed it manually, i didn't feel like running a -Syu yet. i can find libdfhack.so, and the scripts and plugins folders have things in them.Ok, thanks, disregard my speculation in my earlier post. I probably should have asked "is libdfhack.so in the hack folder", although I think that's what you meant.
find . -path ./data/save -prune -or -print
i installed it manually, i didn't feel like running a -Syu yet. i can find libdfhack.so, and the scripts and plugins folders have things in them.Installing things manually on Arch is usually a bad idea especially with DF being installed as a package. The DF package installs things to /opt/dwarffortess and includes a script to make sure everthing is linked in ~/.dwarffortress and that the game is run with the correct working directory. The dfhack package works the same way and makes sure dfhack can find all its and DF's files.
yeah, i figured it was gonna be something like that. i'll run a -Syu when mom goes to bed and grab it that way. (our internet is Not Great)i installed it manually, i didn't feel like running a -Syu yet. i can find libdfhack.so, and the scripts and plugins folders have things in them.Installing things manually on Arch is usually a bad idea especially with DF being installed as a package. The DF package installs things to /opt/dwarffortess and includes a script to make sure everthing is linked in ~/.dwarffortress and that the game is run with the correct working directory. The dfhack package works the same way and makes sure dfhack can find all its and DF's files.
it's giving me 'target not found' when i try to install dfhack as a package ugh i'll have to bother my brother about it tomorrow.Yeah, dfhack is in the AUR (https://wiki.archlinux.org/index.php/Arch_User_Repository), not the official repositories. Another option is to install DF manually (https://dwarffortresswiki.org/index.php/DF2014:Installation#Manual_or_multiple_installations) to a directory in your home directory and install dfhack to there.i should probably change my OS to something less complicated but idk how to do that either so ::)
at that point i'd be better off just installing the latest LNPit's giving me 'target not found' when i try to install dfhack as a package ugh i'll have to bother my brother about it tomorrow.Yeah, dfhack is in the AUR (https://wiki.archlinux.org/index.php/Arch_User_Repository), not the official repositories. Another option is to install DF manually (https://dwarffortresswiki.org/index.php/DF2014:Installation#Manual_or_multiple_installations) to a directory in your home directory and install dfhack to there.i should probably change my OS to something less complicated but idk how to do that either so ::)
you could probably get deities if you give them a nemesis id which just fully gives them a unit when they load in so with a few pokes and prods you could probably play as any historical figure in the game. which from what I can tell form testing legit gives them a body based off their historical figure descriptions.I noticed that dfhack has a new "unretire-anyone" script. It sounds pretty crazy, since you could unretire necromancers or werebeast or something. Has anybody experimented with it?
I'm the author of that particular script, so I'd say I've experimented with it quite a bit. It enables you to play adventure mode as any living (or undead) historical figure with the exception of deities (as they currently lack physical manifestations). This includes necromancers, werewolves, fortress citizens, random villagers, nobles, important animals, forgotten beasts, various other monstrosities, and anyone else mentioned in Legends mode (as well as a number of unlisted individuals who wouldn't have done anything noteworthy yet). People will interact with you as appropriate, and you should have access to whatever powers and knowledge the figure possesses (as well as their limitations; it turns out that cats aren't very good at opening doors). That said, be aware that this won't give you a whole lot of additional functionality beyond that which the game normally provides; playing as a monarch, for example, won't enable you to do anything especially significant with your status at present.
which means a cat god will look like a cat, and a goblin god will look like a goblin.
Can you play as a ghost or reanimated person?
which can be solved by toggling the flier enemy caste flag on the adventurerCan you play as a ghost or reanimated person?
Yeah, I believe I had tested both ghosts and undead at some point (in fact, I distinctly recall fixing the way their names are displayed on the selection list), but it's been a while now. It should be possible so long as such a person exists as a historical figure in your world. I don't think that ghosts manifest during world generation, so you'd probably need to produce one during fortress mode first.
It's also possible to turn any adventurer into a ghost via the "ghostly" command, in case you're not aware of it.
Note that there's currently an issue with attempting to fly as a ghostly non-flying creature, whereby your adventurer gets stuck in mid-air, unable to be controlled.
for k,v in pairs(df.global.world.units.active) do
local HF=df.global.world.history.figures
if v.hist_figure_id==-1 then break else
for c,q in pairs(HF[v.hist_figure_id].entity_links) do
if q.entity_id==df.global.ui.civ_id and v.population_id==-1 and q.entity_id~=df.global.ui.group_id then
print(v.hist_figure_id)
HF[v.hist_figure_id].entity_links:insert("#",{new=df.histfig_entity_link_memberst})
HF[v.hist_figure_id].entity_links[c].entity_id=df.global.ui.group_id
HF[v.hist_figure_id].entity_links[c].link_strength=100
end
end
end
end
--this script should add labors to different race migrants that arrived from animal token entity modding
[lua]# ~df.global.world.raws.buildings.all[0]
<building_def_workshopst: 0000025F649D4010>
code = SOAP_MAKER
id = 0
name = Soap Maker's Workshop
building_type = 13
building_subtype = 23
name_color = <int16_t[]: 0000025F649D4068>
tile = <uint8_t[31][31][]: 0000025F649D406E>
tile_color = <uint8_t[31][31][4][]: 0000025F649D4F72>
tile_block = <uint8_t[31][]: 0000025F649D7C7E>
build_key = 150
needs_magma = false
build_items = <vector<building_def_item*>[2]: 0000025F649D8048>
dim_x = 3
dim_y = 3
workloc_x = 1
workloc_y = 1
build_labors = <vector<unit_labor>[1]: 0000025F649D8070>
labor_description = Soap Making
build_stages = 3
Script to detect equipment corruption, attempting to fix equipment that's not assigned.I'd say it's worth including, maybe under "devel/" if it doesn't actually fix things, or under "fix/" if it does.
...
Based on Toady's answer in FotF, but the script's testing has been limited to verifying that a corrupt entry doesn't reappear.
Note that the script doesn't try to fix corrupt assigned equipment I've seen that in a save), as that likely requires additional handling to deal with the unit to which the equipment has been assigned.
So here is a question I thought of recently. How difficult would it be to create fake entries for the built-in buildings and reactions? What I mean is, the custom building information can be accessed like;You're referring to the craftdwarf's workshop (https://dwarffortresswiki.org/index.php/DF2014:Craftsdwarf%27s_workshop) in this case, right?
...
but there isn't a similar structure for the Leatherworker. It would be great if there was a way to access the information for the built-in stuff the same way I can with the custom ones. I know they would need to be hand built, but I'm just wondering if it would even be possible, and if possible how much work it would be.
So here is a question I thought of recently. How difficult would it be to create fake entries for the built-in buildings and reactions? What I mean is, the custom building information can be accessed like;You're referring to the craftdwarf's workshop (https://dwarffortresswiki.org/index.php/DF2014:Craftsdwarf%27s_workshop) in this case, right?
...
but there isn't a similar structure for the Leatherworker. It would be great if there was a way to access the information for the built-in stuff the same way I can with the custom ones. I know they would need to be hand built, but I'm just wondering if it would even be possible, and if possible how much work it would be.
You should be able to create a building_def_workshopst instance with "df.building_def_workshopst:new()" and assign its fields to whatever values you want; putting this instance in world.raws.buildings.all will likely cause all sorts of issues, though. Figuring out the right values for every field could also be hard. I know Quietust has reverse-engineered raws for some built-in materials, like amber (https://dwarffortresswiki.org/index.php/DF2014:Amber), but not workshops to my knowledge. Maybe you could get some of the right values from a built workshop, but other than that, I'm not sure.
Is there anything in particular that you need this information for? There may be another way to get at the relevant portions of it.
It is kind of strange though, you would think that the built-in buildings/reactions/materials etc... would still need to have a similar structure to the custom ones that the game accesses. I guess they are probably just stored in a different area that isn't mapped or isn't accessible.It's possible that they're stored somewhere else that isn't mapped - if we know enough values, I could probably come up with a script to search for a matching building_def_workshopst instance somewhere in memory.
Builtin workshops populate the "Add Job" menu using the buildingst::fillSidebarMenu() vmethod, which creates instances of various interface_button_buildingst subclasses and inserts them into ui_sidebar_menus.workshop_job.choices_all[]. Exactly which jobs are added depends on the workshop/furnace's subtype, and some workshops don't add anything because their UI is 100% hardcoded (and they typically don't permit custom reactions either).It is kind of strange though, you would think that the built-in buildings/reactions/materials etc... would still need to have a similar structure to the custom ones that the game accesses. I guess they are probably just stored in a different area that isn't mapped or isn't accessible.It's possible that they're stored somewhere else that isn't mapped - if we know enough values, I could probably come up with a script to search for a matching building_def_workshopst instance somewhere in memory.
It's also possible that built-in workshop definitions are stored in a completely different format, or their fields' values are hardcoded wherever the corresponding fields of custom workshops are looked up.
local function allocateNewChunk(hist_entity)
hist_entity.save_file_id = df.global.unit_chunk_next_id
df.global.unit_chunk_next_id = df.global.unit_chunk_next_id+1
hist_entity.next_member_idx = 0
print("allocating chunk:",hist_entity.save_file_id)
end
local function allocateIds(nemesis_record,hist_entity)
if hist_entity.next_member_idx == 100 then
allocateNewChunk(hist_entity)
end
nemesis_record.save_file_id = hist_entity.save_file_id
nemesis_record.member_idx = hist_entity.next_member_idx
hist_entity.next_member_idx = hist_entity.next_member_idx+1
end
function createFigure(unit,he,he_group)
local hf = df.historical_figure:new()
hf.id = df.global.hist_figure_next_id
df.global.hist_figure_next_id = df.global.hist_figure_next_id+1
hf.unit_id = unit.id
hf.nemesis_id = -1
hf.race = unit.race
hf.caste = unit.caste
hf.profession = unit.profession
hf.sex = unit.sex
hf.name:assign(unit.name)
hf.appeared_year = df.global.cur_year
hf.born_year = unit.birth_year
hf.born_seconds = unit.birth_time
hf.curse_year = unit.curse_year
hf.curse_seconds = unit.curse_time
hf.birth_year_bias = unit.birth_year_bias
hf.birth_time_bias = unit.birth_time_bias
hf.old_year = unit.old_year
hf.old_seconds = unit.old_time
hf.died_year = -1
hf.died_seconds = -1
hf.civ_id = unit.civ_id
hf.population_id = unit.population_id
hf.breed_id = -1
hf.cultural_identity = unit.cultural_identity
hf.family_head_id = -1
df.global.world.history.figures:insert("#", hf)
hf.info = df.historical_figure_info:new()
hf.info.whereabouts = df.historical_figure_info.T_whereabouts:new()
hf.info.whereabouts.death_condition_parameter_1 = -1
hf.info.whereabouts.death_condition_parameter_2 = -1
-- set values that seem related to state and do event
--change_state(hf, dfg.ui.site_id, region_pos)
--let's skip skills for now
--local skills = df.historical_figure_info.T_skills:new() -- skills snap shot
-- ...
-- note that innate skills are automaticaly set by DF
hf.info.skills = {new=true}
if he then
he.histfig_ids:insert('#', hf.id)
he.hist_figures:insert('#', hf)
hf.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=unit.civ_id,link_strength=100})
--add entity event
local hf_event_id = df.global.hist_event_next_id
df.global.hist_event_next_id = df.global.hist_event_next_id+1
df.global.world.history.events:insert("#",{new=df.history_event_add_hf_entity_linkst,year=unit.birth_year,
seconds=unit.birth_time,id=hf_event_id,civ=hf.civ_id,histfig=hf.id,link_type=0})
end
if he_group and he_group ~= he then
he_group.histfig_ids:insert('#', hf.id)
he_group.hist_figures:insert('#', hf)
--hf.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=he_group.id,link_strength=100})
end
local soul = unit.status.current_soul
if soul then
hf.orientation_flags:assign(soul.orientation_flags)
end
unit.flags1.important_historical_figure = true
unit.flags2.important_historical_figure = true
unit.hist_figure_id = hf.id
unit.hist_figure_id2 = hf.id
return hf
end
function createNemesis(unit,civ_id,group_id)
local id = df.global.nemesis_next_id
local nem = df.nemesis_record:new()
nem.id = id
nem.unit_id = unit.id
nem.unit = unit
nem.flags:resize(31)
nem.unk10 = -1
nem.unk11 = -1
nem.unk12 = -1
df.global.world.nemesis.all:insert("#",nem)
df.global.nemesis_next_id = id+1
unit.general_refs:insert("#",{new = df.general_ref_is_nemesisst, nemesis_id = id})
nem.save_file_id = -1
local he
if civ_id and civ_id ~= -1 then
he = df.historical_entity.find(civ_id)
he.nemesis_ids:insert("#",id)
he.nemesis:insert("#",nem)
allocateIds(nem,he)
end
local he_group
if group_id and group_id ~= -1 then
he_group = df.historical_entity.find(group_id)
end
if he_group then
he_group.nemesis_ids:insert("#",id)
he_group.nemesis:insert("#",nem)
end
nem.figure = unit.hist_figure_id ~= -1 and df.historical_figure.find(unit.hist_figure_id) or createFigure(unit,he,he_group) -- the histfig check is there just in case this function is called by another script to create nemesis data for a historical figure which somehow lacks it
nem.figure.nemesis_id = id
return nem
end
function sigh()
for k,v in pairs(df.global.world.units.active) do
local HF=df.global.world.history.figures
local HI=df.global.ui.civ_id
local EN=df.global.world.entities.all[HI]
local HP=df.global.ui.group_id
if v.hist_figure_id==-1 and v.civ_id==df.global.ui.civ_id then
createNemesis(v,HI,HP)
--createFigure(v,EN,HP)
print(k)
end
end
end
for k,v in pairs(df.global.world.units.active) do
local HFN=df.global.world.history.figures
local HN=df.global.ui.civ_id
sigh()
if v.hist_figure_id==-1 then break else
for c,q in pairs(HFN[v.hist_figure_id].entity_links) do
if q.entity_id==df.global.ui.civ_id and v.population_id==-1 and q.entity_id~=df.global.ui.group_id then
print(v.hist_figure_id)
HFN[v.hist_figure_id].entity_links:insert("#",{new=df.histfig_entity_link_memberst})
HFN[v.hist_figure_id].entity_links[c].entity_id=df.global.ui.group_id
HFN[v.hist_figure_id].entity_links[c].link_strength=100
end
end
end
end
ok so spent a deep amount of time trying to solve the animal token sapient migrants not having labors in a fort with this.Builtin workshops populate the "Add Job" menu using the buildingst::fillSidebarMenu() vmethod, which creates instances of various interface_button_buildingst subclasses and inserts them into ui_sidebar_menus.workshop_job.choices_all[]. Exactly which jobs are added depends on the workshop/furnace's subtype, and some workshops don't add anything because their UI is 100% hardcoded (and they typically don't permit custom reactions either).It is kind of strange though, you would think that the built-in buildings/reactions/materials etc... would still need to have a similar structure to the custom ones that the game accesses. I guess they are probably just stored in a different area that isn't mapped or isn't accessible.It's possible that they're stored somewhere else that isn't mapped - if we know enough values, I could probably come up with a script to search for a matching building_def_workshopst instance somewhere in memory.
It's also possible that built-in workshop definitions are stored in a completely different format, or their fields' values are hardcoded wherever the corresponding fields of custom workshops are looked up.
I actually took advantage of this (https://github.com/quietust/dfhack-23a/blob/master/plugins/more_jobs.cpp) in my DFHack backport for version 0.23.130.23a to add the ability to craft wooden blocks and trap components, glass trap components, and metal mechanisms.
require "dfhack.buildings"
-- loop over built in workshops
bldg = dfhack.buildings.constructBuilding({pos=pos,type=13,subtype=#,custom=-1,filters={{},{}}})
-- get building information here
dfhack.buildings.deconstruct(bldg)
-- end loop
I can access the required information to create my own <building_def_workshopst> refs for the vanilla buildings. Running this when the game starts up is fairly quick and I get almost everything needed in a more automatic method.
Interesting... The more I think about it, the more I feel like the values for the built-in workshops are probably just hardcoded somewhere and there really isn't a way to automatically look up the values.There's no "probably" involved - if you disassemble the 64-bit Windows version of 0.47.04, the relevant function is at address 0x14045f210, though I'll warn you that it doesn't use a convenient data table, but instead a giant switch() statement filled with code like this:
interface_button_building_new_jobst *button;
button = create_interface_button_building_new_jobst();
button->hotkey_id = HOTKEY_FARMER_PROCESS;
button->building = this;
button->job_type = ProcessPlants;
button = create_interface_button_building_new_jobst();
button->hotkey_id = HOTKEY_FARMER_PROCESS_VIAL;
button->building = this;
button->job_type = ProcessPlantsVial;
button = create_interface_button_building_new_jobst();
button->hotkey_id = HOTKEY_FARMER_PROCESS_BARREL;
button->building = this;
button->job_type = ProcessPlantsBarrel;
button = create_interface_button_building_new_jobst();
button->hotkey_id = HOTKEY_FARMER_CHEESE;
button->building = this;
button->job_type = MakeCheese;
button = create_interface_button_building_new_jobst();
button->hotkey_id = HOTKEY_FARMER_MILK;
button->building = this;
button->job_type = MilkCreature;
button = create_interface_button_building_new_jobst();
button->hotkey_id = HOTKEY_FARMER_SHEAR_CREATURE;
button->building = this;
button->job_type = ShearCreature;
button = create_interface_button_building_new_jobst();
button->hotkey_id = HOTKEY_FARMER_SPIN_THREAD;
button->building = this;
button->job_type = SpinThread;
button->material_category = job_material_category_strand;
As you can probably guess, the above code was taken from the Farmer's Workshop, and it's simple because everything is hardcoded. Some of the others are more complicated, though - for example, the Metalsmith's Forge creates multiple submenus to select the Item Category (weapon/armor/furniture/etc.), the Material (determined based on your civilization and the item category), and finally the Job (also determined based on your civilization and the item category).
[115/578] Building C object depends/libzip/lib/CMakeFiles/zip.dir/zip_add.c.o
FAILED: depends/libzip/lib/CMakeFiles/zip.dir/zip_add.c.o
ccache /usr/bin/cc -DHAVE_CUCHAR -DLINUX_BUILD -DLUA_BUILD_AS_DLL -DPROTOBUF_USE_DLLS -D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_CXX11_ABI=0 -D_LINUX -I../depends/protobuf -I../depends/lua/include -I../depends/md5 -I../depends/tinyxml -I../depends/tthread -I../depends/clsocket/src -I../depends/xlsxio/include -I../depends/libzip/lib -Idepends/libzip -fvisibility=hidden -mtune=generic -m32 -march=i686 -O3 -DNDEBUG -fPIC -MD -MT depends/libzip/lib/CMakeFiles/zip.dir/zip_add.c.o -MF depends/libzip/lib/CMakeFiles/zip.dir/zip_add.c.o.d -o depends/libzip/lib/CMakeFiles/zip.dir/zip_add.c.o -c ../depends/libzip/lib/zip_add.c
In file included from depends/libzip/config.h:4,
from ../depends/libzip/lib/zipint.h:37,
from ../depends/libzip/lib/zip_add.c:36:
depends/libzip/zipconf.h:28:10: warning: type defaults to ‘int’ in declaration of ‘zip_int16_t’ [-Wimplicit-int]
typedef zip_int16_t;
^~~~~~~~~~~
depends/libzip/zipconf.h:29:10: warning: type defaults to ‘int’ in declaration of ‘zip_uint16_t’ [-Wimplicit-int]
typedef zip_uint16_t;
^~~~~~~~~~~~
In file included from ../depends/libzip/lib/zipint.h:38,
from ../depends/libzip/lib/zip_add.c:36:
../depends/libzip/lib/compat.h:146:2: error: #error unsupported size of off_t
#error unsupported size of off_t
^~~~~
[122/578] Building C object depends/libexpat/expat/CMakeFiles/expat.dir/lib/xmltok.c.o
ninja: build stopped: subcommand failed.
grep SIZEOF_OFF_T ./depends/libzip/config.h
:The known bugs in the Embark Assistant have been fixed (or are at least believed to be fixed), although I don't remember if all went into -r2, or some may be in -r3 instead. No unknown bugs have been addressed, and that includes all bug reports sent telepathically.
Maybe there'll be some change to embark-assistant that'll cause it to find one of the embarks I'm looking for instead of always finding none.
:
What OS are you compiling on?
What compiler are you using?
In your build folder, what does running the following command print?Code: [Select]grep SIZEOF_OFF_T ./depends/libzip/config.h
/* #undef SIZEOF_OFF_T */
Edit2:I forgot to update master so it was still on r2. Fixed. For reference, you can see when the branch was last updated at https://github.com/dfhack/dfhack/tree/master - previously, it said "0.47.04-r2" and "August 8".
Tried doing it again with "git checkout master" and "git submodule update" to check out the master branch. It compiled just fine, but when I went to load it up it says it's version "0.47.04-r2 (release)".
help? :-\
Code: [Select]/* #undef SIZEOF_OFF_T */
What Linux distro are you using?
I forgot to update master so it was still on r2. Fixed. For reference, you can see when the branch was last updated at https://github.com/dfhack/dfhack/tree/master - previously, it said "0.47.04-r2" and "August 8".
CMake Warning at depends/jsoncpp-sub/src/lib_json/CMakeLists.txt:36 (MESSAGE):
Locale functionality is not supported
CMake Warning (dev) at depends/libzip/CMakeLists.txt:186 (find_package):
Policy CMP0074 is not set: find_package uses <PackageName>_ROOT variables.
Run "cmake --help-policy CMP0074" for policy details. Use the cmake_policy
command to set the policy and suppress this warning.
CMake variable ZLIB_ROOT is set to:
/usr/lib/i386-linux-gnu
For compatibility, CMake is ignoring the variable.
This warning is for project developers. Use -Wno-dev to suppress it.
?
[121/578] Building C object depends/libzip/lib/CMakeFiles/zip.dir/zip_add_dir.c.o
FAILED: depends/libzip/lib/CMakeFiles/zip.dir/zip_add_dir.c.o
ccache /usr/bin/cc -DHAVE_CUCHAR -DLINUX_BUILD -DLUA_BUILD_AS_DLL -DPROTOBUF_USE_DLLS -D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_CXX11_ABI=0 -D_LINUX -I../depends/protobuf -I../depends/lua/include -I../depends/md5 -I../depends/tinyxml -I../depends/tthread -I../depends/clsocket/src -I../depends/xlsxio/include -I../depends/libzip/lib -Idepends/libzip -fvisibility=hidden -mtune=generic -m32 -march=i686 -O3 -DNDEBUG -fPIC -MD -MT depends/libzip/lib/CMakeFiles/zip.dir/zip_add_dir.c.o -MF depends/libzip/lib/CMakeFiles/zip.dir/zip_add_dir.c.o.d -o depends/libzip/lib/CMakeFiles/zip.dir/zip_add_dir.c.o -c ../depends/libzip/lib/zip_add_dir.c
In file included from depends/libzip/config.h:4,
from ../depends/libzip/lib/zipint.h:37,
from ../depends/libzip/lib/zip_add_dir.c:36:
depends/libzip/zipconf.h:28:10: warning: type defaults to ‘int’ in declaration of ‘zip_int16_t’ [-Wimplicit-int]
typedef zip_int16_t;
^~~~~~~~~~~
depends/libzip/zipconf.h:29:10: warning: type defaults to ‘int’ in declaration of ‘zip_uint16_t’ [-Wimplicit-int]
typedef zip_uint16_t;
^~~~~~~~~~~~
In file included from ../depends/libzip/lib/zipint.h:38,
from ../depends/libzip/lib/zip_add_dir.c:36:
../depends/libzip/lib/compat.h:146:2: error: #error unsupported size of off_t
#error unsupported size of off_t
^~~~~
[122/578] Building C object depends/libzip/lib/CMakeFiles/zip.dir/zip_add_entry.c.o
FAILED: depends/libzip/lib/CMakeFiles/zip.dir/zip_add_entry.c.o
ccache /usr/bin/cc -DHAVE_CUCHAR -DLINUX_BUILD -DLUA_BUILD_AS_DLL -DPROTOBUF_USE_DLLS -D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_CXX11_ABI=0 -D_LINUX -I../depends/protobuf -I../depends/lua/include -I../depends/md5 -I../depends/tinyxml -I../depends/tthread -I../depends/clsocket/src -I../depends/xlsxio/include -I../depends/libzip/lib -Idepends/libzip -fvisibility=hidden -mtune=generic -m32 -march=i686 -O3 -DNDEBUG -fPIC -MD -MT depends/libzip/lib/CMakeFiles/zip.dir/zip_add_entry.c.o -MF depends/libzip/lib/CMakeFiles/zip.dir/zip_add_entry.c.o.d -o depends/libzip/lib/CMakeFiles/zip.dir/zip_add_entry.c.o -c ../depends/libzip/lib/zip_add_entry.c
In file included from depends/libzip/config.h:4,
from ../depends/libzip/lib/zipint.h:37,
from ../depends/libzip/lib/zip_add_entry.c:37:
depends/libzip/zipconf.h:28:10: warning: type defaults to ‘int’ in declaration of ‘zip_int16_t’ [-Wimplicit-int]
typedef zip_int16_t;
^~~~~~~~~~~
depends/libzip/zipconf.h:29:10: warning: type defaults to ‘int’ in declaration of ‘zip_uint16_t’ [-Wimplicit-int]
typedef zip_uint16_t;
^~~~~~~~~~~~
In file included from ../depends/libzip/lib/zipint.h:38,
from ../depends/libzip/lib/zip_add_entry.c:37:
../depends/libzip/lib/compat.h:146:2: error: #error unsupported size of off_t
#error unsupported size of off_t
^~~~~
[123/578] Building C object depends/libzip/lib/CMakeFiles/zip.dir/zip_add.c.o
FAILED: depends/libzip/lib/CMakeFiles/zip.dir/zip_add.c.o
ccache /usr/bin/cc -DHAVE_CUCHAR -DLINUX_BUILD -DLUA_BUILD_AS_DLL -DPROTOBUF_USE_DLLS -D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_CXX11_ABI=0 -D_LINUX -I../depends/protobuf -I../depends/lua/include -I../depends/md5 -I../depends/tinyxml -I../depends/tthread -I../depends/clsocket/src -I../depends/xlsxio/include -I../depends/libzip/lib -Idepends/libzip -fvisibility=hidden -mtune=generic -m32 -march=i686 -O3 -DNDEBUG -fPIC -MD -MT depends/libzip/lib/CMakeFiles/zip.dir/zip_add.c.o -MF depends/libzip/lib/CMakeFiles/zip.dir/zip_add.c.o.d -o depends/libzip/lib/CMakeFiles/zip.dir/zip_add.c.o -c ../depends/libzip/lib/zip_add.c
In file included from depends/libzip/config.h:4,
from ../depends/libzip/lib/zipint.h:37,
from ../depends/libzip/lib/zip_add.c:36:
depends/libzip/zipconf.h:28:10: warning: type defaults to ‘int’ in declaration of ‘zip_int16_t’ [-Wimplicit-int]
typedef zip_int16_t;
^~~~~~~~~~~
depends/libzip/zipconf.h:29:10: warning: type defaults to ‘int’ in declaration of ‘zip_uint16_t’ [-Wimplicit-int]
typedef zip_uint16_t;
^~~~~~~~~~~~
In file included from ../depends/libzip/lib/zipint.h:38,
from ../depends/libzip/lib/zip_add.c:36:
../depends/libzip/lib/compat.h:146:2: error: #error unsupported size of off_t
#error unsupported size of off_t
^~~~~
[126/578] Building C object depends/libzip/lib/CMakeFiles/zip.dir/zip_algorithm_deflate.c.o
FAILED: depends/libzip/lib/CMakeFiles/zip.dir/zip_algorithm_deflate.c.o
ccache /usr/bin/cc -DHAVE_CUCHAR -DLINUX_BUILD -DLUA_BUILD_AS_DLL -DPROTOBUF_USE_DLLS -D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_CXX11_ABI=0 -D_LINUX -I../depends/protobuf -I../depends/lua/include -I../depends/md5 -I../depends/tinyxml -I../depends/tthread -I../depends/clsocket/src -I../depends/xlsxio/include -I../depends/libzip/lib -Idepends/libzip -fvisibility=hidden -mtune=generic -m32 -march=i686 -O3 -DNDEBUG -fPIC -MD -MT depends/libzip/lib/CMakeFiles/zip.dir/zip_algorithm_deflate.c.o -MF depends/libzip/lib/CMakeFiles/zip.dir/zip_algorithm_deflate.c.o.d -o depends/libzip/lib/CMakeFiles/zip.dir/zip_algorithm_deflate.c.o -c ../depends/libzip/lib/zip_algorithm_deflate.c
In file included from depends/libzip/config.h:4,
from ../depends/libzip/lib/zipint.h:37,
from ../depends/libzip/lib/zip_algorithm_deflate.c:34:
depends/libzip/zipconf.h:28:10: warning: type defaults to ‘int’ in declaration of ‘zip_int16_t’ [-Wimplicit-int]
typedef zip_int16_t;
^~~~~~~~~~~
depends/libzip/zipconf.h:29:10: warning: type defaults to ‘int’ in declaration of ‘zip_uint16_t’ [-Wimplicit-int]
typedef zip_uint16_t;
^~~~~~~~~~~~
In file included from ../depends/libzip/lib/zipint.h:38,
from ../depends/libzip/lib/zip_algorithm_deflate.c:34:
../depends/libzip/lib/compat.h:146:2: error: #error unsupported size of off_t
#error unsupported size of off_t
^~~~~
[127/578] Building C object depends/libzip/lib/CMakeFiles/zip.dir/zip_buffer.c.o
FAILED: depends/libzip/lib/CMakeFiles/zip.dir/zip_buffer.c.o
ccache /usr/bin/cc -DHAVE_CUCHAR -DLINUX_BUILD -DLUA_BUILD_AS_DLL -DPROTOBUF_USE_DLLS -D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_CXX11_ABI=0 -D_LINUX -I../depends/protobuf -I../depends/lua/include -I../depends/md5 -I../depends/tinyxml -I../depends/tthread -I../depends/clsocket/src -I../depends/xlsxio/include -I../depends/libzip/lib -Idepends/libzip -fvisibility=hidden -mtune=generic -m32 -march=i686 -O3 -DNDEBUG -fPIC -MD -MT depends/libzip/lib/CMakeFiles/zip.dir/zip_buffer.c.o -MF depends/libzip/lib/CMakeFiles/zip.dir/zip_buffer.c.o.d -o depends/libzip/lib/CMakeFiles/zip.dir/zip_buffer.c.o -c ../depends/libzip/lib/zip_buffer.c
In file included from depends/libzip/config.h:4,
from ../depends/libzip/lib/zipint.h:37,
from ../depends/libzip/lib/zip_buffer.c:37:
depends/libzip/zipconf.h:28:10: warning: type defaults to ‘int’ in declaration of ‘zip_int16_t’ [-Wimplicit-int]
typedef zip_int16_t;
^~~~~~~~~~~
depends/libzip/zipconf.h:29:10: warning: type defaults to ‘int’ in declaration of ‘zip_uint16_t’ [-Wimplicit-int]
typedef zip_uint16_t;
^~~~~~~~~~~~
In file included from ../depends/libzip/lib/zipint.h:38,
from ../depends/libzip/lib/zip_buffer.c:37:
../depends/libzip/lib/compat.h:146:2: error: #error unsupported size of off_t
#error unsupported size of off_t
^~~~~
../depends/libzip/lib/zip_buffer.c: In function ‘_zip_buffer_get_64’:
../depends/libzip/lib/zip_buffer.c:111:35: warning: left shift count >= width of type [-Wshift-count-overflow]
return ((zip_uint64_t)data[7] << 56) + ((zip_uint64_t)data[6] << 48) + ((zip_uint64_t)data[5] << 40) + ((zip_uint64_t)data[4] << 32) + ((zip_uint64_t)data[3] << 24) + ((zip_uint64_t)data[2] << 16) + ((zip_uint64_t)data[1] << 8) + (zip_uint64_t)data[0];
^~
../depends/libzip/lib/zip_buffer.c:111:67: warning: left shift count >= width of type [-Wshift-count-overflow]
return ((zip_uint64_t)data[7] << 56) + ((zip_uint64_t)data[6] << 48) + ((zip_uint64_t)data[5] << 40) + ((zip_uint64_t)data[4] << 32) + ((zip_uint64_t)data[3] << 24) + ((zip_uint64_t)data[2] << 16) + ((zip_uint64_t)data[1] << 8) + (zip_uint64_t)data[0];
^~
../depends/libzip/lib/zip_buffer.c:111:99: warning: left shift count >= width of type [-Wshift-count-overflow]
return ((zip_uint64_t)data[7] << 56) + ((zip_uint64_t)data[6] << 48) + ((zip_uint64_t)data[5] << 40) + ((zip_uint64_t)data[4] << 32) + ((zip_uint64_t)data[3] << 24) + ((zip_uint64_t)data[2] << 16) + ((zip_uint64_t)data[1] << 8) + (zip_uint64_t)data[0];
^~
../depends/libzip/lib/zip_buffer.c:111:131: warning: left shift count >= width of type [-Wshift-count-overflow]
return ((zip_uint64_t)data[7] << 56) + ((zip_uint64_t)data[6] << 48) + ((zip_uint64_t)data[5] << 40) + ((zip_uint64_t)data[4] << 32) + ((zip_uint64_t)data[3] << 24) + ((zip_uint64_t)data[2] << 16) + ((zip_uint64_t)data[1] << 8) + (zip_uint64_t)data[0];
^~
../depends/libzip/lib/zip_buffer.c: In function ‘_zip_buffer_put_64’:
../depends/libzip/lib/zip_buffer.c:273:32: warning: right shift count >= width of type [-Wshift-count-overflow]
data[4] = (zip_uint8_t)((i >> 32) & 0xff);
^~
../depends/libzip/lib/zip_buffer.c:274:32: warning: right shift count >= width of type [-Wshift-count-overflow]
data[5] = (zip_uint8_t)((i >> 40) & 0xff);
^~
../depends/libzip/lib/zip_buffer.c:275:32: warning: right shift count >= width of type [-Wshift-count-overflow]
data[6] = (zip_uint8_t)((i >> 48) & 0xff);
^~
../depends/libzip/lib/zip_buffer.c:276:32: warning: right shift count >= width of type [-Wshift-count-overflow]
data[7] = (zip_uint8_t)((i >> 56) & 0xff);
^~
[128/578] Building C object depends/libzip/lib/CMakeFiles/zip.dir/zip_close.c.o
FAILED: depends/libzip/lib/CMakeFiles/zip.dir/zip_close.c.o
ccache /usr/bin/cc -DHAVE_CUCHAR -DLINUX_BUILD -DLUA_BUILD_AS_DLL -DPROTOBUF_USE_DLLS -D_GLIBCXX_USE_C99 -D_GLIBCXX_USE_CXX11_ABI=0 -D_LINUX -I../depends/protobuf -I../depends/lua/include -I../depends/md5 -I../depends/tinyxml -I../depends/tthread -I../depends/clsocket/src -I../depends/xlsxio/include -I../depends/libzip/lib -Idepends/libzip -fvisibility=hidden -mtune=generic -m32 -march=i686 -O3 -DNDEBUG -fPIC -MD -MT depends/libzip/lib/CMakeFiles/zip.dir/zip_close.c.o -MF depends/libzip/lib/CMakeFiles/zip.dir/zip_close.c.o.d -o depends/libzip/lib/CMakeFiles/zip.dir/zip_close.c.o -c ../depends/libzip/lib/zip_close.c
In file included from depends/libzip/config.h:4,
from ../depends/libzip/lib/zipint.h:37,
from ../depends/libzip/lib/zip_close.c:35:
depends/libzip/zipconf.h:28:10: warning: type defaults to ‘int’ in declaration of ‘zip_int16_t’ [-Wimplicit-int]
typedef zip_int16_t;
^~~~~~~~~~~
depends/libzip/zipconf.h:29:10: warning: type defaults to ‘int’ in declaration of ‘zip_uint16_t’ [-Wimplicit-int]
typedef zip_uint16_t;
^~~~~~~~~~~~
In file included from ../depends/libzip/lib/zipint.h:38,
from ../depends/libzip/lib/zip_close.c:35:
../depends/libzip/lib/compat.h:146:2: error: #error unsupported size of off_t
#error unsupported size of off_t
^~~~~
In file included from depends/libzip/config.h:4,
from ../depends/libzip/lib/zipint.h:37,
from ../depends/libzip/lib/zip_close.c:35:
../depends/libzip/lib/zip_close.c: In function ‘zip_close’:
depends/libzip/zipconf.h:49:25: warning: conversion from ‘long long unsigned int’ to ‘zip_uint64_t’ {aka ‘long unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow]
#define ZIP_UINT64_MAX 0xffffffffffffffffULL
^~~~~~~~~~~~~~~~~~~~~
../depends/libzip/lib/zip_close.c:91:24: note: in expansion of macro ‘ZIP_UINT64_MAX’
unchanged_offset = ZIP_UINT64_MAX;
^~~~~~~~~~~~~~
depends/libzip/zipconf.h:49:25: warning: conversion from ‘long long unsigned int’ to ‘zip_uint64_t’ {aka ‘long unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow]
#define ZIP_UINT64_MAX 0xffffffffffffffffULL
^~~~~~~~~~~~~~~~~~~~~
../depends/libzip/lib/zip_close.c:122:32: note: in expansion of macro ‘ZIP_UINT64_MAX’
zip_uint64_t last_index = ZIP_UINT64_MAX;
^~~~~~~~~~~~~~
ninja: build stopped: subcommand failed.
If you're using something Debian-based, I would expect the binaries from https://github.com/dfhack/dfhack/releases/0.47.04-r3 to work. Probably the 32-bit ones if you have a 32-bit userspace. https://docs.dfhack.org/en/latest/docs/Installing.html explains the difference between the various builds a bit more.
It looks like bits/types.h is from the "libc6-dev" package on Debian. Do you have that package installed? If so, what version?
Some more details about the issue you're encountering would be helpful - we can't fix it without more information.
Are you sure that a matching site exists?
Identifying and fixing a match bug would probably require a case where the search profile is known and a save where a matching location has been located manually (and info of where that location is).Some more details about the issue you're encountering would be helpful - we can't fix it without more information.
I've been generating multiple worlds almost every day for what seems like the past 2 months, and when I run embark-assistant on them it almost always tells me that the are 0 matching world tiles. Before I learned to run the find function twice, it was finding only 1-3 embarks in only one of the world that I genned in a week (and those almost always had some problem that made them unusable). Since I started running find twice, it hasn't found anything.Are you sure that a matching site exists?
No, come to think of it, I'm not.
Identifying and fixing a match bug would probably require a case where the search profile is known and a save where a matching location has been located manually (and info of where that location is).Some more details about the issue you're encountering would be helpful - we can't fix it without more information.
I've been generating multiple worlds almost every day for what seems like the past 2 months, and when I run embark-assistant on them it almost always tells me that the are 0 matching world tiles. Before I learned to run the find function twice, it was finding only 1-3 embarks in only one of the world that I genned in a week (and those almost always had some problem that made them unusable). Since I started running find twice, it hasn't found anything.Are you sure that a matching site exists?
No, come to think of it, I'm not.
However, a full description of the profile might give some indication about whether it's likely to be a bug or a case of the profile being too specific for worlds to be likely to have any matching sites (in which case relaxing the criteria might give you sites that are acceptable, but not perfect). If the world generated are highly tweaked that info would be needed as well, although I won't be much good in predicting how meshes this way or that would affect things.
Thanks.
It's a large non evil embark (so the evil weather parameters are redundant, but won't cause any harm either),
no aquifer,
min height waterfall (where brook has been excluded as the source),
non freezing (which would probably always be the case for moist broadleaf anyway).
Thanks.
However, demanding fire clay,
iron,
gypsum,
kaolinite and
bit-coal is very demanding even without the other restrictions, despite the world gen mineral occurrence is set to Everywhere in a Large world.
For starters, is there any specific reason you demand bit-coal, rather than just any coal (further up in the list)? That would about double the chance for a match.
A short history reduces the impact of the demand for no necros in range,
while staying away from gobbos typically excludes most of the world.
My conclusion from looking at it is that the chances of finding any locations matching those requirements in any given world would be rather low, and I'd either ease up on the requirements,
or find a site that's near what's wanted, and then hack the site to match the requirements, e.g. using the Biome Manipulator to "correct" the biome, evilness, and/or geo biome.
Btw, in embark-assistant it would be nice if there was a way after doing a search to search again using less restrictive search parameters without having to "abort game" and reload the world. I tried using "cancel/clear find" but it didn't seem to do anything.The functionality is there...
The "real" use case may be to search for your ideal site and then relax the criteria when you don't find it,
Apart from the first search being incomplete, I'm not aware of any case where a later search gives a result different from the previous one with exactly the same criteria.
The Biome Manipulator operates on World Tiles, i.e. the ones on the middle map on the pre embark screen.
The Region Manipulator (poorly named) http://www.bay12forums.com/smf/index.php?topic=164136.msg7454345#msg7454345 (http://www.bay12forums.com/smf/index.php?topic=164136.msg7454345#msg7454345) operates on the scale of Mid Level Tiles (the ones on the Local view).
The zip format is compatible with everyone, with the 7z one compatible with many. The basic .tar format can be read by 7zip (which I use), but I have no idea about the various Unix compression options.
Copy the script text (not the whole web page) into a "text" file in <DF>\hack\scripts called "candy_corrector.lua" (without the quotes). Then type "candy_corrector" (again without quotes) into the DFHack console window. Note that it will take the script quite some time to to run because it caused DF to perform quite time consuming data loading actions. The script should be run pre embark, i.e. at the screen showing the world, asking you where to embark. After the script has run you have to find your future location again as the data loading is prompted by moving the cursor over each world tile.
@daishi5:
I've tried to reproduce the issue. The script worked correctly on a couple of 17*17 maps, but does indeed fail on a max sized one. I do believe I understand why it fails, but it will take some time to address and test that, so I'll return later with an update.
I can't think of any other world gen bugs that have scripts correcting them: I don't use any, in any case.
Edit:
OK, I've finally updated the script https://github.com/PatrikLundell/scripts/blob/own_scripts/candy_corrector.lua (https://github.com/PatrikLundell/scripts/blob/own_scripts/candy_corrector.lua) (same link as before).
The script should be a bit faster, show a progress indication of sorts (feature shells progressed), and not suffer from feature shells being unloaded again before being accessed (by doing the processing immediately after the loading, rather than in two separate passes).
Anybody know if there's a script or plugin that provides a "timer" or "alarm clock" function in the game?
Example: I want a reminder to put the squad of migrants in quarantine 3 days before the were transformation date, so I set an alarm clock for the date and the game pauses and gives me a message of some sort.
Example: Having stationed a squad of xbow dorfs somewhere to shoot down keas, I would set a timer to count down 8 days then pause & center on the squad.
(edit: I did search the DFHack documentation and the forum with no luck.)
It really doesn't need to be customized or center on the squad, those were "wishes".
Any kind of simple timer/alarm that pauses would be neato.
I would also suggest taking a look at the Lua announcements API: https://docs.dfhack.org/en/stable/docs/Lua%20API.html#announcements
You can use this to create native DF announcements, that can optionally pause the game and/or show an in-game popup (similar to the cavern discovery message). If you don't need to customize their appearance, this may be an easier approach than warn-starving and gui/hello-world. Pausing announcements also wouldn't need "fpause".
I would definitely recommend "repeat" or the repeat-util module (https://github.com/DFHack/dfhack/blob/develop/library/lua/repeat-util.lua) to get something to run e.g. every in-game day.
is there a way to use dfhack to fix the "void tiles" that appear from d-z'ing ice ramps (http://www.bay12games.com/dwarves/mantisbt/view.php?id=1981)?
the wiki mentions (https://www.dwarffortresswiki.org/index.php/DF2014:Ice#Bugs) that casting ice on the void tile and channeling it out again can fix it, but i'm hoping for a way to just have the computer do the work...
is there a way to use dfhack to fix the "void tiles" that appear from d-z'ing ice ramps (http://www.bay12games.com/dwarves/mantisbt/view.php?id=1981)?
the wiki mentions (https://www.dwarffortresswiki.org/index.php/DF2014:Ice#Bugs) that casting ice on the void tile and channeling it out again can fix it, but i'm hoping for a way to just have the computer do the work...
https://docs.dfhack.org/en/stable/docs/Plugins.html#tiletypes
Dict = {
Socialize = df.need_type.Socialize,
DrinkAlcohol = df.need_type.DrinkAlcohol,
PrayOrMeditate = df.need_type.PrayOrMeditate,
...
}
Does any other way exist to access enum names, and list all enum values? Because such dictionary will require future maintenance. It's not like we have DF updates too often, but still.
Is it possible to receive enum names in Lua?df.need_type
Right now i'm making dictionary (table?)Code: [Select]Dict = {
Does any other way exist to access enum names, and list all enum values? Because such dictionary will require future maintenance. It's not like we have DF updates too often, but still.
Socialize = df.need_type.Socialize,
DrinkAlcohol = df.need_type.DrinkAlcohol,
PrayOrMeditate = df.need_type.PrayOrMeditate,
...
}
df.need_type[x]
[lua]# ~df.need_type[0]
Socialize
[lua]# ~df.need_type.Socialize
0
[lua]# ~df.need_type['Socialize']
0
In addition to this, enum and bitfield types contain a bi-directional mapping between key strings and values, and also map _first_item and _last_item to the min and max values.
cleanowned X
quicksave
repeat -name maintenance -time 1 -timeUnits months -command [ script maintenance.txt ]
If you know the individual commands that you need to run, you can just write them to a text file and then auto-run those commands with the repeat script.
For example, say you want to run the 'cleanowned' script and save your game once a month. You could write a text file named maintenance.txt in your DF directory with the following contents:Code: [Select]cleanowned X
quicksave
then put this in your onMapLoad.init file:Code: [Select]repeat -name maintenance -time 1 -timeUnits months -command [ script maintenance.txt ]
I haven't tested the above, so if anyone spots any errors in those instructions, please chime in. But this general technique should be able to get you what you want.
However, you can avoid a repeat to some extent by using Dwarf Therapist to check your starting 7 (it does work during your careful planning phase) and abort the embark effort if you don't like the dorfs. You're then back to the embark screen and can make a new attempt at the same location. Since you haven't exited DF the changes remain
Is it just me, or is dwarfvet not working properly?
I've followed the instructions below. In DFHack "dwarfvet enable" works, and "dwarfvet report" shows an animal hospital at the correct location, which is accessible to all the injured animals. DFHack lists them all "Telling intelligent unit 17843 (etc.) to report to the hospital". They wander around looking for it and I get spammed repeatedly: "(animal) cancels Rest: Area inaccessible."Spoiler (click to show/hide)
That sounds to me like this issue, which was reported recently: https://github.com/DFHack/dfhack/issues/1681
Unless that's you under a different name, it's not just you having the issue, although I'm not aware of any further attempts at diagnosing/fixing it at this point.
Nilsolm:
Strangely enough, I can't reproduce the issue with DFHack 0.47.04-r3. Animals seem to be able to path to the hospital area just fine. Can you upload a save file maybe? I can have a look at it.
QuoteNilsolm:
Strangely enough, I can't reproduce the issue with DFHack 0.47.04-r3. Animals seem to be able to path to the hospital area just fine. Can you upload a save file maybe? I can have a look at it.
Thanks! Here's the save:
https://dffd.bay12games.com/file.php?id=15281
dfhack.job.removeWorker(unit.job.current_job, 1)
But it seems it's not enough, because I already had dwarfs stuck "socialising", and plant gatherers can't finish the task because "dropoff inaccessible" inside burrow. It also probably not gonna work with different noble interactions.unit.military.individual_drills = {}
unit.social_activities = {}
unit.conversations = {}
unit.activities = {}
And if I could, should I? What pitfalls may arise from it?What should I do to prepare a dwarf before sending him to burrow to avoid being stuck with previous activity?- Any dorf in the process of delivering something to a stockpile will probably have to be relieved of the object hauled.
Right now I'm doing onlyCode: [Select]dfhack.job.removeWorker(unit.job.current_job, 1)
But it seems it's not enough, because I already had dwarfs stuck "socialising", and plant gatherers can't finish the task because "dropoff inaccessible" inside burrow. It also probably not gonna work with different noble interactions.
Could I do something like:Code: [Select]unit.military.individual_drills = {}
And if I could, should I? What pitfalls may arise from it?
unit.social_activities = {}
unit.conversations = {}
unit.activities = {}
- Any dorf in the process of delivering something to a stockpile will probably have to be relieved of the object hauled.Seems so, but I'm still can't find how to do so, using dfhack.
as I've had dorfs take up socializing after they were burrowed:Did they start socializing before or after burrow assignment? Because I had the same issue, but they probably started before assignment, and even the destruction of the guild hall didn't stop them from socialising in it's former location.
The best way to block need fulfillment "jobs" to be taken illegally is probably to ensure there is a "proper" job available in the burrow (such as e.g. a lever pull for a lever with that particular dorf as the only permitted user). You still have the issue with uninterruptible needs fulfillment, but I'd hesitate to try to override that.That's exactly opposite of what I'm doing now. Basically I want to move dwarfs to burrows based on their needs, so they have no other jobs to do till they are finished fulfiling their needs. If someone is interested script could be found here (http://needburrow.rinin.ru/)
ok just now notice my copy of advfort isn't working with buildings and just realize it's because someone changed the module stuff to be a dfhack_flag. check in advfort_items.
odd time to realize there been a revamp to the code structure but I hope I don't have to like edit some important dfhack file to connect a different script to advfort_items, or dummy out that line of code in advfort_item?
edit: it seems the 'require' code got changed to reqscripts .... so I dread if I have to rewrite a whole bunch of my scripts or just the scripts that touch dfhack stuff.
the thing that got me confused was the module check otherwise thanks for the clear up.ok just now notice my copy of advfort isn't working with buildings and just realize it's because someone changed the module stuff to be a dfhack_flag. check in advfort_items.
odd time to realize there been a revamp to the code structure but I hope I don't have to like edit some important dfhack file to connect a different script to advfort_items, or dummy out that line of code in advfort_item?
edit: it seems the 'require' code got changed to reqscripts .... so I dread if I have to rewrite a whole bunch of my scripts or just the scripts that touch dfhack stuff.
Could you clarify exactly what is broken? Is advfort.lua in the DFHack repository not working, or is this your own copy? If it's your own, is there any particular reason you're maintaining your own copy? Are there changes that you would like included in DFHack's version?
I did change how advfort_items needs to be imported in https://github.com/DFHack/scripts/commit/ad67c79f8074a491c144144b2825cd05c80a2236 - it was previously the only script in the scripts repo to use mkmodule() and work with require(), so I changed it for consistency. This did also require changing the require() calls in two places in advfort.lua (see the diff (https://github.com/DFHack/scripts/commit/ad67c79f8074a491c144144b2825cd05c80a2236#diff-0050b00a1fb3cc1d2c200b0f5faa661bf560d515cb852531424a2522c6a16566) for exactly what changed) - those should be all that you need to change on your end, unless you've added more. advfort_items should still be able to be used in exactly the same way, but let me know if there are other issues.
Edit: oh, I think you might have figured it out already. The recommended way to share functions/etc. between scripts is to use reqscript() or script_environment() along with a module check - see the diff above for examples. There is no hard requirement to do it this way for scripts outside of the DFHack/scripts repo, and advfort is the only one I know of that changed recently.
We've only included timestream.lua since 0.47.04-r3, which is currently the latest version. It is a heavily modified version of a script originally by IndigoFenix, though - were you using that? I didn't think it worked in 0.47.04 as-is. If not, where did you get timestream.lua from previously?ok so I thought timestream was something that mess with the calendar for loading in fort mode or adventure mode but yeah good to see someone legit uses it... but it is in my copy of r3.
We could try adding that feature, but since I'm not aware of it being intentionally removed, I'm not really sure where to look. Are you asking for something like "-rate 0.25" to work? From looking through the code briefly, I think that should work as-is (at least, I don't see anything actively preventing it from working).
ok so I thought timestream was something that mess with the calendar for loading in fort mode or adventure mode but yeah good to see someone legit uses it... but it is in my copy of r3.
Hi guys, I want to know if it's possible to give an armor attributes boost using the item-tigger mod tool?modtools/item-trigger, right? I've never used it, but it does appear to make the ID of the defending unit available (as \\DEFENDER_ID), so you could probably adjust the defending unit's armor on the fly. Perhaps it would be easier to adjust armor attributes beforehand, though.
Hi guys, I want to know if it's possible to give an armor attributes boost using the item-tigger mod tool?modtools/item-trigger, right? I've never used it, but it does appear to make the ID of the defending unit available (as \\DEFENDER_ID), so you could probably adjust the defending unit's armor on the fly. Perhaps it would be easier to adjust armor attributes beforehand, though.
Thank you for replying! Can you tell me how I can adjust armor attributes beforehand?Well, it depends on what you want to do. I was thinking just modifying the raws would work for some changes to armor, if you want them to always be in effect. I'm also not very familiar with how the combat system works in general, so other people might have better ideas.
You can adjust values in:
DF folder/raw/objects/item_armor.txt
But to make it apply to an in-progress game, you have to also make the changes in the same raw file of the saved game.
I'm assuming the changes would only apply to newly created armor, but not sure on that.
modtools/item-trigger -onEquip Worn -itemType ARMOR -command [ my-script \\ITEM_ID \\UNIT_ID ]
Oh, so you're probably more interested in detecting when a unit puts on a piece of armor, then. From what I can gather from the docs (https://docs.dfhack.org/en/stable/docs/_auto/modtools.html#modtools-item-trigger), something like this might help:Code: [Select]modtools/item-trigger -onEquip Worn -itemType ARMOR -command [ my-script \\ITEM_ID \\UNIT_ID ]
This would rely on a separate my-script.lua that you write, which would receive an item ID and unit ID as arguments, and could use df.item.find() and df.unit.find() to obtain references to the items/units themselves. (From that point, I don't know exactly how you'd check or adjust the traits you're interested in off the top of my head.)
local args = ...
local item = df.item.find(args[1])
local unit = df.unit.find(args[2])
-- do stuff with unit, item
Question about the family affairs plugin, was there some specific reason it was limited to fortress mode? As far as I can tell virtually none of the checks it runs to see if someone is eligible for marriage actually matter (from a "not crashing the game" sense at least, I get that filtering out hostiles, children, and the insane help prevent accidentally sticking yourself with unproductive relationships). Is there some adv mode specific weirdness I'm missing?Apart from adventurers being a special kind of asexual?
We've only included timestream.lua since 0.47.04-r3, which is currently the latest version. It is a heavily modified version of a script originally by IndigoFenix, though - were you using that? I didn't think it worked in 0.47.04 as-is. If not, where did you get timestream.lua from previously?-rate 0.25 resets to default fps instead, making game to run at 1x speed.
We could try adding that feature, but since I'm not aware of it being intentionally removed, I'm not really sure where to look. Are you asking for something like "-rate 0.25" to work? From looking through the code briefly, I think that should work as-is (at least, I don't see anything actively preventing it from working).
if rate ~= 1 then
last_frame = df.global.world.frame_counter
--if rate > 0 then -- Won't behave well with unit simulation
if rate > 1 and not simulating_desired_fps then
print('timestream: Time running at x'..rate..".")
else
print('timestream: Time running dynamically to simulate '..desired_fps..' FPS.')
if rate ~= 0 then
print('timestream: Rate setting ignored.')
end
-rate 0.25 resets to default fps instead, making game to run at 1x speed.Okay, I see the relevant code now, although it's right after the snippet you posted - if you comment out the "rate = 1" line (add "--" to the beginning of it), does that do what you want?
I'm using some older version, but it works very wellIf my suggestion doesn't work, it would be very helpful to know where you got this older version from (or to have a copy of it).
Anyway, I'm having crashes in DF by totally different reasons now:The message you pasted is a harmless diagnostic message that is not related to the crash. It's in stderr.log because it's occasionally useful for development. Is there any way we could make it clearer that it is not an error? Would having something like "INFO:" prepended make it clearer?
Class not in symbols.xml: 'viewscreen_unitlaborsst': vtable = 0x7fedc200ff0
This happens when I try to manage labour and enable herbalist job for my dwarves.I'm having trouble reproducing this. I was able to enable herbalism on around 100 dwarves across two forts with no crash.
I'll give it a go.-rate 0.25 resets to default fps instead, making game to run at 1x speed.Okay, I see the relevant code now, although it's right after the snippet you posted - if you comment out the "rate = 1" line (add "--" to the beginning of it), does that do what you want?
Mkay. I'm sure I got it from *somewhere* near the original author. If the above does not work, I'll post my ancient script here.QuoteI'm using some older version, but it works very wellIf my suggestion doesn't work, it would be very helpful to know where you got this older version from (or to have a copy of it).
I'm having trouble reproducing this. I was able to enable herbalism on around 100 dwarves across two forts with no crash.Mods were most likely conflict source. I've refreshed DFhack from the latest release and now all the problems are gone.
Does it happen exactly when you press "Enter" with herbalism selected for a dwarf, or at some later time? Are you able to reproduce it consistently? Does it only occur for specific dwarves? Are you using any mods?
hack/scripts/cageammo.lua:74: Cannot read field unit.relations: not found.
All the fields known by the DFHack community are documented as XML files, and yes, I think the relations thing has changed over the years.Where would these xml files? I can only see symbols and some files in stonesense folder.
In addition to using the XML files, you can also use gui/gm-editor to look at structures (which is often easier). For the unit structure, select any unit in a fortress (probably works in adventure mode as well) and invoke gui/gm-editor from the DFHack terminal. This allows you to browse the structure, and thus see what the current one looks like.Thanks. I have ocd when it comes to dealing with problems in the code. To the point, where I can sit for two days straight doing nothing else. But I'm learning to deal with it by asking other people. Kind of works ;)
unit_ref = df.general_ref_contains_unitst:new()
unit_ref.unit_id = unit.id
cage.general_refs:insert('#',unit_ref)
...causes everybody interested to follow the caged creature.what does the second column in the auto cut gems menu represent? documentation doesn't say anything about itCould you clarify which screen you're talking about or provide a screenshot?
what does the second column in the auto cut gems menu represent? documentation doesn't say anything about itCould you clarify which screen you're talking about or provide a screenshot?
Those are the tiles that are specified in the raws for those gems. If those aren't the tiles you see in-game, are you using TWBT? We likely wouldn't be able to support tilesets that require TWBT in this particular screen, unfortunately.Got it, thanks. I was wondering if they were like an indication of rarity or value or something.
for de,oe in pairs(df.global.world.army_controllers.all) do
if oe.mission_report == nil then else
print ( de,oe.mission_report.title)
for si,te in pairs(df.global.world.world_data.sites) do
local Sito=te.id
if oe.site_id== Sito then
local SitePool=df.global.world.world_data.sites[si].unk_1.nemesis
for e,o in pairs(df.global.world.armies.all) do
if oe.id == o.controller_id then
print (e)
local forv=df.global.world.armies.all[e].members[0]
df.global.world.armies.all[e].members:insert("#",{new=true,nemesis_id=SitePool[dfhack.random.new():random(#SitePool)],
stored_fat = forv.stored_fat,
unk_2c= forv.unk_2c,
unk_28= forv.unk_28,
unk_1= forv.unk_1,
unk_30= forv.unk_30,
unk_34= forv.unk_34})
for Del,ete in pairs(df.global.world.armies.all[e].members) do
for De,ee in pairs(SitePool) do
if ete.nemesis_id== ee then
SitePool:erase(De)
end
end
end
end
end
end
end
end
end
so you run this script while your squad is off on a mission now this works if the squad is targeting a site, so far it doesn't work with searching for artifacts or going on messenger missions.for de,oe in pairs(df.global.world.army_controllers.all) do
if oe.mission_report == nil then else
print ( de,oe.mission_report.title)
for e,o in pairs(df.global.world.armies.all) do
if oe.id == o.controller_id then
print (e)
local forv=df.global.world.armies.all[e].members[0]
df.global.world.armies.all[e].members:insert("#",{new=true,nemesis_id=df.global.world.nemesis.all[dfhack.random.new():random(#df.global.world.nemesis.all)].id,
stored_fat = forv.stored_fat,
unk_2c= forv.unk_2c,
unk_28= forv.unk_28,
unk_1= forv.unk_1,
unk_30= forv.unk_30,
unk_34= forv.unk_34})
end
end
end
end
for de,oe in pairs(df.global.world.army_controllers.all) do
if oe.mission_report == nil then else
print ( de,oe.mission_report.title)
for si,te in pairs(df.global.world.world_data.sites) do
local Sito=te.id
if oe.site_id== Sito then
local SitePool=df.global.world.world_data.sites[si].unk_1.nemesis
for e,o in pairs(df.global.world.armies.all) do
if oe.id == o.controller_id then
print (e)
local forv=df.global.world.armies.all[e].members[0]
df.global.world.armies.all[e].members:insert("#",{new=true,nemesis_id=SitePool[dfhack.random.new():random(#SitePool)],
stored_fat = forv.stored_fat,
unk_2c= forv.unk_2c,
unk_28= forv.unk_28,
unk_1= forv.unk_1,
unk_30= forv.unk_30,
unk_34= forv.unk_34})
end
end
end
end
end
end
Is there a particular reason you're assigning these fields and not the other "unk" fields? Do you know what they are? If you do, we should give them proper names.Code: [Select]unk_2c= forv.unk_2c,
unk_28= forv.unk_28,
unk_1= forv.unk_1,
unk_30= forv.unk_30,
unk_34= forv.unk_34})
uhh I don't really I just notice the numbers were filled in and figured why not match that with the rest of the members in the party so to avoid crashes or any weirdness.QuoteIs there a particular reason you're assigning these fields and not the other "unk" fields? Do you know what they are? If you do, we should give them proper names.Code: [Select]unk_2c= forv.unk_2c,
unk_28= forv.unk_28,
unk_1= forv.unk_1,
unk_30= forv.unk_30,
unk_34= forv.unk_34})
I need Catsplosion script analogue that can breed creatures of specie selected by cursor (like for full-heal). For breeding necro's experiments.
female = dfhack.gui.getSelectedUnit()
Edit: you'll also need to remove the lines referencing variables with "total_" in their names.I remember warmist made an impregnate script that was basically this years back when dfusion existed and I think catsplosion was an adaptation of that script.I need Catsplosion script analogue that can breed creatures of specie selected by cursor (like for full-heal). For breeding necro's experiments.
You could likely adapt catsplosion fairly easily - this section (https://github.com/DFHack/scripts/blob/befaee514d6260797d65dc874c4e2e015d104374/catsplosion.lua#L72-L83) appears to be the code that creates/shortens pregnancies, and creating a script containing just the highlighted code, as well as the following line before it, should work:Code: [Select]female = dfhack.gui.getSelectedUnit()
Edit: you'll also need to remove the lines referencing variables with "total_" in their names.
A longer-term solution could be to add support for "catsplosion this", similar to "exterminate this" - it would be more complicated than the process I described above, but if that's something that would be useful, we could add support for it.
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
--print("Creature not found!")
return nil
end
function empregnate(unit)
if unit==nil then
unit=getCreatureAtPos(getxyz())
end
if unit==nil then
error("Failed to empregnate. Unit not selected/valid")
end
if unit.curse then
unit.curse.add_tags2.STERILE=false
end
local genes = unit.appearance.genes
if unit.pregnancy_genes == nil then
print("creating preg ptr.")
if false then
print(string.format("%x %x",df.sizeof(unit:_field("pregnancy_genes"))))
return
end
unit.pregnancy_genes = { new = true, assign = genes }
end
local ngenes = unit.pregnancy_genes
if #ngenes.appearance ~= #genes.appearance or #ngenes.colors ~= #genes.colors then
print("Array sizes incorrect, fixing.")
ngenes:assign(genes);
end
print("Setting preg timer.")
unit.pregnancy_timer=10
unit.pregnancy_caste=1
end
so here's my copy of this old script and the functions that make it work.
Couple questions
1. I am looking into adding new features into the eventful plugin, but am wondering how reasonable that is, in particular a call to check for unit actions. Currently I use a script that uses a custom eventful call in my scripts that I got from one of Putnam's scripts but was thinking that it would be more elegant and more useful to other modders if it were to be added into the actual DFHack eventful plugin. I am currently looking at the plugins/lua/eventful.lua, plugins/eventful.cpp, and library/modules/EventManager.cpp for an idea of how I would go about this and I think I understand how the current calls are working, but my question is, before I do too much more work thinking about how to add new calls, is this something that can be done? From what I understand it is, but just wanted to make sure that there wasn't something I was missing that makes it not possible.
2. I want to update my old journal/detailed unit viewer to my new set of scripts. My previous iteration used the "In-game UI library" from the dfhack lua api page extensively. Just wanted to double check that that was still the "correct" way to add custom screens?
3. Is there a list somewhere of what people are working on for DFHack? I know everyone has there own set of scripts and such in lua and various mods, and can submit pull requests about anything they are working on, but I was wondering if there was a central list of things people are looking to add to the core DFHack repository, just in case there is something I think I would like to add, it would be nice to know if anyone else was working on it.
Couple questions
1. I am looking into adding new features into the eventful plugin, but am wondering how reasonable that is, in particular a call to check for unit actions. Currently I use a script that uses a custom eventful call in my scripts that I got from one of Putnam's scripts but was thinking that it would be more elegant and more useful to other modders if it were to be added into the actual DFHack eventful plugin. I am currently looking at the plugins/lua/eventful.lua, plugins/eventful.cpp, and library/modules/EventManager.cpp for an idea of how I would go about this and I think I understand how the current calls are working, but my question is, before I do too much more work thinking about how to add new calls, is this something that can be done? From what I understand it is, but just wanted to make sure that there wasn't something I was missing that makes it not possible.
2. I want to update my old journal/detailed unit viewer to my new set of scripts. My previous iteration used the "In-game UI library" from the dfhack lua api page extensively. Just wanted to double check that that was still the "correct" way to add custom screens?
3. Is there a list somewhere of what people are working on for DFHack? I know everyone has there own set of scripts and such in lua and various mods, and can submit pull requests about anything they are working on, but I was wondering if there was a central list of things people are looking to add to the core DFHack repository, just in case there is something I think I would like to add, it would be nice to know if anyone else was working on it.
1. IIRC this would be probably added to evenmanager and then to eventful. EventManager has the infrastructure for "every n ticks check this thing" which would be the way to go with this. Eventful itself adds vmethod based callbacks. Though if it's some ease of use script thing it may even live in eventful.lua.
2. Yup. Use as far as possible in inheritance. IIRC usually widgets?
3. I think usual convention is [WIP] marked pull-req in github.
3. That works great. I got a little confused because I was looking under the df-structures git repo and only saw like two pull requests. But now I realize that each repo under the DFHack account has it's own list of pull requests. On a side note, has it always been that way? I thought I remembered them being in a single repo.
Check the modtools folder in sparking. I already got an on-action event in Lua which is "fast enough", though I have been thinking avout moving it to C++.
"df-structures" has been its own repo since structure definitions were implemented in XML (around 2011). "scripts" was split out in 2016, and has issues disabled, since it usually makes more sense to report them in the "dfhack" repo anyway. PRs always need to be made in the repo containing the code being modified, though.
I loosely keep track of issues/PRs with projects, to make it a bit easier to see what issues/PRs were addressed in a release, but they need to be manually added currently: https://github.com/orgs/DFHack/projects/
eventful = require "plugins.eventful"
eventful.enableEvent(eventful.eventType.INTERACTION,1)
eventful.onInteraction.test = function()
print("An interaction happened!")
end
local function repeatfunction(rfunc, N)
--repeats a function every N frames, until the function returns false or nil
local myrfunc
myrfunc = function ()
local timeoutid = dfhack.timeout(N, "frames", function(options)
if rfunc(timeoutid) then myrfunc() end
end)
end
myrfunc()
end
function detectSave(func)
if not func then return false end
--detect quicksaves
repeatfunction(
function()
if not already_saved and (
df.global.ui.main.autosave_request --quicksaves
or dfhack.gui.getCurFocus()=="options" --normal saves, maybe??
) then
already_saved=true
func()
end
return true
end
,1)
--set already_saved to false every six seconds
--(assumes the player doesn't do anything that changes persistent values in less than that)
repeatfunction(
function()
already_saved=false
return true
end
,600)
end
0.47.04-r4 is up! https://github.com/DFHack/dfhack/releases/tag/0.47.04-r4
Lots of buildingplan and quickfort improvements here, as well as several fixes.
Is there a script for removing or fulfilling the need for decent meals? I find this need so frustrating and difficult to fulfill on many dwarves that I basically consider it a bug. Having a dwarf constantly get bad thoughts because I can't provide a source of magpie meat or millet beer gets old fast.You should be able to satisfy all of your citizen's need for decent meals with:
I can deal with all the other needs, as finicky as they are, but I'd really just like to kick this one to the curb.
modtools/set-need -edit -need EatGoodMeal -focus 400 -citizens
Or alternatively, you could get rid of their needs to eat a good meal withmodtools/set-need -remove -need EatGoodMeal -citizens
gui/gm-editor?
I don't know (and don't use the manager), but I'd expect specific orders to get specific parameters.
[...]
Thus, if you want to examine it you should try to make as specific orders as you can (and "NONE" frequently means "ANY").
Hello, I'm trying to read the source code, however, I can't figure out how to locate some header files.<DFHack>\library\include\df\viewscreen_dungeon_monsterstatusst.h
For example: "df/viewscreen_dungeon_monsterstatusst.h"
Can anybody help me with this?
<DFHack>\library\include\df\viewscreen_dungeon_monsterstatusst.hBut I didn't find the files in that folder.
You didn't specify that you were looking at github rather than a local repository. I gave the location of the file in the local repository. If you look at github it may not be present at all, as these files are generated from the XML by the code generation process, so to get at their info you'd have to locate the XML file that defines what is then generated into that .h file (without checking, I'd guess df.viewscreen.xml, although the XML file may not be intuitive). The alternative would be to install the source code locally and generate the .h files, which would give you the C header format.Quote<DFHack>\library\include\df\viewscreen_dungeon_monsterstatusst.hBut I didn't find the files in that folder.
https://github.com/DFHack/dfhack/tree/develop/library/include/df
Having the generated .h files float around on github would result in issues when you generate them locally (e.g. after updating the XML locally), unless there's a way get them to be visible on github but be exempt from source control so they are readily replaced by local generation after being downloaded (but how would that logic know when to download and when not to?).codegen.pl will always generate new headers and replace the existing ones if necessary, although it's only run by CMake if codegen.out.xml is "out-of-date", i.e. older than the df.*.xml files. There are a couple reasons we don't include the headers in the DFHack repo:
I'm trying to build dfhack on windows, but win32\generate-MSVC-release.bat says there are errors.
C:\dfhack\dfhack\build\win32>call "C:\Program Files\Microsoft Visual Studio 14.0\Common7\Tools\vsvars32.bat"
ERROR: Cannot determine the location of the VS Common Tools folder.
C:\dfhack\dfhack\build\win32>cd VC2015_32
C:\dfhack\dfhack\build\win32\VC2015_32>msbuild /m /p:Platform=Win32 /p:Configuration=Release ALL_BUILD.vcxproj
'msbuild' is not recognized as an internal or external command,
operable program or batch file.
C:\dfhack\dfhack\build\win32\VC2015_32>cd ..
C:\dfhack\dfhack\build\win32>pause
Press any key to continue . . .
>"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -property installationPath
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community
CMake Error at CMakeLists.txt:440 (message):
Sphinx not found but BUILD_DOCS enabled
Link:
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\x86_amd64\link.exe /ERRORREPORT:QUEUE /OUT:".\CompilerIdC.exe" /INCREMENTAL:NO /NOLOGO kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /manifest:embed /PDB:".\CompilerIdC.pdb" /SUBSYSTEM:CONSOLE,"5.02" /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:".\CompilerIdC.lib" /MACHINE:X64 Debug\CMakeCCompilerId.obj
LINK : fatal error LNK1181: cannot open input file 'kernel32.lib' [C:\dfhack\dfhack\build\win64\VC2015\CMakeFiles\3.19.2\CompilerIdC\CompilerIdC.vcxproj]
When/where does this show up? It doesn't look DFHack-related to me - is this in CMakeError.log? Are there any console errors from running whatever build command you're running?Code: [Select]Link:
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\x86_amd64\link.exe /ERRORREPORT:QUEUE /OUT:".\CompilerIdC.exe" /INCREMENTAL:NO /NOLOGO kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /manifest:embed /PDB:".\CompilerIdC.pdb" /SUBSYSTEM:CONSOLE,"5.02" /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:".\CompilerIdC.lib" /MACHINE:X64 Debug\CMakeCCompilerId.obj
LINK : fatal error LNK1181: cannot open input file 'kernel32.lib' [C:\dfhack\dfhack\build\win64\VC2015\CMakeFiles\3.19.2\CompilerIdC\CompilerIdC.vcxproj]
Strange, I saw this error after I swtiched to 64 bit version. It seem Any idea?
I've been wondering (and can't find it with searches) if anyone knows of a way to measure the pathing distance between two cursor locations in fort mode.
I've been wondering (and can't find it with searches) if anyone knows of a way to measure the pathing distance between two cursor locations in fort mode.
I don't think that there is one. However there is a quick way to do "Is this reachable" check.
When/where does this show up?It seems to be the script that determines c++ compiler.
C:\dfhack\dfhack\build\win64\VC2015>cmake ..\..\.. -G"Visual Studio 14 Win64" -T v140_xp -DCMAKE_INSTALL_PREFIX="C:\dfhack\dfhack\build\win64\DF" -DBUILD_DEVEL=0 -DBUILD_DEV_PLUGINS=0 -DBUILD_DOCS=1 -DBUILD_STONESENSE=1
-- Selecting Windows SDK version to target Windows 10.0.19041.
-- The C compiler identification is unknown
-- The CXX compiler identification is unknown
CMake Error at CMakeLists.txt:6 (project):
No CMAKE_C_COMPILER could be found.
CMake Error at CMakeLists.txt:6 (project):
No CMAKE_CXX_COMPILER could be found.
-- Configuring incomplete, errors occurred!
See also "C:/dfhack/dfhack/build/win64/VC2015/CMakeFiles/CMakeOutput.log".
See also "C:/dfhack/dfhack/build/win64/VC2015/CMakeFiles/CMakeError.log".
Is it possible that you only have 32-bit build tools installed? I'm not sure what options you have available when installing those. You are on a 64-bit system, right?CMakeOutput.txt
And just to double-check, what are you aiming to do by building DFHack - plugin development, for instance?
The system is: Windows - 10.0.19041 - AMD64
So, I've only ever gotten luck with the full Visual Studio 2015 installed, personally.Thanks. Maybe I should do a full installation to see if it works.
Just having the build tools has been very hard to get to work.
setlocal
"C:\Program Files\CMake\bin\cmake.exe" -DBUILD_TYPE=$(Configuration) -P cmake_install.cmake
if %errorlevel% neq 0 goto :cmEnd
:cmEnd
endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone
:cmErrorLevel
exit /b %1
:cmDone
if %errorlevel% neq 0 goto :VCEnd
--this script uses warmist dialog layout as a base, and some overviews from Max and Rose and Putnam scripts, series of ghost script commands
local dlg=require("gui.dialogs")
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getItemAtKPos(x,y,z) -- gets the item index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.item.all -- load all items
local kickpos=df.global.world.units.active[0].pos
for i = 0, #vector-1 do -- look into all items offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==kickpos.x and cy==kickpos.y and cz==kickpos.z then --compare them
return vector[i] --return index
end
end
--print("item not found!")
return nil
end
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
--print("Creature not found!")
return nil
end
function Ghosto(Gho)
local Gho= getCreatureAtPos(getxyz())
Gho.flags3.ghostly=true
Gho.ghost_info=df.unit_ghost_info:new()
end
function Cleanse(Gho)
local Gho= getCreatureAtPos(getxyz())
Gho.flags3.ghostly=false
end
function Ghosta()
local Targ= getCreatureAtPos(getxyz())
--if Gho.Ghost_info= nil then
--Ghosto()end
for k,v in pairs (df.global.world.units.active) do
if v.flags3.ghostly==true then
local Gho=v
Gho.ghost_info.goal=0
Gho.ghost_info.target.unit=Targ.id
end
end
end
function Ghostb()
local Targ= getCreatureAtPos(getxyz())
--if Gho.Ghost_info= nil then
--Ghosto()end
for k,v in pairs (df.global.world.units.active) do
if v.flags3.ghostly==true then
local Gho=v
Gho.ghost_info.goal=1
Gho.ghost_info.target.unit=Targ.id
end
end
end
function Ghostc()
local Targ= getCreatureAtPos(getxyz())
--if Gho.Ghost_info= nil then
--Ghosto()end
for k,v in pairs (df.global.world.units.active) do
if v.flags3.ghostly==true then
local Gho=v
Gho.ghost_info.goal=2
Gho.ghost_info.target.unit=Targ.id
end
end
end
function Ghostd()
local Targ= getCreatureAtPos(getxyz())
--if Gho.Ghost_info= nil then
--Ghosto()end
for k,v in pairs (df.global.world.units.active) do
if v.flags3.ghostly==true then
Gho=v
Gho.ghost_info.goal=3
Gho.ghost_info.target.unit=Targ.id
end
end
end
function Ghoste()
local Targ= getCreatureAtPos(getxyz())
--if Gho.Ghost_info= nil then
--Ghosto()end
for k,v in pairs (df.global.world.units.active) do
if v.flags3.ghostly==true then
local Gho=v
Gho.ghost_info.goal=5
Gho.ghost_info.target.unit=Targ.id
end
end
end
function Gigapurge()
local adv=df.global.world.units.active[0]
for k,v in pairs(df.global.world.units.active) do
if v.civ_id ~= adv.civ_id then
df.global.world.units.active:erase(k)
end
end
end
MOVEMENT_KEYS = {
A_CARE_MOVE_N = { 0, -1, 0 }, A_CARE_MOVE_S = { 0, 1, 0 },
A_CARE_MOVE_W = { -1, 0, 0 }, A_CARE_MOVE_E = { 1, 0, 0 },
A_CARE_MOVE_NW = { -1, -1, 0 }, A_CARE_MOVE_NE = { 1, -1, 0 },
A_CARE_MOVE_SW = { -1, 1, 0 }, A_CARE_MOVE_SE = { 1, 1, 0 },
--[[A_MOVE_N = { 0, -1, 0 }, A_MOVE_S = { 0, 1, 0 },
A_MOVE_W = { -1, 0, 0 }, A_MOVE_E = { 1, 0, 0 },
A_MOVE_NW = { -1, -1, 0 }, A_MOVE_NE = { 1, -1, 0 },
A_MOVE_SW = { -1, 1, 0 }, A_MOVE_SE = { 1, 1, 0 },--]]
A_CUSTOM_CTRL_D = { 0, 0, -1 },
A_CUSTOM_CTRL_E = { 0, 0, 1 },
CURSOR_UP_Z_AUX = { 0, 0, 1 }, CURSOR_DOWN_Z_AUX = { 0, 0, -1 },
A_MOVE_SAME_SQUARE={0,0,0},
SELECT={0,0,0},
}
ALLOWED_KEYS={
A_MOVE_N=true,A_MOVE_S=true,A_MOVE_W=true,A_MOVE_E=true,A_MOVE_NW=true,
A_MOVE_NE=true,A_MOVE_SW=true,A_MOVE_SE=true,A_STANCE=true,SELECT=true,A_MOVE_DOWN_AUX=true,
A_MOVE_UP_AUX=true,A_LOOK=true,CURSOR_DOWN=true,CURSOR_UP=true,CURSOR_LEFT=true,CURSOR_RIGHT=true,
CURSOR_UPLEFT=true,CURSOR_UPRIGHT=true,CURSOR_DOWNLEFT=true,CURSOR_DOWNRIGHT=true,A_CLEAR_ANNOUNCEMENTS=true,
CURSOR_UP_Z=true,CURSOR_DOWN_Z=true,
}
listofspells={
{text="nothing", spell=doNothing,icon='*'},
{text="Convert-to-Ghost", spell=Ghosto,icon='*'},
{text="Ghost: Scare to death", spell=Ghosta,icon='*'},
{text="Ghost:Stun", spell=Ghostb,icon='*'},
{text="Ghost:Batter", spell=Ghostc,icon='*'},
{text="Ghost:Possess", spell=Ghostd,icon='*'},
{text="Revert-from-ghost", spell=Cleanse,icon='*'},
{text="Ghost:Haunt", spell=Ghoste,key="CUSTOM_X"},
}
dlg.showListPrompt("Directions","Choze Direct",nil, listofspells,function(index,choice) choice.spell() end)
Hey, I've been having a problem with df freezing up for 1-20 minutes on startng DF or generating a new world, only with dfhack installed. any ideas what could be causing this?
make sure its pretty much default as it should be.
Hey, I've been having a problem with df freezing up for 1-20 minutes on startng DF or generating a new world, only with dfhack installed. any ideas what could be causing this?Are you using Windows? If so, hangs on startup can be due to antivirus software. For instance, Windows Defender tends to slow down DFHack at startup, sometimes significantly, probably because of the 100+ DLLs (plugins) that DFHack loads individually. You could check task manager to see if some antivirus service is using up a lot of CPU cycles on startup - if this is happening, my best recommendation would be to exempt the entire DF folder in your antivirus settings.
So uh, I made that resource for handling persistently saving lua tables directly with the json implementation a while ago, but dragged my feet on getting the documentation written up :P It's intended to be used as a module, and handles either saving data globally (available anywhere) or as world data (specific to the save). World data is saved automatically whenever the world is unloaded, as well as whenever the game is saved via autosave or quicksave.
If it's acceptable enough I'll get around to submitting it to the repo, but I'd appreciate if some people went over it first:
(pre-embark)
DIAMOND_BLUE : 496 Z: 75..91
DIAMOND_FY : 72 Z: 75..139
KIMBERLITE : 6317 Z: 75..139
(post embark)
DIAMOND_BLUE : 3 Z: 81..85
DIAMOND_FY : 35 Z: 81..138
KIMBERLITE : 9470 Z: 75..139
I have noticed an issue with the prospect plugin.As for this: first off, thanks for the detailed report! A couple questions:
(Using df47.05 with dfhack-0.47.05-alpha0-210131001-Linux-64bit-gcc-7.)
Occasionally running prospect pre-embark gives a result for Blue Diamonds which is an order of magnitude (or two) off.
From the embark where I first noticed the issue both results using the 'all' option (the post embark after a reveal all)Code: [Select](pre-embark)
DIAMOND_BLUE : 496 Z: 75..91
DIAMOND_FY : 72 Z: 75..139
KIMBERLITE : 6317 Z: 75..139
(post embark)
DIAMOND_BLUE : 3 Z: 81..85
DIAMOND_FY : 35 Z: 81..138
KIMBERLITE : 9470 Z: 75..139
Checking sites on the same worldgen the strange pre-embark results reoccurred at a number of sites across the world, repeatedly 400-1000 blue diamonds predicted. I probably tried about 100 sites spread across the world and about 10 of them had these results. Predictions for faint yellow diamonds were only a fraction of those for blue diamonds at each site. I did not notice any results predicted for other colours of single occurence diamonds (black, green, clear, red, yellow), certainly there were none of this magnitude.
Good news: check-structures-sanity passed on linux64 (as part of trying to debug the above issue) so I think we can put up at least a beta release fairly soon for 0.47.05 here. Granted, the one structures change we've made would only have been caught on linux32, so there's not a guarantee that everything is perfect yet.
As for this: first off, thanks for the detailed report! A couple questions:
- Is it consistently reproducible if you look in the same spot in the same world? If so, could you upload the save and report an issue on GitHub? If the save is small enough, you might be able to attach it on GitHub while reporting it there.
- Have you made any mods / changes to the raws? I'm not sure if this would affect prospect, but it might.
New release - officially in beta now: https://github.com/DFHack/dfhack/releases/tag/0.47.05-beta1
We think things are stable, and haven't really heard any reports of issues with 0.47.05. That said, please let us know as soon as possible if you're experiencing issues with DFHack specific to 0.47.05. (It has been a while, but as a reminder, we rely heavily on user feedback when it comes to new DF releases.)
New release - officially in beta now: https://github.com/DFHack/dfhack/releases/tag/0.47.05-beta1
We think things are stable, and haven't really heard any reports of issues with 0.47.05. That said, please let us know as soon as possible if you're experiencing issues with DFHack specific to 0.47.05. (It has been a while, but as a reminder, we rely heavily on user feedback when it comes to new DF releases.)
Thanks for the quick early release(s). :)
I get a bunch of messages about plugins not being built for this version, although as far as I can tell they are trival.Using dfhack-0.47.05-beta1-Linux-64bit-gcc-7.Spoiler (click to show/hide)
New release - officially in beta now: https://github.com/DFHack/dfhack/releases/tag/0.47.05-beta1
We think things are stable, and haven't really heard any reports of issues with 0.47.05. That said, please let us know as soon as possible if you're experiencing issues with DFHack specific to 0.47.05. (It has been a while, but as a reminder, we rely heavily on user feedback when it comes to new DF releases.)
Thanks for the quick early release(s). :)
I get a bunch of messages about plugins not being built for this version, although as far as I can tell they are trival.Using dfhack-0.47.05-beta1-Linux-64bit-gcc-7.Spoiler (click to show/hide)
Did you by chance install the beta over one of the previous versions of DFhack? I tried that to quickly update my little single tileset "packs", and got the same result, but when I deleted the old /hack folder and tried again, it was clean and error-free.
I get a bunch of messages about plugins not being built for this version, although as far as I can tell they are trival.Glad that a clean install fixed it. I checked the build you downloaded, and all of the plugin versions in it are correct, so my guess would be that you didn't overwrite the existing plugins the first time you installed DFHack. DFHack just looks at the contents of plugins, not their modification times. Also, it's not a "trivial" error, per se - the plugins would be ignored entirely if you see that message. Maybe that message should be clearer that this is the case.
Using dfhack-0.47.05-beta1-Linux-64bit-gcc-7.
I get a bunch of messages about plugins not being built for this version, although as far as I can tell they are trival.Glad that a clean install fixed it. I checked the build you downloaded, and all of the plugin versions in it are correct, so my guess would be that you didn't overwrite the existing plugins the first time you installed DFHack. DFHack just looks at the contents of plugins, not their modification times. Also, it's not a "trivial" error, per se - the plugins would be ignored entirely if you see that message. Maybe that message should be clearer that this is the case.
Using dfhack-0.47.05-beta1-Linux-64bit-gcc-7.
Hmm. What I did was overwrite alpha0 with beta1 with a copy-paste replace all. However I checked after encountering problems and the plugins were those provided by beta1 when I was getting that message, so something else was triggering it. Given that clinodev experienced the same, or similar, something strange does appear to be going on. (I'm sure we won't be the last to try the shortcut of overwriting an existing installation.)
strings hack/plugins/* | grep '0.47' | sort | uniq
My next best guess is that your copy-and-paste technique put things into a separate subfolder of the DF folder (e.g. something like hack/hack/plugins/foo.plug.so instead of hack/plugins/foo.plug.so). This would result in a folder that appears to have new plugins but is being ignored by DFHack.
For reference, this is what I ran to check the versions of the plugins in the copy I downloaded. It will probably print a couple garbage characters, but any recognizable version strings should just be the expected DF or DFHack version (0.47.05-beta1 or 0.47.05-beta1-0-g49b6e814 in this case).Code: [Select]strings hack/plugins/* | grep '0.47' | sort | uniq
Overwriting an existing installation should usually work (documented here (https://docs.dfhack.org/en/stable/docs/Installing.html#upgrading-dfhack), but not with any more detail than what you described).
function Memorypurge()
local adv=df.global.world.units.active[0]
local mem=df.global.world.history.figures[adv.hist_figure_id].info.known_info.known_events
for k,v in pairs(mem) do
if v==nil then break else
mem:erase(k)
end
end
end
Memorypurge()
not sure if good place to ask this, if there is better place let me know and i can move it there.
I've modded a custom race and screwed up a bit in the entity so that my equivelent of militia captains cannot be assigned (forgot to change ID of assigner from dwarfs)
...
I thought I would just need to figure out what the assignment ID was and insert something to the units entity_links, but it seems [SITE] nobles don't show up searching through the entity list and extrapolating a number from nobles without [SITE] gives no result.
For example, if I want to find for what values of df.global.ui.xxx xxx is valid, what is best place to look?That depends on quite a lot - what type(s) are you asking about here? My best answer is for enums, which would typically show up as numbers in Lua. Editing an enum field in gui/gm-editor would bring up a dialog with each (known) valid value, along with their names, and the enum name itself at the top. You could also track down the field in df-structures, like PatrikLundell mentioned. For scripting, you should always use the name ("df.enum_name.ValueName"), never the number, because the raw numbers can change. Valid values for other fields can vary greatly, and df-structures might have more relevant information on a case-by-case basis.
I can even print the nemesis record and entity_link (or values for each index since it is array) but it just gives some text that gives nothing useful... EDIT: and some hex, which is even less useful than the textTry printall() (shorthand: "~" in the "lua" interpreter). I'm guessing you were just getting something like "<nemesis_record: 0x25f25a0>", but if not, an example of what you are seeing would be helpful. Note that gui/gm-editor would also make it a bit easier to browse structures.
df.global.world contains almost everything of interest (there are things outside, like df.global.ui). gui/gm-editor is the standard tool for looking at df structures, but you can also look at the XML source for it (either in a local copy of dfhack if you compile it yourself, or on github (approximately named dfhack/df-structures). The XML has the advantage that there are some comments in there, but gui/gm-editor is usually easier to use because you don't have to guess at the file things are stored in).
Entities are stored in df.global.world.entities.all.
I don't really know what a nemesis record is for, but there are references to them and they, in turn, reference the hist fig targeted, which has been sufficient for my purposes so far.
"gui/gm-editor df.global.ui" will show you the top level of ui, and allows you to traverse further down the tree (when the elements are compound, of course), but, as indicated above, you probably want df.global.world as your top exploration level.
If I'm understanding this correctly, this is a position that you want to be able to assign from the "n" screen, but can't due to a mistake in the raws, yes? If that's the case, I would actually suggest trying to change this on the entity level, to allow the position to be assigned, rather than trying to forcibly assign a position that can't be assigned. I think it would be easier and less error-prone to leave the actual position assignment logic to DF.
The relevant data is probably stored at the entity level (e.g. "df.historical_entity.find(unit.civ_id)") but might be stored in the entity raws only instead ("df.entity_raw.find(unit.civ_id)"). Changes to raws (technically the in-memory versions of raws in this case) typically only affect new entities/objects, so I would recommend looking at the entity itself first.
I would recommend making a backup of your save first, in case you corrupt something irreversibly.
As a side note, you can actually drop the "df.global." prefix in both the "lua" interpreter and "gui/gm-editor" for convenience, e.g. "gui/gm-editor world" will work.
That depends on quite a lot - what type(s) are you asking about here? My best answer is for enums, which would typically show up as numbers in Lua. Editing an enum field in gui/gm-editor would bring up a dialog with each (known) valid value, along with their names, and the enum name itself at the top. You could also track down the field in df-structures, like PatrikLundell mentioned. For scripting, you should always use the name ("df.enum_name.ValueName"), never the number, because the raw numbers can change. Valid values for other fields can vary greatly, and df-structures might have more relevant information on a case-by-case basis.
Try printall() (shorthand: "~" in the "lua" interpreter). I'm guessing you were just getting something like "<nemesis_record: 0x25f25a0>", but if not, an example of what you are seeing would be helpful. Note that gui/gm-editor would also make it a bit easier to browse structures.
As for what a nemesis record is, I suppose that is something that could be documented. I would need to track down past discussions on it. Many structures don't have documentation outside of the XML files, because as of 0.47.05, there are 1721 of them, and both writing the documentation and keeping it up-to-date would be a massive undertaking.
So, my problem was a lack of understanding of gui/gm-editor, I thought it was only used for editing unit personalities and things like that.Ah, you might have been thinking of gui/gm-unit. They have similar names, but gui/gm-editor came first.
I think it isn't a big deal if it has explanatory documentation; I just thought from the name it was something that would be somehow more meaningful, while after looking at what it contains it just has stuff about unitsThere are a couple places where unit-related data is stored: besides the "unit" type, there are also "historical_figure" and "nemesis_record", with references between each other. "identity" might also be relevant sometimes. I was mostly thinking that it would be worth documenting the reasons why some of them only exist sometimes (a wild animal typically only has a "unit" instance, for example), and in certain cases, which source overrides the others (for things like names). I don't have a great source on why "nemesis_record" exists at the moment, but there is a reason.
What would be cool to have documentation on is what you and patrik just told me; though I guess that probably exists and I just am not great at google and didn't even know what I was trying to find.
EDIT: found where to solve the problem, was very easy after you mentioned df.historical_entity.find(unit.civ_id) (though I actually changed for the site entity rather than the civilization entity)If it's something presentable, sharing it here might be useful to others :)
Ah, you might have been thinking of gui/gm-unit. They have similar names, but gui/gm-editor came first.
If it's something presentable, sharing it here might be useful to others :)
So, my problem was a lack of understanding of gui/gm-editor, I thought it was only used for editing unit personalities and things like that.Ah, you might have been thinking of gui/gm-unit. They have similar names, but gui/gm-editor came first.Quote
Yeah it was supposed to be a suite of "Game Master" tools for editing everything like you would be in DnD or GURPS pen and paper rpg.
for i, item in ipairs (df.global.world.items.all) do
if item.flags.in_job then
item.flags.in_job = false
dfhack.println ("Job Flags Unset!")
end
end
I don't think the bug concerning flying stuff sometimes falling through floors is the reason hacked floors don't work correctly, but rather a failure somewhere in the hacking to fill in all the info that's should be filled in.
How much do we know about the interaction between ballista bolts and trees? Could be useful for an updated script that destroys trees.Are you sure ballista bolt tree removal is a good model itself? Unless something has changed recently, tree obliteration still leaves holes in water, for instance, and I don't know if anyone has investigated whether it might cause falling tree cave-ins if you channel away the tile it stood on (and I'd investigate that on a tree that didn't stand in an underground lake for ease of testing).
I use digcircle all the time, but I can't see how (if possible) to make it use "Marker Only" mode, which I would find tremendously helpful.
lair
This command allows you to mark the map as a monster lair, preventing item scatter on abandon. When invoked as lair reset, it does the opposite.
from my understanding you can put stuff on tables in adv camps to prevent scatter but also like adv camps don't really scatter stuff... outside of books and artifacts which bypass the item placement unless you place them on a table or pedestal.Quotelair
This command allows you to mark the map as a monster lair, preventing item scatter on abandon. When invoked as lair reset, it does the opposite.
hmm... is it known whether this works for adventurer camps?
Does dfhack have a way to unconvict someone of a crime? Or failing that, to force-cancel the job that chains up a dwarf for justice?
Is there a way to split stacks with dfhack? If so how?
(Background: The immediate use would be to split a 70 stack of prepared food so that it can be stored in a container. It was produced by an unfortunate-fortunate accident involving booze cooking. In order not to vermin up the tavern it will otherwise spend the next couple of months sitting in the trade depot until, hopefully, a caravan arrives but that solution is far from ideal.)
new_item = item:splitStack(stackSize, true)
if new_item then
new_item:categorize(true)
end
(I'm not sure what "true" means for either, or whether stackSize is the number of items in the new or the old stack, so you'll probably need to experiment.)
Is there a way to split stacks with dfhack? If so how?
(Background: The immediate use would be to split a 70 stack of prepared food so that it can be stored in a container. It was produced by an unfortunate-fortunate accident involving booze cooking. In order not to vermin up the tavern it will otherwise spend the next couple of months sitting in the trade depot until, hopefully, a caravan arrives but that solution is far from ideal.)
Did a bit of searching: it looks like DF's base "item" class actually has a splitStack() virtual method (I didn't know this until now), which means that DFHack can call it too. It returns the new item, and reduces the stack size of the existing item. Here (https://github.com/DFHack/dfhack/blob/7ea44010e084c2b4b03d08f4d97324137bbaefd1/plugins/tweak/tweak.cpp#L450-L451) is the only use of it that I found, in C++. It looks like you would want to call categorize() too so that the new item gets inserted into the right vectors under world.items.
In Lua, this would translate as:Code: [Select]new_item = item:splitStack(stackSize, true)
(I'm not sure what "true" means for either, or whether stackSize is the number of items in the new or the old stack, so you'll probably need to experiment.)
if new_item then
new_item:categorize(true)
end
In Lua, this would translate as:If you look in df-structures, the 2nd parameter of "splitStack" is named "preserve_containment", and it controls whether the newly-split stack will be placed in the world in the same location as its parent item (i.e. if you set it to false then you need to explicitly place it in the world, just like the "createitem" plugin does).Code: [Select]new_item = item:splitStack(stackSize, true)
(I'm not sure what "true" means for either, or whether stackSize is the number of items in the new or the old stack, so you'll probably need to experiment.)
if new_item then
new_item:categorize(true)
end
from my understanding you can put stuff on tables in adv camps to prevent scatter but also like adv camps don't really scatter stuff... outside of books and artifacts which bypass the item placement unless you place them on a table or pedestal.
the menu just vanished after I selected it and pressed enterSounds like a possible script error. Is there any red text printed to the DFHack console when this happens? If so, could you paste it here?
Sounds like a possible script error. Is there any red text printed to the DFHack console when this happens? If so, could you paste it here?No red text showed up in the console.
separate post for a question:
Where are the saved stockpile profiles stored? I thought they were linked to saves, but just noticed they're gone after moving my fort to a new starter pac
edit: never mind, I found the folder, how do I delete this bloody post?
Hi guys,
Is this also the place to post feedback on the DFhack documentation?
If so, I'd like to suggest an addition to the tiletypes info:
something I found out recently: 'paint special normal' makes everything into natural stone. It took me a while to find this and I see a lot of questions on the reddit about how to create natural stone. So I think it's useful to add this, in the explanation but also as an example.
for instance:
use [k] to position your cursor
in DFHack:
paint shape wall
paint special normal
run or [enter]
and you have a natural stone wall
What folder is it stored in? If I could just copy it to new versions, that would save me some time re-doing all the same configurations I regularly use.
Also, I don't think you can delete a post. The forum moderators might be able to though. There is a little "Report to moderator" hyperlink on the lower right.
separate post for a question:
Where are the saved stockpile profiles stored? I thought they were linked to saves, but just noticed they're gone after moving my fort to a new starter pac
edit: never mind, I found the folder, how do I delete this bloody post?
I'm very new to dfhack scripting, so apologies if this is obvious and/or already documented somewhere.
I want to read the "divider" attribute of the "emotion_type" enum in df.unit-thoughts.xml (https://github.com/DFHack/df-structures/blob/master/df.unit-thoughts.xml) from within a lua script, but I can't figure out where that metadata is stored in the lua API.
[lua]# ~df.emotion_type.attrs[0] == df.emotion_type.attrs.ACCEPTANCE
true
Please report bugs in the GitHub issue tracker (http://github.com/DFHack/dfhack/issues). (Bugs reported in this thread can be buried sometimes.)
Reported here, with details on what I think the issue is and some possible fixes: https://github.com/DFHack/dfhack/issues/1803
Reported here, with details on what I think the issue is and some possible fixes: https://github.com/DFHack/dfhack/issues/1803
It's definitely not a rendering issue. Liquid is placed exactly where the unmovable cursor is.
Thanks a lot for the help.Thanks! By the way, this is something that can go in the DF-structures issue tracker. Opened here: https://github.com/DFHack/df-structures/issues/424
In return I've been doing a bit more experimentation with emotions and I've made some progress towards figuring out some of the unknown flags (https://github.com/DFHack/df-structures/blob/master/df.units.xml#L1890-L1893):
IDA was so much easier to get working. I couldn't figure out what to do with the scripts, though.
Tried Ghidra the other day, but I couldn't even figure out how to get to its disassembler. IDA was so much easier to get working. I couldn't figure out what to do with the scripts, though. When I try to run them, I get "Function declaration is expected" errors.What scripts are you running, and do you have more complete errors?
What scripts are you running, and do you have more complete errors?
C:\Program Files\IDA Freeware 7.0\idc\DFHack\vtable.idc: C:\Program Files\IDA Freeware 7.0\idc\DFHack\vtable.idc,7: Function declaration is expected
I'm pretty sure that's an old script that requires you to manually specify each individual vtable you want to add - for 64-bit Windows binaries, run "ms_rtti64.idc" and it'll automatically find and name everything.What scripts are you running, and do you have more complete errors?
For instance, vtable.idc (https://github.com/DFHack/df_misc/blob/master/vtable.idc), having no clue what it actually does.Code: [Select]C:\Program Files\IDA Freeware 7.0\idc\DFHack\vtable.idc: C:\Program Files\IDA Freeware 7.0\idc\DFHack\vtable.idc,7: Function declaration is expected
I'm pretty sure that's an old script that requires you to manually specify each individual vtable you want to add - for 64-bit Windows binaries, run "ms_rtti64.idc" and it'll automatically find and name everything.
C:\Program Files\IDA Freeware 7.0\idc\DFHack\ms_rtti64.idc: C:\Program Files\IDA Freeware 7.0\idc\DFHack\ms_rtti64.idc,7: Function declaration is expected
Same error:What exact version of IDA are you running? I've got 7.0.191002 and it runs that script just fine, though I'm not running it from within IDA's "idc" directory - I'm running it directly from where I cloned the df_misc repo, which is on a different drive and is right next to my DFHack and DF directories.QuoteC:\Program Files\IDA Freeware 7.0\idc\DFHack\ms_rtti64.idc: C:\Program Files\IDA Freeware 7.0\idc\DFHack\ms_rtti64.idc,7: Function declaration is expected
Not sure if I should put this into here or TWBT thread, but...What Linux updates are you referring to? We haven't changed our toolchain or build machine recently, as far as I know (and aren't you running this on an old DF version anyway?), and OS upgrades on your end would be out of our control.
Noticed few very weird things with my custom TWBT. It's not behaving as it used to.
...
My question is then: Is it possible that one of the linux updates changed my setup? If not, any other known ways ➂ could happen?
What exact version of IDA are you running? I've got 7.0.191002 and it runs that script just fine, though I'm not running it from within IDA's "idc" directory - I'm running it directly from where I cloned the df_misc repo, which is on a different drive and is right next to my DFHack and DF directories.
Also, what is the exact file size of the script, and what sort of line endings does it have? Mine is 27,133 bytes and has DOS line endings, but IDA still seems to handle it just fine even when it has UNIX line endings (and is 26,049 bytes long).
"While your at it, make it not randomly crash :P" - Rose, back then.
The one thing I couldn't figure out is how to deal with different sized armor for my single human squad member. How do I check if a particular piece of armor will fit a particular unit? Toady gives the numerical constraints in this FOTF reply (http://www.bay12forums.com/smf/index.php?topic=140544.msg6843526#msg6843526), but I can't find where those size values are stored in the item_armorst (or similar) structures.
local size = df.creature_raw.find(item.maker_race).adultsize
[INORGANIC:CLAY]
[USE_MATERIAL_TEMPLATE:SOIL_TEMPLATE]
[STATE_NAME_ADJ:ALL_SOLID:clay][DISPLAY_COLOR:7:7:0][TILE:23]
[SOIL][b][ENVIRONMENT:SOIL:CLUSTER:100][/b]
[SOLID_DENSITY:1210] SCS = 20/60/20
[MATERIAL_REACTION_PRODUCT:FIRED_MAT:INORGANIC:CERAMIC_EARTHENWARE]
[DISPLAY_UNGLAZED]
if (!isStoneInorganic(key.first))
{
out.printerr(
"Invalid vein material: %s\n",
MaterialInfo(0, key.first).getToken().c_str()
);
return false;
}
for (size_t j = 0; j < queue.size(); j++)
{
if (queue[j]->parent && !queue[j]->parent->placed)
{
out.printerr(
"\nParent vein not placed for %s %s.\n",
MaterialInfo(0,queue[j]->vein.first).getToken().c_str(),
ENUM_KEY_STR(inclusion_type, queue[j]->vein.second).c_str()
);
return false;
}
[...]
I would like to know what makes a parent vein not placed? I'm guessing it is somewhere in void "VeinExtent::place_tiles()"Yeah, I think some sort of sorting improvements might help, based on what I'm reading here. Do you have a set of worldgen/embark parameters that tends to reproduce this? e.g. does it tend to happen more often on larger/smaller embarks, with more/fewer minerals at worldgen time, etc.?Usually the problems stated arise when playing on a world with Minerals:Everywhere. In vanilla DF, it is usually Anhydrite that is the main culprit, since it's a CLUSTER_ONE in Satinspar (CLUSTER_SMALL) in Gypsum (CLUSTER). If that doesn't terminate 3dveins, Forward vein in "" usually does.
--Yeet vermin remains to destroy trees
--keybinding add Ctrl-X destroy-tree
function destroy_tree()
if not dfhack.isMapLoaded() then
qerror("Error: Map not loaded!")
end
local x,y,z = pos2xyz(df.global.cursor)
if x == nil or x < 0 then
qerror("Error: Cursor not active!")
end
for i, item in ipairs(df.global.world.items.other.REMAINS) do
if item.flags.on_ground and not item.flags.in_job then
local proj = dfhack.items.makeProjectile(item)
proj.flags.no_impact_destroy = true
proj.flags.piercing = true
proj.origin_pos.x, proj.origin_pos.y, proj.origin_pos.z = x,y-1,z
proj.target_pos.x, proj.target_pos.y, proj.target_pos.z = x,y,z
proj.cur_pos.x, proj.cur_pos.y, proj.cur_pos.z = x,y-1,z
proj.prev_pos.x, proj.prev_pos.y, proj.prev_pos.z = x,y-1,z
proj.fall_threshold = 1
item.flags.forbid = true
return
end
end
print("No available vermin remains found!")
end
destroy_tree()
>>> bin(7035)
'0b1101101111011'
>>> bin(4885)
'0b1001100010101'
else
{
if (z != min_level[idx]-1 && min_level[idx] <= top_solid)
{
out.printerr(
"Discontinuous layer %d at (%d,%d,%d).\n",
layer->index, x+column.x*16, y+column.y*16, z
);
return false;
}
min_level[idx] = z;
}
}
}
static bool isTransientMaterial(df::tiletype tile)
{
using namespace df::enums::tiletype_material;
switch (tileMaterial(tile))
{
case AIR:
case LAVA_STONE:
case PLANT:
case ROOT:
case TREE:
case MUSHROOM:
return true;
default:
return false;
}
}
function getArmyAtName()
for k,v in pairs(df.global.world.nemesis.all) do
if v.figure.name.nickname=='DFCAT' then
print(k)
local Cat=df.global.world.armies.all
for k1,v1 in pairs(Cat) do
if v1.members==nil then break else
for Del,ete in pairs(v1.members) do
if ete.nemesis_id==v.id then
print(k1)
end
end
end
end
end
end
end
getArmyAtName()
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getItemAtKPos(x,y,z) -- gets the item index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.item.all -- load all items
local kickpos=df.global.world.units.active[0].pos
for i = 0, #vector-1 do -- look into all items offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==kickpos.x and cy==kickpos.y and cz==kickpos.z then --compare them
return vector[i] --return index
end
end
--print("item not found!")
return nil
end
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
--print("Creature not found!")
return nil
end
function getArmyAtName()
for k,v in pairs(df.global.world.nemesis.all) do
if v.figure.name.nickname=='DFCAT' then
print("Nem",k)
local Cat=df.global.world.armies.all
for k1,v1 in pairs(Cat) do
if v1.members==nil then break else
for Del,ete in pairs(v1.members) do
if ete.nemesis_id==v.id then
print("Army",k1)
return k1
end
end
end
end
end
end
end
local horse=getCreatureAtPos(getxyz())
local nem=df.global.world.history.figures[horse.hist_figure_id].nemesis_id --dfhack.units.getNemesis(horse)
local NeArmy=getArmyAtName()
df.global.world.armies.all[NeArmy].members:insert("#",{new=true,nemesis_id=nem,})
and here's the script for moving the station army around which works wonders for nicknaming a hostile bandit member and luring the bandits into a goblin patrolfunction getArmyAtName()
for k,v in pairs(df.global.world.nemesis.all) do
if v.figure.name.nickname=='DFCAT' then
print("Nem",k)
local Cat=df.global.world.armies.all
for k1,v1 in pairs(Cat) do
if v1.members==nil then break else
for Del,ete in pairs(v1.members) do
if ete.nemesis_id==v.id then
print("Army",k1)
return k1
end
end
end
end
end
end
end
local Ned=getArmyAtName()
for k,v in pairs(df.global.world.armies.all) do
if v.flags[0]== true then
print("ArmyOrders Station"," Moving Stationed group To Adv Position")
print("id",k,"X position",(v.pos.x),"y position",(v.pos.y))
df.global.world.armies.all[Ned].controller.pos_x=v.pos.x
df.global.world.armies.all[Ned].controller.pos_y=v.pos.y
end
end
well I could somewhat cross off controlling armies on the list of stuff I wanted to do in DF through DFhack.
1. Start with the debugger or attach later, whatever works for you. With DFHack and its debug info.
2. I don't know about the rtti scripts, but you should run codegen_c_hdr.pl from df_misc using the codegen from the matching DFHack version, import the resulting header in IDA.
3. Depends on what you are looking for, I've used a lot of watchpoints as starting points for reversing since the data is the most well documented part. Alternatively, you could start with a breakpoint on a known function (exported symbol or virtual method).
db 5Dh ; ]
db 0h
Representing a tree trunk tiletype (93) in the tile of a tree one tick away from being chopped down. I allow the program to resume (so it responds,) then pass one tick (using .) and quickly suspend execution before the tree is cut. I enable the breakpoint and resume execution. Unfortunately, it stops some time after the breakpoint, which doesn't get me the instructions that accessed the data. I tried stepping further through the code, but it doesn't reach the check again in a human time frame. After several continues, the data eventually changes to:db 4Eh ; N
db 1h
And the tree is chopped. The tiletype should be 335 for stone floor. I assume 4E 01 represents that in the proper order and endianness?I ran the script on library\include\df\codegen.out.xml to produce codegen.h. Then I attached to the DF process in IDA using "Local Windows debugger", set the compiler to "Visual C++" with the default settings, and did "Load file -> Parse C header file..." and selected codegen.h. Output says "codegen.h: successfully compiled". Is that all I'm supposed to do?
Unfortunately, it stops some time after the breakpoint, which doesn't get me the instructions that accessed the data. I tried stepping further through the code, but it doesn't reach the check again in a human time frame.Watchpoints are triggered when the memory is accessed, the instruction pointer has already been changed and it points to the next instruction. The instruction changing the value should be the one just before where you stopped.
After several continues, the data eventually changes to:x86 is little-endian, so the first byte is the LSB. But 0x14e is 334, not 335. You should be able to tell IDA to view this part of memory as a 16 bits word (or better: use the type imported from codegen.h), then endianness will no longer be an issue.Code: [Select]db 4Eh ; N
And the tree is chopped. The tiletype should be 335 for stone floor. I assume 4E 01 represents that in the proper order and endianness?
db 1h
[snip]
Dwarf_Fortress.exe:00007FF7B6D20840 db 40h ; @
Dwarf_Fortress.exe:00007FF7B6D20841 db 0
Dwarf_Fortress.exe:00007FF7B6D20842 db 15h
Dwarf_Fortress.exe:00007FF7B6D20843 db 0DCh ; Ü
Dwarf_Fortress.exe:00007FF7B6D20844 db 45h ; E
Dwarf_Fortress.exe:00007FF7B6D20845 db 0
Dwarf_Fortress.exe:00007FF7B6D20846 db 0
Dwarf_Fortress.exe:00007FF7B6D20847 db 0
Which is a 64-bit (8 byte) pointer to the memory address "00000045DC150040"? None of the options I've looked at made it more readable.Dwarf_Fortress.exe:00007FF7B6D20840 world::T_plants <<0BAC86020h, 8Bh, 0BAC9D5C8h>, <8Bh, 0BAC9DB70h, 8Bh>,\
Dwarf_Fortress.exe:00007FF7B6D20840 <0C52BD900h, 8Bh, 0C52C8EA0h>, <8Bh, 0C52CD5E0h, 8Bh>,\
Dwarf_Fortress.exe:00007FF7B6D20840 <0C25E17B0h, 8Bh, 0C25E2018h>, <8Bh, 0C25E2190h, 8Bh>>
None of this seems to lead to the address in memory (0000008BB9A80F70) that contains the plant at index 0, nor an area in memory I found through searching (0000008BA0AFD038) that has a pointer to that address. (I didn't find any pointers to that pointer through searching.)Edit: Is IDA treating DF as a 32-bit program? All the data types are half the size they should be, and "db" is the same size as "dw".
dw is 16 bits and not enough for a pointer: b = byte (8 bits), w = word (16 bits), d = double word (32 bits), q = quad word (64 bits).
Alternatively, you could start with a breakpoint on a known function (exported symbol or virtual method).
vtable addresses are in symbols.xml. I guess the rtti scripts are supposed to find them too, but I've never used them.
My guess is that Clément was referring to one of the ms_rtti*.idc scripts from df_misc (https://github.com/DFHack/df_misc) (but I don't know exactly which one - perhaps ms_rtti64.idc for 64-bit DF?)
.text:00000001407ED382 lea rax, off_140F20958
into this:.text:00000001407ED382 lea rax, ??_7item_chainst@@6B@ ; const item_chainst::`vftable'
.text:000000014042E4B8 movzx eax, cs:byte_141699288
.text:000000014042E4BF xor r12d, r12d
.text:000000014042E4C2 cmp cs:byte_14167EC63, r12b
.text:000000014042E4C9 movzx ecx, cs:word_141C3D260
.text:000000014042E4D0 cmovnz eax, r12d
.text:000000014042E4D4 mov cs:byte_141699288, al
.text:000000014042E4DA test cx, cx
.text:000000014042E4DD jz short loc_14042E502
.text:000000014042E4DF cmp cx, di
.text:000000014042E4E2 jnz loc_14042F407
.text:000000014042E4E8 cmp cs:byte_141C3D368, r12b
.text:000000014042E4EF jnz loc_14042F407
.text:000000014042E4F5 cmp cs:byte_141C3D360, r12b
.text:000000014042E4FC jnz loc_14042F407
into this:.text:000000014042E4B8 movzx eax, cs:_pause_state
.text:000000014042E4BF xor r12d, r12d
.text:000000014042E4C2 cmp cs:_debug_nopause, r12b
.text:000000014042E4C9 movzx ecx, cs:_ui.main.mode
.text:000000014042E4D0 cmovnz eax, r12d
.text:000000014042E4D4 mov cs:_pause_state, al
.text:000000014042E4DA test cx, cx
.text:000000014042E4DD jz short loc_14042E502
.text:000000014042E4DF cmp cx, di
.text:000000014042E4E2 jnz loc_14042F407
.text:000000014042E4E8 cmp cs:_ui.squads.in_kill_order, r12b
.text:000000014042E4EF jnz loc_14042F407
.text:000000014042E4F5 cmp cs:_ui.squads.in_move_order, r12b
.text:000000014042E4FC jnz loc_14042F407
How do I run the script to remove uninteresting dead units from mut 'u'nits list?If you're asking for the name of the script, it's "fix/dead-units (https://docs.dfhack.org/en/stable/docs/_auto/fix.html#fix-dead-units)". To run it, just enter that in the DFHack console.
paste them into the IDA "Execute Script" window (File -> Script Command, or Shift+F2). Once you do that (and press "Run"), all of the global variables will be named and the various global structures will be instantiated in-place, so when you look through the code you'll be able to see all of the names instead of just meaningless numbers.
debug001:000000007FFE0FFF debug001 ends
debug001:000000007FFE0FFF
debug038:0000007700000000 ; ===========================================================================
Thanks a lot, that worked!How do I run the script to remove uninteresting dead units from mut 'u'nits list?If you're asking for the name of the script, it's "fix/dead-units (https://docs.dfhack.org/en/stable/docs/_auto/fix.html#fix-dead-units)". To run it, just enter that in the DFHack console.
I suppose I should've mentioned beforehand: if you want those scripts to work, you must not attach to an existing process (as you would with a debugger). Instead, you need to run IDA, go to File -> Open, select the EXE file, let it finish analyzing it, and then run those scripts. You can attach to a running EXE afterwards if you want, but I would strongly recommend saving before doing so (and not saving it afterwards).Spoiler: Disassembly where "aDwarfMode" is used: (click to show/hide)Spoiler: Output of dump_df_globals: (click to show/hide)
That range of addresses doesn't exist in IDA, with nothing visible between the end of "debug001" and the beginning of "debug038":Code: [Select]debug001:000000007FFE0FFF debug001 ends
debug001:000000007FFE0FFF
debug038:0000007700000000 ; ===========================================================================
This function seems to be of interest, but it doesn't use anything that's named:If you don't see any named addresses then you missed a step somewhere, because there should be 3 named addresses in there:
.text:0000000140CF1009 cmp eax, cs:_world.map.x_count
.text:0000000140CF100F jge loc_140CF11A2
.text:0000000140CF1015 test cx, cx
.text:0000000140CF1018 js loc_140CF11A2
.text:0000000140CF101E movsx eax, cx
.text:0000000140CF1021 cmp eax, cs:_world.map.y_count
.text:0000000140CF1027 jge loc_140CF11A2
.text:0000000140CF102D mov r8, cs:_world.map.column_index
Just below that, it's necessary to manually annotate various structure offsets:.text:0000000140CF1009 cmp eax, dword ptr cs:xmmword_141D69550+4
instead of "cs:_world.map.x_count", though.1. Built DFHack, RelWithDebInfo, on a fresh install of "df_47_05_win".For what it's worth, you can just clone the "df-structures" respository and run codegen.pl directly - there's no need to build all of DFHack too.
5. File -> Load File -> Parse C header file..., select codegen.h.Apparently, you need to do these in the opposite order - first run the MakeName statements, then import codegen.h.
7. File -> Script command..., paste in MakeName statements. Confirm _pause_state exists in Names window.
I can add the structure map_block_column to the Structures window. Right-clicking the value gives an option for [r8+map_block_column.plants.ptr]. (The 't' option doesn't appear in this menu, but the shortcut works while the offset's selected. UX annoyance.) Right-clicking on the next one gives the option [r8+map_block_column.plants.endptr], so IDA seems to be pretty smart about that.When you right-click, do you see any key name in the right column of the "Structure offset" menu item (or in any of the other menu items, for that matter)? It's possible that's something I manually configured in my own installation, since I believe it was a default setting in IDA 5.0.
9. Filter for sub_140CF0F90 in the function menu and double-click it. No named addresses present.Unless this is another one of my custom settings, you should be able to press "g" to open a "Jump to address" prompt, then you can type in either an address (such as 0x140CF0F90) or a symbol name (sub_140CF0F90).
There are zero results for "getPlantAtCoords" on the internet (including your post and mine,) so that's kind of puzzling. (Tried Google, DuckDuckGo, and GitHub. At least Bay12Forums can find my post.)That's because I made up that name myself, based on its apparent function. After all, we already do the exact same thing with the names of fields in df-structures.
Apparently, you need to do these in the opposite order - first run the MakeName statements, then import codegen.h.
When you right-click, do you see any key name in the right column of the "Structure offset" menu item (or in any of the other menu items, for that matter)? It's possible that's something I manually configured in my own installation, since I believe it was a default setting in IDA 5.0.
Unless this is another one of my custom settings, you should be able to press "g" to open a "Jump to address" prompt, then you can type in either an address (such as 0x140CF0F90) or a symbol name (sub_140CF0F90).
That's because I made up that name myself, based on its apparent function. After all, we already do the exact same thing with the names of fields in df-structures.
I have a request:
Make a "tweak nestbox-color" for hives. to show material hive is made out of
MaterialInfo mat(mat_type, mat_index);
gps.screenf = mat.material.basic_color[0];
gps.screenbright = mat.material.basic_color[1];
(with some extra error checks to make sure the material is actually valid - if not, it should fall back to 6:1).Could "unk_v40_1" on projectiles be related to abilities/powers? It's set to 4885 for magma crab globs, 7035 for intelligent undead icicles, and -1 for bolts fired from a crossbow. Falling objects seem to have it set to really high garbage values. Could be the "parabolic" or "unk9" flag that has the game ignore that.
Are the other nearby fields still correct? It's possible the structure might be misaligned and you might actually be looking at the "unk_unit_id" field immediately above it.Could "unk_v40_1" on projectiles be related to abilities/powers? It's set to 4885 for magma crab globs, 7035 for intelligent undead icicles, and -1 for bolts fired from a crossbow. Falling objects seem to have it set to really high garbage values. Could be the "parabolic" or "unk9" flag that has the game ignore that.
Update on this: Looks like it's the target unit's id. Magma crab was aiming at one of my undead, my undead were all aiming at the magma crab, and the bolt was aimed at an archery target.
The probe plugin can do this. Docs here: https://docs.dfhack.org/en/latest/docs/Plugins.html#probeThank you :)
Are the other nearby fields still correct? It's possible the structure might be misaligned and you might actually be looking at the "unk_unit_id" field immediately above it.
...
hit_rating 251
unk_21 0
unk_22 40
bow_id 176393
unk_item_id -1
unk_unit_id -1
unk_v40_1 7040
pos_x 0
pos_y 0
pos_z 0
speed_x 0
speed_y 0
speed_z 0
accel_x 0
accel_y 0
accel_z 0
item <item_ammost: 000000C5CCF690E0>
From my own analysis, the function you found appears to be "df::plant *getPlantAtCoords(int x, int y, int z)" - the code immediately below "loc_140CF1080" checks for an exact coordinate match (e.g. for shrubs), the code just above "loc_140CF115B" checks if it's part of a tree branch, and the code below "loc_140CF115B" checks if it's a tree root.
function getPlantAtCoords(x_coord, y_coord, z_coord)
if ((x_coord // 48) * 48) < 0 then
return nil
elseif ((x_coord // 48) * 48) >= df.global.world.map.x_count then
return nil
elseif ((y_coord // 48) * 48) < 0 then
return nil
elseif ((y_coord // 48) * 48) >= df.global.world.map.y_count then
return nil
elseif not df.global.world.map.column_index then
return nil
end
local mbc = df.global.world.map.column_index[x_coord//48*3][y_coord//48*3]
if not mbc then
return nil
end
for _, p in ipairs(mbc.plants) do
if p.pos.x == x_coord and p.pos.y == y_coord and p.pos.z == z_coord then
return p
else
local t = p.tree_info
if t then
local x_index = t.dim_x // 2 - p.pos.x%48 + x_coord%48
local y_index = t.dim_y // 2 - p.pos.y%48 + y_coord%48
local z_dis = z_coord - p.pos.z
if x_index >= 0 and x_index < t.dim_x and
y_index >= 0 and y_index < t.dim_y and
z_dis < t.body_height and z_dis >= -t.roots_depth then
local bits
if z_dis >= 0 then
bits = t.body[z_dis]:_displace(x_index + y_index*t.dim_x)
else
bits = t.roots[-1 - z_dis]:_displace(x_index + y_index*t.dim_x)
end
if bits[0] or bits[1] or bits[2] or bits[3] or bits[4] or bits[5] or bits[6] then
return p
end
end
end
end
end
return nil
end
if bits.whole & 0x7F > 0 then --Any non-blocked tree_tile
Apparently there are holes in our documentation about bitfields. There's a special "whole" field that provides the raw integer value of a bitfield (i.e. "bits.whole").
I'll just leave a comment:Yeah, there are some limitations to Lua (not that it's necessarily a bad thing, but code can't always correspond one-to-one). Higher-level languages often limit what "goto" can do, at least compared to assembly. In Lua's case, for instance:Code: [Select]if bits.whole & 0x7F > 0 then --Any non-blocked tree_tile
It should probably be implemented in C++, really, since you get type checking (coords are int16_t?) and loop continue, which would look more like the assembly. I had to flip some of the conditions.
Edit: Actually, Lua supports GOTO, so I could really make my script look like assembly if I wanted.
A goto may jump to any visible label as long as it does not enter into the scope of a local variable.
Maybe it should be printed for "gui/gm-editor" and the Lua "printall". Could something be done about allowing tree_tiles to be indexed (beyond the z-level) instead of using "_displace", or is that not possible due to them not being vectors? I'll add this to the issue tracker it if it's possible.There's a substantial risk of breaking code by doing that (e.g. if existing code relies on all fields returned by pairs() being booleans); I'd say it's better just to document.
I updated the first post of this thread (http://www.bay12forums.com/smf/index.php?topic=164123.0) with some new chat options. We have a Discord server and an IRC channel on Libera now. (I'm avoiding linking them here in case any links become stale, so check out the first post (http://www.bay12forums.com/smf/index.php?topic=164123.0) for links.)Update: Freenode has effectively been dismantled at this point - user and channel registrations have been wiped, and most previous servers have been split from the network. Sad to see it go. Fortunately, both Discord and Libera are alive and well (the latter has most of the Freenode staff, from my understanding), so those two are our "official" real-time channels for the time being.
The assembly seems to use rdx, r8, and r9 for the coordinates. Is this just an optimization?
Or would the real thing be something like:
getPlantAtCoords(unused, unused, x, unused, y, z, &rbp_value, &x_mod_48, &y_mod_48, &rbx_value)
This is a method on Windows, right? It seems to follow the call convention. Parameter are in rcx, rdx, r8, and r9. rcx is "this" if it is a method call. What were you expecting?
The x64 Application Binary Interface (ABI) uses a four-register fast-call calling convention by default. Space is allocated on the call stack as a shadow store for callees to save those registers. [...] The caller must always allocate sufficient space to store four register parameters, even if the callee doesn't take that many parameters. [...] The callee is responsible for dumping the register parameters into their shadow space if needed.
using df::global::world;
df::plant *getPlantAtCoords(int32_t x, int32_t y, int32_t z)
{
if ( (x / 48) * 48 < 0 )
return NULL;
else if ( (x / 48) * 48 >= world->map.x_count )
return NULL;
else if ( (y / 48) * 48 < 0 )
return NULL;
else if ( (y / 48) * 48 >= world->map.y_count )
return NULL;
else if ( !world->map.column_index )
return NULL;
auto mbc = world->map.column_index[ (x / 48) * 3 ][ (y / 48) * 3 ];
if ( !mbc )
return NULL;
int32_t x_mod_48 = x%48;
int32_t y_mod_48 = y%48;
for(size_t i = 0 ; i < mbc->plants.size(); i++)
{
auto p = mbc->plants[i];
if ( p->pos.x == x && p->pos.y == y && p->pos.z == z )
return p;
auto t = p->tree_info;
if ( !t )
continue;
int32_t x_index = t->dim_x / 2 - p->pos.x%48 + x_mod_48;
int32_t y_index = t->dim_y / 2 - p->pos.y%48 + y_mod_48;
int32_t z_dis = z - p->pos.z;
if ( x_index < 0 || x_index >= t->dim_x )
continue;
else if ( y_index < 0 || y_index >= t->dim_y )
continue;
else if ( z_dis >= t->body_height )
continue;
if ( z_dis < 0 )
{
if ( z_dis < -( t->roots_depth ) )
continue;
if ( (t->roots[z_dis][ x_index + y_index * t->dim_x ].whole & 0x7F) != 0 ) //any non-blocked tree_tile
return p;
}
else if ( (t->body[ -1 - z_dis ][ x_index + y_index * t->dim_x ].whole & 0x7F) != 0 )
return p;
}
return NULL;
}
I haven't tested it at all yet.if (x < 0 || x >= world->map.x_count || y < 0 || y >= world->map.y_count)
return NULL;
x_count and y_count do happen to be multiples of 48 currently, so the "/48 * 48" bits don't affect checks against those fields, but I suppose the behavior could be different if map sizes change in the future.The x and y negative tests can be simplified to "if (x < -47)", etc., although I don't understand why negative values should be accepted at all (and I think some indexing would blow up later if the index is negative).
Did the "x / 48 * 48 < 0"-style checks come directly from disassembly?
movsx r13d, dx ; x_coord into r13
mov eax, 2AAAAAABh ; (2^32 / 6) + 1 into rax {magic number to divide by 6}
mov esi, r13d ; x_coord into rsi
imul r13d ; x_coord / 6 into rdx
sar edx, 3 ; (x_coord / 6) / 8 = x_coord / 48 into rdx
mov eax, edx ; x_coord / 48 into rax
shr eax, 1Fh ; sign bit of (x_coord / 48) into rax
add edx, eax ; add 1 to (x_coord / 48) if negative {correct result for negative numbers}
lea eax, [rdx+rdx*2] ; (x_coord / 48) * 3 into rax
movzx edx, r13w ; x_coord into edx
shl eax, 4 ; (x_coord / 48) * 3 * 16 = (x_coord / 48) * 48 into rax {x tile coord of MLT}
sub esi, eax ; x_coord - ((x_coord / 48) * 48) = x_coord%48 into rsi
sub dx, si ; x_coord - x_coord%48 = (x_coord / 48) * 48 into rdx {x tile coord of MLT (again)}
mov [rsp+30h+arg_10], esi ; save x_coord%48
js loc_7FF65FC211A2 ; if (x_coord - x_coord%48) negative, return null
movsx eax, dx ; (x_coord / 48) * 48 into rax
cmp eax, cs:_world.map.x_count
jge loc_7FF65FC211A2 ; if ((x_coord / 48) * 48) >= map.x_count, return null
I've cut out the y_coord stuff that's mixed with the x_coord stuff for clarity. (Function arguments are actually supposed to be int16_t instead of int32_t. Don't think this makes a difference.):All of that code is directly tied to the current organization of the map, and so would have to be reviewed at a minimum after the map rewrite (which probably would split the plants data into one vector per layer at a minimum, and possibly rework things completely), and probably rewritten from scratch to provide a new logic appropriate for the new logic organization. I'm fairly certain embark size granularity won't change before the map rewrite, unless the rewrite is postponed significantly for some reason.
x_count and y_count do happen to be multiples of 48 currently, so the "/48 * 48" bits don't affect checks against those fields, but I suppose the behavior could be different if map sizes change in the future.
:
Did the "x / 48 * 48 < 0"-style checks come directly from disassembly?
Yes:Code: [Select]...
extern DFHACK_EXPORT bool RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, df::block_square_event * which );
DFHACK_EXPORT bool canWalkBetween(df::coord pos1, df::coord pos2);
Why do some functions in Maps.h use "extern" and others don't? E.g.:If you could provide the function signature and any relevant pieces of the error you're getting, that would help. WRAPM should be all that's necessary in most cases, but sometimes weird argument or return types can trip it up.Code: [Select]extern DFHACK_EXPORT bool RemoveBlockEvent(uint32_t x, uint32_t y, uint32_t z, df::block_square_event * which );
DFHACK_EXPORT bool canWalkBetween(df::coord pos1, df::coord pos2);
How do I add my function to the Lua API? Seems like it's a bit more complicated than adding the line "WRAPM(Maps, getPlantAtCoords)," to LuaApi.cpp.
Edit: Trying to do it like "maps_ensureTileBlock" gives me the error "return value type does not match the function type".
Also, what am I looking at here: "WRAPN(getBlock, (df::map_block* (*)(int32_t,int32_t,int32_t))Maps::getBlock),"There are two implementations of Maps::getBlock - one takes three int32_ts and one takes a df::coord. Casting to a function pointer specifies which one to select. Without it, we'd get a fairly confusing error:
If you could provide the function signature and any relevant pieces of the error you're getting, that would help. WRAPM should be all that's necessary in most cases, but sometimes weird argument or return types can trip it up.
extern DFHACK_EXPORT df::plant *getPlantAtCoords(int32_t x, int32_t y, int32_t z);
inline df::plant *getPlantAtCoords(df::coord pos) { return getPlantAtCoords(pos.x, pos.y, pos.z); }
The function passed to WRAP is the one that's exposed to Lua. Casting the function to a specific type essentially forces the compiler to pick the implementation of that function that can be cast to that type - otherwise it would be ambiguous (I could have phrased this better earlier). if you're choosing to expose the one that takes (int32, int32, int32) params to Lua, you will only be able to pass those from Lua, not a df::coord object, and vice versa if you expose the other implementation. There isn't a way to support both types of arguments without using the Lua C API. If you want to do that, there are several examples - maps_getTileBlock() might be a good one since it returns a map_block pointer.Code: [Select]extern DFHACK_EXPORT df::plant *getPlantAtCoords(int32_t x, int32_t y, int32_t z);
inline df::plant *getPlantAtCoords(df::coord pos) { return getPlantAtCoords(pos.x, pos.y, pos.z); }
Was getting a red squiggly line for "return Lua::PushDFObject" in "static int maps_getPlantAtCoords(lua_State *L)" with the message. Error was result of copying from "maps_getTileBiomeRgn" and editing to "PushDFObject", instead of from "maps_ensureTileBlock" (which returns 1.)
Doing it like getBlock with WRAPN seems to work. Not used to working with function pointers, so the syntax was confusing.
How does the Lua C API way determine which implementation it's using?
Edit: Doesn't look like it's accepting DFCoord (using xyz2pos) in Lua using WRAPN with cast. C API works for both DFCoord and three int32_ts.
--Print the tree at cursor
--@module = true
function printTreeTile(bits,roots)
local old_color = dfhack.color()
local none = true
if bits.trunk then
dfhack.color(COLOR_BROWN)
if roots then
dfhack.print(string.char(172)) --1/4
else
dfhack.print("O")
end
none = false
end
if bits.branches then
dfhack.color(COLOR_GREEN)
dfhack.print(string.char(172)) --1/4
none = false
end
if bits.twigs then
dfhack.color(COLOR_GREEN)
dfhack.print(";")
none = false
end
if bits.connection_north then
dfhack.color(COLOR_RESET)
dfhack.print(string.char(24)) --up arrow
none = false
end
if bits.connection_south then
dfhack.color(COLOR_RESET)
dfhack.print(string.char(25)) --down arrow
none = false
end
if bits.connection_west then
dfhack.color(COLOR_RESET)
dfhack.print(string.char(27)) --left arrow
none = false
end
if bits.connection_east then
dfhack.color(COLOR_RESET)
dfhack.print(string.char(26)) --right arrow
none = false
end
if bits.blocked then
dfhack.color(COLOR_RED)
dfhack.print("x")
none = false
end
if none then
dfhack.color(COLOR_RESET)
dfhack.print(".")
end
dfhack.color(old_color)
end
function printTree(t)
if not t then
return
end
print("---------------------------------------------------------")
for z = t.body_height-1, 0, -1 do
for i = 0, t.dim_x*t.dim_y-1 do
printTreeTile(t.body[z]:_displace(i))
if i%t.dim_x == t.dim_x-1 then
print("\t|")
else
dfhack.print("\t")
end
end
print("---------------------------------------------------------")
end
for z = 0, t.roots_depth-1 do
for i = 0, t.dim_x*t.dim_y-1 do
printTreeTile(t.roots[z]:_displace(i),true)
if i%t.dim_x == t.dim_x-1 then
print("\t|")
else
dfhack.print("\t")
end
end
print("---------------------------------------------------------")
end
end
if not dfhack_flags.module then
p = dfhack.maps.getPlantAtTile(pos2xyz(df.global.cursor))
if p then
printTree(p.tree_info)
end
end
Is there any way to use DFHack to recover a book that has fallen over a waterfall, or is using pumps (including some that would has to be placed at the bottom of a ravine) to dam and drain the riverbed the only way? I tried using autodump, but it seems that books can’t be marked for dumping. Any help would be greatly appreciated.
Is there any way to use DFHack to recover a book that has fallen over a waterfall, or is using pumps (including some that would has to be placed at the bottom of a ravine) to dam and drain the riverbed the only way? I tried using autodump, but it seems that books can’t be marked for dumping. Any help would be greatly appreciated.
Try "gui/gm-editor" with your cursor on the book. You should be able to edit its coordinates.
Maybe I should write a teleport-item script that can move any item anywhere.
Is there any way to use DFHack to recover a book that has fallen over a waterfall, or is using pumps (including some that would has to be placed at the bottom of a ravine) to dam and drain the riverbed the only way? I tried using autodump, but it seems that books can’t be marked for dumping. Any help would be greatly appreciated.
Try "gui/gm-editor" with your cursor on the book. You should be able to edit its coordinates.
Maybe I should write a teleport-item script that can move any item anywhere.
Hello,It's an issue with the script. Looks like you're the first one that's reported it since it broke a year or so ago, so thank you! (It may be a rare case - I don't seem to have a save where I can reproduce the issue easily.)
I was using the "show unit syndromes" in my game, and it was throwing an error on one of the citizens, who had a vampire curse active on them. It wouldn't show syndromes on any other citizens after it found the cursed one.
in the stderr.log, it has this:
Invoking: show-unit-syndromes
E: NoMethodError: undefined method `unk_6c' for #<DFHack::CreatureInteractionEffectBodyMatInteractionst:0x000000090e48c8>
hack/scripts/show-unit-syndromes.rb:840:in `get_effect'
hack/scripts/show-unit-syndromes.rb:886:in `block (2 levels) in <top (required)>'
E:/Dwarf stuff/df_47_05_win/hack/ruby/ruby-autogen-defs.rb:439:in `block in each'
E:/Dwarf stuff/df_47_05_win/hack/ruby/ruby-autogen-defs.rb:439:in `each'
E:/Dwarf stuff/df_47_05_win/hack/ruby/ruby-autogen-defs.rb:439:in `each'
hack/scripts/show-unit-syndromes.rb:886:in `collect'
hack/scripts/show-unit-syndromes.rb:886:in `block in <top (required)>'
hack/scripts/show-unit-syndromes.rb:947:in `[]'
Once I used add-syndrome to remove the syndrome (I used -eraseClass with both VAMPCURSE and DISTURBANCE_CURSE), then show-unit-syndromes ran properly with all citizens with syndromes shown.
Would this be because I am using modded creatures in my game ? Or because those kinds of curses cause a problem with the show-unit-syndromes command ?
I want to make 3 plugins for dfhack.I'd suggest writing Lua scripts rather than plugins, as it seems these are intended to be used as commands entered by the player, and neither of them should require compiled code for efficiency reasons.
One that can cut down the one non food tree that will give the most wood.
Another for taking all the dwarves bad thoughts and listing them with the number of times it has occurred.
The last one would count all the dwarves worn clothes and print out a list with numbers of how many worn clothes there are of each subtype.
Do these sound feasible? Are they replicating functionality already built in?
I plan on starting with the tree one since autochop is a thing.
It's an issue with the script. Looks like you're the first one that's reported it since it broke a year or so ago, so thank you! (It may be a rare case - I don't seem to have a save where I can reproduce the issue easily.)
Here's a fix (https://github.com/DFHack/scripts/commit/2b2967c84697d70eb4dd73c641b8d17767f871e2) that should work. If you can test it out, that'd be great. You can either make the changes manually to hack/scripts/show-unit-syndromes.rb, or replace that file with the new version (https://raw.githubusercontent.com/DFHack/scripts/2b2967c84697d70eb4dd73c641b8d17767f871e2/show-unit-syndromes.rb) (be sure to save the new one with a ".rb" extension and overwrite the existing one).
struct DFHACK_EXPORT plant_tree_info {
df::plant_tree_tile** body; /*!< dimension body_height */
int16_t* extent_east; /*!< dimension body_height */
int16_t* extent_south; /*!< dimension body_height */
int16_t* extent_west; /*!< dimension body_height */
int16_t* extent_north; /*!< dimension body_height */
int32_t body_height;
int32_t dim_x;
int32_t dim_y;
df::plant_tree_tile** roots; /*!< dimension roots_depth */
int32_t roots_depth;
int16_t unk6;
static struct_identity _identity;
public:
plant_tree_info();
};
to find the number of logs that the tree will produce?
If Dwarf Therapist is doing it correctly, there is no individual skill rate, it's only in castes (caste_raw.skill_rates). Did you try fixing the raws first? It might not need to re-run worldgen.
@chickrickepachimecho: Probably, as all the info is in there, although I don't know if anyone has researched how to calculate it.
My first attempt would be to simply walk through the tiles of the tree and count the number of trunks, cut down the tree, and compare the number to the number of logs found (obviously the area should either be free of other logs, or the preexisting logs should be marked with e.g. the 'f'orbidden flag to distinguish the new logs from the old ones).
I'd examine blood thorn (very high yield) as well as two and three tile diameter trees (I think highwood is the only one to produce 3 tile one, with some "normal" tree(s) being capable of producing 2 tile diameter trunks).
Also note that the "body" field has an odd description, saying "dimension body_height", while also describing the matrix as "dimension dim_x*dim_y" (the latter isn't shown in the C struct, but it's in the XML source for the C struct). This leads to the suspicion that it's actually a 3 dimensional array (body_height*dim_x*dim_y), which is what would be required to actually contain the info for all of the relevant tiles. However, be prepared for DF crashing if you try to access the structure that way, as it may well refer to unallocated memory if it's only a two dimensional structure. I'd also try to see if the bit combinations aren't nonsensical, as you shouldn't have "blocked" set together with anything else, and "trunk", "branches", and "twigs" probably should be mutually exclusive).
Edit: I've looked at my woodcutter cutting trees and counted the number of trunk segments present before cutting (using the Mk I eyeball) against the number of logs produced, and they've matched so far (surface trees, producing 2-7 logs). I don't have any wide trunk trees.
for (int i = 0; i < plant->tree_info->dim_x; i++) {
tilesRow = tiles[i];
for (int j = 0; j < plant->tree_info->dim_y; j++) {
trunks += tilesRow[j].bits.trunk;
}
}
for (int i = 0; i < plant->tree_info->body_height; i++) {
tilesRow = tiles[i];
for (int j = 0; j < plant->tree_info->dim_y*plant->tree_info->dim_x; j++) {
trunks += tilesRow[j].bits.trunk;
}
}
#include "Core.h"
#include "Console.h"
#include "Export.h"
#include "PluginManager.h"
#include "DataDefs.h"
#include "df/map_block.h"
#include "df/material.h"
#include "df/tiletype_material.h"
#include "df/plant.h"
#include "df/plant_raw.h"
#include "df/plant_tree_info.h"
#include "df/plant_tree_tile.h"
#include "df/world.h"
#include "modules/Designations.h"
#include "modules/Maps.h"
#include "modules/World.h"
#include <algorithm>
#include <sstream>
#include <string>
using namespace DFHack;
using namespace df::enums;
#define PLUGIN_VERSION 0.1
DFHACK_PLUGIN("talltree");
REQUIRE_GLOBAL(world);
static void markPlant(df::plant* plant);
static void markPlant(df::plant* plant) {
if (Designations::canMarkPlant(plant)) {
Designations::markPlant(plant);
}
}
command_result talltree(color_ostream &out, std::vector<std::string> & params);
command_result talltree(color_ostream &out, std::vector<std::string> & params) {
// Suspend the core before changing internal data
CoreSuspender suspend;
out.print("Valid?");
if (Maps::IsValid()) {
const df::plant_raw *plantRaw;
bool chop = true;
int maxLogs = 0;
df::plant* tallestTree = 0;
std::vector<df::plant*> doneGrowing{};
for (auto & plant : world->plants.all) {
int x = plant->pos.x % 16;
int y = plant->pos.y % 16;
df::map_block* tileBlock = Maps::getTileBlock(plant->pos);
if (plant->flags.bits.is_shrub ||
(plant->material != df::tiletype_material::TREE) ||
!tileBlock ||
tileBlock->designation[x][y].bits.hidden) {
chop = false;
}
else {
plantRaw = df::plant_raw::find(plant->material);
if (plantRaw->material_defs.type[plant_material_def::drink]) {
chop = false;
}
for (auto material : plantRaw->material) {
if (material->flags.is_set(material_flags::EDIBLE_RAW) ||
material->flags.is_set(material_flags::EDIBLE_COOKED)) {
chop = false;
}
}
std::stringstream ss1;
ss1 << "Chop: " << chop;
out.print(ss1.str().c_str());
if (chop) {
df::plant_tree_tile** tiles = plant->tree_info->body;
df::plant_tree_tile* tilesRow;
int trunks = 0;
for (int i = 0; i < plant->tree_info->body_height; i++) {
tilesRow = tiles[i];
for (int j = 0; j < plant->tree_info->dim_y*plant->tree_info->dim_x; j++) {
trunks += tilesRow[j].bits.trunk;
}
}
std::stringstream ss;
ss << "Trunks: " << trunks;
out.print(ss.str().c_str());
maxLogs = std::max(maxLogs, trunks);
if (maxLogs == trunks) {
tallestTree = plant;
}
if (plantRaw->max_trunk_height*plantRaw->max_trunk_diameter <= trunks) {
doneGrowing.push_back(plant);
}
}
}
}
//markPlant(tallestTree);
for (auto & plant : doneGrowing) {
markPlant(plant);
}
}
return CR_OK;
}
DFhackCExport command_result plugin_init(color_ostream &out, std::vector <PluginCommand> &commands) {
commands.push_back(
PluginCommand(
"talltree",
"Cuts down the non fruit trees that will give the most wood, and the ones that are done growing\n",
talltree,
false,
"Finds the non fruit tree that wil give the most wood and designates it for cutting.\n"
"Also cuts down all the non fruit trees that are done growing.\n"
)
);
return CR_OK;
}
DFhackCExport command_result plugin_shutdown(color_ostream &out);
DFhackCExport command_result plugin_shutdown(color_ostream &out) {
return CR_OK;
}
command_result talltree(color_ostream &out, std::vector<std::string> & params) {
// Suspend the core before changing internal data
CoreSuspender suspend;
out << ("Valid?") << std::endl;
if (Maps::IsValid()) {
const df::plant_raw *plantRaw;
bool chop = true;
int maxLogs = 0;
df::plant* tallestTree = 0;
std::vector<df::plant*> doneGrowing{};
for (auto & plant : world->plants.all) {
chop = true;
int x = plant->pos.x % 16;
int y = plant->pos.y % 16;
df::map_block* tileBlock = Maps::getTileBlock(plant->pos);
if (tileBlock) {
df::tiletype_material material = tileMaterial(tileBlock->tiletype[x][y]);
if (material != df::tiletype_material::TREE ||
tileBlock->designation[x][y].bits.hidden) {
chop = false;
}
}
else {
chop = false;
}
if (plant->flags.bits.is_shrub) {
chop = false;
}
else {
plantRaw = df::plant_raw::find(plant->material);
if (plantRaw->material_defs.type[plant_material_def::drink] != -1) {
chop = false;
}
for (auto material : plantRaw->material) {
if (material->flags.is_set(material_flags::EDIBLE_RAW) ||
material->flags.is_set(material_flags::EDIBLE_COOKED)) {
chop = false;
}
}
if (chop) {
df::plant_tree_tile** tiles = plant->tree_info->body;
df::plant_tree_tile* tilesRow;
int trunks = 0;
for (int i = 0; i < plant->tree_info->body_height; i++) {
tilesRow = tiles[i];
for (int j = 0; j < plant->tree_info->dim_y*plant->tree_info->dim_x; j++) {
trunks += tilesRow[j].bits.trunk;
}
}
out << "Trunks: " << trunks << std::endl;;
maxLogs = std::max(maxLogs, trunks);
if (maxLogs == trunks) {
tallestTree = plant;
}
if (plantRaw->max_trunk_height*plantRaw->max_trunk_diameter*plantRaw->max_trunk_diameter <= trunks) {
doneGrowing.push_back(plant);
}
}
}
}
markPlant(tallestTree);
/*
for (auto & plant : doneGrowing) {
markPlant(plant);
}
*/
}
return CR_OK;
}
plantRaw->max_trunk_height*plantRaw->max_trunk_diameter*plantRaw->max_trunk_diameter <= trunks
max_trunk_height and max_trunk_diameter must not be what I think they are. Does anyone know how to see if a tree is done growing?
If you are writing code to programmatically chop down trees then I am intrigued and would like to subscribe to your newsletter. This is the last bit of missing functionality from my dig-now plugin.
If you are writing code to programmatically chop down trees then I am intrigued and would like to subscribe to your newsletter. This is the last bit of missing functionality from my dig-now plugin.
void chopTree(int32_t x, int32_t y, int32_t z, df::unitst &unit) //might take more parameters
{
int32_t unit_dis_x = (x - unit->pos.x);
int32_t unit_dis_y = (y - unit->pos.y);
auto plant = dfhack.maps.getPlantAtTile(x,y,z);
if (plant == NULL)
return;
auto tree = plant->tree_info;
if (tree == NULL)
return;
int32_t tree_min_x = plant->pos.x - (tree->dim_x / 2);
int32_t tree_min_y = plant->pos.y - (tree->dim_y / 2);
int32_t tree_min_z = plant->pos.z;
int32_t tree_max_x = (tree->dim_x - 1) + tree_min_x;
int32_t tree_max_y = (tree->dim_y - 1) + tree_min_y;
int32_t tree_max_z = (tree_min_z - 1) + tree->body_height;
for (int32_t loop_x=0, tile_x=tree_min_x; loop_x < tree->dim_x; loop_x++, tile_x++)
{
for (int32_t loop_y=0, tile_y=tree_min_y; loop_y < tree->dim_y; loop_y++, tile_y++)
{
for (int32_t loop_z=(tree->body_height-1), tile_z=tree_max_z; loop_z >= 0; loop_z--, tile_z--)
{
if (tree->body[loop_z][(tree->dim_x * loop_y) + loop_x].trunk == false)
continue;
int32_t mat = plant->material;
if (mat < 0)
continue;
auto raw_plants = df->global->world->raws->plants->all;
if (mat >= raw_plants.size())
continue;
auto plant_raw = raw_plants[mat];
int32_t mattype = plant_raw->material_defs.type[plant_material_def::tree];
int32_t matindex = plant_raw->material_defs.idx[plant_material_def::tree];
if (mattype == -1)
continue;
//need to update item counts and fulfill mandates
//auto log = createItem(-1,item_type::WOOD,mattype,matindex); //function has more parameters than this
log->pos.x = tile_x;
log->pos.y = tile_y;
log->pos.z = tile_z;
//need to create new df::item_projst;
proj->origin_pos.x = tile_x;
proj->origin_pos.y = tile_y;
proj->origin_pos.z = tile_z;
proj->prev_pos.x = tile_x;
proj->prev_pos.y = tile_y;
proj->prev_pos.z = tile_z;
proj->item = log;
proj->cur_pos.x = tile_x;
proj->cur_pos.y = tile_y;
proj->cur_pos.z = tile_z;
//insert proj.id into log general_ref
proj->firer = unit;
//set no_impact_destroy|bouncing|piercing|parabolic|unk9|no_collide:
proj->flags.whole |= 0xB15;
proj->pos_x = 0;
//proj->pos_y = 0; //this isn't used?
proj->pos_z = 0;
proj->speed_x = unit_dis_x * 10000;
proj->speed_y = unit_dis_y * 10000;
proj->speed_z = 0;
//proj->accel_x = 0; //this isn't used?
proj->accel_y = 0;
//proj->accel_z = 0; //this isn't used?
//need to calculate proj->hit_rating
log->flags.in_job = true;
}
}
}
}
//need to handle tiletypes, etc. It's in a separate loop for some reason.
(They aren't all int32_t, I just didn't bother to double check.)How do you go about debugging a plugin that crashes? So far, I've been commenting code out, but I would like a faster way.Start the game, hook up a debugger to it, and catch the crash (or set up a breakpoint at the plugin entry and single step through the code).
Does anyone know how to see if a tree is done growing?
Does anyone know how to see if a tree is done growing?
Consider using the plant's age? Counting trunk tiles isn't going to work for that because tree growth can be stunted due to obstacles, including other trees. (In addition to any randomness in layout.)
As a starting point for the first one, here's one of my scripts intended to control which trees to cut, sparing one of each resource providing kind of tree, selecting the ones closest to the center of the embark. It skips cavern trees, and deals with elven logging restrictions.I would recommend using `dfhack.designations.markPlant()` instead - it does the same thing, but the inverse, `unmarkPlant()`, is significantly more complicated and it's good to use library functions for consistency. This one admittedly seems to be missing from the Lua API docs.Code: [Select]<snip>
if trees_designated < max_trees then
cur.designation [x % 16] [y % 16].dig = df.tile_dig_designation.Default
cur.flags.designated = true
Or you could point me to a list of all the possible methods or objects within "unit", because I can't find it.
Is there a way to use this structThis recent script by Bumber (http://www.bay12forums.com/smf/index.php?topic=164123.msg8290143#msg8290143) might be useful as a reference to figure out how to interpret and count the number of tiles in the structure. I don't know if the number of logs produced by a tree corresponds exactly to anything (e.g. the number of trunk tiles), but there's a chance it does, or that it's close.Code: [Select]struct DFHACK_EXPORT plant_tree_info {
to find the number of logs that the tree will produce?
<snip>
How do you go about debugging a plugin that crashes? So far, I've been commenting code out, but I would like a faster way.That depends - what platform are you developing on?
:I don't think dorf clothing behavior has been identified in detail. On the top level, dorfs tend to replace clothing worn down to the 'x' level (which I expect to be 1) as soon as there's a replacement available. If there isn't a replacement, they'll wear clothes until they rot off their bodies and are destroyed, resulting in a bad thought (I suspect that might be level 5, which, in that case, would exist only very briefly until the clothing item is destroyed by a DF cleanup sweep).
As for the other plugin I want to create that will replace worn clothes. It appears that worn clothes getWear value go from 1 to 4, at the least, based on code, and 0 means no wear. The code does not show whether there is an upper limit, but that 4 and 5 would have the same wear label. At what point is worn clothing scrapped by a dwarf?
:I don't think dorf clothing behavior has been identified in detail. On the top level, dorfs tend to replace clothing worn down to the 'x' level (which I expect to be 1) as soon as there's a replacement available. If there isn't a replacement, they'll wear clothes until they rot off their bodies and are destroyed, resulting in a bad thought (I suspect that might be level 5, which, in that case, would exist only very briefly until the clothing item is destroyed by a DF cleanup sweep).
As for the other plugin I want to create that will replace worn clothes. It appears that worn clothes getWear value go from 1 to 4, at the least, based on code, and 0 means no wear. The code does not show whether there is an upper limit, but that 4 and 5 would have the same wear label. At what point is worn clothing scrapped by a dwarf?
However, dorfs are often caught hoarding pristine clothing, including masterworks ones, so there's something going on on top of wear. Some or all dorfs probably replace clothes of a lower quality with higher quality ones, and some probably replace clothes of the same quality but with a lower value with more valuable ones. I don't think this behavior has been researched, but I wouldn't be surprised if personality traits affect how much "better" a piece of clothing would have to be to be claimed.
I don't know if the number of logs produced by a tree corresponds exactly to anything (e.g. the number of trunk tiles), but there's a chance it does, or that it's close.
Edit: it seems that you all have figured out that it's more complicated. I mainly just wanted to track down this script again. Carry on!
It's an issue with the script. Looks like you're the first one that's reported it since it broke a year or so ago, so thank you! (It may be a rare case - I don't seem to have a save where I can reproduce the issue easily.)
Here's a fix (https://github.com/DFHack/scripts/commit/2b2967c84697d70eb4dd73c641b8d17767f871e2) that should work. If you can test it out, that'd be great. You can either make the changes manually to hack/scripts/show-unit-syndromes.rb, or replace that file with the new version (https://raw.githubusercontent.com/DFHack/scripts/2b2967c84697d70eb4dd73c641b8d17767f871e2/show-unit-syndromes.rb) (be sure to save the new one with a ".rb" extension and overwrite the existing one).
This fix will also be in the next DFHack release, which is hopefully soon.
function sigh2()
for k,v in pairs(df.global.world.squads.all) do
if v.alias == 'DFHACK' then
print(df.global.world.squads.all[k].alias)
for ki,vi in pairs(df.global.world.squads.all[k].positions) do
if df.global.world.squads.all[k].positions[ki].occupant ~= -1 then
local occup=vi.occupant
--local occup=df.global.world.squads.all[k].positions[ki].occupant
--print(occup)
local nemoccup=df.global.world.history.figures[occup].nemesis_id
insertreport(nemoccup)
end
end
end
end
end
--end
function insertreport(nemoccup)
for de,oe in pairs(df.global.world.army_controllers.all) do
if oe.mission_report == nil then else
print ( de,oe.mission_report.title)
for e,o in pairs(df.global.world.armies.all) do
if oe.id == o.controller_id then
print (e)
--return e,o
local forv=df.global.world.armies.all[e].members[0]
df.global.world.armies.all[e].members:insert("#",{new=true,nemesis_id=nemoccup,
stored_fat = forv.stored_fat,
unk_2c= forv.unk_2c,
unk_28= forv.unk_28,
unk_1= forv.unk_1,
unk_30= forv.unk_30,
unk_34= forv.unk_34})
end
end
end
end
end
sigh2()
idenfitied several fields
Just discovered that DF uses binary search to find a construction at a tile. It's probable that DF does so in a number of places.
So then: Does DFHack take into account that some DF vectors must be sorted when it inserts new entries into them?
Does a vector insert handle that on its own?
Just discovered that DF uses binary search to find a construction at a tile. It's probable that DF does so in a number of places.We're aware. A lot vectors are sorted by a primitive "id" field of each item they contain (or something with a similar name), and "df.some_type.find()" uses this to speed up searches. For insertions, there is "insert_into_vector" (in C++, in MiscUtils.h) and "utils.insert_sorted" in Lua. However, in most cases, IDs cannot be reused, so newly-created items should have the largest ID and can be inserted at the end of vectors without causing problems. This doesn't apply to items with more complex keys, like constructions sorted by coordinate, and I'm not even sure if insert_sorted() works for those.
So then: Does DFHack take into account that some DF vectors must be sorted when it inserts new entries into them?
Does a vector insert handle that on its own?
This doesn't apply to items with more complex keys, like constructions sorted by coordinate, and I'm not even sure if insert_sorted() works for those.
I use this revealdesignated script a lot while playing:You can submit it as a pull request to the scripts (https://github.com/DFHack/scripts) repo. That way it can get reviewed and improved before committing. For example, you could rewrite the script as a tiletypes command and it would probably execute much faster.
...
If this is useful, could you incorporate it into the DFHack release? Maybe it would be useful for other people as well. And then I wouldn't have to keep installing it by hand. :)
# Add additional script search paths here
# Blank lines and lines that start with "#" will be ignored
# Paths preceded by "+" will be searched first
# Paths preceded by "-" will be searched after the default paths
+/home/myk/src/dfhack-scripts
I'm using Windows. Looks like DF reliably crashes when attempting to delete something that's already been deleted, so I can check using lua.While a crash can indicate that DF has already deleted something, the inverse is not necessarily true: the lack of a crash does not mean that DF has not already deleted something, nor does it mean that the crash won't occur in the future or in different environments (other systems, DF versions, DFHack versions, etc.).
Looks like deleting tree_info deletes extent_east, etc., (didn't check tree_tiles) but deleting a plant doesn't delete its tree_info. Guess I can't rely on it.I verified on Linux that this is not the case:
t = df.plant_tree_info:new()
t.extent_east = df.new('int16_t', 120)
~t -- in lua interpreter
t:delete()
~t
delete() frees the parent plant_tree_info struct, and causes it to be overwritten with garbage, but extent_east is left intact (all 0s), meaning that it was not deleted.@lethosorThat's exactly what my previous post was intended to answer "no" to. I realize now that it's confusing because you had also mentioned plant.tree_info in addition to plant_tree_info.extents_east. delete() does not delete any subfields of plant or plant_tree_info or most other structs, because their destructors are not virtual.
Does calling delete() on the plant struct delete extent_east?
In which module should I put a function that deletes a plant (given a df::plant) and removes it from the plant vectors?Good question. Maybe Maps?
...
if (plant->contaminants.capacity != 0)
std::vector<df::spatter_common *>().swap(plant->contaminants);
delete plant;
return;
How do I check if the plant->contaminants vector is not NULL, and then delete it (without deleting the spatters the pointers aim at?)
Will this work?Code: [Select]...
if (plant->contaminants.capacity != 0)
std::vector<df::spatter_common *>().swap(plant->contaminants);
delete plant;
return;
Or should I use clear() or resize(0) and then shrink_to_fit()?
for j, gref in ipairs(posting.job.general_refs) do
if gref.unit_id == some_unit_id then
do_stuff
end
end
but I don't know what gref is going to be. If it's general_ref_unit_slaughtereest or general_ref_unit_workerst, it has unit_id, but general_ref_building_holderst doesn't.Cannot read field general_ref_building_holderst.unit_id: not found.
stack traceback:
[C]: in metamethod '__index'
what is the nicest way to check from Lua if a structure has a field?
if gref.unit_id != nil and gref.unit_id == some_unit_id then
Does this work? (I think it might still work if you remove "!= nil", unless it breaks when unit_id == 0.)Spatters on a plant should be removed together with the plant it's tied to. If the spatter resides in a different place and the vector just refers to them, you should locate that place and remove them from there [...]
what is the nicest way to check from Lua if a structure has a field?Code: [Select]if gref.unit_id != nil and gref.unit_id == some_unit_id then
Does this work? (I think it might still work if you remove "!= nil", unless it breaks when unit_id == 0.)
what is the nicest way to check from Lua if a structure has a field?The usual solution is to use pcall() around a function that just accesses the field, then handle any errors as you see fit. For example:
I want the following loop.Code: [Select]for j, gref in ipairs(posting.job.general_refs) do
but I don't know what gref is going to be. If it's general_ref_unit_slaughtereest or general_ref_unit_workerst, it has unit_id, but general_ref_building_holderst doesn't.
if gref.unit_id == some_unit_id then
do_stuff
end
end
In current form, the loop throwsQuoteCannot read field general_ref_building_holderst.unit_id: not found.
stack traceback:
[C]: in metamethod '__index'
local ok, val = pcall(function()
return gref.unit_id
end)
Feel free to use better variable names, of course. Docs for pcall() can be found here: https://www.lua.org/manual/5.3/manual.html#pdf-pcallIdeally, it would just return nil in this case, but this may be too much to ask :)I think it would be too big of a compatibility break at this point. I think the behavior is mostly for parity with other languages where accessing an invalid field throws an error. Granted, in C++, that error occurs at compile-time, but in Ruby it would occur at runtime like in Lua. There's also the matter of existing fields that can be defined but nil (i.e. null pointers in C++), which the current behavior makes more clear to Lua.
What I was hoping for is access to the same information __index has in some form. Usually, the place to look would be the metatable of the object, but it's a string in this case. But maybe we could have some meta-info in the basic type structures in the df-table, so we can do something like the following.what is the nicest way to check from Lua if a structure has a field?I don't believe there is a way to look up what fields a given type has from Lua (although the C++ layer that __index calls definitely has this information), but you could build a table of "type => fields" or "type => has_unit_id_field" mappings yourself, or cache it as you go.
if df[getmetatable(gref)].fields.unit_id then
print('Has unit_id field!')
end
The getmetatable(gref) is here a string like "general_ref_building_holderst".local function getFieldOrNil(obj, field)
for k,v in pairs(obj) do
if k == field then
return v
end
end
return nil
end
if getFieldOrNil(gref, 'unit_id') == unit.id then
doStuff()
end
(function getFlag(dfvalue, key)
-- Utility function for safely requesting info from df data
if not dfvalue or not key then return nil end --pairs crash prevention
flagtypetable = flagtypetable or {} --memoization so it doesn't iterate through the dfvalue if we've already checked that type of value for given flag
if flagtypetable[dfvalue._type] and flagtypetable[dfvalue._type][key] then return dfvalue[key] end
if flagtypetable[dfvalue._type] and flagtypetable[dfvalue._type][key]==false then return nil end
if not flagtypetable[dfvalue._type] then flagtypetable[dfvalue._type] = {} end
for akey, avalue in pairs(dfvalue) do
if akey == key then
flagtypetable[dfvalue._type][key] = true
return dfvalue[akey]
end
end
flagtypetable[dfvalue._type][key] = false
end
The first lookup will iterate, further lookups will be just function calls that checks table.Yeah, I use something like that alongside building a table for what was already checked
I think there might have been some cases where memoriziation was undesirable, but don't recall them right now.
what is the nicest way to check from Lua if a structure has a field?If you're not performing the check very often, you might be better off just calling gref:getUnit() and examining the value returned - if it's a reference to a unit, it will give you a pointer to the unit itself (and you can then check its ID accordingly), and if it's a reference to something else you'll get a null pointer instead.
I want the following loop.Code: [Select]for j, gref in ipairs(posting.job.general_refs) do
but I don't know what gref is going to be. If it's general_ref_unit_slaughtereest or general_ref_unit_workerst, it has unit_id, but general_ref_building_holderst doesn't.
if gref.unit_id == some_unit_id then
do_stuff
end
end
what is the nicest way to check from Lua if a structure has a field?If you're not performing the check very often, you might be better off just calling gref:getUnit() and examining the value returned - if it's a reference to a unit, it will give you a pointer to the unit itself (and you can then check its ID accordingly), and if it's a reference to something else you'll get a null pointer instead.
I want the following loop.Code: [Select]for j, gref in ipairs(posting.job.general_refs) do
but I don't know what gref is going to be. If it's general_ref_unit_slaughtereest or general_ref_unit_workerst, it has unit_id, but general_ref_building_holderst doesn't.
if gref.unit_id == some_unit_id then
do_stuff
end
end
how would I discover this function on my own? There is no mention in the Lua API doc (https://docs.dfhack.org/en/stable/docs/Lua%20API.html), getmetatable(gref) is a string, df.general_ref is of no help either.You would discover this function by looking at the structure definitions (https://github.com/DFHack/df-structures) - in this specific case, you would have found it here (https://github.com/DFHack/df-structures/blob/master/df.refs.xml#L90).
how would I discover this function on my own? There is no mention in the Lua API doc (https://docs.dfhack.org/en/stable/docs/Lua%20API.html), getmetatable(gref) is a string, df.general_ref is of no help either.You would discover this function by looking at the structure definitions (https://github.com/DFHack/df-structures) - in this specific case, you would have found it here (https://github.com/DFHack/df-structures/blob/master/df.refs.xml#L90).
<job: 000001D919322E30>
id = 37238
list_link = nil
posting_index = -1
completion_timer = 0
unk4 = 0
flags = <job_flags: 000001D919322E5C>
working = true
by_manager = true
expire_timer = 0
recheck_cntdn = 0
wait_timer = 0
unk11 = -1
order_id = 192
The question is how do I append a job to the df.global.world.jobs.list from Lua correctly and how do I (and should I) find a posting for it?Luckily for you, I've got a job helper script I never finished just sitting around :P
-- I don't fully understand how this works, so this might be horribly wrong
-- The DF job assigner needs a job to be in postings for it to automatically be assigned to a unit.
function addJobToPostings(job)
local addedIndex = false
-- Find the first free empty postings to add a job to
for index, posting in ipairs(df.global.world.jobs.postings) do
if posting.job == nil then
-- It's free real estate!
posting.job = job
posting.anon_1 = 0
posting.flags.dead = false
job.posting_index = posting.idx
addedIndex = posting.idx
break
end
end
if addedIndex then
print("Added job " .. job.id .. " to postings as index " .. addedIndex)
else
print("Couldn't find posting slot to add in job " .. job.id)
end
return addedIndex
end
The question is how do I append a job to the df.global.world.jobs.list from Lua correctly and how do I (and should I) find a posting for it?Luckily for you, I've got a job helper script I never finished just sitting around :P
For appending the job, use dfhack's dfhack.job.linkIntoWorld function (see this section (https://docs.dfhack.org/en/stable/docs/Lua%20API.html#job-module) of the docs).
For making a posting, you only need to do that if you want to advertise it as available for a unit to take. If you're manually assigning a unit to the job, you won't need to make a posting. This is the code I was using, but it might not be the best way.
Nice, thanks. Did you figure what posting.anon_1 does since you are setting it to 0?I don't think so (?). I likely just copied what I saw happening in regular jobs I was checking and hoped for the best :P The comment in the structs (https://github.com/DFHack/df-structures/blob/master/df.world.xml#L630) says "not saved" - I'm not sure if that means that value is never saved (and so is useless) or if it's supposed to be some flag determining if the job is saved or not (yay ambiguity).
And in the rather unlikely case all postings are in use, can we just create a new one?Also not sure on that. The game has a lot of postings available, so it's unlikely to run out. I think I had intended at some point to manually create enough jobs in vanilla to fill all the postings just to see what happens (does the game handle that by preventing you from making new jobs?).
Another thing I notice I'm missing for what I have in mind: is there a way to find the building the job in onJobCompleted originated from (if there is any)?This (https://github.com/DFHack/scripts/blob/master/modtools/reaction-trigger.lua#L75) is what modtools/reaction-trigger does for getting the unit and building from a job. (tl;dr Find the general ref with type of df.general_ref_type.BUILDING_HOLDER, take building_id from it, then use df.building.find(building_id) to get the building.
Thanks, I somehow completely missed the job.general_refs. But seeing how reaction-trigger does it makes me wonder: is it better to check gref:getType() == df.general_ref_type.BUILDING_HOLDER or building = gref:getBuilding(); if building then?Nice, thanks. Did you figure what posting.anon_1 does since you are setting it to 0?Another thing I notice I'm missing for what I have in mind: is there a way to find the building the job in onJobCompleted originated from (if there is any)?This (https://github.com/DFHack/scripts/blob/master/modtools/reaction-trigger.lua#L75) is what modtools/reaction-trigger does for getting the unit and building from a job. (tl;dr Find the general ref with type of df.general_ref_type.BUILDING_HOLDER, take building_id from it, then use df.building.find(building_id) to get the building.
is it better to check gref:getType() == df.general_ref_type.BUILDING_HOLDER or building = gref:getBuilding(); if building then?If you know that you're looking for one specific reference type, it's better to check getType() because that just calls a function which immediately returns an integer, while getBuilding has to perform a binary search through the list of every building in your fortress to find the one to which it refers (which isn't too slow but is definitely slower than checking the reference type).
On another note, the job from eventful.onJobCompleted kept being destroyed resulting in some strange stuff happening (mostly crashes :))) as I kept trying to use it. The fix was making a clone at the very beginning (job = dfhack.job.cloneJobStruct(job)). So, I'm wondering: is this (job being destroyed after leaving the event handler) by design? The job parameter in eventful.onJobCompleted is already a copy, so it feels kind of strange to make yet another copy of it in order to use it. (But it may be actually is by design because of object ownership and stuff -- if that's the case, pretty please put a note about it in the docs)Did you have to make a copy of the job to use it safely inside the event handler, or after the event handler returned? Relying on the latter is generally not safe (not limited to this case) because the copy of the job is destroyed after the handler returns, and usually results in a crash more often in cases where you know the object you're dealing with is about to be deleted (as is the case with jobs that have completed). I'm not opposed to adding a note about it in the docs, but it's not necessarily specific to this event. If you are accessing the job long after it has been destroyed, I might suggest only making a copy of the data you need, as opposed to everything that's part of the job - that way, you don't need to worry about freeing your copy of the job.
On another note, the job from eventful.onJobCompleted kept being destroyed resulting in some strange stuff happening (mostly crashes :))) as I kept trying to use it. The fix was making a clone at the very beginning (job = dfhack.job.cloneJobStruct(job)). So, I'm wondering: is this (job being destroyed after leaving the event handler) by design? The job parameter in eventful.onJobCompleted is already a copy, so it feels kind of strange to make yet another copy of it in order to use it. (But it may be actually is by design because of object ownership and stuff -- if that's the case, pretty please put a note about it in the docs)Did you have to make a copy of the job to use it safely inside the event handler, or after the event handler returned? Relying on the latter is generally not safe (not limited to this case) because the copy of the job is destroyed after the handler returns, and usually results in a crash more often in cases where you know the object you're dealing with is about to be deleted (as is the case with jobs that have completed). I'm not opposed to adding a note about it in the docs, but it's not necessarily specific to this event.
If you are accessing the job long after it has been destroyed, I might suggest only making a copy of the data you need, as opposed to everything that's part of the job - that way, you don't need to worry about freeing your copy of the job.
It was after the event handler returned. Inside everything worked. I think the main source of my confusion is the fact that the job object passed to the event handler already is already a copy (and that I haven't programmed for systems without garbage collector for quite some time). I kind of assumed the original was destroyed but the copy will live on.Yeah, the lack of a garbage collector is part of the issue here, as well as the fact that the conventional use-case is for event handlers to be the only users of the cloned job. EventManager can't know whether anything retained a reference to the job copy it made, so the only logical options are to free the job copy immediately after event handlers return, or to never free it (and leak memory), so it chooses the former.
Does this mean I should free the job created using dfhack.job.cloneJobStruct(job) somehow? What about my case, where this job is linked into the world?I forgot that you were trying to do that (I thought your own code was doing things with the copy of the job, not DF). In this case, ignore my advice - you will need to create a new job (like with dfhack.job.cloneJobStruct()) and link it back into the world.
Any chance we can call the function doing the job assignments from Lua?The usual answer to this type of question is "no", unless the function is virtual (which doesn't appear to be the case) and there is actually a single function doing what you want (which isn't guaranteed).
Has any research been done on pathing flags? I see add_path_flags in units.xml and unit_path_flags in advmode.xml, but they're just int32's.I've done some limited research against those path flags in version 0.28.181.40d, and it's quite likely that most of them will still be correct. I've been mainly posting my findings on the DFHack Discord server (in the "#reverse-engineering" channel), but this is what I was able to find:
If you search for the byte sequence 93F833 in the disassembly, you should end up in a subfunction that seems to be calculating them from unit flags. A ghostly unit carrying items seems to return 0x93F833 for the flags. I'd research them myself, but I'm busy with stuff relating to tree chopping (such as fluid pathing, which just uses an immediate value 0x0 for the pathing flags.)
* 0x00000001 - desperate (set when very hungry or thirsty)
* 0x00000002 - reckless (set when insane or when force-moving as an Adventurer)
* 0x00000004 - BuildingDestroyer1 (allow wooden doors)
* 0x00000008 - Lockpicker (allow doors not connected to levers), also set for BuildingDestroyer2
* 0x00000010 - stuck in impassable building
* 0x00000020 - allow unrevealed tiles
* 0x00000040 - can't open doors
* 0x00000080 - unknown, related to doors
* 0x00000100 - creature can learn
* 0x00000200 - unknown
* 0x00000400 - can jump off cliffs (when insane)
* 0x00000800 - can fly
* 0x00001000 - allow shallow water (1-5)
* 0x00002000 - allow deep water (6+)
* 0x00004000 - allow underwater (7 and 3+ water above)
* 0x00008000 - allow shallow magma (1-5)
* 0x00010000 - allow deep magma (6+)
* 0x00020000 - allow under magma (7 and 3+ magma above)
* 0x00040000 - already in water
* 0x00080000 - already in magma
* 0x00100000 - walk on normal land
* 0x00200000 - immobile_land
* 0x00400000 - dwarf, walk_tag equals zero
* 0x00800000 - unknown, tied to an unused "tile liquid" flag
0x4: True causes a check for the material of a hatch to be in range 419d to 618d (both inclusive.) Still BuildingDestroyer1.
0x8: True skips a check for pet_passable on hatch. Still Lockpicker.
0x10: True skips a check for pet_passable on hatch. Still "stuck in impassible building".
0x40: True causes a check for any hatch. Still "can't open doors".
.text:000000014015804E mov cs:word_1416B1990, ax
What's the process for mapping a DF global variable? I found a large global array used in (fluid) pathing. It starts 8 bytes after "agreement_next_id" (on Windows 64-bit, 0.47.05.)The variable you're looking at is named "line", and it's mentioned in the Global Variable Table (which is marked by 0x12345678+0x12345678+0x87654321+0x87654321 and begins at address 0x1410b5040 on Win64).
Entries are structs of size 12 (not pointers,) which I'm calling path_tile for now. The struct contains the block x, y, and z (for use with block_index,) then the x%16 and y%16 tile offset, and finally an unknown/unused value (potentially used in other kinds of pathing.) All these are int16_t.
I'm guessing the array has 240000 entries total, since it adds entries at (base of array + 0x15F900), then flip-flops on which part is reading/writing each iteration. There aren't any checks done on the length of the array, it just keeps going until an iteration finishes where it didn't find a tile with a fluid and a path_cost less than world.next_path_cost.
Function I'm looking at is sub_140157F80. Here's where it puts the initial block x into the start of the array:Code: [Select].text:000000014015804E mov cs:word_1416B1990, ax
The variable you're looking at is named "line", and it's mentioned in the Global Variable Table (which is marked by 0x12345678+0x12345678+0x87654321+0x87654321 and begins at address 0x1410b5040 on Win64).
From what I recall, that array is only used as a temporary buffer for pathfinding calculations, and none of its state ever persists between operations.
If you think there's value in adding it to structures, then we can certainly add it.
Hi! I’ve been having a few problems with autochop.
...
Hi! I’ve been having a few problems with autochop.
The first problem is that it seems to designate way too many trees for felling. On one of my previous forts, my stocks screen listed something like 25,000+ logs and my computer ground to a halt for at least five minutes when ever I cursored down to the line for logs. I checked the autochop dashboard and it said that it was set to keep only a maximum of 100 logs on hand at a time. Something is obviously going wrong here if autochop is ignoring it’s maximum limit.
Hi! I’ve been having a few problems with autochop.
The first problem is that it seems to designate way too many trees for felling. On one of my previous forts, my stocks screen listed something like 25,000+ logs and my computer ground to a halt for at least five minutes when ever I cursored down to the line for logs. I checked the autochop dashboard and it said that it was set to keep only a maximum of 100 logs on hand at a time. Something is obviously going wrong here if autochop is ignoring it’s maximum limit.
I pulled up a test fort and set autochop to a maximum of 10 logs. Autochop did designate every tree for chopping, but as soon as my woodcutter had cut down enough trees, it undesignated every tree again. It seems to check every in-game day, so if you have a lot of woodcutters, this behavior might result in a few too many logs. I let it run for a couple more in-game days and it didn't designate any more trees, so it seems to be working on my end, at least over the short term.
There is a feature request here (https://github.com/DFHack/dfhack/issues/876) to limit the number of trees designated, but it hasn't been completed yet. There is also this one (https://github.com/DFHack/dfhack/issues/1269) for an annual limit on trees chopped (to keep elves happy).
A couple questions:
- How old is this fort?
- What DFHack version are you using? (The version number from running "help" or in the options (Esc) menu is ideal.)
- Do you have enough logs that are accessible? (i.e. not forbidden, in jobs, etc). One possibility I can think of is that if a lot of logs are queued to be placed in a bin, autochop might not consider them as available, and could end up causing more trees to be cut down. The "z"-stocks screen would still show a lot of logs in this case.
I'm thinking of implementing a military uniforms importer/exporter (as per issue #651 (https://github.com/DFHack/dfhack/issues/651)). What other military-related functionality would be useful? That is, if I built a "military" plugin, what would you want it to do?
If the answer is "not much", I'll probably keep it simple and just write a "uniforms" plugin that just does uniform import/export like how the "orders" plugin works for manager orders.
It is probably possible to apply a uniform while not touching weapons, but I want to understand a little more about what you're doing. I believe the usual flow is to assign uniforms first, and then specify weapon choices. How exactly are you accustomed to interacting with your military if you are assigning weapons first and then changing the uniform?
- Do you have enough logs that are accessible? (i.e. not forbidden, in jobs, etc). One possibility I can think of is that if a lot of logs are queued to be placed in a bin, autochop might not consider them as available, and could end up causing more trees to be cut down. The "z"-stocks screen would still show a lot of logs in this case.I'm pretty sure that logs cannot be placed in bins.
- Do you have enough logs that are accessible? (i.e. not forbidden, in jobs, etc). One possibility I can think of is that if a lot of logs are queued to be placed in a bin, autochop might not consider them as available, and could end up causing more trees to be cut down. The "z"-stocks screen would still show a lot of logs in this case.I'm pretty sure that logs cannot be placed in bins.
It's possible that the job cancel bug I discovered the other day is involved here. My current test fort, which is running using the new job cancel method, does not exhibit the behavior that is being reported here; rather, it works as expected, autodesignating every tree on the map, and then undesignating them as soon as enough trees are cut down, as expected. But if the jobs are not cancelling properly (as I was seeing in my prior test fort), then it might continue to chop even though autochop tried (but failed) to cancel the jobs.
Please tell me it still leaves manually designated trees alone.As far as I can tell, autochop has never left manually designated trees alone. The point of the plugin is that it controls when trees get cut down, so it will designate or undesignate them as appropriate. ab9rf's work isn't changing that.
Please tell me it still leaves manually designated trees alone.As far as I can tell, autochop has never left manually designated trees alone. The point of the plugin is that it controls when trees get cut down, so it will designate or undesignate them as appropriate. ab9rf's work isn't changing that.
However, you can configure trees to be ignored (via burrows or disabling e.g. fruit trees in autochop settings), and those restrictions apply to both designating and undesignating trees.
movzx eax, ds:(byte_7FF67649E388 - 7FF676350000h)[rdx+rax]
mov ecx, ds:(off_7FF67649E380 - 7FF676350000h)[rdx+rax*4]
Is it starting at address 14E380h, then offsetting by rdx (7FF676350000h, resulting in byte_7FF67649E388 again,) then adding rax to that and moving the value there into eax? (With that value being an index for off_7FF67649E380?) Are my assumptions correct?Yes, that's exactly what it's doing - in 64-bit binaries, VC++ implements switch() statement jump tables using 32-bit relative offsets for everything, probably because it makes the code smaller.
Why wouldn't it be something like "movzx eax, ds:(byte_7FF67649E388)[rax]" instead?Because that would presumably require using a 64-bit absolute address (with fixup), and I'm not sure whether that's actually possible in amd64 assembly.
Does anyone know a way to make a pet not a pet?you could just force the slaughtering of the pet with dfhack by messing with the creature's unit flags to set to slaughter if you're already tossing them into magma.
I tried poking through gm-edit but I can't find anything obvious.
I absolutely despise livestock as pets, normally I teleport them into magma, but I'm looking for a "kinder, gentler" approach (and more meat).
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
--print("Creature not found!")
return nil
end
function Butcherunit(unit)
if unit==nil then
unit=getCreatureAtPos(getxyz())
end
if unit==nil then
error("Failed to Heal unit. Unit not selected/valid")
end
unit.flags2.slaughter = true
unit.flags1.tame = true
though this set of functions if you covert them into a lua script will require looking at the pet to target them for butchery also probably might also lead to butchery of say anyone you aim this at like sapient folks.
Does anyone know a way to make a pet not a pet?you could just force the slaughtering of the pet with dfhack by messing with the creature's unit flags to set to slaughter if you're already tossing them into magma.
I tried poking through gm-edit but I can't find anything obvious.
I absolutely despise livestock as pets, normally I teleport them into magma, but I'm looking for a "kinder, gentler" approach (and more meat).Code: [Select]function getxyz() -- this will return pointers x,y and z coordinates.
though this set of functions if you covert them into a lua script will require looking at the pet to target them for butchery
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
--print("Creature not found!")
return nil
end
function Butcherunit(unit)
if unit==nil then
unit=getCreatureAtPos(getxyz())
end
if unit==nil then
error("Failed to Heal unit. Unit not selected/valid")
end
unit.flags2.slaughter = true
unit.flags1.tame = true
also probably might also lead to butchery of say anyone you aim this at like sapient folks.
Setting the slaughter flag isn't enough :(You might also want to check for `histfig_hf_link_pet_ownerst` records and remove them, otherwise the owner is likely to get an unhappy thought about losing a pet.
I've tried removing important historical figure too. No go. Need to find the pet flag and remove it.
Aha! I found it, relationship_ids, pet, set to -1
Setting the slaughter flag isn't enough :(You might also want to check for `histfig_hf_link_pet_ownerst` records and remove them, otherwise the owner is likely to get an unhappy thought about losing a pet.
I've tried removing important historical figure too. No go. Need to find the pet flag and remove it.
Aha! I found it, relationship_ids, pet, set to -1
int32_t getLocalTime(int16_t region_x) //returns local time in hundredths of an hour
{
if (gametype == DWARF_ARENA || gametype == ADVENTURER_ARENA)
{
return world->arena_settings->time;
}
else
{
int32_t center_displace = region_x - world->world_data->world_width / 2;
return ( (center_displace + cur_year_tick%10 + cur_season_tick*10 + 1200) % 1200 )*2
}
}
Pocket (17): 0.32 hours
Smaller (33): 0.64 hours
Small (65): 1.28 hours
Medium (129): 2.56 hours
Large (257): 5.12 hours
function MobileFort()
local MapCurx=df.global.gview.view.child.child.map_x
local MapCury=df.global.gview.view.child.child.map_y
local Fortsite=df.global.ui.site_id
df.global.world.world_data.sites[Fortsite-1].pos.x=MapCurx
df.global.world.world_data.sites[Fortsite-1].pos.y=MapCury
end
if df.global.gview.view.child.child==nil then
print("this script requires you to be in the raid menu to work")
else
MobileFort()
end
function Riding2(unitSource,unitTarget)
local curpos
if df.global.ui_advmode.menu==1 then
curpos=df.global.cursor
else
print ("No cursor located! You would have slammed into the ground and exploded.")
return
end
unitSource.path.dest.x=curpos.x
unitSource.path.dest.y=curpos.y
unitSource.path.dest.z=curpos.z
unitSource.path.goal=209
end
unitTarget = curpos
if df.global.world.units.active[0].relationship_ids.RiderMount==nil then
unitSource = df.global.world.units.active[0]
else
for k,v in pairs(df.global.world.units.active) do
if v.id==df.global.world.units.active[0].relationship_ids.RiderMount then
unitSource=v
end
end
end
Riding2(unitSource,unitTarget)
this script works with looking in the direction you want the mount to go and just mashing the wait key as the mount goes in that direction... or get confused and double backs to the starting position...Is there any way to make it so that revealing damp stone doesn't remove designations with DFHack, without using reveal?
while (*window_x + w < pos.x+5) *window_x += 10;
while (*window_y + h < pos.y+5) *window_y += 10;
while (*window_x + 5 > pos.x) *window_x -= 10;
while (*window_y + 5 > pos.y) *window_y -= 10;
if (*window_x < (pos.x + 5 - w))
*window_x += ((pos.x + 5 - w) - *window_x - 1)/10*10 + 10;
if (*window_y < (pos.y + 5 - h))
*window_y += ((pos.y + 5 - h) - *window_y - 1)/10*10 + 10;
if (*window_x > (pos.x - 5))
*window_x -= (*window_x - (pos.x - 5) - 1)/10*10 + 10;
if (*window_y > (pos.y - 5))
*window_y -= (*window_y - (pos.y - 5) - 1)/10*10 + 10;
I hate asking stupid questions, but in 0.47.05-r1 I'm not able to get these to work.
They're not listed when I do "ls". Do I need to load them first somehow?
[DFHack]# fix/drop-webs -all
fix/drop-webs is not a recognized command.
[DFHack]# fix/drop-webs
fix/drop-webs is not a recognized command.
[DFHack]# fix-webs -all
fix-webs is not a recognized command.
[DFHack]# drop-webs
drop-webs is not a recognized command.
(edit: Or is this because I'm using r1 ? )
fix/drop-webs was added in 0.47.05-r2. See https://docs.dfhack.org/en/stable/docs/NEWS.html#dfhack-0-47-05-r2 for the release notes. You'd probably want to head straight to R3 though.Ah, now I see that the stupidity on my part was not looking for release notes in the newer version, and they're right there in the docs!!!
For cleaning spotclean will remove mud and other contaminants from individual tiles.Fantastic! Thanks.
I did my best to search the docs, but didn't find anything, so I thought I'd just ask:
Is there any script/plugin which can add and remove mud from tiles? Adding mud to facilitate farming, removing mud as a matter of cleaning?
I know you can indirectly add mud with liquids, by spawning water, but this can be problematic in frozen environments.
The Tiletypes command lets you modify anything and everything about a tile. You should be able to add mud with it.That might be the one thing you can't do with tiletypes (https://docs.dfhack.org/en/stable/docs/Plugins.html#tiletypes) - there's no MUDDY FLOOR shape, no MUDDY STONE material, no MUDDY special, and no Muddy option.
That might be the one thing you can't do with tiletypes (https://docs.dfhack.org/en/stable/docs/Plugins.html#tiletypes) - there's no MUDDY FLOOR shape, no MUDDY STONE material, no MUDDY special, and no Muddy option.That's because there is no generic "MUDDY" flag (like there was back in 40d) - you have to create a "material_spatter" event with its material set to MUD:NONE (and state set to SOLID), add it to the map block in question, then set the "amount" for the appropriate tile within it.
Well, the "liquids" script/plugin allows you to drop 1/7 water on tiles, which will cause them to become muddy as the water evaporates, so you can muddy tiles using existing tools even if there's no tool that allows you to modify splatter directly.Unless those tiles are in a permanently frozen biome, which was the bizarre use-case which led me to bring up the topic to begin with. It's easy enough to create that 1/7 water with buckets otherwise.
That might be the one thing you can't do with tiletypes (https://docs.dfhack.org/en/stable/docs/Plugins.html#tiletypes) - there's no MUDDY FLOOR shape, no MUDDY STONE material, no MUDDY special, and no Muddy option.That's because there is no generic "MUDDY" flag (like there was back in 40d) - you have to create a "material_spatter" event with its material set to MUD:NONE (and state set to SOLID), add it to the map block in question, then set the "amount" for the appropriate tile within it.
Unfortunately, there don't appear to be any plugins or scripts which do this, but it shouldn't be too difficult to write a new one - it's mostly the same process as creating new mineral veins (which is supported within at least one plugin).
I haven't checked, but I suspect it's the spatter itself that permits farm plot placement since there's no "mud" flag in tile_occupancy/tile_designation (like there was in 40d and earlier).That might be the one thing you can't do with tiletypes (https://docs.dfhack.org/en/stable/docs/Plugins.html#tiletypes) - there's no MUDDY FLOOR shape, no MUDDY STONE material, no MUDDY special, and no Muddy option.That's because there is no generic "MUDDY" flag (like there was back in 40d) - you have to create a "material_spatter" event with its material set to MUD:NONE (and state set to SOLID), add it to the map block in question, then set the "amount" for the appropriate tile within it.
Unfortunately, there don't appear to be any plugins or scripts which do this, but it shouldn't be too difficult to write a new one - it's mostly the same process as creating new mineral veins (which is supported within at least one plugin).
Out of curiosity, is it the spatter that facilitates farming, or does another flag get set when the spatter is detected?
:lua df.global.ui.main.mode = df.ui_sidebar_mode.ArenaWeather
and pressing 'm' for "Mud everywhere". :PLoading bindings from data/init/interface.txt
New window size: 800x300
Font size: 10x12
Resizing grid to 80x25
Resizing font to 10x12
Resetting textures
Can't load plugin stonesense
DFHack is ready. Have a nice day!
DFHack version 0.47.05-r3 (release) on x86
Type in '?' or 'help' for general help, 'ls' to see all commands.
[DFHack]#
./dfhack: line 89: 18696 Segmentation fault setarch "$setarch_arch" -R env LD_PRELOAD="$PRELOAD_LIB" ./libs/Dwarf_Fortress "$@"
That's not necessarily a DFHack crash, that line 89 is actually in a script that runs DF - the code that actually seg faulted could be in DF, DFHack, or some library.
Do you know how to find core dumps in your distro?
I'm having trouble getting the tailor plugin to work. I've enabled it, and a status check confirms it's enabled, but even after several years ingame it has yet to actually do anything. There is plenty of worn clothing in need of replacement, and a bookkeeper is assigned, with an office. However, no clothing orders are ever placed, nor is any clothing produced. Am I missing a step? The documentation (https://docs.dfhack.org/en/stable/docs/Plugins.html#tailor) doesn't indicate anything more.I seem to be having this problem as well. Has anyone figured out how to get this to work reliably?
I remember reading in the past that bookkeepers, after reaching a certain skill level, basically never go back to update the stockpile records, essentially becoming clairvoyant. If the update records job is never triggered, I suppose the clothing check would not trigger either? However, I cannot confirm this is how bookkeeping works at the moment, the wiki doesn't seem to mention anything like this anymore, even though I could have sworn reading that somewhere at some point.
I really like the idea of the tailor plugin, so I'd love to get it to work!
Update: I had a thought to try increasing bookkeeper accuracy from the default lowest setting to see if it would trigger the tailor plugin. It didn't go well - consistently results in the tailor plugin crashing the game. Created a dfhack ticket: https://github.com/DFHack/dfhack/issues/2006I'm having trouble getting the tailor plugin to work. I've enabled it, and a status check confirms it's enabled, but even after several years ingame it has yet to actually do anything. There is plenty of worn clothing in need of replacement, and a bookkeeper is assigned, with an office. However, no clothing orders are ever placed, nor is any clothing produced. Am I missing a step? The documentation (https://docs.dfhack.org/en/stable/docs/Plugins.html#tailor) doesn't indicate anything more.I seem to be having this problem as well. Has anyone figured out how to get this to work reliably?
I remember reading in the past that bookkeepers, after reaching a certain skill level, basically never go back to update the stockpile records, essentially becoming clairvoyant. If the update records job is never triggered, I suppose the clothing check would not trigger either? However, I cannot confirm this is how bookkeeping works at the moment, the wiki doesn't seem to mention anything like this anymore, even though I could have sworn reading that somewhere at some point.
I really like the idea of the tailor plugin, so I'd love to get it to work!
I just dropped by to mention that it appears that “enable nestboxes” doesn’t seem to work. I’ve seen dwarves gather fertilized eggs even with it enabled. I’m using DFHack 0.47.05-r3.Can you double-check the output of "plug nestboxes" when this happens?
I just dropped by to mention that it appears that “enable nestboxes” doesn’t seem to work. I’ve seen dwarves gather fertilized eggs even with it enabled. I’m using DFHack 0.47.05-r3.Can you double-check the output of "plug nestboxes" when this happens?
Is there documentation somewhere on how to read the output of show-unit-syndromes?The output varies a lot based on all of the effects of each syndrome, so it's hard to give a concise answer that covers everything. Most of the terminology used is straight from the game, so familiarising yourself with the syndrome tokens (http://dwarffortresswiki.org/index.php/DF2014:Syndrome#The_anatomy_of_a_syndrome) will likely explain a lot.
- Beast sickness [permanent]
- - Bleeding [permanent] (size delays,size dilutes,vascular only,localized) power=100
- - Drowsiness [permanent] (size delays,size dilutes) power=100
- - ###Fever [332-1059-2434] (size delays,size dilutes) power=100
I think I slightly mis-explained what the ### means (oops). They should be showing whenever that effect is inactive, either because the START is yet to be reached, or because the END has been passed. I only mentioned the possibility of it not having yet started, but like in your case, it's likely that the ###s are appearing for the fever effect because that effect's end has long since passed.Quote- Beast sickness [permanent]
- - Bleeding [permanent] (size delays,size dilutes,vascular only,localized) power=100
- - Drowsiness [permanent] (size delays,size dilutes) power=100
- - ###Fever [332-1059-2434] (size delays,size dilutes) power=100
So when there are three numbers in square brackets, is that related to START/PEAK/END? It still doesn't seem right, because this guy has had the syndrome for months or a year now, so 300 ticks away seems unlikely, and when I first looked at the output months ago I don't remember those numbers being in the hundreds of thousands. And is power equivalent to SEV? The wiki says SEV 1000 is pretty severe, so 100 seems not so bad, which is consistent with my dwarf not bleeding out or starving to death in bed. I've got a kitten with it, too, and it doesn't seem to be affected other than "Urvad Oltarosod, Kitten (Tame) cancels Sleep: In Custody" when I gelded him.
Thanks, that's helpful. It's surprising to me how many of the dfhack tools aren't documented or even acknowledged in the dfhack docs.Most of the dfhack scripts should be listed in the documentation, like this script in question here (https://docs.dfhack.org/en/stable/docs/_auto/base.html#show-unit-syndromes). I think the main problems with the script is that it doesn't follow the normal dfhack conventions for sending arguments (usually you're supposed to have them lead with a -, e.g. -help), and it's documentation not really explaining what some of the things actually mean (like the ###).
local dlg=require("gui.dialogs")
-- ok so this script is made for summoning gods into DF, to make this work you need to nickname someone historical that would form an army.
-- so the best choice would be either another adventurer or adv follower if you want the god to show up after fast traveling or assign the DFCAT nickname on some random peasant you talk to once.
-- going to give a warning about how this script messes with nemesis and probably unstable.
-- so BEST not use this on any important saves you care about and just test this out on an disposable save.
-- oh and the gods you summon won't really talk back if you reply to them.
-- this script uses coding from spawn unit and gui scripting to make it work, don't think this works in fort mode as this was made and tested in adv mode.
-- hopefully you have a better time getting gods to fight each other better than I did.
function deitysummoning()
local Ark={}
for k,v in pairs(df.global.world.history.figures) do
if v.flags.deity==true or v.flags.force==true or v.flags.skeletal_deity==true or v.flags.rotting_deity==true then
BoaName=dfhack.TranslateName(v.name)
table.insert (Ark,{dfhack.TranslateName(v.name).." "..v.name.nickname,nil,v})
end
end
local f=function(Name,C)
deitysummoning2(C[3])
end
dlg.showListPrompt("pantheon list","Select your deity for summoning",COLOR_WHITE,Ark,f)
end
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
local function allocateNewChunk(hist_entity)
hist_entity.save_file_id = df.global.unit_chunk_next_id
df.global.unit_chunk_next_id = df.global.unit_chunk_next_id+1
hist_entity.next_member_idx = 0
print("allocating chunk:",hist_entity.save_file_id)
end
local function allocateIds(nemesis_record,hist_entity)
if hist_entity.next_member_idx == 100 then
allocateNewChunk(hist_entity)
end
nemesis_record.save_file_id = hist_entity.save_file_id
nemesis_record.member_idx = hist_entity.next_member_idx
hist_entity.next_member_idx = hist_entity.next_member_idx+1
end
function createNemesis(HistfigID)
local id = df.global.nemesis_next_id
local nem = df.nemesis_record:new()
nem.id = id
nem.flags:resize(31)
nem.unk10 = -1
nem.unk11 = -1
nem.unk12 = -1
df.global.world.nemesis.all:insert("#",nem)
df.global.nemesis_next_id = id+1
nem.save_file_id = -1
local he
if civ_id and civ_id ~= -1 then
he = df.historical_entity.find(civ_id)
he.nemesis_ids:insert("#",id)
he.nemesis:insert("#",nem)
allocateIds(nem,he)
end
local he_group
if group_id and group_id ~= -1 then
he_group = df.historical_entity.find(group_id)
end
if he_group then
he_group.nemesis_ids:insert("#",id)
he_group.nemesis:insert("#",nem)
end
nem.figure=HistfigID
nem.figure.nemesis_id = id
return nem
end
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getItemAtKPos(x,y,z) -- gets the item index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.item.all -- load all items
local kickpos=df.global.world.units.active[0].pos
for i = 0, #vector-1 do -- look into all items offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==kickpos.x and cy==kickpos.y and cz==kickpos.z then --compare them
return vector[i] --return index
end
end
--print("item not found!")
return nil
end
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
--print("Creature not found!")
return nil
end
function getArmyAtName(Divineslot)
for k,v in pairs(df.global.world.nemesis.all) do
if v.figure.name.nickname=='DFCAT' then
print("Nem",k)
local Cat=df.global.world.armies.all
for k1,v1 in pairs(Cat) do
if v1.members==nil then break else
for Del,ete in pairs(v1.members) do
if ete.nemesis_id==v.id then
print("Army",k1)
return k1
end
end
end
end
end
end
end
function deitysummoning2(HistfigID)
Divineslot=createNemesis(HistfigID)
local horse=Divineslot
print('God Nem ID',Divineslot.id)
print('God Nem.figure ID',Divineslot.figure.id)
local NeArmy=getArmyAtName()
df.global.world.armies.all[NeArmy].members:insert("#",{new=true,nemesis_id=horse.id,})
end
deitysummoning()
Done. Never used github before, hope I did that right.That (https://github.com/DFHack/dfhack/issues/2050) looks good. Thanks!
It would be super helpful if autonick listened for "autonick help" or "autonick -help" instead of just irreversibly running itself when you try to find out the details of what it does. Even erroring out on any argument would be an improvement.
The GUI wrapper for createitem throws an 'unrecognized command' when I poke at it - any help for that? Seems to very much not be working as intended, any how.
[DFHack]# create-items
Create first necessity items under the cursor.
...
[DFHack]# createitem
Usage:
Syntax: createitem <item> <material> [count]
...
[DFHack]# gui/create-item
gui/create-item: A unit needs to be selected to use gui/create-item.
The GUI wrapper for createitem throws an 'unrecognized command' when I poke at it - any help for that? Seems to very much not be working as intended, any how.Please give more details. What exact command are you running? What exact output are you getting?
repeat -name autoShearCreature -time 14 -timeUnits days -command [ workorder ShearCreature ]
It looks like it isn't that hard to determine if you have a manager assigned. This is how workorder itself does it: https://github.com/DFHack/scripts/blob/master/workorder.lua#L144
I'm a little surprised, though, that you'd rather silence the warning that you have no manager assigned. Isn't it a good indication that you've forgotten to assign a manager? Granted that you don't really *need* a manager until your fort reaches 20 citizens, but why put it off?
The GUI wrapper for createitem throws an 'unrecognized command' when I poke at it - any help for that? Seems to very much not be working as intended, any how.Please give more details. What exact command are you running? What exact output are you getting?
gui/create-item: A unit needs to be selected to use gui/create-item....being currently in process of supplying a remedy for that.
questport (https://docs.dfhack.org/en/stable/docs/_auto/base.html#questport) is for teleporting to different world tiles, teleport is for local tiles.Is it possible to use it automaitc, without manually enter Quest screen?
Wanna visit all squares of the world with single character for data exporting purposeWhy? Would reveal-adv-map (https://docs.dfhack.org/en/stable/docs/_auto/base.html#reveal-adv-map) do what you want?
dfhack-run etc from this documentation doesn't work for meCan you explain how it "doesn't work"? What command are you trying to run?
I’m getting a message on startup saying that fortplan was built with r3 and not r4.Fortplan was removed in 0.47.05-r4 (https://docs.dfhack.org/en/stable/docs/NEWS.html#dfhack-0-47-05-r4) in favor of quickfort. I'm guessing this message is coming up because you upgraded to r4 by writing over an existing r3 installation, and the fortplan plugin simply does not exist in r4 so it was not overwritten. Our recommended approach to upgrading DFHack (https://docs.dfhack.org/en/stable/docs/Installing.html#upgrading-dfhack) involves deleting the entire hack folder first, which avoids situations like this.
Only particulary. I want map all map in Isoworld oversized mod. For this i need took so what you need to do is write a weird script that connect isoworld load data with moving around the adventurer's army position.
1)TP to global map tile.
2)Wait until Isoworld load data
Size of 3x3 embark size will be mapped automatically.
3)Repeat from point 1. Where are else a lot of little additions to this, but this is basic idea. I need do it fully automatically because it's thousands little jumps to explore all the map.
df.global.world.armies
coding and maybe df.global.ui_advmode
but I don't know how isoworld works and if you need to leave fast travel... if you do then mapping out the ocean locations would be a bit tricky as you can't really fast travel away on an ocean tile and the current fast travel teleport scripts probably need you to be unloaded from the game field So, docs/Compile.rst says to use "git clone --recursive https://github.com/DFHack/dfhack". However, GitHub made security changes quite some time ago that require you to go through additional hoops to push your changes back to the GitHub repository. One option involves using "git@github.com:DFHack/dfhack" instead and generating an SSH key.
What other things are necessary when splitting an item stack using splitStack? Currently when using it (in this case, on an item on the floor) it will create the split item as a phantom item, invisible and uninteractable, though listed if you mouse over the tile with the look menu. I've tried then using dfhack.items.moveToGround in case it needed placing somewhere, but that doesn't seem to fix it.Somewhat counterintuitively, dfhack.items.moveToGround only works on items that are properly located somewhere in the world - if they're already in the "detached" state (as they are immediately after splitting a stack), the operation will fail. What you need to do is call the moveToGround(x,y,z) vmethod on the item itself.
Unfortunately, even after changing to use the vmethod version, I'm still having the same problem with phantom items :cWhat other things are necessary when splitting an item stack using splitStack? Currently when using it (in this case, on an item on the floor) it will create the split item as a phantom item, invisible and uninteractable, though listed if you mouse over the tile with the look menu. I've tried then using dfhack.items.moveToGround in case it needed placing somewhere, but that doesn't seem to fix it.Somewhat counterintuitively, dfhack.items.moveToGround only works on items that are properly located somewhere in the world - if they're already in the "detached" state (as they are immediately after splitting a stack), the operation will fail. What you need to do is call the moveToGround(x,y,z) vmethod on the item itself.
Anybody know why autochop might be marking my trees in marker instead of standard?DF is not very good about clearing flags from tiles, so it is very possible that there are leftover "marker" flags on the surface tiles that are getting in the way. That being said, autochop should really handle this case and ensure the marker flag is off for trees that it designates. Could you file a bug for this at https://github.com/DFHack/dfhack/issues ?
Now that I think about it, I'm pretty sure you also need to categorize(true) the item after it's been split.Unfortunately, even after changing to use the vmethod version, I'm still having the same problem with phantom items :cWhat other things are necessary when splitting an item stack using splitStack? Currently when using it (in this case, on an item on the floor) it will create the split item as a phantom item, invisible and uninteractable, though listed if you mouse over the tile with the look menu. I've tried then using dfhack.items.moveToGround in case it needed placing somewhere, but that doesn't seem to fix it.Somewhat counterintuitively, dfhack.items.moveToGround only works on items that are properly located somewhere in the world - if they're already in the "detached" state (as they are immediately after splitting a stack), the operation will fail. What you need to do is call the moveToGround(x,y,z) vmethod on the item itself.
Out of curiosity, what does categorize(true) do? I searched through the DFHack documentation and could find anything about a function with that name.Now that I think about it, I'm pretty sure you also need to categorize(true) the item after it's been split.Unfortunately, even after changing to use the vmethod version, I'm still having the same problem with phantom items :cWhat other things are necessary when splitting an item stack using splitStack? Currently when using it (in this case, on an item on the floor) it will create the split item as a phantom item, invisible and uninteractable, though listed if you mouse over the tile with the look menu. I've tried then using dfhack.items.moveToGround in case it needed placing somewhere, but that doesn't seem to fix it.Somewhat counterintuitively, dfhack.items.moveToGround only works on items that are properly located somewhere in the world - if they're already in the "detached" state (as they are immediately after splitting a stack), the operation will fail. What you need to do is call the moveToGround(x,y,z) vmethod on the item itself.
Also, it looks like moving the item to the ground is only necessary if you specify false for the last parameter - if you specify true, then the newly-split item will be placed in the exact same location as the original item (on the floor, in a container, in a building, or even in another creature's inventory), just like when food items get claimed for eating.
Out of curiosity, what does categorize(true) do? I searched through the DFHack documentation and could find anything about a function with that name.It's not part of DFHack, but a virtual method within Dwarf Fortress itself.
Fascinating. Thanks for the explanation. I assume I can look at this with gui/gm-editor, right? If it requires Lua, than I’m currently putting off learning Lua while I try and learn C++ first (I first tried to learn C, but it turned out the book I got was too old. Then I found a found a good website for learning C++…)Out of curiosity, what does categorize(true) do? I searched through the DFHack documentation and could find anything about a function with that name.It's not part of DFHack, but a virtual method within Dwarf Fortress itself.
If you look at df.global.world.items, you will see a list named "all" and a whole bunch of lists inside "other". What categorize() does is insert references to the item into all of the "other" lists according to the item's properties (and the "true" parameter in particular adds it to the "IN_PLAY" list at the top).
These "other" lists are used primarily for finding job items more quickly - instead of a hungry dwarf having to check every single item in your fortress (including all of the rocks in your mining tunnels), it just needs to look in the "ANY_EDIBLE_RAW" list to find something to eat. In case you're curious, Buildings are "categorized" in the exact same way.
For what it's worth, I just checked the "find food" code (which uses splitStack) in an older DF version and it does immediately call categorize(true) on the newly-split item. Of course, it's also possible the version I'm looking at is too old (which is certainly possible since the version I checked was v0.23.130.23a) and that the current version needs to do more stuff afterwards, but it'll take me a while to find the relevant code in 0.47.05.
That did it - thanks! I could finally release one of the mods I've been sitting on :DNow that I think about it, I'm pretty sure you also need to categorize(true) the item after it's been split.Unfortunately, even after changing to use the vmethod version, I'm still having the same problem with phantom items :cWhat other things are necessary when splitting an item stack using splitStack? Currently when using it (in this case, on an item on the floor) it will create the split item as a phantom item, invisible and uninteractable, though listed if you mouse over the tile with the look menu. I've tried then using dfhack.items.moveToGround in case it needed placing somewhere, but that doesn't seem to fix it.Somewhat counterintuitively, dfhack.items.moveToGround only works on items that are properly located somewhere in the world - if they're already in the "detached" state (as they are immediately after splitting a stack), the operation will fail. What you need to do is call the moveToGround(x,y,z) vmethod on the item itself.
Also, it looks like moving the item to the ground is only necessary if you specify false for the last parameter - if you specify true, then the newly-split item will be placed in the exact same location as the original item (on the floor, in a container, in a building, or even in another creature's inventory), just like when food items get claimed for eating.
Weirdly the categorized items would disappear completely if splitStack was used with copying contaminants set to falseThat flag isn't for copying contaminants - it's for preserving contAINment (i.e. putting it inside the same building, item, or unit inventory as the original item). I suppose "same_location" might've been a better name for that parameter, since even I've misread it in the same way that you did.
Oh whoops, I guess since I'd been making a mod based around contaminants while reading the documentation, I had them on my mind :P That makes more sense.Weirdly the categorized items would disappear completely if splitStack was used with copying contaminants set to falseThat flag isn't for copying contaminants - it's for preserving contAINment (i.e. putting it inside the same building, item, or unit inventory as the original item). I suppose "same_location" might've been a better name for that parameter, since even I've misread it in the same way that you did.
Fascinating. Thanks for the explanation. I assume I can look at this with gui/gm-editor, right? If it requires Lua, than I’m currently putting off learning Lua while I try and learn C++ first (I first tried to learn C, but it turned out the book I got was too old. Then I found a found a good website for learning C++…)Depends on what "this" is. You can definitely look at df.global.world or other globals (you can actually leave off the "df.global" prefix in gui/gm-editor and the Lua interpreter, so running "gui/gm-editor world" will work, or "gui/gm-editor world.items" if you want to pull up the items collection directly).
GitHub did drop password-based auth over HTTPS within the past year (you have to use a token or an alternative to push now) - I'm assuming that's what Bumber is referring to. They also dropped the git:// protocol more recently.
I definitely want to keep HTTPS as the default in Compile.rst for the reasons Myk said - for read-only access, it is the simplest and does not require extra setup steps. If you'd like to update Contributing.rst with instructions on switching to SSH (maybe references to existing GitHub docs?), that would be welcomed.
if dfhack.world.isFortressMode() then
df.global.gview.view.child=df.viewscreen_dungeonmodest:new()
df.global.gview.view.child.parent=df.global.gview.view
df.global.gamemode=1
df.global.gametype=1
else
df.global.gview.view.child=df.viewscreen_dwarfmodest:new()
df.global.gview.view.child.parent=df.global.gview.view
df.global.gamemode=0
df.global.gametype=0
end
-- Makes the game immediately save the state.
--[====[
Adv quicksave
=========
If called in adv mode, makes DF immediately switches to fort mode then saves the game by setting a flag
normally used in seasonal auto-save. then switches back to adv mode big warning the saves that are backup are set to fort mode so probably best run say a mode set script to switch back to adv mode then save the game again as last I check that seems to fix one crash after switching back to adv mode and fast traveling a bit.
]====]
local gui = require("gui")
--luacheck: defclass={run:bool}
QuicksaveOverlay = defclass(QuicksaveOverlay, gui.Screen)
function QuicksaveOverlay:render()
if not self.run then
self.run = true
save()
self:renderParent()
self:dismiss()
end
end
if not dfhack.isMapLoaded() then
qerror("World and map aren't loaded.")
end
function adv2fort ()
if dfhack.world.isFortressMode() then
df.global.gview.view.child=df.viewscreen_dungeonmodest:new()
df.global.gview.view.child.parent=df.global.gview.view
df.global.gamemode=1
df.global.gametype=1
else
df.global.gview.view.child=df.viewscreen_dwarfmodest:new()
df.global.gview.view.child.parent=df.global.gview.view
df.global.gamemode=0
df.global.gametype=0
end
end
if not dfhack.world.isFortressMode() then
adv2fort()
end
local ui_main = df.global.ui.main
local flags4 = df.global.d_init.flags4
local function restore_autobackup()
if ui_main.autosave_request and dfhack.isMapLoaded() then
dfhack.timeout(10, 'frames', restore_autobackup)
else
flags4.AUTOBACKUP = true
end
end
function save()
-- Request auto-save
ui_main.autosave_request = true
-- And since it will overwrite the backup, disable it temporarily
if flags4.AUTOBACKUP then
flags4.AUTOBACKUP = false
restore_autobackup()
end
print 'The game should save the state now.'
end
QuicksaveOverlay():show()
dfhack.timeout(10, 'frames',adv2fort)
df.global.ui.main.mode=10
df.global.selection_rect.start_x=0
df.global.selection_rect.start_y=0
df.global.selection_rect.start_z=610
df.global.selection_rect.end_x=-1430
df.global.selection_rect.end_y=900
df.global.selection_rect.end_z=0
df.global.cursor.x=400
df.global.cursor.y=400
df.global.cursor.z=0
function DeresidentCiv(unit,trgunit)
local adv=df.global.world.units.active[0].civ_id
for k,v in pairs(df.global.world.units.active) do
if v.civ_id==adv then
if v== nil then
error("Invalid creature")
end
v.flags2.resident=false
end
end
return true
end
edit edit: ok so I ended up making two different versions of warmist's tofort(well 8ish this one is 6.0) and this one has the accidental mechanic of letting you keep access to your fort's military and squad while you explore the world in adv mode and switch back into fort mode, setting up means to do Squad level attack commands if you brought along your fort military on the trip or lets you enlist folks on site and command them to fight. env: ‘./libs/Dwarf_Fortress’: No such file or directory
/usr/share/games/dwarf-fortress/gamedata
gcc (Debian 10.2.1-6) 10.2.1 20210110
dfhack-0.47.05-r5-Linux-64bit-gcc-7.tar.bz2
The install directory isYou can't install DFHack directly onto the Debian DF packages - those packages change the locations of DF's files in ways that DFHack can't find them.Code: [Select]/usr/share/games/dwarf-fortress/gamedata
I installed dfhack versionCode: [Select]dfhack-0.47.05-r5-Linux-64bit-gcc-7.tar.bz2
The recent FotF talk about keyboard and mouse support (especially, iirc, about the possibility of removing the ability to move the cursor with the keyboard) has got me wondering…If the initial release is Windows-only, that will significantly slow development. We may not be able to make much progress at all until there are releases for other platforms because a lot of us are using Linux, and most of our CI only runs in Linux too.
How will DFHack be affected by the Steam release?
function SummonSquad()
for ks,vs in pairs(df.global.world.armies.all) do
if vs.flags[0]== true then
if df.global.ui.squads.list~=nil then
for li2,nk2 in pairs (df.global.ui.squads.list) do
for li3,nk3 in pairs (nk2.positions) do
Awombo=df.global.world.nemesis.all[vs.members[0].nemesis_id].figure
if nk3.occupant~=-1 or nk3.occupant==Awombo.id then
wombo=df.global.world.history.figures[nk3.occupant].nemesis_id
Awombo=df.global.world.history.figures[nk3.occupant].nemesis_id
vs.members:insert("#",{new=true,nemesis_id=wombo})
end
end
end
end
end
end
end
--thanks warmist for the help on getCurViewscreen
local old_screen=dfhack.gui.getCurViewscreen()
local new_screen=df.viewscreen_setupadventurest:new()
old_screen.child=new_screen
new_screen.parent=old_screen
new_screen.page=2
for k,v in pairs(df.global.world.history.figures) do
if df.global.world.nemesis.all==nil then
df.global.world.nemesis.all:insert("#",{new=true,figure=df.global.world.history.figures[k]})
end
end
for ki,vi in pairs(df.global.world.nemesis.all) do
new_screen.nemesis_ids:insert("#",ki)
end
local old_screen=dfhack.gui.getCurViewscreen()
local new_screen=df.viewscreen_new_regionst:new()
old_screen.child=new_screen
new_screen.parent=old_screen
new_screen.worldgen_paused=true
local old_screen=dfhack.gui.getCurViewscreen()
local new_screen=df.viewscreen_setupadventurest:new()
old_screen.child=new_screen
new_screen.parent=old_screen
for Rawid,Creature in pairs (df.global.world.raws.creatures.all) do
new_screen.races_info:insert("#",{new=true,race=Rawid,anon_1=20000,playable=true})
if Creature.flags.HAS_ANY_INTELLIGENT_LEARNS==true then
new_screen.race_ids:insert("#",Rawid)
end
end
new_screen.highlighted_entity_ids:insert("#",'-1')
for Civ_id,Ent in pairs (df.global.world.entities.all) do
new_screen.highlighted_entity_ids:insert("#",Ent.id)
for _,Setup in pairs (new_screen.races_info) do
if Ent.race==Setup.race then
Setup.entity_id=Ent.id
end
end
end
df.global.gamemode=df.game_mode.ADVENTURE
df.global.gametype=df.game_type.ADVENTURE_MAIN
--thanks to Warmist for the old_screen new_screen addition and Open-legends devs for inspiration
local old_screen=dfhack.gui.getCurViewscreen()
local new_screen=df.viewscreen_new_regionst:new()
old_screen.child=new_screen
new_screen.parent=old_screen
new_screen.worldgen_paused=true
df.global.gamemode=3
df.global.gametype=11
local WorldGenStat=df.global.world.worldgen_status
WorldGenStat.state=8
if new_screen.worldgen_presets~=0 then
local WGParms=df.global.world.worldgen.worldgen_parms
WGParms.beast_end_year=df.global.world.worldgen.worldgen_parms.beast_end_year+1000
WGParms.end_year=df.global.world.worldgen.worldgen_parms.end_year+1000
new_screen.worldgen_presets:insert("#",df.global.world.worldgen.worldgen_parms)
else
old_screen.worldgen_presets:insert("#",{new=true,df.global.world.worldgen.worldgen_parms})
end
Heyo, question about scripts here. I know hardly anything about how to write DFhack scripts, but how would I go about modifying the cannibalism script to apply to all sentient corpses as soon as they die? Note that I have written literally 0 dfhack scripts or plugins, but if someone could let me know, that'd be neat.
-- Makes any creature that dies cannibal-able
local eventful = require "plugins.eventful"
function on_item_created(item_id)
local item = df.item.find(item_id)
item.flags.dead_dwarf = false
end
--------------------
initialized = initialized or false
function init()
-- Set up everything to default values here
initialized = true
eventful.enableEvent(eventful.eventType.ITEM_CREATED, 1)
eventful.onItemCreated["auto-cannabalism"] = on_item_created
end
function reset()
-- Set things to nil/default here
initialized = false
eventful.onItemCreated["auto-cannabalism"] = nil
end
dfhack.onStateChange["auto-cannabalism"] = function(code)
-- Wipe / reset data whenever loaded state changes
if code == SC_WORLD_UNLOADED then
reset()
elseif code == SC_WORLD_LOADED then
if ( not initialized ) then
init()
end
end
end
if ( not initialised ) then
init()
end
I think I may have used the wrong template, meaning that the script will always be running for the whole game session rather than per-world, but I'm too lazy at the moment to check/fix it :pLooks to me like the onStateChange handler is clearly checking for SC_WORLD_(UN)LOADED to determine when to enable/disable the event handler, so it should work the way you want. Cool idea to use the ITEM_CREATED event, by the way - I had been thinking of using UNIT_DEATH, but it's a bit more work to connect to the item that way.Yeah, I originally tried using the UNIT_DEATH event, looping through the unit's corpse pieces and removing the dead_dwarf flag, but it didn't seem to work - I think maybe a delay before setting the flag is required with that method, and that's too much faff :P
Heyo, question about scripts here. I know hardly anything about how to write DFhack scripts, but how would I go about modifying the cannibalism script to apply to all sentient corpses as soon as they die? Note that I have written literally 0 dfhack scripts or plugins, but if someone could let me know, that'd be neat.
Here's something I just quickly threw together - it might need a little testing. It should automatically do the cannibalism thing whenever an item is created (except ones created due to traders, migrants, invaders and spider webs - some limitation on the event used), and yeah, a creature dying counts as creating a corpse :b.
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
--print("Creature not found!")
return nil
end
unit=getCreatureAtPos(getxyz())
df.global.ui_advmode.interactions.party_core_members:insert("#",unit.hist_figure_id)
local old_screen=dfhack.gui.getCurViewscreen()
local new_screen=df.viewscreen_setupadventurest:new()
old_screen.child=new_screen
new_screen.parent=old_screen
for Rawid,Creature in pairs (df.global.world.raws.creatures.all) do
new_screen.races_info:insert("#",{new=true,race=Rawid,anon_1=20000,playable=true})
if Creature.flags.HAS_ANY_INTELLIGENT_LEARNS==true then
new_screen.race_ids:insert("#",Rawid)
end
end
new_screen.highlighted_entity_ids:insert("#",'-1')
for Civ_id,Ent in pairs (df.global.world.entities.all) do
new_screen.highlighted_entity_ids:insert("#",Ent.id)
for _,Setup in pairs (new_screen.races_info) do
if Ent.race==Setup.race then
Setup.entity_id=Ent.id
end
end
end
for Civ_id,Ent in pairs (df.global.world.entities.all) do
for boat,camp in pairs (df.global.world.world_data.sites) do
if camp.name.first_name=="BOAT" or camp.type==0 then
local Campent=camp.entity_links
for _2,Campe in pairs(Campent) do
if Campe.flags.residence==true or Campe.flags.land_for_holding==true then
new_screen.highlighted_entity_ids:insert("#",Campe.entity_id)
for _,Setup in pairs (new_screen.races_info) do
Setup.entity_id=Campe.entity_id
end
end
end
end
end
end
df.global.gamemode=df.game_mode.ADVENTURE
df.global.gametype=df.game_type.ADVENTURE_MAIN
so I probably should advise to use this you probably want to have a player fort made or use one of my many old camp boat scripts so that the starting site gets picked up.But I've run into another problem with the gamelog.txt, which is that it doesn't care at all what save you're running from. Everything from every fort goes in willy-nilly. So I thought, while I'm writing a timestamp, why not write the name of the fortress? How hard can that be?It depends on what you mean by "name of the fortress". Patrik's suggestion of "world.world_data.name" is the in-game name that you see on the right side of the screen in the "load game" menu, among other places. If you want the folder, that is "world.cur_savegame.save_dir" (and if you want the full path, use "dfhack.getSavePath()" in Lua).
I am at an utter loss here. I had been able to find df.global.cur_year only because timestream uses it--I learned a lot from timestream, actually. And I found where it came from, in https://github.com/DFHack/df-structures/blob/master/df.globals.xml (had to go online--despite the Lua API docs' assurance, \hack\library\xml only contains how-to-update and SYNTAX). There are some interesting things there, but not, as far as I can tell, the name of the fortress.I second the recommendation to use gui/gm-editor. Specifically, you can start with "gui/gm-editor df.global". A lot of game-specific stuff is under "world", and a lot of other things are under "ui". Those are probably the biggest two. You can get to globals directly with just their name, e.g. "gui/gm-editor world".
Really, this is a more general question, though I really do want to know how to return the fortress name, or at least the world name. But how do you find what you're looking for in the dozens of xml files? Is it just a question of sheer dogged perseverance and trial and error?
df.world.xml:1512: <stl-string name='save_dir'/>
@doublestrafe:Are you saying that there's documentation on what some of these are? If so, and you don't just mean the API docs, any pointers?
You'd need the DFHack operation to unpack the name (too long since I did anything with DFHack, so I'd have to locate it in some script or look at the documentation if I was to say what it is).
I don't know if is something already happened to someone, but I experienced this event with the "reveal" command.at that point you use revflood to fill in the rest of the map and hopefully fix the issue.
(I've already posted in the bi-weekly thread on reddit and I received already some answers about, but I would share here also, if I can)
I revealed the map, then I saved the game. Game crashed, I loaded the game, but the map was still all visible, and it wasn't possible to use the unreveal command on DFHack, because is not allowed while the map is not revealed (even if it is actually).
I don't know if is something already happened to someone, but I experienced this event with the "reveal" command.at that point you use revflood to fill in the rest of the map and hopefully fix the issue.
(I've already posted in the bi-weekly thread on reddit and I received already some answers about, but I would share here also, if I can)
I revealed the map, then I saved the game. Game crashed, I loaded the game, but the map was still all visible, and it wasn't possible to use the unreveal command on DFHack, because is not allowed while the map is not revealed (even if it is actually).
-- changes from advmode to fort safely and back (hopefully)
--[[Now to warn you about saving and loading in either game mode might lead
to some side issues, for saving in fort mode switching to adv mode needs the adv to be `bodyswap`
back in again. for Saving in Adv to switch to Fort mode you probably be safe-ish if the site.
isn't a nano fort or a site that isn't 3x3 small. otherwise the game will try to resize the map.
oh and switching to fort mode on any pre-gen site larger than 4x4 might implode one pc if
you don't have like 20 gigs of memory for the 16x16 embark size.
ok so this version of this script has be updated so that caravans and visitors and migrants
could show up at any edge of the map... and maybe leave from any spot... of the z level you assign the site too.
should be warned if a caravan decides to exit not from the assign z level you set like above or below the plane the game will crash.
so uhh I probably should get to figuring out how to map the surface at some point...]]--
if df.global.gamemode==df.game_mode.ADVENTURE then
local adv=df.global.world.units.active[0]
local comp={}
local ui=df.global.ui
function advGlobalPos()
local wd=df.global.world.world_data
return wd.adv_region_x*16+wd.adv_emb_x,wd.adv_region_y*16+wd.adv_emb_y
end
--[[function inSite()
local tx,ty=advGlobalPos()
for k,v in pairs(df.global.world.world_data.sites) do
local tp={v.pos.x,v.pos.y}
if tx>=tp[1]*16+v.rgn_min_x and tx<=tp[1]*16+v.rgn_max_x and
ty>=tp[2]*16+v.rgn_min_y and ty<=tp[2]*16+v.rgn_max_y then
return v
end
end
end]]--
function inSite()
local tx,ty=advGlobalPos()
for k,v in pairs(df.global.world.world_data.sites) do
local tp2={v.global_min_x, v.global_min_y, v.global_max_x, v.global_max_y}
if tx>=tp2[1] and tx<=tp2[3] and
ty>=tp2[2] and ty<=tp2[4] then
return v
end
end
end
local site=inSite()
if site==nil then
qerror("No site you are in found.")
end
local nomad={}
local h_ent
if ui.main.fortress_entity~=nil or ui.main.fortress_site~=nil then
ui.main.fortress_entity=nil
ui.main.fortress_site=nil
end
--[[for k,v in pairs(df.global.world.history.figures[adv.hist_figures_id].entity_links) do
if v==df.histfig_entity_link_positionst then
nomad=v.entity_id
break
end
end]]--
for k,v in pairs(df.global.world.history.figures[adv.hist_figure_id].entity_links) do
if v~=histfig_entity_link_memberst and not v==nil then
nomad=df.global.world.entities.all[v.entity_id]
print(v.entity_id)
else if v==histfig_entity_link_memberst then
nomad=df.global.world.history.figures[adv.hist_figure_id].entity_links[0]
end
end
end
if ui.main.fortress_entity==nil then
for k,v in pairs(df.global.world.entities.all) do
--if v.type==df.historical_entity_type.SiteGovernment and v.id==nomad.id then --maybe match race too?
if v.id==nomad.id then --maybe match race too?
if nomad.positions.own==nil then
break else
h_ent=v
break
end
end
if v.id~=nomad.id then
if v.type==df.historical_entity_type.SiteGovernment then
h_ent=v
end
end
end
if h_ent==nil then
qerror("Valid historical entity not found. sorry...")
end
ui.main.fortress_entity=h_ent
else
h_ent=ui.main.fortress_entity
end
function cleardesignate(adv)
local adv=df.global.world.units.active[0]
df.global.ui.main.mode=10
df.global.selection_rect.start_x=0
df.global.selection_rect.start_y=0
df.global.selection_rect.start_z=610
df.global.selection_rect.end_x=-14300
df.global.selection_rect.end_y=9000
df.global.selection_rect.end_z=0
df.global.cursor.x=400
df.global.cursor.y=400
df.global.cursor.z=0
dfhack.gui.getCurViewscreen():feed_key(1)
df.global.cursor.x=adv.pos.x
df.global.cursor.y=adv.pos.y
df.global.cursor.z=adv.pos.z
dfhack.gui.getCurViewscreen():feed_key(1)
dfhack.run_command('revflood')
end
local carter={}
function addToEntity(entity,unit,addtoLead)
local nem=dfhack.units.getNemesis(unit)
local hfig=nem.figure
for k,v in pairs(hfig.entity_links) do
--print(v.entity_id,entity.id)
if v.entity_id==entity.id then break else
carter=true
end
end
if carter==true then
hfig.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=entity.id,link_strength=100})
entity.nemesis_ids:insert("#",nem.id)
entity.nemesis:insert("#",nem)
entity.histfig_ids:insert("#",hfig.id)
entity.hist_figures:insert("#",hfig)
end
if addtoLead then
local lead_id
for k,v in pairs(entity.positions.own) do
if v.flags.IS_LEADER==true then
lead_id=v.id
break
end
end
if lead_id~=nil then
for k,v in pairs(entity.positions.assignments) do
if v.position_id==lead_id then
v.histfig=hfig.id
break
end
end
end
end
end
ui.civ_id=adv.civ_id
ui.race_id=adv.race
df.global.pause_state=true
ui.unk_races:insert("#",adv.race)
ui.site_id=site.id
if ui.main.fortress_site==nil then ui.main.fortress_site=site end
if site.entity_links==nil then site.entity_links:insert("#", {new=true, df.entity_site_link, target=site.id, entity_id=ui.fortress_entity.id,entity_site_link_flags.residence, entity_site_link_flags.trade_partner }) end
ui.group_id=h_ent.id
ui.game_state=2
if #ui.tasks.discovered_plants==0 then
df.global.ui.tasks.discovered_creature_foods:insert("#",0)
df.global.ui.tasks.discovered_creatures:insert("#",0)
df.global.ui.tasks.discovered_plants:insert("#",0)
df.global.ui.tasks.discovered_plant_foods:insert("#",0)
end
if #ui.alerts.list==0 then
ui.alerts.list:insert("#",{new=true,name="Dummy alert"})
ui.alerts.next_id=1
end
local nem2=dfhack.units.getNemesis(adv)
local hfig2=nem2.figure
local nomad2=hfig2.entity_links[0]
local nomad3=hfig2.entity_links
local Test={}
for co2,mp2 in pairs(nomad3) do
--print(mp2.entity_id,h_ent.id)
if mp2.entity_id~=h_ent.id then Test=true else
Test=false
end
end
if Test==true then
addToEntity(h_ent,adv,true)
end
for co,mp in pairs(df.global.world.units.active) do
if mp.relationship_ids.GroupLeader==adv.id then
local comp=mp
if test==true then
addToEntity(h_ent,comp,false)
end
end
end
function sigh()
mapcheck()
for k,v in pairs(df.global.ui.map_edge.surface_z) do
df.global.ui.map_edge.surface_z[k]=adv.pos.z
--v=152
end
end
function mapcheck()
for k,v in pairs(df.global.ui.unk2a8c[0]) do
if v.unk1==0 then
v.unk1=12
v.unk2=12
end
if df.global.ui.unk_mapedge_x~=nil then
df.global.ui.unk_mapedge_x:insert("#","0")
df.global.ui.unk_mapedge_x:insert("#",math.random(0,143))
df.global.ui.unk_mapedge_x:insert("#","143")
df.global.ui.unk_mapedge_x:insert("#",math.random(0,143))
df.global.ui.unk_mapedge_y:insert("#",math.random(0,143))
df.global.ui.unk_mapedge_y:insert("#","0")
df.global.ui.unk_mapedge_y:insert("#",math.random(0,143))
df.global.ui.unk_mapedge_y:insert("#","143")
df.global.ui.unk_mapedge_z:insert("#",adv.pos.z)
df.global.ui.unk_mapedge_z:insert("#",adv.pos.z)
df.global.ui.map_edge.surface_x:insert("#",0)
df.global.ui.map_edge.surface_x:insert("#",math.random(0,143))
df.global.ui.map_edge.surface_y:insert("#",math.random(0,143))
df.global.ui.map_edge.surface_y:insert("#",0)
df.global.ui.map_edge.surface_x:insert("#",143)
df.global.ui.map_edge.surface_x:insert("#",math.random(0,143))
df.global.ui.map_edge.surface_y:insert("#",math.random(0,143))
df.global.ui.map_edge.surface_y:insert("#",143)
df.global.ui.map_edge.surface_z:insert("#",adv.pos.z)
df.global.ui.map_edge.surface_z:insert("#",adv.pos.z)
df.global.ui.map_edge.surface_z:insert("#",adv.pos.z)
df.global.ui.map_edge.surface_z:insert("#",adv.pos.z)
for _,Lay in pairs(df.global.ui.map_edge.layer_x) do
Lay:insert("#",math.random(0,143))
end
for _,Lay in pairs(df.global.ui.map_edge.layer_y) do
Lay:insert("#",math.random(0,143))
end
for _,Lay in pairs(df.global.ui.map_edge.layer_z) do
Lay:insert("#",adv.pos.z)
end
end
end
end
Tab={}
Tab2={}
for k,Lay2 in pairs(df.global.ui.map_edge.layer_z) do
for k2,Lay3 in pairs(df.global.ui.unk2a8c[0]) do
if k then break else
Tab:insert("#",k)
end
Tab2:insert("#",k2)
return Tab,Tab2
end
end
if #df.global.ui.map_edge.layer_z[0] == 0 then
print("map edge empty proceed to fill")
sigh()
else
print("welp map edge currently full")
end
if #ui.kitchen.item_types==0 then
ui.kitchen.item_types:insert("#",0)
ui.kitchen.item_subtypes:insert("#",0)
ui.kitchen.mat_types:insert("#",0)
ui.kitchen.mat_indices:insert("#",0)
ui.kitchen.exc_types:insert("#",0)
end
if #ui.unk_9==0 then
ui.unk_9:insert("#",0)
ui.unk_9:insert("#",0)
ui.unk_10:insert("#",0)
ui.unk_10:insert("#",0)
ui.unk_11:insert("#",0)
ui.unk_11:insert("#",0)
end
--fix training info
if h_ent.training_knowledge == nil then
h_ent.training_knowledge={new=true}
end
df.global.gamemode=df.game_mode.DWARF
df.global.gametype=df.game_type.DWARF_MAIN
if df.global.gview.view.child.child==nil then df.global.gview.view.child=df.viewscreen_dwarfmodest:new()
df.global.gview.view.child.parent=df.global.gview.view
end
cleardesignate()
print("Mode change complete.")
else
local adv=dfhack.gui.getSelectedUnit(true)
if adv then
--swap units...
end
df.global.gamemode=df.game_mode.ADVENTURE
df.global.gametype=df.game_type.ADVENTURE_MAIN
df.global.gview.view.child=df.viewscreen_dungeonmodest:new()
df.global.gview.view.child.parent=df.global.gview.view
print("Mode change complete.")
end
ok so mapedge-z is used for when you're exploring the world and to prevent shifting to a site and noticing all the visitors are now underground due to being in a higher location than the last time you used the tofort(as tofort only fills in the map edge once and stops once it checks to see it's filled)adv=df.global.world.units.active[0]
for _,Lay in pairs(df.global.ui.map_edge.layer_z) do
Lay=adv.pos.z
end
for _,Lay in pairs(df.global.ui.unk_mapedge_z) do
Lay=adv.pos.z
end
for _,Lay in pairs(df.global.ui.map_edge.surface_z) do
Lay=adv.pos.z
end
repeat --name barracks-beds --onday 1 --timeUnits months --command [ quickfort run swap_barracks_beds.csv --cursor=26,54,137 ]
local quickfort = reqscript('quickfort')
quickfort.apply_blueprint{mode='query', data=myblueprintdata}
Is dfhack.world.cur_year_tick_advmode working as intended?
Bleh. I was in fact thinking of df.global.cur_year_tick_advmode. Too many similarly named places and too much staring at them in the last few days. Think I was thinking of functions like dfhack.world.ReadCurrentTick(), which would be under your control and could theoretically be pointing at the wrong thing.Is dfhack.world.cur_year_tick_advmode working as intended?
You mean df.global.world, not dfhack.world, right? cur_year_tick_advmode only exists in the former. Those variables are set by DF, not DFHack. They "work as intended" by definition. It's just that we might not fully understand what they mean.
while #df.global.ui.tasks.discovered_creatures > 0 do
df.global.ui.tasks.discovered_creatures:erase(#df.global.ui.tasks.discovered_creatures-1)
end
while #df.global.ui.tasks.discovered_creature_foods > 0 do
df.global.ui.tasks.discovered_creature_foods:erase(#df.global.ui.tasks.discovered_creature_foods-1)
end
while #df.global.ui.tasks.discovered_plants > 0 do
df.global.ui.tasks.discovered_plants:erase(#df.global.ui.tasks.discovered_plants-1)
end
while #df.global.ui.tasks.discovered_plant_foods > 0 do
df.global.ui.tasks.discovered_plant_foods:erase(#df.global.ui.tasks.discovered_plant_foods-1)
end
while #df.global.ui.unk_mapedge_x > 0 do
df.global.ui.unk_mapedge_x:erase(#df.global.ui.unk_mapedge_x-1)
end
while #df.global.ui.unk_mapedge_y > 0 do
df.global.ui.unk_mapedge_y:erase(#df.global.ui.unk_mapedge_y-1)
end
while #df.global.ui.unk_mapedge_z > 0 do
df.global.ui.unk_mapedge_z:erase(#df.global.ui.unk_mapedge_z-1)
end
while #df.global.ui.map_edge.surface_x > 0 do
df.global.ui.map_edge.surface_x:erase(#df.global.ui.map_edge.surface_x-1)
end
while #df.global.ui.map_edge.surface_y > 0 do
df.global.ui.map_edge.surface_y:erase(#df.global.ui.map_edge.surface_y-1)
end
while #df.global.ui.map_edge.surface_z > 0 do
df.global.ui.map_edge.surface_z:erase(#df.global.ui.map_edge.surface_z-1)
end
while #df.global.ui.map_edge.layer_z[0] > 0 do
df.global.ui.map_edge.layer_z[0]:erase(#df.global.ui.map_edge.layer_z[0]-1)
end
while #df.global.ui.map_edge.layer_z[1] > 0 do
df.global.ui.map_edge.layer_z[1]:erase(#df.global.ui.map_edge.layer_z[1]-1)
end
while #df.global.ui.map_edge.layer_z[2] > 0 do
df.global.ui.map_edge.layer_z[2]:erase(#df.global.ui.map_edge.layer_z[2]-1)
end
while #df.global.ui.map_edge.layer_z[3] > 0 do
df.global.ui.map_edge.layer_z[3]:erase(#df.global.ui.map_edge.layer_z[3]-1)
end
while #df.global.ui.map_edge.layer_z[4] > 0 do
df.global.ui.map_edge.layer_z[4]:erase(#df.global.ui.map_edge.layer_z[4]-1)
end
while #df.global.ui.map_edge.layer_x[0] > 0 do
df.global.ui.map_edge.layer_x[0]:erase(#df.global.ui.map_edge.layer_x[0]-1)
end
while #df.global.ui.map_edge.layer_x[1] > 0 do
df.global.ui.map_edge.layer_x[1]:erase(#df.global.ui.map_edge.layer_x[1]-1)
end
while #df.global.ui.map_edge.layer_x[2] > 0 do
df.global.ui.map_edge.layer_x[2]:erase(#df.global.ui.map_edge.layer_x[2]-1)
end
while #df.global.ui.map_edge.layer_x[3] > 0 do
df.global.ui.map_edge.layer_x[3]:erase(#df.global.ui.map_edge.layer_x[3]-1)
end
while #df.global.ui.map_edge.layer_x[4] > 0 do
df.global.ui.map_edge.layer_x[4]:erase(#df.global.ui.map_edge.layer_x[4]-1)
end
while #df.global.ui.map_edge.layer_y[0] > 0 do
df.global.ui.map_edge.layer_y[0]:erase(#df.global.ui.map_edge.layer_y[0]-1)
end
while #df.global.ui.map_edge.layer_y[1] > 0 do
df.global.ui.map_edge.layer_y[1]:erase(#df.global.ui.map_edge.layer_y[1]-1)
end
while #df.global.ui.map_edge.layer_y[2] > 0 do
df.global.ui.map_edge.layer_y[2]:erase(#df.global.ui.map_edge.layer_y[2]-1)
end
while #df.global.ui.map_edge.layer_y[3] > 0 do
df.global.ui.map_edge.layer_y[3]:erase(#df.global.ui.map_edge.layer_y[3]-1)
end
while #df.global.ui.map_edge.layer_y[4] > 0 do
df.global.ui.map_edge.layer_y[4]:erase(#df.global.ui.map_edge.layer_y[4]-1)
end
function fillunk(tbl,low,high)
for v =low, high do
tbl[v].unk1=0
tbl[v].unk2=0
end
end
fillunk(df.global.ui.unk2a8c[0],0,143)
fillunk(df.global.ui.unk2a8c[1],0,143)
fillunk(df.global.ui.unk2a8c[2],0,143)
fillunk(df.global.ui.unk2a8c[3],0,143)
so this mostly means switching between fort mode and adv mode is mostly stable.Full changelog:Spoiler (click to show/hide)
You can absolutely upgrade in the middle of a fort! You can just upgrade DFHack without touching anything else.
I wonder if I can do a clean install and copy my save over…As long as you're already using 0.47.05, then this should absolutely work. DFHack itself doesn't break save compatibility. You could always make a backup copy of your save folder first to be safe, though.
Just to be sure, you're using DFHack 0.47.05-r7, the version just released two days ago, correct? This issue was marked as fixed (https://github.com/DFHack/scripts/pull/424) in -r7.
What told you there would be iron, and how did you decide there wasn't any?
Just to be sure, you're using DFHack 0.47.05-r7, the version just released two days ago, correct? This issue was marked as fixed (https://github.com/DFHack/scripts/pull/424) in -r7.
Yes, I made a fresh install just today for the sole purpose of trying this out.
And lethosor fixed (https://github.com/DFHack/scripts/pull/446/files) the typo I introduced. You can edit the scripts/internal/gm-unit/editor_skills.lua file yourself if you don't want to wait until the next DFHack release:
On line 71, replace "ignore_keys" with "edit_ignore_keys"
World generation tends to ignore stop commands after it gets slow enough. Is there a way to use a script to force-stop worldgen at a certain year?
local Uni=dfhack.gui.getSelectedUnit()
local buildings={}
for _,izm in pairs (df.global.world.buildings.all) do
if df.building_workshopst:is_instance(izm) and izm.type==1 then
milk=izm.jobs[0]
print(milk.id)
milk.general_refs:insert("#",{new=df.general_ref_unit_milkeest,unit_id=Uni.id})
end
end
function petition()
local Shell=dfhack.gui.getSelectedUnit(true)
--local Shell=getCreatureAtPos(getxyz())
local Agree=df.global.world.agreements.all
Agree:insert("#",{new=true,id='-10',next_party_id=2,next_details_id=1})
local dd=Agree[#Agree - 1]
--for d,dd in ipairs(Agree) do
--if dd.id==(-10) then
--print('oh no')
dd.id = df.global.agreement_next_id
df.global.agreement_next_id = df.global.agreement_next_id + 1
dd.details:insert("#",{new=true,type=3})
dd.parties:insert("#",{new=true})
dd.parties:insert("#",{new=true})
dd.details[0].data.Citizenship=df.agreement_details_data_citizenship:new()
dd.details[0].data.Citizenship.applicant=0
dd.details[0].data.Citizenship.government=1
dd.details[0].data.Citizenship.site=df.global.ui.site_id
dd.parties[0].histfig_ids:insert("#",0)
dd.parties[0].histfig_ids[0]=Shell.hist_figure_id
dd.parties[1].id=1
dd.parties[1].entity_ids:insert("#",0)
dd.parties[1].entity_ids[0]=df.global.ui.group_id
df.global.ui.petitions:insert("#",dd.id)
end
function petition2()
local Shell=dfhack.gui.getSelectedUnit(true)
--local Shell=getCreatureAtPos(getxyz())
local Agree=df.global.world.agreements.all
Agree:insert("#",{new=true,id='-10',next_party_id=2,next_details_id=1})
local dd=Agree[#Agree - 1]
--for d,dd in ipairs(Agree) do
--if dd.id==(-10) then
--print('oh no')
dd.id = df.global.agreement_next_id
df.global.agreement_next_id = df.global.agreement_next_id + 1
dd.details:insert("#",{new=true,type=2})
dd.parties:insert("#",{new=true})
dd.parties:insert("#",{new=true})
dd.details[0].data.Residency=df.agreement_details_data_residency:new()
dd.details[0].data.Residency.reason=3
--dd.details[0].data.Residency.reason=48
dd.details[0].data.Residency.applicant=0
dd.details[0].data.Residency.government=1
dd.details[0].data.Residency.site=df.global.ui.site_id
dd.parties[0].histfig_ids:insert("#",0)
dd.parties[0].histfig_ids[0]=Shell.hist_figure_id
dd.parties[1].id=1
dd.parties[1].entity_ids:insert("#",0)
dd.parties[1].entity_ids[0]=df.global.ui.group_id
df.global.ui.petitions:insert("#",dd.id)
end
petition2()
petition()
local dlg=require("gui.dialogs")
-- ok so this script is made for summoning gods into DF, to make this work you need to nickname someone historical that would form an army.
-- so the best choice would be either another adventurer or adv follower if you want the god to show up after fast traveling or assign the DFCAT nickname on some random peasant you talk to once.
-- going to give a warning about how this script messes with nemesis and probably unstable.
-- so BEST not use this on any important saves you care about and just test this out on an disposable save.
-- oh and the gods you summon won't really talk back if you reply to them.
-- this script uses coding from spawn unit and gui scripting to make it work, don't think this works in fort mode as this was made and tested in adv mode.
-- hopefully you have a better time getting gods to fight each other better than I did.
--that been edited to spawn items so it less likely to crash and corrupt saves hopefully
function deitysummoning()
local Ark={}
for k,v in pairs(df.global.world.artifacts.all) do
if v.item.flags.artifact==true or v.flags.artifact_mood==true then
BoaName=dfhack.TranslateName(v.name)
table.insert (Ark,{dfhack.TranslateName(v.name).." "..v.name.nickname,nil,v})
end
end
local f=function(Name,C)
deitysummoning2(C[3])
end
dlg.showListPrompt("artifact list","Select your item for summoning",COLOR_RED,Ark,f)
end
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getItemAtKPos(x,y,z) -- gets the item index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.item.all -- load all items
local kickpos=df.global.world.units.active[0].pos
for i = 0, #vector-1 do -- look into all items offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==kickpos.x and cy==kickpos.y and cz==kickpos.z then --compare them
return vector[i] --return index
end
end
--print("item not found!")
return nil
end
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
--print("Creature not found!")
return nil
end
function deitysummoning2(HistfigID)
Divineslot=HistfigID
local horse=getCreatureAtPos(getxyz())
print('God Nem ID',Divineslot.id)
print('God Nem.figure ID',horse.id)
horse.inventory:insert("#",{new=true,item=HistfigID.item,mode=2,body_part_id=0})
end
deitysummoning()
(https://cdn.discordapp.com/attachments/302956330304667649/1040633009541488670/item-summon-uhh-yeah.gif)Is Advfort's tame animal job bugged? I couldn't get it to work, sadly, or was it always kind of not something possible to do?oh yeah advfort tame animal job is probably not possible to do, at best you could... toggle the tame flag on an animal through gm-editor... or use tofort to switch into fort mode while on site and go through the animal taming process through there.
if it is, is there any way for me to try and "tame" animals in adv mode?
(sorry if this is kind of out of place but this seemed like the right place to me at the time)
local dlg=require("gui.dialogs")
--warmist corpse explosion script modified to open doors now with gui function...
-- the F options will set door flags to false and the T options will set them to true
-- the ------- line option does nothing and there for visual to help separate the list
local items={}
function doors()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[0]=true
end
end
end
return true
end
end
function doors1()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[1]=true
end
end
end
return true
end
end
function doors2()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[2]=true
end
end
end
return true
end
end
function doors3()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[3]=true
end
end
end
return true
end
end
function doors4()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[4]=true
end
end
end
return true
end
end
function doors5()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[5]=true
end
end
end
return true
end
end
function doors6()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[6]=true
end
end
end
return true
end
end
function fdoors()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[0]=false
end
end
end
return true
end
end
function fdoors1()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[1]=false
end
end
end
return true
end
end
function fdoors2()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[2]=false
end
end
end
return true
end
end
function fdoors3()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[3]=false
end
end
end
return true
end
end
function fdoors4()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[4]=false
end
end
end
return true
end
end
function fdoors5()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[5]=false
end
end
end
return true
end
end
function fdoors6()
for _,checked_item in pairs(df.global.world.items.all) do
if df.item_doorst:is_instance(checked_item) and checked_item.flags.in_building then
table.insert(items,checked_item)
end
end
if #items==0 then
print"no Doors"
else
for k,v in pairs(items) do
knock=v.general_refs[0].building_id
for o,lay in pairs(df.global.world.buildings.all) do
if lay.id==knock then
lay.door_flags[6]=false
end
end
end
return true
end
end
function none()
--"divider line for gui"
end
listofspells={
{text="forbidden", spell=doors,icon='T'},
{text="internal", spell=doors1,icon='T'},
{text="taken_by_invaders", spell=doors2,icon='T'},
{text="used_by_intruder", spell=doors3,icon='T'},
{text="closed", spell=doors4,icon='T'},
{text="operated_by_mechanisms", spell=doors5,icon='T'},
{text="pet_passable", spell=doors6,icon='T'},
{text="----------", spell=none,icon='-'},
{text="forbidden", spell=fdoors,icon='F'},
{text="internal", spell=fdoors1,icon='F'},
{text="taken_by_invaders", spell=fdoors2,icon='F'},
{text="used_by_intruder", spell=fdoors3,icon='F'},
{text="closed", spell=fdoors4,icon='F'},
{text="operated_by_mechanisms", spell=fdoors5,icon='F'},
{text="pet_passable", spell=fdoors6,icon='F'},
}
dlg.showListPrompt("Doorections","Choze Doorect option 'T'rue or 'F'alse",nil, listofspells,function(index,choice) choice.spell() end)
How do I stop dfhack from autosaving my game?
How do I stop dfhack from autosaving my game?In the file PE-0.47.05-r11/LNP/graphics/Vettlingr/raw/onLoad_gfx_Vettlingr.init add a # to the beginning of the line that contains -command [ quicksave ], that will make that line a comment and prevent it from running.
local dlg=require("gui.dialogs")
-- ok so this script is made for summoning gods into DF re-re-edited to just summon any historical figure so far, for this to work all you need is to be in fast traveling.
-- after which the game will spawn in a unit directly next to you.
-- going to give a warning about how this script messes with nemesis and probably less unstable than summoning gods though you might get killed summoning a larger hostile foe.
-- so BEST not use this on any important saves you care about and just test this out on an disposable save.
-- so far as a script that summons folks go this cuts down looking for them.
-- this script uses coding from spawn unit and gui scripting to make it work, don't think this works in fort mode as this was made and tested in adv mode.
-- so far getting two folks to hostile creatures and beasts fight with this is a breeze.
function justsummoning()
local Ark={}
for k,v in pairs(df.global.world.history.figures) do
BoaName=dfhack.TranslateName(v.name)
table.insert (Ark,{dfhack.TranslateName(v.name).." "..v.name.nickname,nil,v,search_key = dfhack.TranslateName(v.name):lower()})
end
local f=function(Name,C)
justsummoning2(C[3])
end
dlg.showListPrompt("HistFig list","Select your figure for summoning",COLOR_WHITE,Ark,f,nil,nil,true)
end
function getArmyAtName(Divineslot)
for k,v in pairs(df.global.world.nemesis.all) do
if v.flags.ACTIVE_ADVENTURER then
print("Adv Nem",k)
local Cat=df.global.world.armies.all
for k1,v1 in pairs(Cat) do
if v1.members==nil then break else
for Del,ete in pairs(v1.members) do
if ete.nemesis_id==v.id then
print("Adv Army",k1)
return k1
end
end
end
end
end
end
end
function justsummoning2(HistfigID)
Divineslot=HistfigID.nemesis_id
local horse=Divineslot
print('Summon Nem ID',Divineslot)
local NeArmy=getArmyAtName()
df.global.world.armies.all[NeArmy].members:insert("#",{new=true,nemesis_id=horse})
end
justsummoning()
Any news on the agui library? I’m asking because it isn’t available as a precompiled package for my system and it’s needed for stonesense and isoworld. I did have it working before on an older version of DFHack using instructions that somebody had posted to an older DFHack thread, but those instructions stopped working with newer versions of DFHack.Can you link to those instructions? The only thing I see that uses agui is isoworld, and it appears to bundle agui, so I'm not sure why installing it separately is necessary.
Any news on the agui library? I’m asking because it isn’t available as a precompiled package for my system and it’s needed for stonesense and isoworld. I did have it working before on an older version of DFHack using instructions that somebody had posted to an older DFHack thread, but those instructions stopped working with newer versions of DFHack.Can you link to those instructions? The only thing I see that uses agui is isoworld, and it appears to bundle agui, so I'm not sure why installing it separately is necessary.
for k,v in pairs(df.global.world.history.events_death) do
if df.history_event_hist_figure_diedst:is_instance(v) and v.slayer_hf==-1 and v.death_cause~=0 then
v.slayer_hf=0
end
end
You can track which tools have been validated in the steam release here: https://docs.google.com/spreadsheets/d/1hiDlo8M_bB_1jE-5HRs2RrrA_VZ4cRu9VXaTctX_nwk/edit?usp=drivesdk
Check the Tools sheet. We're validating them in "waves". Right now we're in wave 2.
You can track which tools have been validated in the steam release here: https://docs.google.com/spreadsheets/d/1hiDlo8M_bB_1jE-5HRs2RrrA_VZ4cRu9VXaTctX_nwk/edit?usp=drivesdkIs there some particular install instructions to install it in the Steam version, even if only to get the already validated tools? Tried to just override, but then DF refuses to work because of the SQL override.
Check the Tools sheet. We're validating them in "waves". Right now we're in wave 2.
I think 2022 is required on Windows now - that sounds familiar. DF definitely upgraded to a newer compiler than 2015, but I can't quite tell from our build image changes (https://github.com/BenLubar/build-env/commits/master/msvc) whether it was 2022.We know for certain that it's at least 2019, and I believe Toady told us (possibly through a backchannel) that he was moving to 2022.
Installing VC2022 (in addition to the preexisting 2015, 2017, and 2019 versions, the last two of which were updated) didn't help in getting cmake find the required generator, nor did a forced reinstall of cmake (in case the identification of those "generators" were somehow part of that package but failed to be identified as a version update).
Thanks lethosor for pointing out both that I was barking up the wrong tree, but that barking would have ensued even if I'd found the right one.if you have more than one version of VS installed, cmake may not find the one you want (it picks one "at random"). You need to tell cmake which version you want it to use by setting the CMAKE_GENERATOR_INSTANCE variable. See https://cmake.org/cmake/help/latest/variable/CMAKE_GENERATOR_INSTANCE.html (https://cmake.org/cmake/help/latest/variable/CMAKE_GENERATOR_INSTANCE.html)
Also thanks to Bumber for the suggestion, although it didn't work.
In the past I've used the fallback of the Powershell command prompt when .bat files started directly from the File Explorer failed to work, and I've tried that here as well. I believe I've found the tool Bumber refers to (rather different path on my system, but it seems to be the same tool, which can also be invoked from VS itself), but unfortunately that doesn't help cmake find the version it's looking for. It was worth trying, however.
@ab9rf: Thanks for your suggestion. I may well fail to understand things correctly, but I don't think the issue is designation of the version as "cmake ..\..\.. -G"Visual Studio 17 2022" -A x64 -DCMAKE_INSTALL_PREFIX="%_DF_PATH%" seems to insist on using the 2022 version, producing the following error message:Your cmake is too old. cmake versions prior to 3.21 do not know what VS 2022 is and thus cannot use it.
CMake Error: Could not create named generator Visual Studio 17 2022
OK, it looks like I'm stuck for the time being. DFHack doesn't seem to recognize the Classic version, which is what I'd intended to use until keyboard support has been restored (at which time I intend to buy it via Itch.io, not Steam, which may have a different layout from the 4 steam versions it seems to recognize, based on the stderr.log contents).I believe Steam, Itch, and Classic are all different executables. Steam is definitely different, at least. I might be able to generate symbols for classic. We do fully intend to support all of them, but most analysis has been happening on the Steam build so far.
OK, it looks like I'm stuck for the time being. DFHack doesn't seem to recognize the Classic version, which is what I'd intended to use until keyboard support has been restored (at which time I intend to buy it via Itch.io, not Steam, which may have a different layout from the 4 steam versions it seems to recognize, based on the stderr.log contents).we have yet to analyze either the itch.io or classic executables yet, so we only have symbols.xml for steam releases. it's on the agenda, certainly, but i haven't done it in part because we know that a major structure (gamest) is larger in Steam than in classic (and probably also itch) because there's stuff in there for the steam workshop, and so we need to come up with a strategy for dealing with having a structure that is different between releases of the same version on the same platform, which we've never had to deal with before now. we discussed this a few days ago, but honestly i've been sick since christmas eve and haven't had the chance to process the discussion and turn the results of it into a working strategy
wine explorer "Dwarf Fortress.exe"
Wow, you already have a build of DFHack going. Is v50 a bigger load of work to update DFHack for than previous major updates? Or is every major update about the same for DFHack?This one is easily the most substantial amount of work to update for since the shift to 64-bit builds in 2015.
On the DFHack side of things, has there been (or are there any plans to) changes to the persistent storage stuff? Previously I was using that heavily, but I moved to mixing it with writing/reading a basic JSON file when the game was saved/loaded. Wondering if I should keep doing that or if I should go back to using the persistent storage, or if I should use a completely different system?We implemented a persistence API some time ago (in DFHack 0.44.12-r3), using JSON written to a cosave file in the game folder.
On the DFHack side of things, has there been (or are there any plans to) changes to the persistent storage stuff? Previously I was using that heavily, but I moved to mixing it with writing/reading a basic JSON file when the game was saved/loaded. Wondering if I should keep doing that or if I should go back to using the persistent storage, or if I should use a completely different system?We implemented a persistence API some time ago (in DFHack 0.44.12-r3), using JSON written to a cosave file in the game folder.
Yes, internally, DF works about the same (for the most part).
Regarding DFHack, persistent storage itself hasn't changed much, but the way plugins and scripts keep state has changed a bit. There is a much stronger push for tools to persist the state that players set while playing. The rallying cry is "initless", since the idea is to allow players to use DFHack tools effectively with zero init file editing (though init files still work if that's how the player wants to do things). As part of this effort, scripts have gained a way to automatically re-enable themselves according to their saved state. Before, a player would have to explicitly run a script (either manually or from an init file) before the script would have a chance to check any saved state. This prevented scripts from automatically reloading their state and continuing where they left off.
Now, scripts have a standard way to load state and set hooks at DF startup like plugins have always enjoyed: https://docs.dfhack.org/en/latest/docs/dev/Lua%20API.html#enabling-and-disabling-scripts
In UI news, the DFHack widget library is getting an overhaul. Two overhauls, actually. The current widget library is getting a lot more mouse support, like dragging, resizing, and being able to scroll the widget under the mouse cursor with the mouse wheel.
We're also working on an entirely new widget API that will allow much more "professional" UIs. This is still in the experimental phase, but it's coming along nicely.
Another big advancement is the DFHack overlay, which allows you to easily inject information and functionality into existing viewscreens. See https://docs.dfhack.org/en/latest/docs/dev/overlay-dev-guide.html
So, fun times ahead for modders : )
Issues on GitHub are welcome too, although there is some risk of duplicating work there (we're trying to get PRs up for changes within a reasonable time, but it doesn't always happen)we are definitely behind in merging structures updates, but this isn't really surprising given that (a) it's been holidays and (b) there is a lot going on right now
I would quite literally KILL for a script to run that creates individual work details for every labor and sets them all to Only Selected. I spend 5-10 minutes just setting that shit up every damn time I play.
I would quite literally KILL for a script to run that creates individual work details for every labor and sets them all to Only Selected. I spend 5-10 minutes just setting that shit up every damn time I play.
Better yet, just play the game as designed until the utilities come out which are actually designed to do this and use the global flag that turns off the work details system completely, instead of repeatedly banging your head against the wall to get a system to do something you don't want it to?
(https://user-images.githubusercontent.com/977482/192902807-dbe3a187-85b0-4cfc-a6ca-9344d3beeaa2.png)How do we move/remove this launcher text?
You have to edit unit.enemy.were_caste and unit.enemy.normal_caste as well
I don't think quicksave is working correctly, no saves were created for me.Saving is funky nowadays. It appears that quicksave will trigger an **autosave**, and increment the autosave counter. At least this is what it did for me when I tested it and tracked down where the saves were going. I updated the docs to reflect this in the next version.
I have a pretty generic question. Do we have a plan to ease the distribution of scripted mods in the workshop?Some initial thoughts here: https://github.com/DFHack/dfhack/issues/2667
Hi people.Sounds perfectly possible. Could you file a feature request at https://github.com/DFHack/dfhack/issues ?
It would be really good if the religions of your civilisation were highlighted in some way.
I don't think quicksave is working correctly, no saves were created for me.Saving is funky nowadays. It appears that quicksave will trigger an **autosave**, and increment the autosave counter. At least this is what it did for me when I tested it and tracked down where the saves were going. I updated the docs to reflect this in the next version.
EDIT2: Question is moot. It's docker alone that's literally filling up nearly the entirety of my /var partition when it downloads things to build the MSVC image. I literally can't free up enough space to let docker finish the job without repartitioning my system drive. And to people having this issue in the future: temporarily symlinking the folder to a larger drive/partition doesn't work, because docker won't proceed through symlinks.
Like, this is the biggest stumbling block keeping me from playing more. It takes me 10-20 minutes to set up work details alone every single time I play a new fortress. It's made worse by how slow back-spacing out the default title of the work detail is.Yeah, I agree. My two gripes:
1) That there isn't a detail for each job by default.
2) That you can't edit the base details.
you do not have to torture yourself by making sure you can assign soap makers, it's okay, you can make fantastically successful forts with only two or three custom work details, please don't act like it's our problem that you feel the need to wrestle with a UI that is screaming at you to do something else, this is what third-party utilities are for
Does the alpha release have a quick way of handling work details (like automatically making a single detail for each labour)?
local labors = {
"CARPENTER",
"STONE_CARVER",
"MASON",
"ANIMALTRAIN",
"ANIMALCARE",
"BUTCHER",
"TRAPPER"
}
for i,l in ipairs(labors) do
local detail = df.work_detail:new()
detail.name = df.unit_labor.attrs[l].caption
detail.work_detail_flags.enabled = true
detail.allowed_labors[df.unit_labor[l]] = true
detail.icon = 10+((i-1)%8)
df.global.plotinfo.hauling.work_details:insert('#', detail)
end
Fill the "labors" list from https://github.com/DFHack/df-structures/blob/c4d78c229aa0edd68a69cd5b19d5ad35a5b71098/df.skills.xml#L841
Thank you my friend. My OCD is truly grateful.Does the alpha release have a quick way of handling work details (like automatically making a single detail for each labour)?Code: [Select]local labors = {
Fill the "labors" list from https://github.com/DFHack/df-structures/blob/c4d78c229aa0edd68a69cd5b19d5ad35a5b71098/df.skills.xml#L841
"CARPENTER",
"STONE_CARVER",
"MASON",
"ANIMALTRAIN",
"ANIMALCARE",
"BUTCHER",
"TRAPPER"
}
for i,l in ipairs(labors) do
local detail = df.work_detail:new()
detail.name = df.unit_labor.attrs[l].caption
detail.work_detail_flags.enabled = true
detail.allowed_labors[df.unit_labor[l]] = true
detail.icon = 10+((i-1)%8)
df.global.plotinfo.hauling.work_details:insert('#', detail)
end
In hack/scripts in a file with a .lua extension. Then type the name of the file (without the extension) in dfhack command prompt to run the script. See the documentation (https://docs.dfhack.org/en/latest/docs/dev/Lua%20API.html#scripts)Thanks, that worked! There was a weird error thing generated afterward, but the actual work details were still put in.
You put SDLreal in there too? It's not just a backup, the replacement SDL.dll calls into it for display etc
Hi people.Sounds perfectly possible. Could you file a feature request at https://github.com/DFHack/dfhack/issues ?
It would be really good if the religions of your civilisation were highlighted in some way.
You don't need to lose your world. Copy the world elsewhere and then reinstall (in the Classic version I make new installations and copy the saved world over, but I guess things work differently with the Premium ones. It ought to be possible to reinstall on top of a Premium version installation without losing the save if they've implemented it in a reasonable way, but I'd recommend copying the save anyway, just in case).
The notes for 50.05-alpha3 mentions that Itch.io and Classic support is scheduled to come with DF 50.06. Can you explain why?No, it's that the DFHack team identified a structural issue with data layout. This was really evident to us almost immediately, the first time we analyzed an itch.io image, which would have around December 10 or so, well before Putnam was hired. Putnam subsequently confirmed our conclusion. The issue itself arises from some #ifdefs in the middle of the definition of gamest for data specific to mod management, which has extra functionality for Steam editions which requires extra data.
My guess would be that Toady/Putnam (quite possibly the latter) has identified a layout solution that would make the layout of DF proper data the same in all versions, and move all vendor specific data to vendor specific locations outside of the space used by DF natively, but that's obviously just a guess.
"equivalent padding", that sounds tricky. I would have thought hiding the steam stuff behind a pointer indirection would have been enough. But I did not really look what is this steam-specific data and how it is organized. I may be missing something.While I can't speak for Putnam here, my take on that decision would be that it is the one that requires no changes at all outside of the single header file that defines the structure in question. Indirecting the Steam-specific data behind a pointer to another structure would necessitate adding code to allocate (and deallocate) that additional structure and to use indirections when accessing its content, which is therefore a more "involved" change than just adding an #else and some dummy variables to an #ifdef that is already there.
[DFHack]# :lua df.global.game.external_flag=1
[DFHack]# :lua !df.global.game.external_flag
0
Also I have a weird bug with the classic version only where game.external_flag is always reset to 0 when a fortress is loaded (I can set it in the main menu but not in game).Code: [Select][DFHack]# :lua df.global.game.external_flag=1
[DFHack]# :lua !df.global.game.external_flag
0
I had to remove fields to make external_flag work on classic, so I made gamest smaller. If it is actually bigger, it means there are extra fields after external_flag.Well, if you had to remove fields to make external_flag align, it means there is something larger before external_flag on classic (vs Steam). It could be extra fields, or the padding Putnam added in an attempt to align gamest.
I honestly do not know what Bay12 is doing here.
I checked during initial analysis to verify that gamest in classic was _not smaller_ than it was in steam. I neglected to check whether it was _larger_, in part because it didn't make sense for it to be.I figured out my error above: I transposed two digits while manually doing these calculations. The address of game in .07-Classic is 0x141e0ecb0, which is not what I used above.
Sadly, it is. We know that the external flag is the last element in gamest, and so the length of gamest is the difference between the address of game and the address of external flag (plus four bytes). In Steam, this length is 0x141e2554 - 0x141e1adc0 + 4 = 0xa7a8; in Classic this length is 0x141e193d4 - 0x141e0ebc0 + 4 = 0xa818. For some reason that is inexplicable to me, gamest is now actually _longer_ on Classic than on Steam.
I don't have an analyzed 50.05 Classic image handy, but in 50.04-Classic this was 0x141dbf3c - 0x141db4fc0 +4 = 0xa400, while in 50.04-Steam this was 0x141dcb52c - 0x141dc10b0 + 4 = 0xa480.
I honestly do not know what Bay12 is doing here.
PS E:\Kelly\Projects\df_misc> & ruby -I../metasm dump_df_globals.rb --raw 'E:\kelly\df\df_50_07_steam\Dwarf Fortress.exe' | where-object {$_.contains("game")}
Global table starts at 141318080h
<global-address name='game.external_flag' value='0x141e25564'/>
<global-address name='game' value='0x141e1adc0'/>
<global-address name='gamemode_cansave' value='0x141876a15'/>
<global-address name='gamemode' value='0x141319840'/>
<global-address name='gametype' value='0x14131983c'/>
PS E:\Kelly\Projects\df_misc> & ruby -I../metasm dump_df_globals.rb --raw 'E:\kelly\df\df_50_07_classic\Dwarf Fortress.exe' | where-object {$_.contains("game")}
Global table starts at 14130c050h
<global-address name='game.external_flag' value='0x141e193d4'/>
<global-address name='game' value='0x141e0ecb0'/>
<global-address name='gamemode_cansave' value='0x14187e18c'/>
<global-address name='gamemode' value='0x14130d804'/>
<global-address name='gametype' value='0x14130d7dc'/>
steam: game.external_flag - game = 0x141e25564 - 0x141e1adc0 = 0xa7a4
classic: game.external_flag - game = 0x141e193d4 - 0x141e0ecb0 = 0xa724L
Despite that, I can still get some data from the Classic version using DFHack, with many things seemingly working (gui/gm-editor, some basic scripts to look at some data of interest, such as units, for instance). I've run a script over the DF data structures to try to find things that I might be able to fix, and after blacklisting parts of it (crashing or running out of memory on "broken" structures) I've been able to go through it, although without finding anything that's within my capability to fix so far, unfortunately.To the best of my knowledge, the only variable that is mislocated due to this issue that is actually used in DFHack is, ironically, the external flag, which is only used at present by autolabor. With this single exception, none of the variables in the "tail" of gamest (the members that appear after mod_manager) that is misaligned due to this issue are currently being used by any tool or script that is being distributed with DFHack.
Still, it's usable as a probe (with extra caution) with Classic, despite there apparently being a big wart on it.
.text:0000000140CB5976 loc_140CB5976: ; CODE XREF: sub_140CB3AD0+1E35↑j
.text:0000000140CB5976 BF 07 00 00 00 mov edi, 7 ; START_DWARF_COUNT
.text:0000000140CB597B 48 8B 74 24 60 mov rsi, [rsp+2C0h+unit]
.text:0000000140CB5980 4C 8B 75 B8 mov r14, [rbp+1C0h+var_208]
.text:0000000140CB5984 44 8D 67 F8 lea r12d, [rdi-8] ; OKAY! This parameter needs to be set to -1 !
.text:0000000140CB5984 ; That chooses caste at random.
.text:0000000140CB5984 ; If it's >= 0, it selects that caste.
.text:0000000140CB5988
.text:0000000140CB5988 loc_140CB5988: ; CODE XREF: sub_140CB3AD0+1FEB↓j
.text:0000000140CB5988 FF CF dec edi
.text:0000000140CB598A E8 A1 49 3B FF call new_unit
.text:0000000140CB598F 48 8B D8 mov rbx, rax
.text:0000000140CB5992 48 89 44 24 60 mov [rsp+2C0h+unit], rax
.text:0000000140CB5997 45 8B C4 mov r8d, r12d
.text:0000000140CB599A 45 33 C9 xor r9d, r9d
.text:0000000140CB599D 0F B7 15 28 26 15 01 movzx edx, cs:word_141E07FCC ; example value: 23Ch, 572, DWARF. Unit type.
.text:0000000140CB59A4 48 8B C8 mov rcx, rax
.text:0000000140CB59A7
.text:0000000140CB59A7 this call crashes when startdwarf is 10 or higher.
.text:0000000140CB59A7 turns out that's because of the caste flag in r12d
.text:0000000140CB59A7 (passed in r8d, per above code).
.text:0000000140CB59A7 This parameter should be -1. When startdwarf is forced to 10,
.text:0000000140CB59A7 r12d is set to (10-8) = 2, which is a nonexistant caste.
.text:0000000140CB59A7 E8 94 4D 27 00 call sub_140F2A740 ; parameters: rcx=*unit,
.text:0000000140CB59A7 ; dx = creature type index (572=DWARF),
.text:0000000140CB59A7 ; r8w = caste (-1 for random),
.text:0000000140CB59A7 ; r9b flag: set unit 6-byte field @ 0D70h
I've found start_dwarf_count for 50.07 Classic.We found the startdwarf location relatively early on in the development cycle (we have a script for it that worked, so it was trivial), but we decided (as lethosor notes) not to publish it because our testing showed that it failed for counts greater than 9. I never investigated deeply to figure out why, since we wanted to get a release out and it wasn't worth blocking for this, and honestly I've never worked back to the problem.
Unfortunately, it's not going to be that easy. Setting it above 9 causes a crash.Quote from: 50.07.text:0000000140CB5976 loc_140CB5976: ; CODE XREF: sub_140CB3AD0+1E35↑j
.text:0000000140CB5976 BF 07 00 00 00 mov edi, 7 ; START_DWARF_COUNT
.text:0000000140CB597B 48 8B 74 24 60 mov rsi, [rsp+2C0h+unit]
.text:0000000140CB5980 4C 8B 75 B8 mov r14, [rbp+1C0h+var_208]
.text:0000000140CB5984 44 8D 67 F8 lea r12d, [rdi-8] ; OKAY! This parameter needs to be set to -1 !
.text:0000000140CB5984 ; That chooses caste at random.
.text:0000000140CB5984 ; If it's >= 0, it selects that caste.
.text:0000000140CB5988
.text:0000000140CB5988 loc_140CB5988: ; CODE XREF: sub_140CB3AD0+1FEB↓j
.text:0000000140CB5988 FF CF dec edi
.text:0000000140CB598A E8 A1 49 3B FF call new_unit
.text:0000000140CB598F 48 8B D8 mov rbx, rax
.text:0000000140CB5992 48 89 44 24 60 mov [rsp+2C0h+unit], rax
.text:0000000140CB5997 45 8B C4 mov r8d, r12d
.text:0000000140CB599A 45 33 C9 xor r9d, r9d
.text:0000000140CB599D 0F B7 15 28 26 15 01 movzx edx, cs:word_141E07FCC ; example value: 23Ch, 572, DWARF. Unit type.
.text:0000000140CB59A4 48 8B C8 mov rcx, rax
.text:0000000140CB59A7
.text:0000000140CB59A7 this call crashes when startdwarf is 10 or higher.
.text:0000000140CB59A7 turns out that's because of the caste flag in r12d
.text:0000000140CB59A7 (passed in r8d, per above code).
.text:0000000140CB59A7 This parameter should be -1. When startdwarf is forced to 10,
.text:0000000140CB59A7 r12d is set to (10-8) = 2, which is a nonexistant caste.
.text:0000000140CB59A7 E8 94 4D 27 00 call sub_140F2A740 ; parameters: rcx=*unit,
.text:0000000140CB59A7 ; dx = creature type index (572=DWARF),
.text:0000000140CB59A7 ; r8w = caste (-1 for random),
.text:0000000140CB59A7 ; r9b flag: set unit 6-byte field @ 0D70h
This excessively-optimized code depends on knowing that EDI is set to 7 as a space-saving way to set R12D to -1, using the calculation (7-8).
R12 is then used to select the caste of the unit being generated. -1 means random; 0 means FEMALE; 1 means MALE; 2 or higher lead to a crash.
We need to enter the call to sub_140F2A740 with R8 == -1. I don't see a way to squeeze that into the current code.
Anyone have thoughts?
If someone comes up with a patch that works for start dwarf counts less than 7 (such as, oh I don't know, one), that I might be more interested in...
Poking at it a bit, the obvious solution is to patch n into the startdwarf location and (255-n) in the last byte of the LEA instruction, exactly 16 bytes later.
Where is the "init.d" directory mentioned in the documentation (https://docs.dfhack.org/en/latest/docs/Core.html#init-d-lua) supposed to be created? It says "main DF directory" but it does not work for me. The only occurence of init.d I find in the code is for "raw/init.d".I'm asking again. Sorry for spamming, I think it may have been forgotten at the end of the previous page.
Or what is the best way to run a lua command on startup? Should I rather create a "dfhack-config/init/dfhack-....init" file containing ":lua ..."?
Not to mention it's not remotely likely to work with any future linux builds, which will presumably be built with gcc (or perhaps clang) and thus won't have the same optimizer as MSVC.If someone comes up with a patch that works for start dwarf counts less than 7 (such as, oh I don't know, one), that I might be more interested in...
But... you can. I just did it in the debugger. The only trick is, you need to embark with at least 6 animals. (4 purchased animals and the 2 wagon animals.)Poking at it a bit, the obvious solution is to patch n into the startdwarf location and (255-n) in the last byte of the LEA instruction, exactly 16 bytes later.
On inspection, that would work. The limit would be126127 dwarves before the LEA starts adding instead of subtracting.
Fragile as anything, though. There's no telling what the optimizer will do to the code in future releases. I sure wouldn't want to hardcode a 16 byte offset in hopes that it magically will always work.
Thank you for looking at this.
I know the DFHack devs are focusing on .50.xx, but is there any chance you could do a small QoL improvement for those that prefer .47.05 ?It is unlikely that we will make any further official releases for 47.05 for any reason other than to fix a serious bug. We had to significantly redesign our workflow automation to make builds for v50, which means we no longer have the ready means to make release builds for prior versions. Any release for 47.05 would have to be hand-rolled, and we're not likely to go to all the trouble required to do that for anything less than the discovery of a showstopper-level bug.
When a dwarf makes an artifact that's a family heirloom, I put a display case in their room for it. Finding the artifact among the dozens of pages of Non-Sortable artifacts is a real pain that gets more painful as the decades pass... can it be made sortable?
This would really make the game less frustrating and I still love .47.05
Thanks for the reply. It's hard being a dinosaur.I know the DFHack devs are focusing on .50.xx, but is there any chance you could do a small QoL improvement for those that prefer .47.05 ?It is unlikely that we will make any further official releases for 47.05 for any reason other than to fix a serious bug. We had to significantly redesign our workflow automation to make builds for v50, which means we no longer have the ready means to make release builds for prior versions. Any release for 47.05 would have to be hand-rolled, and we're not likely to go to all the trouble required to do that for anything less than the discovery of a showstopper-level bug.
When a dwarf makes an artifact that's a family heirloom, I put a display case in their room for it. Finding the artifact among the dozens of pages of Non-Sortable artifacts is a real pain that gets more painful as the decades pass... can it be made sortable?
This would really make the game less frustrating and I still love .47.05
Where is the "init.d" directory mentioned in the documentation (https://docs.dfhack.org/en/latest/docs/Core.html#init-d-lua) supposed to be created? It says "main DF directory" but it does not work for me. The only occurence of init.d I find in the code is for "raw/init.d".I'm asking again. Sorry for spamming, I think it may have been forgotten at the end of the previous page.
Or what is the best way to run a lua command on startup? Should I rather create a "dfhack-config/init/dfhack-....init" file containing ":lua ..."?
Thanks for the reply. It's hard being a dinosaur.
Thanks for the reply. It's hard being a dinosaur.
Note that we made a branch for 0.47.05-compatible code, so if you do want to make a code change that implements a feature, you can check out that branch, make whatever changes are needed, and build it yourself. Docs for how to do that are here: https://docs.dfhack.org/en/0.47.05-r8/docs/dev/Compile.html
The branch is named "v0.47.05", so you'll need to switch to that branch from the default "develop" branch.
local old_screen=dfhack.gui.getCurViewscreen()
local new_screen=df.viewscreen_choose_game_typest:new()
old_screen.child=new_screen
new_screen.parent=old_screen
new_screen.gametypes:insert("#",0)
new_screen.gametypes:insert("#",2)
--nano-boat-station-for-void-forts
local dlg=require("gui.dialogs")
function teleportboatnames2()
local Ark={}
for k,v in pairs(df.global.world.world_data.sites) do
if v.name.first_name=="STATION" then
BoaName=dfhack.TranslateName(v.name)
table.insert (Ark,{dfhack.TranslateName(v.name).." "..v.name.nickname,nil,v})
end
end
local f=function(Name,C)
MobileFortNano(C[3])
--teleportboatlista(C[3])
end
dlg.showListPrompt("list of stations","Select Station(s) to settle here",COLOR_WHITE,Ark,f)
end
function MobileFortNano(siteID)
local MapCure=df.global.gview.view.child.location
local MapCurx=MapCure.region_pos.x
local MapCury=MapCure.region_pos.y
local MapCurEmbark=MapCure.embark_pos_min
local Fortsite=siteID.index
df.global.world.world_data.sites[Fortsite].pos.x=MapCurx
df.global.world.world_data.sites[Fortsite].pos.y=MapCury
df.global.world.world_data.sites[Fortsite].rgn_min_x=MapCurEmbark.x
df.global.world.world_data.sites[Fortsite].rgn_max_x=MapCurEmbark.x
df.global.world.world_data.sites[Fortsite].rgn_min_y=MapCurEmbark.y
df.global.world.world_data.sites[Fortsite].rgn_max_y=MapCurEmbark.y
df.global.world.world_data.sites[Fortsite].global_min_x=MapCurEmbark.x
df.global.world.world_data.sites[Fortsite].global_max_x=MapCurEmbark.x
df.global.world.world_data.sites[Fortsite].global_min_y=MapCurEmbark.y
df.global.world.world_data.sites[Fortsite].global_max_y=MapCurEmbark.y
end
if df.global.gview.view.child==nil then
print("this script requires you to be in the raid menu to work")
else
teleportboatnames2()
end
andlocal dlg=require("gui.dialogs")
function nameStation()
df.global.world.world_data.sites[df.global.plotinfo.site_id-1].name.first_name="STATION"
df.global.world.world_data.sites[df.global.plotinfo.site_id-1].name.nickname="VoidShuttle"
dlg.showMessage("Travel-Hack","Converting fort into VoidStation")
end
nameStation()
so Station move script functions in embark menu as you set up your embark size and select the destination while not confirming the warning prompt that shows up as you use that to lock the coord in place so the script can function. for k,v in pairs(df.global.world.world_data.sites) do
if v.type~=0 then
v.flags[0]=true
end
end
for k,v in pairs(df.global.world.world_data.sites) do
if v.type~=0 then
v.flags[0]=false
end
end
local si2=df.global.gview.view.child.child.army_controller
local si=df.global.gview.view.child.child.army_controller[0].site_id
for fo,rm in pairs (si2) do
if rm.type==2 then
if rm.data.InvasionOrder.unk_1==5 then
rm.data.InvasionOrder.unk_1=2
end
end
end
local dlg=require("gui.dialogs")
function teleportboatnames3()
local Ark={}
for k,v in pairs(df.global.world.world_data.sites) do
if v.name.first_name=="STATION" then
BoaName=dfhack.TranslateName(v.name)
table.insert (Ark,{dfhack.TranslateName(v.name).." "..v.name.nickname,nil,v})
end
end
local f=function(Name,C)
MobileFortNano(C[3])
end
dlg.showListPrompt("list of stations","Select Station(s) to settle here",COLOR_WHITE,Ark,f)
end
function stationmove()
if df.global.gview.view.child==nil then
print("this script requires you to be in the raid menu to work")
else
teleportboatnames3()
end
end
function MobileFortNano(siteID)
--local MapCurx=df.global.gview.view.child.location._x
local MapCure=df.global.gview.view.child.location
--local MapCure=df.global.gview.view.child.child.location
local MapCurx=MapCure.region_pos.x
local MapCury=MapCure.region_pos.y
local MapCurEmbark=MapCure.embark_pos_min
local MapCurEmbark2=MapCure.embark_pos_max
local Fortsite=siteID.index
local FortSize=df.global.world.world_data.sites[Fortsite]
local math1=FortSize.rgn_min_x-FortSize.rgn_max_x
local math1a=FortSize.rgn_min_y-FortSize.rgn_max_y
local math2=MapCurEmbark.x-MapCurEmbark2.x
local math2a=MapCurEmbark.y-MapCurEmbark2.y
if math1==math2 and math1a==math2a then
--print("works")
df.global.world.world_data.sites[Fortsite].pos.x=MapCurx
df.global.world.world_data.sites[Fortsite].pos.y=MapCury
df.global.world.world_data.sites[Fortsite].rgn_min_x=MapCurEmbark.x
df.global.world.world_data.sites[Fortsite].rgn_max_x=MapCurEmbark2.x
df.global.world.world_data.sites[Fortsite].rgn_min_y=MapCurEmbark.y
df.global.world.world_data.sites[Fortsite].rgn_max_y=MapCurEmbark2.y
df.global.world.world_data.sites[Fortsite].global_min_x=MapCurEmbark.x
df.global.world.world_data.sites[Fortsite].global_max_x=MapCurEmbark2.x
df.global.world.world_data.sites[Fortsite].global_min_y=MapCurEmbark.y
df.global.world.world_data.sites[Fortsite].global_max_y=MapCurEmbark2.y
else
dlg.showMessage("Travel-Hack-Error","incorrect fort size change to correct size")
print('incorrect fort size change to correct size')
end
end
stationmove()
--this script is made as means to petition folks to join a fort manually requires mining cursor to work
--doesn't work on folks who don't have a historical figure so watch out.
--this script re-uses warmist and other folks in the dfhack community functions to get access to the cursor
local ui=plotinfo
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
return nil
end
function petition()
local ui=plotinfo
local Shell=getCreatureAtPos(getxyz())
local Agree=df.global.world.agreements.all
Agree:insert("#",{new=true,id='-10',next_party_id=2,next_details_id=1})
local dd=Agree[#Agree - 1]
print('oh no')
dd.id = df.global.agreement_next_id
df.global.agreement_next_id = df.global.agreement_next_id + 1
dd.details:insert("#",{new=true,type=3})
dd.parties:insert("#",{new=true})
dd.parties:insert("#",{new=true})
dd.details[0].data.Citizenship=df.agreement_details_data_citizenship:new()
dd.details[0].data.Citizenship.applicant=0
dd.details[0].data.Citizenship.government=1
dd.details[0].data.Citizenship.site=df.global.plotinfo.site_id
dd.parties[0].histfig_ids:insert("#",0)
dd.parties[0].histfig_ids[0]=Shell.hist_figure_id
dd.parties[1].id=1
dd.parties[1].entity_ids:insert("#",0)
dd.parties[1].entity_ids[0]=df.global.plotinfo.group_id
df.global.plotinfo.petitions:insert("#",dd.id)
end
function petition2()
local ui=plotinfo
local Shell=getCreatureAtPos(getxyz())
local Agree=df.global.world.agreements.all
Agree:insert("#",{new=true,id='-10',next_party_id=2,next_details_id=1})
local dd=Agree[#Agree - 1]
print('oh no')
dd.id = df.global.agreement_next_id
df.global.agreement_next_id = df.global.agreement_next_id + 1
dd.details:insert("#",{new=true,type=2})
dd.parties:insert("#",{new=true})
dd.parties:insert("#",{new=true})
dd.details[0].data.Residency=df.agreement_details_data_residency:new()
dd.details[0].data.Residency.reason=3
dd.details[0].data.Residency.applicant=0
dd.details[0].data.Residency.government=1
dd.details[0].data.Residency.site=df.global.plotinfo.site_id
dd.parties[0].histfig_ids:insert("#",0)
dd.parties[0].histfig_ids[0]=Shell.hist_figure_id
dd.parties[1].id=1
dd.parties[1].entity_ids:insert("#",0)
dd.parties[1].entity_ids[0]=df.global.plotinfo.group_id
df.global.plotinfo.petitions:insert("#",dd.id)
end
petition2()
petition()
local MapCure=df.global.gview.view.child.child
local options = {}
local argparse = require('argparse')
local commands = argparse.processArgsGetopt({...}, {
{'d', 'dead', handler=function() options.dead = true end}
})
--this script uses my old campboat set up to make a gui list for the starting 7
--and a list for the creatures you would be converting them into.
local dlg=require("gui.dialogs")
function teleportboatnames2()
local Ark={}
for k,v in pairs(MapCure.s_unit) do
BoaName=dfhack.TranslateName(v.name)
table.insert (Ark,{dfhack.TranslateName(v.name).." "..v.name.nickname,nil,v,search_key = BoaName:lower()})
end
local f=function(Name,C)
unretire_all(C[3])
end
dlg.showListPrompt("list of units","Select starting unit to settle here",COLOR_WHITE,Ark,f,nil,nil,true)
end
function unretire_all(unit2)
Ark2={}
for num,se in pairs(df.global.world.raws.creatures.all) do
CreatureName=se.creature_id
table.insert (Ark2,{se.creature_id.." "..se.name[0],nil,num,search_key = CreatureName:lower()})
end
local f=function(Name,C2)
unit2.name.nickname=df.global.world.raws.creatures.all[C2[3]].name[0]
unit2.enemy.normal_race=C2[3]
for cas,cast in pairs(df.global.world.raws.creatures.all[C2[3]].caste) do
if unit2.enemy.normal_caste > cas then
unit2.enemy.normal_caste=cas
end
end
end
dlg.showListPrompt("list of creatures","Select creature to change here",COLOR_WHITE,Ark2,f,nil,nil,true)
end
if df.global.gview.view.child==nil then
print("this script requires you to be in the raid menu to work")
else
teleportboatnames2()
end
this has to be done on the prepare carefully embark screen, and this wouldn't save the profile to these creature ids.Instead of an announcement, we *could* instead have an on-screen alert widget that shows important status messages (with options to hide, jump to target, etc.). It could appear near the lower left corner when there is trouble to report.
also, are non-citizens not handled by autoslab? if not, should they be?
This will be more discoverable in the future, but there is a global setting for whether boulders will be chosen for building constructions. You can set it via the commandline
buildingplan set boulders false
Or in any filter dialog in the "global settings" tab
Support for scripts in DF mods
Whaaaat?? Yes, it's true. Scripts with mods now work automatically as soon as you activate them for a world. No more manual copying of scripts after "installation". This allows everything from single scripts to total conversion mods that add new scripted gameplay elements to work seamlessly, straight from the DF Steam Workshop. See the DFHack modding guide for details. You can also subscribe to the example Workshop mod to see a real example of how to distribute scripts via Steam Workshop (though the format is applicable to mods distributed by non-Steam means as well).
--proof of concept of an announcement menu.
local dlg=require("gui.dialogs")
local JUNK={}
local Dang=df.global.world.status.announcements
for ultimate,checked_Ann in pairs(Dang) do
local mono=checked_Ann.text
table.insert(JUNK,{mono,search_key = mono:lower()})
end
dlg.showListPrompt("Announcement list","scroll through the list of Announced alerts",COLOR_WHITE,JUNK,nil,nil,nil,nil,true)
--@ module = true
if not dfhack_flags.module then
print("data/installed_mods/example_mod (100)/scripts_modactive/example_mod.lua ran.")
else
print("data/installed_mods/example_mod (100)/scripts_modactive/example_mod.lua ran as a module.")
end
local ok, ret = pcall(function()
-- do something sketchy
end)
local bld = thebuidlinginquestion
if bld:getType() == df.building_type.Workshop then
-- access workshop fields
elseif bld::getType() == df.building_type.Stockpile then
-- access stockpile fields
end
[ID:lighter_green_glass]
[NUMERIC_VERSION:100]
[DISPLAYED_VERSION:1.00]
[EARLIEST_COMPATIBLE_NUMERIC_VERSION:100]
[EARLIEST_COMPATIBLE_DISPLAYED_VERSION:1.00]
[AUTHOR:0x517A5D]
[NAME:Lighter Green Glass Items]
[DESCRIPTION:Reduces the density of green glass, which reduces the weight of green glass items. Requires DFHack.]
--@ module = true
local utils = require('utils')
local mod_ID = 'lighter_green_glass' -- ID of the mod, per init.txt.
local script_name = 'lighter_green_glass' -- name of this script, without the .lua extension.
local GLOBAL_KEY = script_name -- unique key for the state-change callback.
if not dfhack_flags.module then
print(string.format("The %s script is auto-executed when the %s mod is loaded;", script_name, mod_ID))
print('It is not intended to be invoked directly. No action taken.')
return
end
---- define locals. Note: only initialize locals that will never change, even on world reload.
local gg_original_density = df.global.world.raws.mat_table.builtin.GLASS_GREEN.solid_density
---- implementation code
local function activate_mod_raws_changes()
df.global.world.raws.mat_table.builtin.GLASS_GREEN.solid_density = math.floor(gg_original_density/5)
end
local function deactivate_mod_raws_changes()
df.global.world.raws.mat_table.builtin.GLASS_GREEN.solid_density = gg_original_density
end
local function mod_is_loaded()
-- df.global.world.object_loader.object_loader_order_id[] is an array of strings.
-- search it for the ID of our mod. if it exists, the mod is loaded.
return(utils.linear_index(df.global.world.object_loader.object_load_order_id, mod_ID, 'value') ~= nil)
end
---- install hook for callbacks.
if dfhack_flags.module then
dfhack.onStateChange[GLOBAL_KEY] = function(event)
if event == SC_WORLD_LOADED and mod_is_loaded() then
activate_mod_raws_changes()
elseif event == SC_WORLD_UNLOADED and mod_is_loaded() then
deactivate_mod_raws_changes()
end
end
end
Is this the proper way to do a mod's auto-run module? Are there best practices that I'm not following?
lighter_green_glass (100)/scripts_modactive
in order to get picked up and loaded.You don't need this part. Once your script is in the correct location, it will load if and only if it is active for the current world.Code: [Select]local function mod_is_loaded()
-- df.global.world.object_loader.object_loader_order_id[] is an array of strings.
-- search it for the ID of our mod. if it exists, the mod is loaded.
return(utils.linear_index(df.global.world.object_loader.object_load_order_id, mod_ID, 'value') ~= nil)
end
You don't need to check for dfhack_flags.module since you've already filtered out the cases where that variable is not true. Also, as mentioned above, you don't need to check if the mod is loaded since being in the correct path will do that for you.Code: [Select]if dfhack_flags.module then
dfhack.onStateChange[GLOBAL_KEY] = function(event)
if event == SC_WORLD_LOADED and mod_is_loaded() then
activate_mod_raws_changes()
elseif event == SC_WORLD_UNLOADED and mod_is_loaded() then
deactivate_mod_raws_changes()
end
end
end
dfhack.onStateChange[GLOBAL_KEY] = function(event)
if event == SC_WORLD_LOADED then
activate_mod_raws_changes()
elseif event == SC_WORLD_UNLOADED then
deactivate_mod_raws_changes()
dfhack.onStateChange[GLOBAL_KEY] = nil
end
end
This was a workaround, because I was experiencing the mod being active. I was pretty sure the mod wasn't being loaded into the new world, but rather that it was still active in the dfhack.onStateChange[] table. I'll put in your patch to remove the onStateChange[], hopefully removing the need for this.QuoteYou don't need this part. Once your script is in the correct location, it will load if and only if it is active for the current world.Code: [Select]local function mod_is_loaded()
-- df.global.world.object_loader.object_loader_order_id[] is an array of strings.
-- search it for the ID of our mod. if it exists, the mod is loaded.
return(utils.linear_index(df.global.world.object_loader.object_load_order_id, mod_ID, 'value') ~= nil)
end
You don't need to check for dfhack_flags.module since you've already filtered out the cases where that variable is not true. Also, as mentioned above, you don't need to check if the mod is loaded since being in the correct path will do that for you.I did know that. That test is in there for development debugging. Like:
if dfhack_flags.module then
dfhack.onStateChange[GLOBAL_KEY] = function(event)
if event == SC_WORLD_LOADED and mod_is_loaded() then
activate_mod_raws_changes()
elseif event == SC_WORLD_UNLOADED and mod_is_loaded() then
deactivate_mod_raws_changes()
end
end
else -- this branch is only for debugging.
print(string.format('%s: manually invoking activate_mod_raws_changes()', script_name))
activate_mod_raws_changes()
end
I have to say, this is a beautifully simple mod. Do you mind if I use it as an example in the modding guide?
If a world with your mod active is loaded a second time, the script will be reloaded as a module before the world loaded event and will have an opportunity to reattach the handler.
I've been thinking about the help text and the enable/disable capability. I certainly understand why those are useful/necessary for standard scripts. But I feel that, if you don't want the mod, don't install it into your world.
Actually, I don't even see the need for the scripts_modactive/internal subdirectory, except maybe for some absolutely massive total-conversion mod. I think most people will just lump all their code into one script.
You have my permission.
IMPORTANT EDIT:If a world with your mod active is loaded a second time, the script will be reloaded as a module before the world loaded event and will have an opportunity to reattach the handler.
This bit is not true (dfhack 50.07-beta2). I'm guessing you've implemented this but not yet had a release that contains it. For now, I'm reverting to NOT unhooking the event handler and using mod_is_loaded().
(https://cdn.discordapp.com/attachments/302956330304667649/1092653280577998890/announcement-showcase.png)Code: ("ann.lua") [Select]--proof of concept of an announcement menu.
local dlg=require("gui.dialogs")
local JUNK={}
local Dang=df.global.world.status.announcements
for ultimate,checked_Ann in pairs(Dang) do
local mono=checked_Ann.text
table.insert(JUNK,{mono,search_key = mono:lower()})
end
dlg.showListPrompt("Announcement list","scroll through the list of Announced alerts",COLOR_WHITE,JUNK,nil,nil,nil,nil,true)
ok so out of boredom I decided to write up a script that grabs all the alerts/reports and shoves them into a list... there was a previous version of this script that grab every report but some folks don't want to sift through combat logs so I made this instead. there was an attempt at adding a filter on it but shrugs.
In general, you can protect yourself from errors with pcall. For example:Yeah, I had a similar structure in the end. But thanks, pcall's been useful elsewhere.Code: [Select]local ok, ret = pcall(function()
-- do something sketchy
end)
but in this case, you'd probably be best off with testing the type of the pointer before trying to access fields that only exist in some types.Code: [Select]local bld = thebuidlinginquestion
if bld:getType() == df.building_type.Workshop then
-- access workshop fields
elseif bld::getType() == df.building_type.Stockpile then
-- access stockpile fields
end
local function create_and_link_construction(pos, item, top_of_wall)
local construction = df.construction:new()
utils.assign(construction.pos, pos)
construction.item_type = item:getType()
construction.item_subtype = item:getSubtype()
construction.mat_type = item:getMaterial()
construction.mat_index = item:getMaterialIndex()
construction.flags.top_of_wall = top_of_wall
construction.flags.no_build_item = not top_of_wall
construction.original_tile = get_original_tiletype(pos)
utils.insert_sorted(df.global.world.constructions, construction,
'pos', pos_cmp)
end
Cannot write field: null and autovivify not requested.Can you provide an example of the code you're using that triggers this error? There are a couple ways I could interpret your last statement, and I'm not sure which one you mean.
I guess I could just create a new entry and copy the other fields and insert a non-null value there, and remove the old one, but is there a better way?
object.some_construction_field = {
item_type = df.item_type.Foo,
item_subtype = -1,
-- more stuff
}
where "some_construction_field" is normally a pointer to an object, that is one case I know of that could cause the error you saw. In order for this to work, you can add a special "new=true" entry to the table, which indicates to our C++/Lua wrapper that a new object should be created based on the type of the field you're assigning the table to, like this:object.some_construction_field = {
new = true,
item_type = df.item_type.Foo,
item_subtype = -1,
-- more stuff
}
This process is referred to as "auto-vivification" in our Lua API docs: https://docs.dfhack.org/en/stable/docs/dev/Lua%20API.html#recursive-table-assignmentobject.some_construction_field = {
new = df.construction,
item_type = df.item_type.Foo,
item_subtype = -1,
-- more stuff
}
local help = [====[
======================
]====]
utils = require('utils')
--debug.setmetatable(nil, {__index = function()end})
function findworkorder(workorder_id)
-- does something like this exist? df.global.world.manager_orders.find(workorder_id)
for _, mgr_order in ipairs(df.global.world.manager_orders) do
if mgr_order.id == workorder_id then
return mgr_order
end
end
qerror('not found')
end
function processworkorder(workorder_id)
--df.global.world.manager_orders
local workorder
local job
local mode = 1 -- for quickly switching between different approaches to the problem
local workorder_jobitems = {}
if pcall(findworkorder, workorder_id) then
workorder = findworkorder(workorder_id)
else
qerror('Work order not found')
end
job = dfhack.gui.getSelectedJob(true) or qerror('No job selected')
print("test")
if mode == 1 then
table.remove(workorder,'items')
workorder['items'] = workorder_jobitems
workorder.items = utils.clone(job.job_items, true)
else
--workorder.items = {}
workorder['items'] = workorder_jobitems
for _, copy_job_item in ipairs(job.job_items) do
--workorder.items:insert('#', copy_job_item)
table.insert(workorder.items, copy_job_item)
end
end
end
-- main script
local opt = ...
if opt and opt ~= "" then
if tonumber(opt) then
if tonumber(opt) >= 0 then
processworkorder(math.floor(tonumber(opt)))
else
print("The number should be non-negative.")
end
return
else
print(help)
end
else
print("No work order provided, here's a list of existing work orders:")
for _, mgr_order in ipairs(df.global.world.manager_orders) do
if mgr_order.reaction_name ~= nil and mgr_order.reaction_name ~= "" then
print(mgr_order.id .. ": " .. df.job_type[mgr_order.job_type] .. " (" .. mgr_order.reaction_name .. ")")
else
print(mgr_order.id .. ": " .. df.job_type[mgr_order.job_type])
end
end
end
new_workorder.items:insert({
new = true,
field1 = value,
-- ...
})
Are you essentially trying to get gui/workorder-details working for the Steam version? Or is this still for 0.47.05 and the existing gui/workorder-details meets your needs?Nah, I'm in no hurry to try 0.50. I didn't get workorder-details to work with 0.47.05-r5, but it appears to work, and function as needed with r8. So, the problem I had with my Dwarf Fortress experience has been resolved (thanks!), it was more about me trying to learn why the approach I took with DFHack didn't work, and lethosor cleared that up. Thanks!
our in-game Dwarf Therapist-like interface needs an entirely new UI. Existing functionality (and maybe design elements) might be salvageable from the old plugin.
Integrate with or replace work details?
for _,b in ipairs(df.global.world.buildings.all) do for key,_ in pairs(b) do if key == 'contained_items' then for j,v in ipairs(b.contained_items) do i=v.item; f=i.flags; if v.use_mode == 0 and f.in_building and not f.trader then print(b._type,j,dfhack.items.getDescription(i,0)); end; end; end; end; end;
b=df.global.world.buildings.other.TRADE_DEPOT[0]; for j,v in ipairs(b.contained_items) do i=v.item; if v.use_mode == 0 and not i.flags.foreign and not i.flags.trader then i.flags.in_building = true; end; end;
How can I make a DF shotcut that starts the game without DFHack?We don't support this currently for the Windows build of DF (i.e. the only one for v50). You could probably write a custom script that renames the necessary DLLs, although then you would need to write another script that renames them back if you want to run with DFHack. For 50.08-r1 and newer (again, on Windows) you would want to rename "dfhooks.dll" to something else to disable DFHack.
if the dwarves could move the embark wagon after they embark
...05-r11\Dwarf Fortress 0.47.05/hack/scripts/unsuspend.lua:130: attempt to index a nil value (local 'bld')
if the dwarves could move the embark wagon after they embark
I believe all the related state is known, so yes. I think moving buildings is within the realm of possibility. In fact, moving the wagon is already implemented for deep-embark. The logic would just need refactoring. Could you file a feature request at https://github.com/DFHack/dfhack/issues ?
...05-r11\Dwarf Fortress 0.47.05/hack/scripts/unsuspend.lua:130: attempt to index a nil value (local 'bld')
I believe this bug was fixed in January. We're unlikely to do another release for DF-0.47.05, but you can manually apply the change to the unsuspend.lua in the hack/scripts directory. The code change is detailed here: https://github.com/DFHack/scripts/commit/49b8fde00a441c21b9e65fbab472f33f36823094
There were certainly a lot of overlay and UI changes at about that time. Which other features are you looking for but can't find? There should be nothing missing, just moved.
I’ve been thinking…(https://cdn.discordapp.com/attachments/302956330304667649/1107144843437801472/wagonmove-script-showcase.gif)
I’ve alway’s though that it’s be nice if the dwarves could move the embark wagon after they embark. I know that this would require several new systems that are not in the game right now.
In the meantime, however, is there a DFHack command that will teleport the embark wagon (and it’s contents) to a chosen position on the map? It’d make things a lot faster if my dwarves didn’t have to walk so long when transferring items from the wagon to the initial stockpile.
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function movewagon(curx,cury,curz)
for de,oe in pairs(df.global.world.buildings.other.WAGON) do
if oe.name == 'Nomad' then
oe.centerx=curx
oe.x1=curx-1
oe.x2=curx+1
oe.centery=cury
oe.y1=cury-1
oe.y2=cury+1
oe.z=curz
end
end
end
movewagon(getxyz())
this script requires you to name the wagon Nomad and use the keyboard cursor for it to work
I’ve been thinking…(https://cdn.discordapp.com/attachments/302956330304667649/1107144843437801472/wagonmove-script-showcase.gif)
I’ve alway’s though that it’s be nice if the dwarves could move the embark wagon after they embark. I know that this would require several new systems that are not in the game right now.
In the meantime, however, is there a DFHack command that will teleport the embark wagon (and it’s contents) to a chosen position on the map? It’d make things a lot faster if my dwarves didn’t have to walk so long when transferring items from the wagon to the initial stockpile.
so like this?
currently working on a series of nomad focus scripts that converts the wagon as the main building for storing items and moving around the map as having it be stationary would lead to it being lost when I unload the map chunk that has it.Code: ("wagonmove.lua") [Select]function getxyz() -- this will return pointers x,y and z coordinates.
this script requires you to name the wagon Nomad and use the keyboard cursor for it to work
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function movewagon(curx,cury,curz)
for de,oe in pairs(df.global.world.buildings.other.WAGON) do
if oe.name == 'Nomad' then
oe.centerx=curx
oe.x1=curx-1
oe.x2=curx+1
oe.centery=cury
oe.y1=cury-1
oe.y2=cury+1
oe.z=curz
end
end
end
movewagon(getxyz())
so far in my nomad runs the contents in the wagon stayed when loading and unloading chunks of the map.
though if you unretire the most of the contents would be out of the wagon so moving it would just be moving the main standing idle location.
usually I have it like that as a sanity check for the code to not scoop up any other wagons if I move to a location that has one.so far in my nomad runs the contents in the wagon stayed when loading and unloading chunks of the map.
though if you unretire the most of the contents would be out of the wagon so moving it would just be moving the main standing idle location.
Is there any way to make it so that you don’t need to name the wagon?
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function movewagon(curx,cury,curz)
for de,oe in pairs(df.global.world.buildings.other.WAGON) do
oe.centerx=curx
oe.x1=curx-1
oe.x2=curx+1
oe.centery=cury
oe.y1=cury-1
oe.y2=cury+1
oe.z=curz
end
end
movewagon(getxyz())
Don't you need to update the building occupancy on tiles, or something?
Also, I think you can do something like "copyall(df.global.cursor)" instead of using a custom function. (Might be "pos2xyz" otherwise.)
“Building occupancy”?
--script using warmist spellbook code to make a cheap gui for expanding and contracting a player fort.
-- this script is pretty dangerous in that it reveals the expanded map chunk, and when contracting it culls anyone in that map chunk.
-- so far probably best to use this with the void fort scripts as it allows one to expand and explore the world while keeping your site.
--oh warning if you're about to collapse a map chunk make sure to clear any stockpiles and civ zones you made in that map chunk.
--oh and don't retire unless you're back to the original fort embark size or the game will corrupt that fort and or crash.
--also it will take a while for any results to happen. I have no idea on speeding up the process.
--if you use this for some nomad playstyle best learn how to haul goods across map and burrowing folks.
--as you might lose folks while traveling.
local dlg=require("gui.dialogs")
function GoN()
local Site=df.global.plotinfo.main.fortress_site
local Nor=Site.global_min_y-1
Site.global_min_y=Nor
end
function NoN()
local Site=df.global.plotinfo.main.fortress_site
local Nor=Site.global_min_y+1
Site.global_min_y=Nor
end
function GoS()
local Site=df.global.plotinfo.main.fortress_site
local Sor=Site.global_max_y+1
Site.global_max_y=Sor
end
function NoS()
local Site=df.global.plotinfo.main.fortress_site
local Sor=Site.global_max_y-1
Site.global_max_y=Sor
end
function GoE()
local Site=df.global.plotinfo.main.fortress_site
local Eor=Site.global_max_x+1
Site.global_max_x=Eor
end
function NoE()
local Site=df.global.plotinfo.main.fortress_site
local Eor=Site.global_max_x-1
Site.global_max_x=Eor
end
function GoW()
local Site=df.global.plotinfo.main.fortress_site
local Wor=Site.global_min_x-1
Site.global_min_x=Wor
end
function NoW()
local Site=df.global.plotinfo.main.fortress_site
local Wor=Site.global_min_x+1
Site.global_min_x=Wor
end
function Go()
for k,v in pairs(df.global.world.armies.all) do
if v.flags[0]== true then
local Nor=v.pos.y-1
v.pos.y=Nor
end
end
end
function doNothing()
print("doing nothing real good but here have a site")
--require("plugins.dfusion.adv_tools").addSite(nil,nil,nil,nil,nil,nil,df.global.world.units.active[0].civ_id)
end
function doNothing2()
dlg.showMessage("Travel-Hack","Bypassing Message to access FastTravel")
df.global.ui_advmode.message=""
--require("plugins.dfusion.adv_tools").addSite(nil,nil,nil,nil,nil,nil,df.global.world.units.active[0].civ_id)
end
function greetAndStuff()
dlg.showMessage("Greetings", "Seasons greatings and lols")
end
MOVEMENT_KEYS = {
A_CARE_MOVE_N = { 0, -1, 0 }, A_CARE_MOVE_S = { 0, 1, 0 },
A_CARE_MOVE_W = { -1, 0, 0 }, A_CARE_MOVE_E = { 1, 0, 0 },
A_CARE_MOVE_NW = { -1, -1, 0 }, A_CARE_MOVE_NE = { 1, -1, 0 },
A_CARE_MOVE_SW = { -1, 1, 0 }, A_CARE_MOVE_SE = { 1, 1, 0 },
--[[A_MOVE_N = { 0, -1, 0 }, A_MOVE_S = { 0, 1, 0 },
A_MOVE_W = { -1, 0, 0 }, A_MOVE_E = { 1, 0, 0 },
A_MOVE_NW = { -1, -1, 0 }, A_MOVE_NE = { 1, -1, 0 },
A_MOVE_SW = { -1, 1, 0 }, A_MOVE_SE = { 1, 1, 0 },--]]
A_CUSTOM_CTRL_D = { 0, 0, -1 },
A_CUSTOM_CTRL_E = { 0, 0, 1 },
CURSOR_UP_Z_AUX = { 0, 0, 1 }, CURSOR_DOWN_Z_AUX = { 0, 0, -1 },
A_MOVE_SAME_SQUARE={0,0,0},
SELECT={0,0,0},
}
ALLOWED_KEYS={
A_MOVE_N=true,A_MOVE_S=true,A_MOVE_W=true,A_MOVE_E=true,A_MOVE_NW=true,
A_MOVE_NE=true,A_MOVE_SW=true,A_MOVE_SE=true,A_STANCE=true,SELECT=true,A_MOVE_DOWN_AUX=true,
A_MOVE_UP_AUX=true,A_LOOK=true,CURSOR_DOWN=true,CURSOR_UP=true,CURSOR_LEFT=true,CURSOR_RIGHT=true,
CURSOR_UPLEFT=true,CURSOR_UPRIGHT=true,CURSOR_DOWNLEFT=true,CURSOR_DOWNRIGHT=true,A_CLEAR_ANNOUNCEMENTS=true,
CURSOR_UP_Z=true,CURSOR_DOWN_Z=true,
}
listofspells={
{text="nothing", spell=doNothing,icon='*'},
{text="expand: North+", spell=GoN,key="CUSTOM_W"},
{text="expand: South+", spell=GoS,key="CUSTOM_S"},
{text="expand: East+", spell=GoE,key="CUSTOM_D"},
{text="expand: West+", spell=GoW,key="CUSTOM_A"},
{text="Contract: North-", spell=NoN,key="CUSTOM_W"},
{text="Contract: South-", spell=NoS,key="CUSTOM_S"},
{text="Contract: East-", spell=NoE,key="CUSTOM_D"},
{text="Contract: West-", spell=NoW,key="CUSTOM_A"},
}
dlg.showListPrompt("Directions","Choze Direct",nil, listofspells,function(index,choice) choice.spell() end)
:lua df.global.world.status.popups:resize(0)
hmm I guess one could write a lua script that turns off the announcement pop up for that so you can ignore themgui/gm-editor df.global.plotinfo.main.fortress_site
then messing with the global min/max x and y coords“Building occupancy”?
https://github.com/DFHack/df-structures/blob/29801b0043b884d04a7e63a995258ebf2b67cbaf/df.map.xml#L114
Uh, I recently started using autodump for a bunch of feeder stockpiles surrounding a quantum stockpile... and the problem I have is that occasionally the wheelbarrow assigned to the stockpile gets dumped. Dunno why, but I guess it could be because a hauling job involving the wheelbarrow was interrupted and the wheelbarrow had to be hauled back.
@xzaxza, so to clarify, you're saying that the stockpile autodump feature (presumably in 0.47.05 since that feature is still in an open PR for v50) is sometimes marking wheelbarrows that are ostensibly owned by the stockpile? Yeah, I'm not sure what's going on there. A github issue would be appreciated so I can remember to look into it when I finish https://github.com/DFHack/dfhack/pull/3285Yes, I'm talking about the stockpile integration functionality of autodump, not the magical item mover/destroyer part. I made one: https://github.com/DFHack/dfhack/issues/3430
Hmm, I didn't notice it before I enabled autodump. I'm 27 years and 6000+ stones in, and all that time before enabling autodump I dumped all the stones manually, and I didn't really see wheelbarrows marked for dumping and/or them ending up forbidden in quantum stockpiles. Of course it's always possible the root cause is something else, but it looks like autodump to me.Uh, I recently started using autodump for a bunch of feeder stockpiles surrounding a quantum stockpile... and the problem I have is that occasionally the wheelbarrow assigned to the stockpile gets dumped. Dunno why, but I guess it could be because a hauling job involving the wheelbarrow was interrupted and the wheelbarrow had to be hauled back.
I don't think that autodump is responsible here. At least this happens in the base game without autodump (or even dfhack). All evidence I've seen points to job interruption causing the behaviour, as you suspect. Unfortunately once it starts the wheelbarrow often gets stuck in a cycle of being dumped into the quantum stockpile and then returned to the feeder stockpile endlessly. (The way to break the cycle is to temporarily forbid the wheelbarrow so that it's stockpile assignement gets reset.)
Although I guess it's possible that autodump could be triggering whatever in the base game causes the problem?
I though garbage dumps and stockpiles were two separate things, and dumping (including autodumping) moved things to the closest garbage dump regardless of where those items were. Isn’t this just regular behavior?Yeah, they're two separate thinks, but I have a following setup:
F F F
F Q F
F F F
Where the F's are a stone stockpile, in this case set to receive only one of the main layer stones on the map, and Q is a 1x1 stockpile and also a dump zone. So, the process is essentially this:function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function movewagon(curx,cury,curz)
for de,oe in pairs(df.global.world.buildings.other.WAGON) do
if oe.name == 'Nomad' then
oe.centerx=curx
oe.x1=curx-1
oe.x2=curx+1
oe.centery=cury
oe.y1=cury-1
oe.y2=cury+1
oe.z=curz
for Del,ete in pairs(oe.contained_items) do
ete.item.pos.x=oe.centerx
ete.item.pos.y=oe.centery
ete.item.pos.z=oe.z
end
end
end
end
movewagon(getxyz())
this script requires the mining cursor to function and this new version of the wagon move will now update the positions of the items store on the wagon.local contain={}
function stuffwagon(curx,cury,curz)
for k,v in pairs(df.global.world.buildings.other.WORKSHOP_ANY) do
for con,tain in pairs(v.contained_items) do
for de,oe in pairs(df.global.world.buildings.other.WAGON) do
if oe.name == 'Nomad' then
if tain.use_mode==0 then
oe.contained_items:insert("#",{new=true,item=v.contained_items[con].item,use_mode=0})
clearitems(oe,tain,v.contained_items)
end
end
end
end
end
for k,v in pairs(df.global.world.buildings.other.FURNACE_ANY) do
for con,tain in pairs(v.contained_items) do
for de,oe in pairs(df.global.world.buildings.other.WAGON) do
if oe.name == 'Nomad' then
if tain.use_mode==0 then
oe.contained_items:insert("#",{new=true,item=v.contained_items[con].item,use_mode=0})
clearitems(oe,tain,v.contained_items)
end
end
end
end
end
end
function clearitems(oe,tain,Vcon)
for Del,ete in pairs(oe.contained_items) do
for De2,ee in pairs(Vcon) do
if ete.item.id== ee.item.id then
Vcon:erase(De2)
end
end
end
end
stuffwagon()
--this script uses old dfhack code to store items on to the wagon via keyboard cursors.
--probably more stable than the other scripts that store items onto the wagon but requires it to be on the floor.
local contain={}
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function stuffwagon(curx,cury,curz)
for de,oe in pairs(df.global.world.buildings.other.WAGON) do
if oe.name == 'Nomad' then
local vector=df.global.world.items.all -- load all items
for i = 0, #vector-1 do -- look into all items offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==curx and cy==cury and cz==curz then --compare them
dfhack.items.moveToBuilding(vector[i],oe) --return index
end
end
end
end
end
stuffwagon(getxyz())
In short, emigration.lua worked in past, but no longer works. I fixed it (https://github.com/wsfsbvchr/dfhack-utils/blob/main/emigration-fix.lua). Do you want it?
--old old script made to update the hist_fig_less migrants and second citizens gain from messing with animal entity tokens
--this script uses code from dfhack's spawn unit code mostly the histfig data stuff and I guess the nemesis record stuff looking at it now.
-- this is a run to patch citizens script and not automated
-- uhh stability warning on what it does to histfigs and any future issues that comes from it down the line.
ui=df.global.plotinfo
local function allocateNewChunk(hist_entity)
hist_entity.save_file_id = df.global.unit_chunk_next_id
df.global.unit_chunk_next_id = df.global.unit_chunk_next_id+1
hist_entity.next_member_idx = 0
print("allocating chunk:",hist_entity.save_file_id)
end
local function allocateIds(nemesis_record,hist_entity)
if hist_entity.next_member_idx == 100 then
allocateNewChunk(hist_entity)
end
nemesis_record.save_file_id = hist_entity.save_file_id
nemesis_record.member_idx = hist_entity.next_member_idx
hist_entity.next_member_idx = hist_entity.next_member_idx+1
end
function createFigure(unit,he,he_group)
local hf = df.historical_figure:new()
hf.id = df.global.hist_figure_next_id
df.global.hist_figure_next_id = df.global.hist_figure_next_id+1
hf.unit_id = unit.id
hf.nemesis_id = -1
hf.race = unit.race
hf.caste = unit.caste
hf.profession = unit.profession
hf.sex = unit.sex
hf.name:assign(unit.name)
hf.appeared_year = df.global.cur_year
hf.born_year = unit.birth_year
hf.born_seconds = unit.birth_time
hf.curse_year = unit.curse_year
hf.curse_seconds = unit.curse_time
hf.birth_year_bias = unit.birth_year_bias
hf.birth_time_bias = unit.birth_time_bias
hf.old_year = unit.old_year
hf.old_seconds = unit.old_time
hf.died_year = -1
hf.died_seconds = -1
hf.civ_id = unit.civ_id
hf.population_id = unit.population_id
hf.breed_id = -1
hf.cultural_identity = unit.cultural_identity
hf.family_head_id = -1
df.global.world.history.figures:insert("#", hf)
hf.info = df.historical_figure_info:new()
hf.info.whereabouts = df.historical_figure_info.T_whereabouts:new()
hf.info.whereabouts.death_condition_parameter_1 = -1
hf.info.whereabouts.death_condition_parameter_2 = -1
-- set values that seem related to state and do event
--change_state(hf, dfg.plotinfo.site_id, region_pos)
--let's skip skills for now
--local skills = df.historical_figure_info.T_skills:new() -- skills snap shot
-- ...
-- note that innate skills are automaticaly set by DF
hf.info.skills = {new=true}
if he then
he.histfig_ids:insert('#', hf.id)
he.hist_figures:insert('#', hf)
hf.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=unit.civ_id,link_strength=100})
--add entity event
local hf_event_id = df.global.hist_event_next_id
df.global.hist_event_next_id = df.global.hist_event_next_id+1
df.global.world.history.events:insert("#",{new=df.history_event_add_hf_entity_linkst,year=unit.birth_year,
seconds=unit.birth_time,id=hf_event_id,civ=hf.civ_id,histfig=hf.id,link_type=0})
end
if he_group and he_group ~= he then
he_group.histfig_ids:insert('#', hf.id)
he_group.hist_figures:insert('#', hf)
hf.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=he_group.id,link_strength=100})
end
local soul = unit.status.current_soul
if soul then
hf.orientation_flags:assign(soul.orientation_flags)
end
unit.flags1.important_historical_figure = true
unit.flags2.important_historical_figure = true
unit.hist_figure_id = hf.id
unit.hist_figure_id2 = hf.id
return hf
end
function createNemesis(unit,civ_id,group_id)
local id = df.global.nemesis_next_id
local nem = df.nemesis_record:new()
nem.id = id
nem.unit_id = unit.id
nem.unit = unit
nem.flags:resize(31)
nem.unk10 = -1
nem.unk11 = -1
nem.unk12 = -1
df.global.world.nemesis.all:insert("#",nem)
df.global.nemesis_next_id = id+1
unit.general_refs:insert("#",{new = df.general_ref_is_nemesisst, nemesis_id = id})
nem.save_file_id = -1
local he
if civ_id and civ_id ~= -1 then
he = df.historical_entity.find(civ_id)
he.nemesis_ids:insert("#",id)
he.nemesis:insert("#",nem)
allocateIds(nem,he)
end
local he_group
if group_id and group_id ~= -1 then
he_group = df.historical_entity.find(group_id)
end
if he_group then
he_group.nemesis_ids:insert("#",id)
he_group.nemesis:insert("#",nem)
end
nem.figure = unit.hist_figure_id ~= -1 and df.historical_figure.find(unit.hist_figure_id) or createFigure(unit,he,he_group) -- the histfig check is there just in case this function is called by another script to create nemesis data for a historical figure which somehow lacks it
nem.figure.nemesis_id = id
return nem
end
function sigh()
for k,v in pairs(df.global.world.units.active) do
local HF=df.global.world.history.figures
local HO=df.global.plotinfo.civ_id
local EN=df.global.world.entities.all[HO]
local HP=df.global.plotinfo.group_id
if v.hist_figure_id==-1 and v.civ_id==df.global.plotinfo.civ_id then
createNemesis(v,HO,HP)
--createFigure(v,EN,HP)
print(k)
end
end
end
for k,v in pairs(df.global.world.units.active) do
local HFN=df.global.world.history.figures
local HN=df.global.plotinfo.civ_id
sigh()
if v.hist_figure_id==-1 then break else
for c,q in pairs(HFN[v.hist_figure_id].entity_links) do
if q.entity_id==df.global.plotinfo.civ_id and v.population_id==-1 and q.entity_id~=df.global.plotinfo.group_id then
print(v.hist_figure_id)
HFN[v.hist_figure_id].entity_links:insert("#",{new=df.histfig_entity_link_memberst})
HFN[v.hist_figure_id].entity_links[c].entity_id=df.global.plotinfo.group_id
HFN[v.hist_figure_id].entity_links[c].link_strength=100
end
end
end
end
local contain={}
function unretirewagon(curx,cury,curz)
for de,oe in pairs(df.global.world.buildings.other.WAGON) do
if oe.name == 'Nomad' then
local vector=df.global.world.items.all -- load all items
for i = 0, #vector-1 do -- look into all items offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==oe.centerx and cy==oe.centery and cz==oe.z then --compare them
dfhack.items.moveToBuilding(vector[i],oe) --return index
end
end
end
end
end
unretirewagon()
In short, emigration.lua worked in past, but no longer works. I fixed it (https://github.com/wsfsbvchr/dfhack-utils/blob/main/emigration-fix.lua). Do you want it?
Please! Could you open a PR against the scripts repo? https://github.com/DFHack/scripts/pulls
We can discuss it over the code review.
dfhack.gui.autoDFAnnouncement(df.announcement_type.cancel_job, unit.pos, message, COLOR_YELLOW)
But it opens the embark "Strike the earth" message. Maybe I got the type wrong.
--this is a script that is a modified version of the recruit script as this doesn't randomly select a historical figure but choose one out of a list.
--for this to work you need to send someone off on a mission probably a safe one like exploring ruins and in the middle of the mission run this script.
-- it might take a few tries to see if it works but if you see two printed out messages showcasing the army id and the mission report info then it might have worked.
--oh it also reuses the campboat script for list gui functions
local dlg=require("gui.dialogs")
function teleportboatnames2()
local Ark={}
for k,v in pairs(df.global.world.nemesis.all) do
BoaName=dfhack.TranslateName(v.figure.name)
table.insert (Ark,{dfhack.TranslateName(v.figure.name).." "..v.figure.name.nickname,nil,v,search_key = BoaName:lower()})
end
local f=function(Name,C)
OrderRecruit(C[3])
end
dlg.showListPrompt("list of Nemesis havers","Select Being(s) to settle here",COLOR_WHITE,Ark,f,nil,nil,true)
end
function OrderRecruit(Nemes)
for de,oe in pairs(df.global.world.army_controllers.all) do
if oe.mission_report == nil then else
print ( de,oe.mission_report.title)
for e,o in pairs(df.global.world.armies.all) do
if oe.id == o.controller_id then
print (e)
local forv=df.global.world.armies.all[e].members[0]
df.global.world.armies.all[e].members:insert("#",{new=true,nemesis_id=Nemes.id,
stored_fat = forv.stored_fat,
unk_2c= forv.unk_2c,
unk_28= forv.unk_28,
unk_1= forv.unk_1,
unk_30= forv.unk_30,
unk_34= forv.unk_34})
end
end
end
end
end
teleportboatnames2()
ok so here's a modified version of the old recruit script for targeting one nemesis having hist fig in the world. --modification of the recruit script that uses the squad option in armies to pad out your army troops and to summon 30 unknown no name units at your enemies and possibly sending them back to you.
--these are inhabitants beings with no historical figures so if you got a script that could give them one or retire you're probably going to have some troubles getting them army ready.
--this script requires the squad to be mid mission to work if you don't see 2 printed info then it didn't stick.
function RecruitInhabit(Nemes)
for de,oe in pairs(df.global.world.army_controllers.all) do
if oe.mission_report == nil then else
print ( de,oe.mission_report.title)
for e,o in pairs(df.global.world.armies.all) do
if oe.id == o.controller_id then
print (e)
local forv=df.global.world.armies.all[e].members[0]
local long=df.global.world.nemesis.all[forv.nemesis_id].unit.population_id
df.global.world.armies.all[e].squads:insert("#",{new=true,count=30,race=df.global.plotinfo.race_id,population_id=long,entity_id=df.global.plotinfo.civ_id})
end
end
end
end
end
RecruitInhabit()
edit ok so here's another script that uses a modified recruit script to instead fill out the army's squad data with inhabitants... this holds potential for putting any creature in the game and any creature with any interaction.--script using warmist spellbook code to make a cheap gui for expanding and contracting a player fort.
-- this script is pretty dangerous in that it reveals the expanded map chunk, and when contracting it culls anyone in that map chunk.
-- so far probably best to use this with the void fort scripts as it allows one to expand and explore the world while keeping your site.
--oh warning if you're about to collapse a map chunk make sure to clear any stockpiles and civ zones you made in that map chunk.
--oh and don't retire unless you're back to the original fort embark size or the game will corrupt that fort and or crash.
--also it will take a while for any results to happen. I have no idea on speeding up the process.
--:edit 7/7/2023 this version of nomad script speeds up the process instantly due to messing with cur_season_ticks
--if you use this for some nomad playstyle best learn how to haul goods across map and burrowing folks.
--as you might lose folks while traveling.
local dlg=require("gui.dialogs")
function GoN()
local Site=df.global.plotinfo.main.fortress_site
local Nor=Site.global_min_y-1
Site.global_min_y=Nor
df.global.cur_season_tick=3999
end
function NoN()
local Site=df.global.plotinfo.main.fortress_site
local Nor=Site.global_min_y+1
Site.global_min_y=Nor
df.global.cur_season_tick=3999
end
function GoS()
local Site=df.global.plotinfo.main.fortress_site
local Sor=Site.global_max_y+1
Site.global_max_y=Sor
df.global.cur_season_tick=3999
end
function NoS()
local Site=df.global.plotinfo.main.fortress_site
local Sor=Site.global_max_y-1
Site.global_max_y=Sor
df.global.cur_season_tick=3999
end
function GoE()
local Site=df.global.plotinfo.main.fortress_site
local Eor=Site.global_max_x+1
Site.global_max_x=Eor
df.global.cur_season_tick=3999
end
function NoE()
local Site=df.global.plotinfo.main.fortress_site
local Eor=Site.global_max_x-1
Site.global_max_x=Eor
df.global.cur_season_tick=3999
end
function GoW()
local Site=df.global.plotinfo.main.fortress_site
local Wor=Site.global_min_x-1
Site.global_min_x=Wor
df.global.cur_season_tick=3999
end
function NoW()
local Site=df.global.plotinfo.main.fortress_site
local Wor=Site.global_min_x+1
Site.global_min_x=Wor
df.global.cur_season_tick=3999
end
function Go()
for k,v in pairs(df.global.world.armies.all) do
if v.flags[0]== true then
local Nor=v.pos.y-1
v.pos.y=Nor
df.global.cur_season_tick=3999
end
end
end
function doNothing()
print("doing nothing real good but here have a site")
--require("plugins.dfusion.adv_tools").addSite(nil,nil,nil,nil,nil,nil,df.global.world.units.active[0].civ_id)
end
function doNothing2()
dlg.showMessage("Travel-Hack","Bypassing Message to access FastTravel")
df.global.ui_advmode.message=""
--require("plugins.dfusion.adv_tools").addSite(nil,nil,nil,nil,nil,nil,df.global.world.units.active[0].civ_id)
end
function greetAndStuff()
dlg.showMessage("Greetings", "Seasons greatings and lols")
end
MOVEMENT_KEYS = {
A_CARE_MOVE_N = { 0, -1, 0 }, A_CARE_MOVE_S = { 0, 1, 0 },
A_CARE_MOVE_W = { -1, 0, 0 }, A_CARE_MOVE_E = { 1, 0, 0 },
A_CARE_MOVE_NW = { -1, -1, 0 }, A_CARE_MOVE_NE = { 1, -1, 0 },
A_CARE_MOVE_SW = { -1, 1, 0 }, A_CARE_MOVE_SE = { 1, 1, 0 },
--[[A_MOVE_N = { 0, -1, 0 }, A_MOVE_S = { 0, 1, 0 },
A_MOVE_W = { -1, 0, 0 }, A_MOVE_E = { 1, 0, 0 },
A_MOVE_NW = { -1, -1, 0 }, A_MOVE_NE = { 1, -1, 0 },
A_MOVE_SW = { -1, 1, 0 }, A_MOVE_SE = { 1, 1, 0 },--]]
A_CUSTOM_CTRL_D = { 0, 0, -1 },
A_CUSTOM_CTRL_E = { 0, 0, 1 },
CURSOR_UP_Z_AUX = { 0, 0, 1 }, CURSOR_DOWN_Z_AUX = { 0, 0, -1 },
A_MOVE_SAME_SQUARE={0,0,0},
SELECT={0,0,0},
}
ALLOWED_KEYS={
A_MOVE_N=true,A_MOVE_S=true,A_MOVE_W=true,A_MOVE_E=true,A_MOVE_NW=true,
A_MOVE_NE=true,A_MOVE_SW=true,A_MOVE_SE=true,A_STANCE=true,SELECT=true,A_MOVE_DOWN_AUX=true,
A_MOVE_UP_AUX=true,A_LOOK=true,CURSOR_DOWN=true,CURSOR_UP=true,CURSOR_LEFT=true,CURSOR_RIGHT=true,
CURSOR_UPLEFT=true,CURSOR_UPRIGHT=true,CURSOR_DOWNLEFT=true,CURSOR_DOWNRIGHT=true,A_CLEAR_ANNOUNCEMENTS=true,
CURSOR_UP_Z=true,CURSOR_DOWN_Z=true,
}
listofspells={
{text="nothing", spell=doNothing,icon='*'},
{text="expand: North+", spell=GoN,key="CUSTOM_W"},
{text="expand: South+", spell=GoS,key="CUSTOM_S"},
{text="expand: East+", spell=GoE,key="CUSTOM_D"},
{text="expand: West+", spell=GoW,key="CUSTOM_A"},
{text="Contract: North-", spell=NoN,key="CUSTOM_T"},
{text="Contract: South-", spell=NoS,key="CUSTOM_G"},
{text="Contract: East-", spell=NoE,key="CUSTOM_H"},
{text="Contract: West-", spell=NoW,key="CUSTOM_F"},
}
dlg.showListPrompt("Directions","Choze Direct",nil, listofspells,function(index,choice) choice.spell() end)
--keyboard cursor that uses the widget of gui/liquids as I don't know how to write widgets
--proof of concept for adding an extra keyboard cursor to df50 that doesn't rely on mining tools.
local gui = require('gui')
local guidm = require('gui.dwarfmode')
local widgets = require('gui.widgets')
local SpawnLiquidCursor = {
[df.tile_liquid.Water] = dfhack.screen.findGraphicsTile('MINING_INDICATORS', 0, 0),
[df.tile_liquid.Magma] = dfhack.screen.findGraphicsTile('MINING_INDICATORS', 1, 0),
[df.tiletype.RiverSource] = dfhack.screen.findGraphicsTile('LIQUIDS', 0, 0),
}
SpawnLiquid = defclass(SpawnLiquid, widgets.Window)
SpawnLiquid.ATTRS {
frame_title='Cursor Keyboard menu',
frame={b = 4, r = 4, w = 50, h = 12},
}
function SpawnLiquid:init()
self.type = df.tile_liquid.Water
self.tile = SpawnLiquidCursor[self.type]
self:addviews{
widgets.Label{
frame = {l = 0, t = 0},
text = {{ text = self:callback('getLabel') }}
},
widgets.HotkeyLabel{
frame = {l = 0, b = 1},
label = 'does nothing',
auto_width = true,
key = 'KEYBOARD_CURSOR_LEFT',
on_activate = self:callback('moveleft'),
disabled = function() return self.level == 1 end
},
widgets.HotkeyLabel{
frame = { l = 19, b = 1},
label = 'also Does nothing',
auto_width = true,
key = 'KEYBOARD_CURSOR_RIGHT',
on_activate = self:callback('moveright'),
disabled = function() return self.level == 7 end
},
widgets.HotkeyLabel{
frame = { l = 19, b = 1},
label = 'also Does nothing',
auto_width = true,
key = 'KEYBOARD_CURSOR_UP',
on_activate = self:callback('moveup'),
disabled = function() return self.level == 7 end
},
widgets.HotkeyLabel{
frame = { l = 19, b = 1},
label = 'also Does nothing',
auto_width = true,
key = 'KEYBOARD_CURSOR_DOWN',
on_activate = self:callback('movedown'),
disabled = function() return self.level == 7 end
},
widgets.CycleHotkeyLabel{
frame = {l = 0, b = 2},
label = 'cursor color:',
auto_width = true,
key = 'CUSTOM_Q',
options = {
{ label = "Water", value = df.tile_liquid.Water, pen = COLOR_CYAN },
{ label = "Magma", value = df.tile_liquid.Magma, pen = COLOR_RED },
{ label = "River", value = df.tiletype.RiverSource, pen = COLOR_BLUE },
},
initial_option = 0,
on_change = function(new, _)
self.type = new
self.tile = SpawnLiquidCursor[new]
end,
},
}
end
-- TODO: More reactive label dependant on options selected.
function SpawnLiquid:getLabel()
return ([[Click on a tile to spawn a %s/7 level of %s]]):format(
self.level,
self.type == 0 and "Water" or self.type == 1 and "Magma" or "River"
)
end
function SpawnLiquid:getLiquidLevel(position)
local tile = dfhack.maps.getTileFlags(position)
if self.mode == SpawnLiquidMode.ADD then
return math.max(0, math.min(tile.flow_size + self.level, 7))
elseif self.mode == SpawnLiquidMode.REMOVE then
return math.max(0, math.min(tile.flow_size - self.level, 7))
end
return self.level
end
function SpawnLiquid:increaseLiquidLevel()
self.level = math.min(self.level + 1, 7)
end
function SpawnLiquid:decreaseLiquidLevel()
self.level = math.max(self.level - 1, 1)
end
function SpawnLiquid:moveleft()
local move=df.global.cursor
move.x=move.x-1
end
function SpawnLiquid:moveright()
local move=df.global.cursor
move.x=move.x+1
end
function SpawnLiquid:moveup()
local move=df.global.cursor
move.y=move.y-1
end
function SpawnLiquid:movedown()
local move=df.global.cursor
move.y=move.y+1
end
function SpawnLiquid:spawn(pos)
--local MapCure=df.global.gview.view.child
mouse_pos = dfhack.gui.getMousePos()
df.global.cursor.x=mouse_pos.x
df.global.cursor.y=mouse_pos.y
df.global.cursor.z=mouse_pos.z
end
function SpawnLiquid:getPen()
return self.type == df.tile_liquid.Water and COLOR_BLUE or COLOR_RED, "X", self.tile
end
function SpawnLiquid:getBounds(start_position, end_position)
return {
x1=math.min(start_position.x, end_position.x),
x2=math.max(start_position.x, end_position.x),
y1=math.min(start_position.y, end_position.y),
y2=math.max(start_position.y, end_position.y),
z1=math.min(start_position.z, end_position.z),
z2=math.max(start_position.z, end_position.z),
}
end
function SpawnLiquid:onRenderFrame(dc, rect)
SpawnLiquid.super.onRenderFrame(self, dc, rect)
local mouse_pos = dfhack.gui.getMousePos()
if self.is_dragging then
if df.global.enabler.mouse_lbut == 0 then
self.is_dragging = false
elseif mouse_pos and not self:getMouseFramePos() then
self:spawn(mouse_pos)
end
end
guidm.renderMapOverlay(self:callback('getPen'), self:getBounds(
self.area_first_pos or df.global.cursor, df.global.cursor
))
end
function SpawnLiquid:onInput(keys)
if SpawnLiquid.super.onInput(self, keys) then return true end
if keys._MOUSE_L_DOWN and not self:getMouseFramePos() then
local mouse_pos = dfhack.gui.getMousePos()
self:spawn()
end
end
SpawnLiquidScreen = defclass(SpawnLiquidScreen, gui.ZScreen)
SpawnLiquidScreen.ATTRS {
focus_path = 'spawnliquid',
pass_movement_keys = true,
pass_mouse_clicks = false,
force_pause = true,
}
function SpawnLiquidScreen:init()
self:addviews{SpawnLiquid{}}
end
function SpawnLiquidScreen:onDismiss()
view = nil
end
view = view and view:raise() or SpawnLiquidScreen{}:show()
function wagon(curx,cury,curz)
for de,oe in pairs(df.global.world.buildings.other.WAGON) do
if oe.name == 'Nomad' then
local regionx=math.floor(oe.centerx/47)
local regiony=math.floor(oe.centery/47)
print("X region", regionx,
"Y region", regiony)
local site=df.global.plotinfo.main.fortress_site
print("X region + min", regionx + site.global_min_x,
"Y region + min", regiony + site.global_min_y )
end
end
end
wagon()
I think this is prime for folks to fully explore the worlds with their fort citizens... combine this with that force petition script and you could play out series of oregon trail like sessions.(https://i.ibb.co/stWBRN6/bild.png)
DFhack on steam frontpage? :DSteam's "front page" is dynamically generated by Steam for each user, but yes, DFHack is (now) eligible for consideration in that process and so it is possible that Steam's adaptive marketing system will promote DFHack to people that Steam thinks might be interested.Quote(https://i.ibb.co/stWBRN6/bild.png)
I experimented a bit in Lua, and it looks like the entire pipe, not just the raw adamantine tiles, are now flagged as .local_feature type feature_init_deep_special_tubest .
I don't see a way to tell which already-mined tiles had adamantine.
I would also note that this could be done in a mod Lua script, with the serious downside that the player would need to install and select the mod at world generation time.
Is it possible to get a function to read and display wilderness/cavern agitation levels, to say monitor how close you are to attracting agitated wildlife or cavern ambushes?
<int16_t name='irritation_attacks' comment='maxes at 10?'/>
If I’m reading df.map.xml right, it should be under df.map.feature.irritation_level. Dividing that by 10K should give the chance of an attack.
Also, @Myk, df.map.xml also says:Quote from: df.map.xml<int16_t name='irritation_attacks' comment='maxes at 10?'/>
Does that mean that there can be no more than 10 attacks by agitated creatures at one time?
Is it possible to get a function to read and display wilderness/cavern agitation levels, to say monitor how close you are to attracting agitated wildlife or cavern ambushes?
:lua for i,mf in ipairs(df.global.world.features.map_features) do l=-1;pcall(function()l=mf.layer;end);print(string.format("feature %d %s %s-%s underground_region #%d irritation_level %d irritation_attacks %d",i,mf._type,df.layer_type[mf.start_depth],df.layer_type[mf.end_depth],l,mf.feature.irritation_level,mf.feature.irritation_attacks));end;
feature 0 <type: feature_init_subterranean_from_layerst> Cavern1-Cavern1 underground_region #11 irritation_level 4008 irritation_attacks 0
feature 1 <type: feature_init_subterranean_from_layerst> Cavern2-Cavern2 underground_region #31 irritation_level 7924 irritation_attacks 0
feature 2 <type: feature_init_subterranean_from_layerst> Cavern3-Cavern3 underground_region #56 irritation_level 11408 irritation_attacks 0
feature 3 <type: feature_init_magma_core_from_layerst> MagmaSea-MagmaSea underground_region #81 irritation_level 3180 irritation_attacks 0
feature 4 <type: feature_init_underworld_from_layerst> Underworld-Underworld underground_region #106 irritation_level 0 irritation_attacks 0
feature 5 <type: feature_init_outdoor_riverst> Surface-Surface underground_region #-1 irritation_level 288 irritation_attacks 0
feature 6 <type: feature_init_subterranean_from_layerst> Cavern1-Cavern1 underground_region #6 irritation_level 1760 irritation_attacks 0
feature 7 <type: feature_init_deep_special_tubest> MagmaSea-Underworld underground_region #-1 irritation_level 20040 irritation_attacks 0
feature 8 <type: feature_init_deep_special_tubest> MagmaSea-Underworld underground_region #-1 irritation_level 0 irritation_attacks 0
:lua print(string.format("outdoor_irritation:%d",df.global.plotinfo.outdoor_irritation))
outdoor_irritation:35000
If I’m reading df.map.xml right, it should be under df.map.feature.irritation_level. Dividing that by 10K should give the chance of an attack.Nope, that's not quite how you read the file. Each XML file is a collection of loosely-related types, and you are looking at the definition of the "feature" struct (https://github.com/DFHack/df-structures/blob/fc4961f51f2773cff0dfe76e3ad719c027051046/df.map.xml#L581-L603). So if you can find an instance of feature, it will have these fields.
Is there a (soft or hard) size limit on the value string?I believe there is no longer a hard limit. There used to be a 65k limit when we stored data as histfig names, but now it should be getting stored in JSON instead. You can verify this by looking for a .dat file in your save folder named something starting with "dfhack" - it should actually contain JSON.
Is it preferable to have one entry with a very (very) long string, or many (many) entries with empty strings, using the ints[] instead?
Or should I look for another way? I could modify the map itself....What exactly are you trying to store? I believe we still have a way to associate a custom bitmap with map blocks, if you're trying to save some data that relates to map tiles.
local json = require('json')
local persist = require('persist-table')
local GLOBAL_KEY = 'myscriptname'
g_state = g_state or {} -- fill me with data (can be in nested tables)
-- call when your state changes and you want to make sure it is saved
local function persist_state()
persist.GlobalTable[GLOBAL_KEY] = json.encode(g_state)
end
-- add a hook to load persisted state when a fort is loaded
dfhack.onStateChange[GLOBAL_KEY] = function(sc)
if sc ~= SC_MAP_LOADED or df.global.gamemode ~= df.game_mode.DWARF then
return
end
g_state = json.decode(persist.GlobalTable[GLOBAL_KEY] or '') or {}
end
If I’m reading df.map.xml right, it should be under df.map.feature.irritation_level. Dividing that by 10K should give the chance of an attack.
Also, @Myk, df.map.xml also says:Quote from: df.map.xml<int16_t name='irritation_attacks' comment='maxes at 10?'/>
Does that mean that there can be no more than 10 attacks by agitated creatures at one time?
gui/gm-editor tells me the structure is
df.global.world.features.map_features[].feature.irritation_level
and
df.global.world.features.map_features[].feature.irritation_attacksIs it possible to get a function to read and display wilderness/cavern agitation levels, to say monitor how close you are to attracting agitated wildlife or cavern ambushes?
This horrible one-liner prints (some of) the data for each map feature. But I don't think it covers the surface.I don't know at the moment where that data is.Run from the DFHack prompt.Code: [Select]:lua for i,mf in ipairs(df.global.world.features.map_features) do l=-1;pcall(function()l=mf.layer;end);print(string.format("feature %d %s %s-%s underground_region #%d irritation_level %d irritation_attacks %d",i,mf._type,df.layer_type[mf.start_depth],df.layer_type[mf.end_depth],l,mf.feature.irritation_level,mf.feature.irritation_attacks));end;
Sample output:Code: [Select]feature 0 <type: feature_init_subterranean_from_layerst> Cavern1-Cavern1 underground_region #11 irritation_level 4008 irritation_attacks 0
feature 1 <type: feature_init_subterranean_from_layerst> Cavern2-Cavern2 underground_region #31 irritation_level 7924 irritation_attacks 0
feature 2 <type: feature_init_subterranean_from_layerst> Cavern3-Cavern3 underground_region #56 irritation_level 11408 irritation_attacks 0
feature 3 <type: feature_init_magma_core_from_layerst> MagmaSea-MagmaSea underground_region #81 irritation_level 3180 irritation_attacks 0
feature 4 <type: feature_init_underworld_from_layerst> Underworld-Underworld underground_region #106 irritation_level 0 irritation_attacks 0
feature 5 <type: feature_init_outdoor_riverst> Surface-Surface underground_region #-1 irritation_level 288 irritation_attacks 0
feature 6 <type: feature_init_subterranean_from_layerst> Cavern1-Cavern1 underground_region #6 irritation_level 1760 irritation_attacks 0
feature 7 <type: feature_init_deep_special_tubest> MagmaSea-Underworld underground_region #-1 irritation_level 20040 irritation_attacks 0
feature 8 <type: feature_init_deep_special_tubest> MagmaSea-Underworld underground_region #-1 irritation_level 0 irritation_attacks 0
Hmm. Outdoor irritation is kept separate from that, in a variable in plotinfo.Code: [Select]:lua print(string.format("outdoor_irritation:%d",df.global.plotinfo.outdoor_irritation))
Sample output:Code: [Select]outdoor_irritation:35000
Thanks. Ive tried putting these in .lua files in the dfhack-config/scripts folder, but when i attempt to call them from the dfhack gui launcher using lua -f "printial" or "printial.lua" it outputs "cannot open printial.lua: no such file or directory."
I usually dont try to mess with dfhack at all, so i really dont know what im doing. attempting to run the last one you gave there directly through the launcher only outputs "(lua command):1: ')' expected near '.'"
@BoltgunThat seems to be the case. In this case, the creature was in a cage but as soon as they are freed, the activities fills will conflicts. Time to make a script to sort that out. Thanks.
Have you cleared out any conflict they are involved in?
I essentially haven't done anything with DF for a couple of years (still waiting for a playable version, first it was the mess left when the Premium tangent started, and then it was the removal of keyboard support), so the memory is rather fuzzy, but there's a structure that keeps track of conflicts and you could deal with invasion triggered conflict cascades by setting the conflict level in these structures to a non lethal brawl level rather than to the death. I suspect (former) enemies may still have such a lingering hostility structure.
Once you figure it out, could you update makeown? This sounds like it should always be done when making a unit part of the fort.
Is there an easy way to clear the enemy status cache?ok so from my time messing with my own petition script uhh you might need to mess with the historical figure data as their relationship with the civ might be stained or with other folks.
I'm a bit on a dead end to my old "corrupt enemies into friends" script. The script is simple and involve makeown, but it does not seem to clear hostility right. The targeted creatures walk through the fort before getting into a fight with what I believe to be specific units that see it as an enemy. Closing the save and reloading after running makeown prevent that issue.
I tried setting all the units cache slot to -1, setting the entire cache rel_map to -1 and slot_used to false. Got any idea what else I could do ?
It hasn't been on our radar. Is anyone interested in updating it? It looks like a good candidate to be converted to Lua and distributed as an external mod as an example of how to do mod integration.
Both `steam-engine` and `building-hacks` are plugins that affect buildings, but they are not the same. Much of the low level logic will probably still work for both plugins, but the UI components will need a makeover.
We did make a small change to the seedwatch plugin, but I haven't seen any crashes with it. Do you have any mods installed? Could you possibly zip up the savegame that crashes (plus your mods directory) so we can take a look at it?
{
"i" :
[
-1,
30,
-1,
-1,
-1,
-1,
-1
],
"k" : "seedwatch/seed/34",
"s" : ""
},
local count = 0
for _, item in ipairs(df.global.world.items.other.BAR) do
if not item.flags.in_inventory then goto continue end
local gref = dfhack.items.getGeneralRef(item, df.general_ref_type.CONTAINED_IN_ITEM)
if not gref then goto continue end
local bin = df.item.find(gref.item_id)
if not bin or not df.item_binst:is_instance(bin) then goto continue end
if dfhack.items.moveToGround(item, bin.pos) then count = count + 1 end
::continue::
end
print(('dumped %d bar(s) out of bins'):format(count))
If you save that into a file named dfhack-config/scripts/barbin.lua, you can run it by running barbin in the DFHack command launcher.
If you save that into a file named dfhack-config/scripts/barbin.lua, you can run it by running barbin in the DFHack command launcher.
SteamLibrary/steamapps/common/Dwarf Fortress$ ./dfhack
./dwarfort: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by ./dwarfort)
./dwarfort: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./dwarfort)
./dwarfort: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by ./dwarfort)
./dwarfort: /lib/x86_64-linux-gnu/libstdc++.so.6: version `CXXABI_1.3.13' not found (required by ./dwarfort)
./dwarfort: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./hack/libdfhack.so)
./dwarfort: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by ./hack/libdfhack.so)
./dwarfort: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.30' not found (required by ./hack/libdfhack.so)
./dwarfort: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by ./libg_src_lib.so)
./dwarfort: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by ./libg_src_lib.so)
./dwarfort: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./libg_src_lib.so)
./dwarfort: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.29' not found (required by ./libg_src_lib.so)
./dwarfort: /lib/x86_64-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.30' not found (required by ./libg_src_lib.so)
./dwarfort: /lib/x86_64-linux-gnu/libstdc++.so.6: version `CXXABI_1.3.13' not found (required by ./libg_src_lib.so)
./dwarfort: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by hack/libprotobuf-lite.so)
./dwarfort: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by hack/liblua.so)
EDIT: Ah goddamit, I'm probably looking at upgrading my distro to the next major version. Because apparently manually forcing an update to glibc is a sure-fire way to experience Fun in the DF sense of the word on Linux.
It's not that old, DF should have been built using an older version.
I had trouble with running a modtools command. Can you apply one in the tutorial?any script can be run with the dfhack.run_script() command, modtools or otherwise (see https://docs.dfhack.org/en/latest/docs/dev/Lua%20API.html#general-script-api). Which one were you having trouble running? Does the script have the "unavailable" tag (https://docs.dfhack.org/en/stable/unavailable-tag-index.html#cap-m )? If so, it hasn't been updated yet and you can't expect it to work.
I missed the definition of the variable modId in the final example.It's just a string that's set to the name of your mod. There are a few examples of the variable being set in the modding guide. Ctrl-F for "modid".
de mod deactivates by closing the game, but doesnt reactivatie. Id like to have my script always active, if my main mod is loaded .This is what the onStateChange handler is for in https://docs.dfhack.org/en/stable/docs/guides/modding-guide.html#putting-it-all-together
I guess it does not work without dfhack either (GLIBC_2.34 is required by dwarfort, not only libdfhack.so). According to distrowatch, Linux Mint 21.2 Victoria (released two month ago) has glibc 2.35. I guess you have Linux Mint 20.3 Una from 2022, it only has glibc 2.31. It's not that old, DF should have been built using an older version.if i understand what i've heard correctly, the dwarf fortress linux builds were built using Ubuntu 22.04, so you need to be at least as new as Ubuntu 22.04 to ride this particular ride.
Edit: Ubuntu 20.04 uses glibc 2.31, so it is older than 2022. Still, it should be the best target version.
If you don't have a vampire unit yet, maybe save your game, run migrants-now until you get a migrant vampire (use cursecheck to identify them), examine them with gui/unit-syndromes to get the syndrome ID, then load back your original save and modify your other unit.
:lua for k,v in ipairs(world.raws.syndromes.all) do for a,b in ipairs(v.ce) do if df.creature_interaction_effect_add_simple_flagst:is_instance(b) and b.tags1.BLOODSUCKER then print(k) end end end
local nomad={}
local dlg=require("gui.dialogs")
local h_ent=nil
--local test= tonumber(...)
function teleportboatnames3()
local Ark={}
for k,v in pairs(df.global.world.world_data.sites) do
BoaName=dfhack.TranslateName(v.name, true)
table.insert (Ark,{dfhack.TranslateName(v.name, true).." "..v.name.nickname,nil,v,search_key = BoaName:lower()})
end
local f=function(Name,C)
WarpEntityNames(C[3])
end
dlg.showListPrompt("list of stations","Select Station(s) to settle here",COLOR_WHITE,Ark,f,nil,nil,true)
end
function WarpEntityNames(Siteid)
local entity=Siteid.entity_links
local Ark1={}
for k1,v1 in pairs(entity) do
local BoaName1=dfhack.TranslateName(df.global.world.entities.all[v1.entity_id].name, true)
table.insert (Ark1,{dfhack.TranslateName(df.global.world.entities.all[v1.entity_id].name, true).." "..df.global.world.entities.all[v1.entity_id].name.nickname,nil,v1.entity_id,search_key = BoaName1:lower()})
end
local f=function(Name,C)
unretire_all(C[3],Siteid)
end
dlg.showListPrompt("list of groups","Select group(s) to switch here",COLOR_WHITE,Ark1,f,nil,nil,true)
end
function stationmove()
if df.global.gview.view.child.child==nil then
print("this script requires you to be in the raid menu to work")
else
teleportboatnames3()
end
end
function MouseSeek3(siteID)
for x,y in pairs(df.global.world.entities.all) do
for x2,y2 in pairs(siteID) do
if y.name.first_name=="CREW" and y.id==y2.entity_id then
local EntMouse2=y2.entity_id
if y2.former_flag.residence==true then
y2.former_flag.residence=false
y2.flags.residence=true
end
return EntMouse2
end
end
end
end
function MouseSeek4(siteID)
for x,y in pairs(siteID) do
if y.flags.land_for_holding==true then
local EntMouse3=y.entity_id
print(EntMouse3)
return EntMouse3
end
if y.former_flag.land_for_holding==true then
y.former_flag.land_for_holding=false
y.flags.land_for_holding=true
end
end
end
function unretire_all(enti,siteID)
local Selectpark=df.global.world.entities.all[enti]
if df.global.plotinfo.main.fortress_entity~=nil or df.global.plotinfo.main.fortress_site~=nil then
df.global.plotinfo.main.fortress_entity=nil
df.global.plotinfo.main.fortress_site=nil
end
df.global.plotinfo.main.fortress_site=siteID
df.global.plotinfo.main.fortress_entity=Selectpark
df.global.plotinfo.site_id=siteID.id
df.global.plotinfo.group_id=enti
if MouseSeek4(siteID.entity_links)==nil then
df.global.plotinfo.civ_id=enti else
df.global.plotinfo.civ_id=MouseSeek4(siteID.entity_links)
end
aftermath()
end
function DeresidentAll(unit,trgunit)
for k,v in pairs(df.global.world.units.active) do
if v== nil then
error("Invalid creature")
end
v.flags2.resident=false
end
return true
end
function clearpopups()
df.global.world.status.popups:resize(0)
df.global.game.main_interface.options.open=false
df.global.game.main_interface.options.context=0
df.global.plotinfo.game_over=false
end
teleportboatnames3()
function aftermath()
df.global.cur_season_tick=3998
dfhack.gui.getCurViewscreen():feed_key(2)
df.global.pause_state=false
print("unpaused", df.global.enabler.frame_last)
df.global.pause_state=false
if df.global.pause_state==true then
df.global.pause_state=false
end
dfhack.timeout(20,"frames",function() df.global.pause_state=false end)
dfhack.timeout(33,"frames",function() df.global.pause_state=true DeresidentAll() print("unpaused") end)
dfhack.timeout(40,"frames",function() df.global.pause_state=true DeresidentAll() clearpopups() print("unpaused") end)
dfhack.timeout(100,"frames",function() df.global.pause_state=true DeresidentAll() clearpopups() print("unpaused") end)
dfhack.timeout(500,"frames",function() df.global.pause_state=true DeresidentAll() clearpopups() print("unpaused") end)
dfhack.timeout(1,"ticks",function() dfhack.gui.getCurViewscreen():feed_key(278)
DeresidentAll()
print("paused", df.global.enabler.frame_last) end)
end
function clearpopups()
df.global.world.status.popups:resize(0)
df.global.game.main_interface.options.open=false
df.global.game.main_interface.options.context=0
df.global.plotinfo.game_over=false
end
clearpopups()
function DeresidentAll(unit,trgunit)
for k,v in pairs(df.global.world.units.active) do
if v== nil then
error("Invalid creature")
end
v.flags2.resident=false
end
return true
end
dfhack.gui.getCurViewscreen():feed_key(278)
print("unpaused")
df.global.pause_state=false
dfhack.timeout(1,"ticks",function() end)
DeresidentAll()
print("paused")
:lua @df.unit_labor
I will investigate if I'm able to do so only when DF becomes playable, i.e. keyboard support is returned. That doesn't stop mouse tolerant people to take up the task earlier, as this is part of DFHack.
I can mention two things here:
- The biome determination logic was changed prior to the Premium release, so some biome determinations were incorrect. The underlying logic could use an update, although it's no longer part of this plugin itself. The differences were not huge, but present.
- I've noticed that mousing around pre embark causes mid level tiles to get loaded, so once you can order the cursor to move with keyboard input again you should be able to load all of this info into memory before performing any processing, thus removing issues with having to cache information and processing the data in two passes. It can be noted that the information is loaded unordered, but that can be handled e.g. with an intermediate coordinate translation matrix.
You can also list them in-game. For example, open gui/launcher and run:thanks! exaclty what i neededCode: [Select]:lua @df.unit_labor
Interesting. I thought it was in Lua. I was asking about other people who knew Lua because I don’t know it and I’m currently learning C++ (and learning two programming languages at once is a bad idea…). Anyways, I took a look at embark-assistant.cpp, and it’s chock full of stuff I haven’t learned about yet (although, I have noticed that it uses ‘#pragma once’ in the header files, which the site I’m learning from recommends against as it makes the program non-portable, recommending a “header guard” using #ifdef, #define, and #endif instead).
Interesting. I thought it was in Lua. I was asking about other people who knew Lua because I don’t know it and I’m currently learning C++ (and learning two programming languages at once is a bad idea…). Anyways, I took a look at embark-assistant.cpp, and it’s chock full of stuff I haven’t learned about yet (although, I have noticed that it uses ‘#pragma once’ in the header files, which the site I’m learning from recommends against as it makes the program non-portable, recommending a “header guard” using #ifdef, #define, and #endif instead).
We use "#pragma once" because it's portable enough for our purposes. All compilers we use support it, and we are limited to those specific compilers because DF uses them. it has a couple advantages over header guards - it's harder to make mistakes (e.g. by forgetting one of the three lines a header guard requires), and it's guaranteed to avoid name conflicts with header guards using the same name.
[ITEM_SHIELD:ITEM_SHIELD_EXAMPLE]
[NAME:Example Shield]
{DESCRIPTION:Knocks enemy backwards half the time when you block}
... DF Item Stuff ...
{ON_BLOCK}
{SCRIPT:unit/propel -unit OPPONENT -source BLOCKER -velocity [ 50 50 0 ] -mode Relative:50}
[ITEM_WEAPON:RAPID_FIRE_CROSSBOW]
[NAME:Fast Shooting Crossbow]
{DESCRIPTION:Shoots bolts very fast}
... DF Item Stuff ...
{FIRE_RATE:10:2:1} -- Base fire rate is 10, increases by 2 for each skill level, max shot speed is 1
[ITEM_WEAPON:BLOOD_SWORD]
[NAME:Blood Sword]
{DESCRIPTION:Drains blood from opponent when wounded}
... DF Item Stuff ...
{ON_WOUND}
{SCRIPT:unit/change-body -unit OPPONENT -blood \-10:100}
{SCRIPT:unit/change-body -unit HOLDER -blood 10:100}
0,0,1,0,0
0,0,1,0,0
1,1,T,1,1
0,0,1,0,0
0,0,1,0,0
Is there a script to elevate a fortress, or to trigger elevation in some way?
sorry for the vagueness :-)Is there a script to elevate a fortress, or to trigger elevation in some way?
do you mean change the displayed elevation relative to sea level? or do you mean physically transporting it to a different z-level?
Custom tokens - I see that custom tokens are now supported by DFHack, which is very nice. I had previously done this using curly brackets and parsing the raws when the game was started (see examples below). Looking through the custom-raw-tokens.lua code it seem that multi-line tokens aren't a thing though, is this correct? No problem if it is, I can just change my stuff to be a single line(s) as needed.
<sniped>
Custom tokens - I see that custom tokens are now supported by DFHack, which is very nice. I had previously done this using curly brackets and parsing the raws when the game was started (see examples below). Looking through the custom-raw-tokens.lua code it seem that multi-line tokens aren't a thing though, is this correct? No problem if it is, I can just change my stuff to be a single line(s) as needed.
Hi, yea, custom-raw-tokens' dev (wolfboyft) here. I'm glad you like it! It's been a while since I made it, I don't really remember the reasonings behind everything, but yeah, it does indeed only support single line tokens, and only once in an instance of item subtype or whatever. It's just a search for the first instance of a token, returning the token's extra info. It's basically just for simple key:value(s) stuff, not working with/like the raw parsing state machine, like vanilla tokens often do. It's not super gorgeous in how it does it either, in that multiple instances of a tag don't raise errors (iirc), etc. Multi-line tokens would be pretty cool, though. I can't think off the top of my head how they would work, but I've forgotten a lot so...
Glad to see some interest! Good luck with your projects.
Extending the custom-raw-tokens functionality to support your use case is likely possible too. If you need it, others might as well.
local dlg=require("gui.dialogs")
--script that selects a unit from the active unit list and shove them in the currently selected cage
--for nameless units probably nickname them to make it easier to pick them out of the list
--might crash and or try to shove an artifact item in the list if you mess with non-historical figure units
function deitysummoning()
local Ark={}
for k,v in pairs(df.global.world.units.active) do
BoaName=dfhack.TranslateName(v.name)
if v.flags1.inactive==false then
table.insert (Ark,{dfhack.TranslateName(v.name).." "..v.name.nickname ..df.global.world.raws.creatures.all[v.race].creature_id,nil,v})
end
end
local f=function(Name,C)
shove(C[3])
end
dlg.showListPrompt("unit list","Select a unit for shoving into a cage",COLOR_WHITE,Ark,f)
end
function shove(unit)
for ko,vo in pairs(df.global.world.buildings.other.IN_PLAY) do
if vo.id==df.global.game.main_interface.view_sheets.viewing_bldid then
vo.assigned_units:insert("#",unit.id)
end
end
end
deitysummoning()
--this is a gui warmist spellbook modification to house a bunch of Curse tomb modification scripts/rumpack/DisTomb
-- there one spell the create tomb cursor one that you want to do first or the other spell scripts wouldn't work
-- the other spells are used for editing the curse tomb you selected if you make more uhh hope you remember which triggering point is which.
local dlg=require("gui.dialogs")
function getItemAtPos(x,y,z) -- gets the item index @ x,y,z coord
--local x,y,z=getxyz() --get 'X' coords
local vector=df.global.world.items.all -- load all items
for i = 0, #vector-1 do -- look into all items offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z and df.item_corpsest:is_instance(vector[i]) or cx==x and cy==y and cz==z and df.item_corpsepiecest:is_instance(vector[i]) then --compare them
return vector[i] --return index
end
end
--print("item not found!")
return nil
end
function Tomb2()
local Ark2={}
for k,v in pairs(df.global.world.cursed_tombs) do
--BoaName=dfhack.TranslateName(v.name)
table.insert (Ark2,{k.." "..k,nil,v})
--end
end
local f=function(Name,C)
Tombmaker5(C[3])
end
dlg.showListPrompt("Tomb list","Select a tomb to start the next part",COLOR_WHITE,Ark2,f)
end
function Tomb3()
local Ark2={}
for k,v in pairs(df.global.world.cursed_tombs) do
--BoaName=dfhack.TranslateName(v.name)
table.insert (Ark2,{k.." "..k,nil,v})
--end
end
local f=function(Name,C)
Tombmaker2(C[3])
end
dlg.showListPrompt("Tomb list","Select a tomb to start the next part",COLOR_WHITE,Ark2,f)
end
function Tomb3a()
local Ark2={}
for k,v in pairs(df.global.world.cursed_tombs) do
--BoaName=dfhack.TranslateName(v.name)
table.insert (Ark2,{k.." "..k,nil,v})
--end
end
local f=function(Name,C)
CorpsePoint(C[3])
end
dlg.showListPrompt("Tomb list","Select a tomb to start the next part",COLOR_WHITE,Ark2,f)
end
function Tomb4()
local Ark2={}
for k,v in pairs(df.global.world.cursed_tombs) do
--BoaName=dfhack.TranslateName(v.name)
table.insert (Ark2,{k.." "..k,nil,v})
--end
end
local f=function(Name,C)
Tombfinder(C[3])
end
dlg.showListPrompt("Tomb list","Select a tomb to start the next part",COLOR_WHITE,Ark2,f)
end
function Tomb5()
local Ark2={}
for k,v in pairs(df.global.world.cursed_tombs) do
--BoaName=dfhack.TranslateName(v.name)
table.insert (Ark2,{k.." "..k,nil,v})
--end
end
local f=function(Name,C)
Tombmove(C[3])
end
dlg.showListPrompt("Tomb list","Select a tomb to start the next part",COLOR_WHITE,Ark2,f)
end
function Tombmaker2(e)
local Ark2={}
for k,v in pairs(df.global.world.items.other.IN_PLAY) do
if df.item_corpsest:is_instance(v) or df.item_corpsepiecest:is_instance(v) then -- checked_item.flags.in_building then
--if v.unit_id=nil then else
--if v.hist_figure_id=nil then end
for k2,v2 in pairs(df.global.world.units.active) do
if v.unit_id==v2.id and v2.flags1.inactive==true then
BoaName=dfhack.TranslateName(v2.name)
table.insert (Ark2,{dfhack.TranslateName(v2.name).." "..v2.name.nickname,nil,v.id})
end
end
end
end
local f=function(Name,corpse)
e.coffin_skeletons:insert("#",corpse[3])
end
dlg.showListPrompt("Corpse list","Select a body to add to the cursed tomb",COLOR_WHITE,Ark2,f)
end
function Tombfinder(e)
e.triggered=false
end
function Tombmaker5(e)
local Ark2={}
for k1,v1 in pairs(df.global.world.raws.interactions) do
for k,v in pairs(v1.effects) do
--if df.interaction_effect_animatest:is_instance(v.effects) or df.interaction_effect_resurrectst:is_instance(v.effects) then -- checked_item.flags.in_building then
if df.interaction_effect_animatest:is_instance(v) or df.interaction_effect_resurrectst:is_instance(v) then -- checked_item.flags.in_building then
table.insert (Ark2,{v1.name.." "..v1.id,nil,v1.id})
end
end
end
local f=function(Name,C)
e.disturbance=C[3]
end
dlg.showListPrompt("spell list","Select a interaction for shoving into a tomb",COLOR_WHITE,Ark2,f)
end
function Tombmove(e)
e.coffin_pos.x=df.global.cursor.x
e.coffin_pos.y=df.global.cursor.y
e.coffin_pos.z=df.global.cursor.z
local v1=df.global.cursor.x-1
local v2=df.global.cursor.y-1
local v3=df.global.cursor.x+1
local v4=df.global.cursor.y+1
local v5=df.global.cursor.z
e.trigger_regions[0].x_min=v1
e.trigger_regions[0].y_min=v2
e.trigger_regions[0].z_min=v5
e.trigger_regions[0].x_max=v3
e.trigger_regions[0].y_max=v4
e.trigger_regions[0].z_max=v5
end
function SpellSeal()
function Tombmaker(v1,v2,v3,v4,v5,corpse,necro)
Mum=df.global.world.cursed_tombs
Mum:insert("#",{new=true,triggered=false,disturbance=necro,site_id=df.global.plotinfo.site_id})
local clu2=Mum[#Mum-1]
if necro==nil then
necro=Tombmaker6(clu2)
end
clu2.coffin_pos.x=df.global.cursor.x
clu2.coffin_pos.y=df.global.cursor.y
clu2.coffin_pos.z=df.global.cursor.z
clu2.trigger_regions:insert("#",{new=true,
x_min=v1,
y_min=v2,
z_min=v5,
x_max=v3,
y_max=v4,
z_max=v5})
end
function Tombmaker6(e)
local Ark2={}
for k,v in pairs(df.global.world.raws.interactions) do
if df.interaction_effect_animatest:is_instance(v.effects) or df.interaction_effect_resurrectst:is_instance(v.effects) then -- checked_item.flags.in_building then
table.insert (Ark2,{v.name.." "..v.id,nil,v.id})
end
end
local f=function(Name,C)
e.disturbance=C[3]
end
dlg.showListPrompt("spell list","Select a spell for shoving into a tomb",COLOR_WHITE,Ark2,f)
end
Tombmaker(df.global.cursor.x-1,df.global.cursor.y-1,df.global.cursor.x+1,df.global.cursor.y+1,df.global.cursor.z,nil,12)
--dofile("hack/scripts/rumpack/DisTomb.lua")
end
function CorpsePoint(e)
corpse=getItemAtPos(pos2xyz(df.global.cursor))
e.coffin_skeletons:insert("#",corpse.id)
end
function doNothing()
print("doing nothing real good but here have a message")
end
listofspells={
{text="nothing", spell=doNothing,icon='*'},
{text="Necro:Alter Spell", spell=Tomb2,key="CUSTOM_V"},
{text="Necro:Add Corpses", spell=Tomb3,key="CUSTOM_M"},
{text="Necro:Add Corpses-cursor", spell=Tomb3a,key="CUSTOM_X"},
{text="Necro:Reset Curse", spell=Tomb4,key="CUSTOM_T"},
{text="Necro:Move Tomb-cursor", spell=Tomb5,key="CUSTOM_O"},
{text="Necro:CreateTomb-cursor", spell=SpellSeal,key="CUSTOM_N"},
}
dlg.showListPrompt("Directions","Choze order",nil, listofspells,function(index,choice) choice.spell() end)
ok so the list of working spell commandsBinary signing is a complicated area ...That "smart screen" warning is caused by the Dwarf Fortress.exe, not from DFHack, because Dwarf Fortress itself isn't signed. When running Dwarf Fortress.exe the first time it's triggering the warning:
...I didn't touched that checkbox. The "df_50_11_win.zip" and the "dfhack-50.11-r2-Windows-64bit.zip" were still marked with this text (under file properties):
By the way, I've heard that there is a "this file was downloaded from the internet" checkbox that you can untick before extracting DFHack from its distribution archive. Do you happen to remember if you unticked that box?
I can imagine that DFHack is suspicious for the scanner because it is hooking in another app, just like malware would do it.DFHack behaves just like any other DLL. There's nothing in DFHack that will look like malware to a heuristic scanner. DFHack does not "hook" another app, it actually loads as an invited DLL (using the 'dfhooks' mechanism provided by Bay12) and uses Bay12's published endpoints to connect to DF.
Hello everyone! Was wondering if there are any plans to add classic keyboard support and keybinds to the .50 version of the game? Really missing those old keybinds.Toady One has said that keyboard support will be restored (July last year), although there's no time line. This is not a DFHack issue, but one for the base game.
Hello everyone! Was wondering if there are any plans to add classic keyboard support and keybinds to the .50 version of the game? Really missing those old keybinds.so I went and made a DF50 keyboard cursor (http://www.bay12forums.com/smf/index.php?topic=164123.msg8487568) script that just a modified version of a different gui liquids script that display an x where the in game cursor is currently placed.
What do I do when I have found out information, that is currently not in df-structurers?
Or when I have a suggestion for a script-improvement?
Well, it is this: you can kill a unit "by old age". by setting old_age and old_time to any low number. old_age is the age in which the unit dies, and old_time is the time THAT YEAR in which it dies.
So the script 'exterminate' can be updated by a new way of killing people. How nice.
Whats better: this can probably be used to kill off-site units, if you know their unit-id.
Does anyone know if there's a Lua one-liner to print the agitation level of a fort? Or are there potentially several map regions? I've been trying to figure out how to get any of the fort's map data from the Lua interpreter but haven't had much luck.
Also, is there a way to "undiscover" cavern layers with the Lua interpreter?
Edit: I hid the feature, but I see I'm still getting visitors who want to slay monsters. Is there another flag I have to toggle to stop that?If the cavern is blocked off with natural walls, you could use revflood to hide them, but that won't work with constructed walls (by design). If you use tiletypes to add in natural walls, though, you could use revflood to hide the caverns again. That *might* help with the monster slayers, but I'm not sure.
The cavern is still visible too, which is fine, but I guess that's also controlled by other map data.
I tried first by running gui/autochop, but there apparently weren't any options to exclude trees based on their products.
It even happens if I just type in modtools/item-trigger and no further arguments
modtools/item-trigger enables some EventManager events when it is run. My guess is that there is a crash bug in EventManager for one of those event types. What are you trying to build? This may be worth releasing a new 47.05 version of DFHack for.
for k,v in pairs(df.global.world.world_data.sites) do
if v.type~=0 then
v.flags[0]=false
end
if v.type==2 or v.type==6 or v.type==7 or v.type==9 or v.type==10 then
Mum=df.global.world.world_data.embark_notes
Mum:insert("#",{new=true,tile=32, fg_color=12, bg_color=2, name=dfhack.TranslateName(v.name),left=v.rgn_min_x,right=v.rgn_max_x,top=v.rgn_min_y,bottom=v.rgn_max_y})
local clu2=Mum[#Mum-1]
clu2.pos.x=v.pos.x
clu2.pos.y=v.pos.y
end
end
for k,v in pairs(df.global.world.world_data.sites) do
if v.type==1 or v.type==3 or v.type==4 or v.type==5 or v.type==10 then
if v.realization==nil then else
v.flags[0]=true
for ok,oe in pairs(v.realization.buildings) do
local regionx=math.floor(oe.min_x/47)
local regionx2=math.floor(oe.max_x/47)
local regiony=math.floor(oe.min_y/47)
local regiony2=math.floor(oe.max_y/47)
local regionmath1=math.floor((v.global_min_x+regionx)/16)
local regionmath2=math.floor((v.global_max_x+regionx2)/16)
local regionmath3=math.floor((v.global_min_y+regiony)/16)
local regionmath4=math.floor((v.global_max_y+regiony2)/16)
local globalmath1=math.floor((v.global_min_x+regionx)-(regionmath1*16))
local globalmath2=math.floor((v.global_max_x+regionx2)-(regionmath2*16))
local globalmath3=math.floor((v.global_min_y+regiony)-(regionmath3*16))
local globalmath4=math.floor((v.global_max_y+regiony2)-(regionmath4*16))
function embarknote(oe,v,regionmath1,regionmath3,globalmath1,globalmath2,globalmath3,globalmath4)
local buildcolor=oe.type
Mum=df.global.world.world_data.embark_notes
Mum:insert("#",{new=true,tile=buildcolor, fg_color=10, bg_color=5, name=dfhack.TranslateName(v.name),
left=globalmath1,
right=globalmath2,
top=globalmath3,
bottom=globalmath4})
local clu2=Mum[#Mum-1]
clu2.pos.x=regionmath1
clu2.pos.y=regionmath3
end
print (oe.type)
if oe.type~=16 then
embarknote(oe,v,regionmath1,regionmath3,globalmath1,globalmath2,globalmath3,globalmath4)
end
end
end
end
end
(https://cdn.discordapp.com/attachments/302956330304667649/1183289548478890024/map-gif-showcase.gif)--this script uses old dfhack code to store items on to the wagon via keyboard cursors.
--probably more stable than the other scripts that store items onto the wagon but requires it to be on the floor.
local contain={}
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function stuffwagon(curx,cury,curz)
for de,oe in pairs(df.global.world.buildings.all) do
if oe.name == 'Target' then
local vector=df.global.world.items.all -- load all items
for i = 0, #vector-1 do -- look into all items offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==curx and cy==cury and cz==curz then --compare them
dfhack.items.moveToBuilding(vector[i],oe) --return index
end
end
end
end
end
stuffwagon(getxyz())
(tested by atom smashing some of them which brought in a brief increase in FPS).If the increase was only brief/temporary and your FPS dropped back down, there is probably a different underlying cause.
(tested by atom smashing some of them which brought in a brief increase in FPS).If the increase was only brief/temporary and your FPS dropped back down, there is probably a different underlying cause.
Anyway, I suspect something along these lines may already be possible with "autodump" plus some filtering tool (extended stocks screen?), so that would probably be our recommendation. If there are filters missing that you think would be useful, we're open to suggestions. Generally, for this sort of thing, I'd prefer to extend existing commands over creating new single-purpose ones.
we're working on an "items" tool that can filter by properties and make specific changes (like mark for dumping). this sounds like it might be useful to specify filters for in that toolFWIW, this is similar to the set of features I had in mind when I was referring to the extended stocks screen (https://docs.dfhack.org/en/stable/docs/tools/stocks.html).
The thing is, I was fighting a massive invasion at the time, so the bodies kept pilling up. It's definitely that as the FPS stabilized after it was over. And yes, this is definitely possible with the autodump (used it to teleport bodies to the bridge for atom smashing), but marking all the bodies and other trash for dumping is a hassle, even with rectangle mass marking feature.Right - my point was that the "stocks" plugin allows (well, allowed) you to search by criteria other than location. e.g. you could select all items with "corpse" in their name and mark them for dumping, then use autodump if you wanted.
custom work details to be saved/restored to/from disk
egg-layers that are assigned to a pen/pasture only claim nestboxes that are within that pen/pasture?
custom work details to be saved/restored to/from disk
Very possible. I just did something similar for difficulty settings and standing orders.Quoteegg-layers that are assigned to a pen/pasture only claim nestboxes that are within that pen/pasture?
This one I'm less sure about. I don't know exactly how egg layers choose their nestboxes. I've noticed that *most* of the time, they'll claim the one that `autonestbox` assigns them to, but not always. I'd have to look into it.
Could you create feature requests for these two at https://github.com/DFHack/dfhack/issues ? Right now we're focused on getting ready for DF 50.12, and I don't want to forget about these.
modtools/transform-unit doesn't seem to like the creature name in the raws due to the spacingYou can enclose strings such as that in quotes to pass them into a tool. Like with createitem ROUGH "INORGANIC:LAPIS LAZULI".
--book made for messing with trading warning might mess things up down the road with the save... this script uses warmist spellbook gui script as a basic layout
local dlg=require("gui.dialogs")
function traderlocate()-- seeks traders
for k,v in pairs (df.global.world.units.active) do
if v.flags1.merchant==true then
return v
end
end
end
function getxyz() -- this will return pointers x,y and z coordinates.
local x=df.global.cursor.x
local y=df.global.cursor.y
local z=df.global.cursor.z
return x,y,z -- return the coords
end
function getItemAtKPos(x,y,z) -- gets the item index @ x,y,z coord
local vector=df.global.world.item.all -- load all items
local kickpos=df.global.world.units.active[0].pos
for i = 0, #vector-1 do -- look into all items offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==kickpos.x and cy==kickpos.y and cz==kickpos.z then --compare them
return vector[i] --return index
end
end
return nil
end
function getCreatureAtPos(x,y,z) -- gets the creature index @ x,y,z coord
local vector=df.global.world.units.all -- load all creatures
for i = 0, #vector-1 do -- look into all creatures offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
return nil
end
function getItemAtPos(x,y,z) -- gets the item index @ x,y,z coord
local vector=df.global.world.items.all -- load all items
for i = 0, #vector-1 do -- look into all items offsets
local curpos=vector[i].pos --get its coordinates
local cx=curpos.x
local cy=curpos.y
local cz=curpos.z
if cx==x and cy==y and cz==z then --compare them
return vector[i] --return index
end
end
return nil
end
function Mount (rider,horse)--swiped the old dragging script for this
rider.relationship_ids.Draggee=horse.id
horse.relationship_ids.Dragger=rider.id
return
end
function traderpack()
unit_1 = traderlocate()
if unit_1 == nil then
print ("You must make a merchant. Use merch-add first.")
return
end
unit_2 = getCreatureAtPos(getxyz())
if unit_2 == nil then
print ("You must place the cursor over the second target.")
return
end
dfhack.with_suspend(Mount, unit_1, unit_2)
end
function merchadd()
local unit=getCreatureAtPos(getxyz())
unit.flags1.merchant=true
end
function merchminus()
local unit=getCreatureAtPos(getxyz())
unit.flag.merchant=false
end
function traderadd()--you run this in the trade menu
local Trado=df.global.game.main_interface.trade
for k,v in pairs (df.global.world.units.active) do
if v.flags1.merchant==true then
Trado.merchant_trader=v
Trado.civ=df.global.world.entities.all[v.civ_id]
Trado.havetalker=1
end
end
end
function caravan()
for k,v in pairs (df.global.world.units.active) do
if v.flags1.merchant==true then
Vcivid=v.civ_id
caravan2(Vcivid)
end
end
end
function Caravanfinish()
df.global.plotinfo.caravans[0].time_remaining=100
end
function caravan2(Vcivid)
local Carav=df.caravan_state:new()
df.global.plotinfo.caravans:insert("#",{new=true, total_capacity=10000, trade_state = 1, time_remaining= 4000, mood=100, entity=Vcivid})
end
local options = {}
local argparse = require('argparse')
local commands = argparse.processArgsGetopt({...}, {
{'d', 'dead', handler=function() options.dead = true end}
})
listofspells={
{text="trade:merch-add", spell=merchadd,key="CUSTOM_T"},
{text="trade:merch-minus", spell=merchminus,key="CUSTOM_R"},
{text="trade:packholder-add", spell=traderpack,icon='*'},
{text="trade:caravan-add", spell=caravan,icon='*'},
{text="trade:caravan-finish", spell=Caravanfinish,icon='*'},
{text="trade:menu-swap", spell=traderadd,icon='*'},
}
dlg.showListPrompt("Spelz","Choze spel",nil, listofspells,function(index,choice) choice.spell() end)
--this script is a rough concept of seeing if one could serve food to others
-- this script will search for prepared meals and slam them into a cup a tavern keeper is holding
-- which kinda requires monitoring the tavern keeper to see if they are holding a cup to use this script.
-- for the person who wants a resturant feel to the fort or got goblins and want to see them eat stuff.
--potentially unstable and may leave uncleared general refs in cups which makes them look like they are full.
local crab=df.global.plotinfo.main.fortress_site
local lobster=df.global.world.activities.all
local shrimp=df.global.world.items.other.FOOD
local items={}
for _,checked_item in pairs(df.global.world.items.other.IN_PLAY) do
if df.item_foodst:is_instance(checked_item) and not df.general_ref_activity_eventst:is_instance(checked_item.general_refs) then
table.insert(items,checked_item)
end
end
for _3,craft in pairs(lobster) do
if craft.type==10 then
for _4,act in pairs(craft.events) do
act.unk_v42_1:insert("#",{new=true, item_id=items[dfhack.random.new():random(#items)].id ,unk_1=1, unk_2=0} )
local u_ref=df.general_ref_activity_eventst:new()
u_ref.activity_id=craft.id
u_ref.event_id=act.event_id
local u_store=df.general_ref_contains_itemst:new() -- the cup
local u_contain=df.general_ref_contained_in_itemst:new() -- the stuff in the cop
u_store.item_id=items[dfhack.random.new():random(#items)].id
u_contain.item_id=act.unk_v42_1[0].item_id
local salmon2=df.global.world.items.other.IN_PLAY
for _dc,serve3b in pairs(salmon2) do
if serve3b.id==act.unk_v42_1[0].item_id then
local tuna3=serve3b.general_refs
tuna3:insert(#tuna3,u_store)
end
end
local salmon=act.unk_v42_1[0].item_id
for _5,serve in pairs(items,act.unk_v42_1) do
for _6,serve2 in pairs(act.unk_v42_1) do
if serve.id==serve2.item_id then
serve.general_refs:insert(#serve.general_refs,u_ref)
serve.general_refs:insert(#serve.general_refs,u_contain)
end
end
end
end
end
end
ok so here's a script that started off on a normal idea of can I control what tavern serve to folks?local old_screen=dfhack.gui.getCurViewscreen()
local new_screen=df.viewscreen_legendsst:new()
old_screen.child=new_screen
new_screen.parent=old_screen
new_screen.page:insert("#",{new=true,header="Open Legends",mode=0,index=-1,scroll_position_list=0,scrolling_list=false,
scroll_position_text=0,
scrolling_text=false
})
df.global.gamemode=df.game_mode.DWARF
df.global.gametype=df.game_type.DWARF_MAIN
local old_screen=dfhack.gui.getCurViewscreen()
--local new_screen=df.viewscreen_choose_game_typest:new()
local new_screen=df.viewscreen_dwarfmodest:new()
old_screen.child=new_screen
new_screen.parent=old_screen
new_screen.page:insert("#",{new=true,header="Open Legends",mode=0,index=-1,scroll_position_list=0,scrolling_list=false,
scroll_position_text=0,
scrolling_text=false
})
I just opened a discussion for requirements for the new unit overview screen here: https://www.reddit.com/r/dwarffortress/comments/1bwue80/dfhack_adventure_mode_poll_results_and_next_steps/
Anyone who doesn't want to comment on reddit can of course reply here too.