Before you give up: Look at my Underhive Settlement reboot, I make prepared meals in reactions there just fine. All you need is a healthy daub of DFHack code and everything "Just Works (tm)".
Here is the heart of the code in question:
function makeMeal(id, parts)
local types = {}
local ids = {}
for i, part in ipairs(parts) do
ids[part[1]] = i
types[i] = part[2]
end
return function(reaction, reaction_product, unit, input_items, input_reagents, output_items, call_native)
call_native.value = false
local item = df['item_foodst']:new()
item.id = df.global.item_next_id
df.global.world.items.all:insert('#',item)
df.global.item_next_id = df.global.item_next_id + 1
-- These always seem to be -1.
item.entity = -1
item.recipe_id = -1
item:setSubtype(dfhack.items.findSubtype(id))
local mats, stk = {}, 0
for x = 0, #input_items - 1, 1 do
local i = ids[input_reagents[x].code]
if i ~= nil then
mats[i] = dfhack.matinfo.decode(input_items[x])
cooked = items.GetMRP("COOKED_MAT", mats[i])
if cooked ~= nil then
mats[i] = cooked
end
stk = stk + input_items[x].stack_size
input_items[x].stack_size = 1
end
end
for i, typ in ipairs(types) do
if typ == "FISH" then
-- This is super weird, but it seems that fish items have their material numbers reversed?
-- Without this you end up with meals made from odd materials. For example sludge prawns report
-- they are material 19:27 and the "rival" creature is 27:19. If you cook sludge prawns you end
-- up with meals made from that rival creature.
-- Maybe "FISH" type items are supposed to use a creature/caste pair instead of a material? Either
-- one seems to work just fine...
item.ingredients:insert('#', {
new = true,
anon_1 = 0,
item_type = df.item_type[typ],
unk_4 = -1,
mat_type = mats[i].index,
mat_index = mats[i].type,
maker = -1,
unk_10 = 0, -- No quality
unk_14 = 0,
unk_18 = -1,
})
else
item.ingredients:insert('#', {
new = true,
anon_1 = 0,
item_type = df.item_type[typ],
unk_4 = -1,
mat_type = mats[i].type,
mat_index = mats[i].index,
maker = -1,
unk_10 = 0, -- No quality
unk_14 = 0,
unk_18 = -1,
})
end
end
item:assignQuality(unit, reaction.skill)
item:setMaker(unit.id)
item.stack_size = stk
item:categorize(true)
levelUp(unit, reaction.skill, 30)
output_items:insert('#', item)
end
end
It's complicated by the fact that this works hand in hand with some special "glue" code generated by Rubble, but you should be able to divine the basic principles either from this or by looking at the whole system.
The only possible sticking point is if reaction hooks work in adventure mode or not (I think they do).