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 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:
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.)
Fantastic.
I figured DF was able to do this natively by observing how food tasked for eating was split from the stack before a dwarf collected it, but never would have found the C++ function, or been able to convert it to lua myself. This is what I came up with:
--[====[
splitstack
=========
Splits a selected larger stack of food items into two smaller ones as long as the size of the new stack is smaller than the original. Otherwise it creates a new stack of the size given and leaves a single item from the original stack.
For example:
splitstack 5
produces a new stack of size 5 and reduces the original stack by a size of 5, unless the original stack is 5 or smaller in which case it produces a new stack of size 5 and leaves an old stack of size 1. (Of note in quick testing it was possible to multiply raw mussels using this command!)
]====]
local item = dfhack.gui.getSelectedItem(true) or qerror("No stack selected")
local count = tonumber (...)
new_item = item:splitStack(count, true)
if new_item then
new_item:categorize(true)
end
I'm not a coder so I just cobbled together some bits and piece from other scripts until it worked 'well enough' for the case I wanted. It works okay (as far as I can tell) for food items, and can even multiply raw mussels
but testing other materials shows strange behaviour, bones copy regardless of stack size supplied rather than split (splitstack 5 on cat bone [4] produces
another stack of cat bone [4]), and thread and cloth can be 'split' into stacks of multiple items. No doubt someone who actually knows what they are doing could sanitise it.
To answer your one of your questions, the stackSize variable is the new stack (I'm pretty sure/as far as I can tell) and it inherits the properties of the original stack (e.g. forbidden status).
(It would be relatively easy to limit the behaviour to only splitting by checking if the stack was of sufficient size but sorting out the material variability would be for me much more time than I am willing to dedicate.)