From a86bc8de45f913b2a5dbc8de82f99b5c903b3267 Mon Sep 17 00:00:00 2001 From: sizzlins Date: Tue, 21 Apr 2026 19:58:12 +0700 Subject: [PATCH 1/4] Fix gui/create-item bugs (#5782) - Bag powders and seeds dynamically via modtools/create-item moveToBag - Allow ITEMS_SOFT in ARMOR fallback - Map PANTS to ARMOR for correct cloth restriction --- gui/create-item.lua | 4 ++-- modtools/create-item.lua | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/gui/create-item.lua b/gui/create-item.lua index cf5c12a25..3ed1e1b8f 100644 --- a/gui/create-item.lua +++ b/gui/create-item.lua @@ -81,7 +81,7 @@ local function getRestrictiveMatFilter(itemType, opts) return (mat.flags.ITEMS_AMMO) end, ARMOR = function(mat, parent, typ, idx) - return (mat.flags.ITEMS_ARMOR or mat.flags.LEATHER) + return (mat.flags.ITEMS_ARMOR or mat.flags.LEATHER or mat.flags.ITEMS_SOFT) end, INSTRUMENT = function(mat, parent, typ, idx) return (mat.flags.ITEMS_HARD) @@ -106,7 +106,7 @@ local function getRestrictiveMatFilter(itemType, opts) for k, v in ipairs { 'GOBLET', 'FLASK', 'TOY', 'RING', 'CROWN', 'SCEPTER', 'FIGURINE', 'TOOL' } do itemTypes[v] = itemTypes.INSTRUMENT end - for k, v in ipairs { 'SHIELD', 'HELM' } do + for k, v in ipairs { 'SHIELD', 'HELM', 'PANTS' } do itemTypes[v] = itemTypes.ARMOR end for k, v in ipairs { 'SHOES', 'GLOVES' } do diff --git a/modtools/create-item.lua b/modtools/create-item.lua index 03a60b352..fd839adf7 100644 --- a/modtools/create-item.lua +++ b/modtools/create-item.lua @@ -68,6 +68,37 @@ local function moveToContainer(item, creator, container_type) return bucket end +local function moveToBag(item, creator) + local containerMat = dfhack.matinfo.find('PLANT_MAT:PIG_TAIL:THREAD') + if not containerMat then + for _, n in ipairs(df.global.world.raws.plants.all) do + if n.flags.THREAD_PLANT then + containerMat = dfhack.matinfo.find('PLANT_MAT:' .. n.id .. ':THREAD') + break + end + end + if not containerMat then + containerMat = dfhack.matinfo.find('CREATURE_MAT:COW:LEATHER') + end + if not containerMat then + for _, c in ipairs(df.global.world.raws.creatures.all) do + for _, m in ipairs(c.material) do + if m.flags.LEATHER then + containerMat = dfhack.matinfo.find('CREATURE_MAT:' .. c.creature_id .. ':' .. m.id) + break + end + end + if containerMat then break end + end + end + end + local boxType = dfhack.items.findType('BOX:NONE') + local boxes = dfhack.items.createItem(creator, boxType, -1, containerMat.type, containerMat.index) + local box = boxes[1] + dfhack.items.moveToContainer(item, box) + return box +end + -- this part was written by four rabbits in a trenchcoat (ppaawwll) local function createCorpsePiece(creator, bodypart, partlayer, creatureID, casteID, generic) -- (partlayer is also used to determine the material if we're spawning a "generic" body part (i'm just lazy lol)) @@ -301,6 +332,8 @@ local function createItem(mat, itemType, quality, creator, description, amount) return moveToContainer(item, creator, 'BARREL') elseif mat_token == 'WATER' or mat_token == 'LYE' then return moveToContainer(item, creator, 'BUCKET') + elseif item_type == 'POWDER_MISC' or item_type == 'SEEDS' then + return moveToBag(item, creator) end return items end From 0dce32a4dd6c09e0897b5dcb4e33090ddf6df582 Mon Sep 17 00:00:00 2001 From: sizzlins Date: Tue, 21 Apr 2026 21:38:53 +0700 Subject: [PATCH 2/4] fix(create-item): ensure seeds and powders spawn in bags instead of chests, and fix seed stacking --- modtools/create-item.lua | 71 +++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/modtools/create-item.lua b/modtools/create-item.lua index fd839adf7..9fe14ab23 100644 --- a/modtools/create-item.lua +++ b/modtools/create-item.lua @@ -68,35 +68,53 @@ local function moveToContainer(item, creator, container_type) return bucket end -local function moveToBag(item, creator) +local function moveToBag(itemsToBag, creator) + local items_list = type(itemsToBag) == 'table' and not itemsToBag._type and itemsToBag or {itemsToBag} + if #items_list == 0 then return {} end + -- Use plant thread for bag material (pig tail fiber) local containerMat = dfhack.matinfo.find('PLANT_MAT:PIG_TAIL:THREAD') if not containerMat then for _, n in ipairs(df.global.world.raws.plants.all) do - if n.flags.THREAD_PLANT then - containerMat = dfhack.matinfo.find('PLANT_MAT:' .. n.id .. ':THREAD') - break + for _, m in ipairs(n.material) do + if m.flags.THREAD_PLANT then + containerMat = dfhack.matinfo.find('PLANT_MAT:' .. n.id .. ':' .. m.id) + if containerMat then break end + end end + if containerMat then break end end - if not containerMat then - containerMat = dfhack.matinfo.find('CREATURE_MAT:COW:LEATHER') - end - if not containerMat then - for _, c in ipairs(df.global.world.raws.creatures.all) do - for _, m in ipairs(c.material) do - if m.flags.LEATHER then - containerMat = dfhack.matinfo.find('CREATURE_MAT:' .. c.creature_id .. ':' .. m.id) - break - end + end + if not containerMat then + containerMat = dfhack.matinfo.find('CREATURE_MAT:COW:LEATHER') + end + if not containerMat then + for _, c in ipairs(df.global.world.raws.creatures.all) do + for _, m in ipairs(c.material) do + if m.flags.LEATHER then + containerMat = dfhack.matinfo.find('CREATURE_MAT:' .. c.creature_id .. ':' .. m.id) + if containerMat then break end end - if containerMat then break end end + if containerMat then break end end end - local boxType = dfhack.items.findType('BOX:NONE') - local boxes = dfhack.items.createItem(creator, boxType, -1, containerMat.type, containerMat.index) - local box = boxes[1] - dfhack.items.moveToContainer(item, box) - return box + if not containerMat then + print('[create-item] ERROR: no bag material found') + return {item} + end + -- Use BAG item type, NOT BOX. BOX creates chests/coffers (item_boxst), + -- BAG creates actual bags (item_bagst) for holding powder/seeds. + local bagType = dfhack.items.findType('BAG:NONE') + local ok, bags = pcall(dfhack.items.createItem, creator, bagType, -1, containerMat.type, containerMat.index) + if not ok then + print('[create-item] ERROR creating bag: ' .. tostring(bags)) + return {item} + end + local bag = bags[1] + for _, it in ipairs(items_list) do + dfhack.items.moveToContainer(it, bag) + end + return {bag} end -- this part was written by four rabbits in a trenchcoat (ppaawwll) @@ -332,7 +350,7 @@ local function createItem(mat, itemType, quality, creator, description, amount) return moveToContainer(item, creator, 'BARREL') elseif mat_token == 'WATER' or mat_token == 'LYE' then return moveToContainer(item, creator, 'BUCKET') - elseif item_type == 'POWDER_MISC' or item_type == 'SEEDS' then + elseif item_type == 'POWDER_MISC' then return moveToBag(item, creator) end return items @@ -377,8 +395,12 @@ function hackWish(accessors, opts) until count end if not mattype or not itemtype then return end - if df.item_type.attrs[itemtype].is_stackable then - local mat = typesThatUseCreaturesExceptCorpses[df.item_type[itemtype]] and {matindex, casteId} or {mattype, matindex} + -- Force stackable behavior for POWDER_MISC even if the engine + -- reports is_stackable=false (Steam DF version issue) + local item_type_name = df.item_type[itemtype] + if df.item_type.attrs[itemtype].is_stackable + or item_type_name == 'POWDER_MISC' then + local mat = typesThatUseCreaturesExceptCorpses[item_type_name] and {matindex, casteId} or {mattype, matindex} return createItem(mat, {itemtype, itemsubtype}, quality, unit, description, count) end local items = {} @@ -392,6 +414,9 @@ function hackWish(accessors, opts) end end end + if df.item_type[itemtype] == 'SEEDS' then + items = moveToBag(items, unit) + end if opts.pos then for _,item in ipairs(items) do dfhack.items.moveToGround(item, opts.pos) From cb7e88e6c2eec09351d9b2e99374e0940ab5753a Mon Sep 17 00:00:00 2001 From: sizzlins Date: Tue, 21 Apr 2026 21:42:10 +0700 Subject: [PATCH 3/4] fix(create-item): remove debug prints and fix error return value when bagging fails --- modtools/create-item.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modtools/create-item.lua b/modtools/create-item.lua index 9fe14ab23..40fad5eb0 100644 --- a/modtools/create-item.lua +++ b/modtools/create-item.lua @@ -99,16 +99,16 @@ local function moveToBag(itemsToBag, creator) end end if not containerMat then - print('[create-item] ERROR: no bag material found') - return {item} + -- print('[create-item] ERROR: no bag material found') + return items_list end -- Use BAG item type, NOT BOX. BOX creates chests/coffers (item_boxst), -- BAG creates actual bags (item_bagst) for holding powder/seeds. local bagType = dfhack.items.findType('BAG:NONE') local ok, bags = pcall(dfhack.items.createItem, creator, bagType, -1, containerMat.type, containerMat.index) if not ok then - print('[create-item] ERROR creating bag: ' .. tostring(bags)) - return {item} + -- print('[create-item] ERROR creating bag: ' .. tostring(bags)) + return items_list end local bag = bags[1] for _, it in ipairs(items_list) do From 1d6f952d584f2cd46cc05c24190528198d264534 Mon Sep 17 00:00:00 2001 From: sizzlins Date: Wed, 22 Apr 2026 11:13:59 +0700 Subject: [PATCH 4/4] fix(create-item): properly set item subtype for PLANT_GROWTH items --- gui/create-item.lua | 2 +- modtools/create-item.lua | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/gui/create-item.lua b/gui/create-item.lua index 3ed1e1b8f..d7ddcfecd 100644 --- a/gui/create-item.lua +++ b/gui/create-item.lua @@ -129,7 +129,7 @@ local function getMatFilter(itemtype, opts) PLANT = function(mat, parent, typ, idx) return mat.flags.STRUCTURAL_PLANT_MAT end, - LEAVES = function(mat, parent, typ, idx) + PLANT_GROWTH = function(mat, parent, typ, idx) return mat.flags.STOCKPILE_PLANT_GROWTH end, MEAT = function(mat, parent, typ, idx) diff --git a/modtools/create-item.lua b/modtools/create-item.lua index 40fad5eb0..6c06e3c90 100644 --- a/modtools/create-item.lua +++ b/modtools/create-item.lua @@ -376,6 +376,20 @@ function hackWish(accessors, opts) if not itemok then return end local matok, mattype, matindex, casteId, bodypart, partlayerID, corpsepieceGeneric = accessors.get_mat(itemtype, opts) if not matok then return end + + -- For PLANT_GROWTH, itemsubtype must be the growth index within the plant's raws. + -- If it's -1, we search the plant raws to find the growth that matches this material. + if df.item_type[itemtype] == 'PLANT_GROWTH' and itemsubtype == -1 then + for _, plant in ipairs(df.global.world.raws.plants.all) do + for i, growth in ipairs(plant.growths) do + if growth.mat_type == mattype and growth.mat_index == matindex then + itemsubtype = i + break + end + end + if itemsubtype ~= -1 then break end + end + end if not no_quality_item_types[df.item_type[itemtype]] then qualityok, quality = accessors.get_quality() if not qualityok then return end