From 407710e2d45f4199b2eb4958a1466794ac99880a Mon Sep 17 00:00:00 2001 From: sizzlins Date: Thu, 23 Apr 2026 21:42:11 +0700 Subject: [PATCH 1/4] new script: item-uses - show all workshop uses for selected item Adds a new DFHack command 'item-uses' that introspects the selected item and lists every workshop and task that can use it. Features: - Material flag inspection (IS_METAL, WOOD, LEATHER, IS_DYE, etc.) - Plant flag analysis (DRINK, MILL, DRY, THREAD, etc.) - Material reaction product discovery (DRINK_MAT, DYE_MAT, CHEESE_MAT, etc.) - Full reaction/workshop matching against all game reactions - Dynamic workshop name resolution using df.workshop_type/df.furnace_type enums - Smart reagent filtering to avoid false positives from containers and secondary reagents - Proper gating of 'make items' uses to raw materials only (finished goods like armor and furniture correctly show 'Encrust' and 'Melt' instead) - Growth-aware: distinguishes PLANT vs PLANT_GROWTH for accurate results Usage: select an item in-game, then run 'item-uses' from the DFHack console. --- item-uses.lua | 363 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 item-uses.lua diff --git a/item-uses.lua b/item-uses.lua new file mode 100644 index 0000000000..d1ecdc8516 --- /dev/null +++ b/item-uses.lua @@ -0,0 +1,363 @@ +-- item-uses: Shows what workshops and tasks can use the selected item +local item = dfhack.gui.getSelectedItem(true) +if not item then qerror('Select an item first!') end + +local desc = dfhack.items.getReadableDescription(item) +local item_type = item:getType() +local item_subtype = item:getSubtype() +local mat_type = item:getMaterial() +local mat_index = item:getMaterialIndex() +local mi = dfhack.matinfo.decode(item) +local material = mi and mi.material or nil + +local uses = {} +local function add_use(workshop, task) + if not uses[workshop] then uses[workshop] = {} end + for _, v in ipairs(uses[workshop]) do if v == task then return end end + table.insert(uses[workshop], task) +end + +-- Helper: check material reaction product +local function mat_has_product(mat, pid) + if not mat then return false end + local ok, r = pcall(function() + for i = 0, #mat.reaction_product.id - 1 do + if tostring(mat.reaction_product.id[i].value) == pid then return true end + end + return false + end) + return ok and r or false +end + +-- Helper: check material reaction class +local function mat_has_class(mat, cname) + if not mat then return false end + local ok, r = pcall(function() + for i = 0, #mat.reaction_class - 1 do + if tostring(mat.reaction_class[i].value) == cname then return true end + end + return false + end) + return ok and r or false +end + +local function has_flag(name) + if not material then return false end + local ok, v = pcall(function() return material.flags[name] end) + return ok and v +end + +-- Map enum names to readable workshop names +local workshop_readable = { + None='Workshop', Carpenters="Carpenter's workshop", Farmers="Farmer's workshop", + Masons="Mason's workshop", Craftsdwarfs="Craftsdwarf's workshop", + Jewelers="Jeweler's workshop", MetalsmithsForge="Metalsmith's forge", + MagmaForge="Magma forge", Bowyers="Bowyer's workshop", + Mechanics="Mechanic's workshop", Siege='Siege workshop', + Butchers="Butcher's shop", Leatherworks='Leatherworks', + Tanners="Tanner's shop", Clothiers="Clothier's shop", + Fishery='Fishery', Still='Still', Loom='Loom', Quern='Quern', + Kennels='Kennels', Kitchen='Kitchen', Ashery='Ashery', + Dyers="Dyer's shop", Millstone='Millstone', Tool='Tool workshop', +} +local furnace_readable = { + WoodFurnace='Wood furnace', Smelter='Smelter', GlassFurnace='Glass furnace', + Kiln='Kiln', MagmaSmelter='Magma smelter', + MagmaGlassFurnace='Magma glass furnace', MagmaKiln='Magma kiln', +} + +local BTYPE_WORKSHOP = tonumber(df.building_type.Workshop) +local BTYPE_FURNACE = tonumber(df.building_type.Furnace) + +local function get_workshop_name(r) + local count = 0 + pcall(function() count = #r.building.type end) + if count == 0 then return 'Unknown workshop' end + + for idx = 0, count - 1 do + local name = nil + pcall(function() + local btype = tonumber(r.building.type[idx]) + local st = tonumber(r.building.subtype[idx]) + local custom = tonumber(r.building.custom[idx]) + + if btype == BTYPE_WORKSHOP then + local enum_name = df.workshop_type[st] + if enum_name and enum_name ~= 'Custom' then + name = workshop_readable[enum_name] or enum_name + elseif custom and custom >= 0 then + name = df.global.world.raws.buildings.all[custom].name + end + elseif btype == BTYPE_FURNACE then + local enum_name = df.furnace_type[st] + if enum_name and enum_name ~= 'Custom' then + name = furnace_readable[enum_name] or enum_name + elseif custom and custom >= 0 then + name = df.global.world.raws.buildings.all[custom].name + end + end + end) + if name and #name > 0 then return name end + end + return 'Unknown workshop' +end + +-- Container/tool reagent codes to skip +local skip_codes = { + ['barrel/pot']=true, ['barrel']=true, ['pot']=true, ['jug']=true, + ['container']=true, ['bucket']=true, ['bag']=true, ['empty container']=true, + ['lye-bearing item']=true, ['anvil']=true, ['die']=true, +} + +--------------------------------------------------------------------------- +-- 1. BUILT-IN USES (material flags) +--------------------------------------------------------------------------- +-- Raw material item types (where "make items from X" applies) +local raw_material_types = {} +for _, tname in ipairs({ + 'WOOD','BAR','BOULDER','BLOCKS','SKIN_TANNED','CLOTH','THREAD','ROUGH', + 'SMALLGEM','BONE','SHELL','GLOB','PLANT','PLANT_GROWTH','MEAT', + 'FISH_RAW','SEEDS','LIQUID_MISC','POWDER_MISC','CHEESE','EGG', +}) do + local v = df.item_type[tname] + if v then raw_material_types[v] = true end +end +local is_raw = raw_material_types[item_type] or false + +if has_flag('EDIBLE_RAW') then add_use('General', 'Eat raw') end +if has_flag('EDIBLE_COOKED') then + add_use('Kitchen', 'Cook in meal (easy/fine/lavish)') +end +if has_flag('ALCOHOL_PLANT') and is_raw then add_use('Still', 'Brew drink from plant') end +if has_flag('IS_DYE') then + add_use("Dyer's shop", 'Dye thread') + add_use("Dyer's shop", 'Dye cloth') +end +-- "Make items from X" uses gated behind raw material types +if has_flag('WOOD') and is_raw then + add_use("Carpenter's workshop", 'Make wooden items/furniture') + add_use('Wood furnace', 'Make charcoal/ash') +end +if has_flag('IS_METAL') then + if is_raw then add_use("Metalsmith's forge", 'Forge metal items') end + add_use('Smelter', 'Melt metal item') -- any metal item can be melted +end +if has_flag('IS_STONE') and is_raw then + add_use("Mason's workshop", 'Construct stone furniture/blocks') + add_use("Craftsdwarf's workshop", 'Make stone crafts') +end +if has_flag('LEATHER') and is_raw then add_use('Leatherworks', 'Make leather items') end +if has_flag('BONE') and is_raw then add_use("Craftsdwarf's workshop", 'Make bone crafts') end +if has_flag('SHELL') and is_raw then add_use("Craftsdwarf's workshop", 'Make shell crafts') end +if has_flag('POWDER_MISC_PLANT') and is_raw then add_use('Millstone/Quern', 'Mill plant') end +if has_flag('LIQUID_MISC_PLANT') and is_raw then add_use('Still', 'Extract from plants') end +if has_flag('SOAP') then add_use('Hospital', 'Use for cleaning') end +if has_flag('IS_GLASS') and is_raw then add_use('Glass furnace', 'Make glass items') end + +-- Encrusting: finished goods can be encrusted with gems at Jeweler's +if not is_raw and not (item_type == df.item_type.DRINK or item_type == df.item_type.COIN) then + add_use("Jeweler's workshop", 'Encrust with gem') +end + +-- Material reaction products +if mat_has_product(material, 'DRINK_MAT') then add_use('Still', 'Brew drink') end +if mat_has_product(material, 'BAG_ITEM') and is_raw then add_use("Farmer's workshop", 'Process plant to bag') end +if mat_has_product(material, 'THREAD') and is_raw then add_use("Farmer's workshop", 'Process plant to thread') end +if mat_has_product(material, 'MILL_MAT') and is_raw then add_use('Millstone/Quern', 'Mill into powder') end +if mat_has_product(material, 'PRESS_LIQUID_MAT') and is_raw then add_use('Screw press', 'Press liquid') end +if mat_has_product(material, 'CHEESE_MAT') and is_raw then add_use("Farmer's workshop", 'Make cheese') end +if mat_has_product(material, 'RENDER_MAT') then add_use('Kitchen', 'Render fat') end +if mat_has_product(material, 'SOAP_MAT') and is_raw then add_use("Soap maker's workshop", 'Make soap') end +if mat_has_product(material, 'DYE_MAT') and item_type == df.item_type.PLANT then + add_use('Millstone/Quern', 'Mill into dye') + add_use("Dyer's shop", 'Use as dye (after milling)') +end + +--------------------------------------------------------------------------- +-- 2. PLANT FLAGS +--------------------------------------------------------------------------- +if mi and mi.plant then + local function has_pflag(n) + local ok, v = pcall(function() return mi.plant.flags[n] end) + return ok and v + end + -- These plant flags only apply to PLANT items (not growths) + local is_plant = (item_type == df.item_type.PLANT) + if has_pflag('DRINK') and is_plant then add_use('Still', 'Brew drink from plant') end + if has_pflag('EDIBLE_GROWTH') and item_type == df.item_type.PLANT_GROWTH then + -- Growth is only cookable if its own material has EDIBLE_COOKED + if has_flag('EDIBLE_COOKED') or has_flag('EDIBLE_RAW') then + add_use('Kitchen', 'Cook in meal (edible growth)') + end + end + if has_pflag('MILL') and is_plant then add_use('Millstone/Quern', 'Mill plant') end + if has_pflag('THREAD') and is_plant then add_use("Farmer's workshop", 'Process to thread') end + if has_pflag('EXTRACT_BARREL') and is_plant then add_use('Still', 'Extract to barrel') end + if has_pflag('EXTRACT_VIAL') and is_plant then add_use('Still', 'Extract to vial') end + if has_pflag('DRY') and is_plant then add_use("Farmer's workshop", 'Process plant (dry)') end +end + +-- Growth material checks +if item_type == df.item_type.PLANT_GROWTH then + pcall(function() + local plant_raw = df.global.world.raws.plants.all[mat_index] + local growth = plant_raw.growths[item_subtype] + local gmi = dfhack.matinfo.decode(growth.mat_type, growth.mat_index) + if gmi and gmi.material then + -- DYE_MAT milling only applies to PLANT items, not growths + if mat_has_product(gmi.material, 'DRINK_MAT') then + add_use('Still', 'Brew drink from growth') + end + end + end) +end + +--------------------------------------------------------------------------- +-- 3. ITEM TYPE USES +--------------------------------------------------------------------------- +if item_type == df.item_type.PLANT then + add_use("Farmer's workshop", 'Process plant') +elseif item_type == df.item_type.SEEDS then + add_use('Farm plot', 'Plant seeds') +elseif item_type == df.item_type.BOULDER then + add_use("Mason's workshop", 'Construct furniture') + add_use("Craftsdwarf's workshop", 'Make crafts') +elseif item_type == df.item_type.ROUGH then + add_use("Jeweler's workshop", 'Cut rough gem') +elseif item_type == df.item_type.SMALLGEM then + add_use("Jeweler's workshop", 'Encrust with gem') +elseif item_type == df.item_type.WOOD then + add_use("Carpenter's workshop", 'Make wooden furniture/items') + add_use('Wood furnace', 'Make charcoal/ash') + add_use("Bowyer's workshop", 'Make crossbow') +elseif item_type == df.item_type.CLOTH then + add_use("Clothier's shop", 'Make clothing') + add_use("Dyer's shop", 'Dye cloth') +elseif item_type == df.item_type.THREAD then + add_use('Loom', 'Weave into cloth') + add_use("Dyer's shop", 'Dye thread') +elseif item_type == df.item_type.SKIN_TANNED then + add_use('Leatherworks', 'Make leather items') +elseif item_type == df.item_type.MEAT then + add_use('Kitchen', 'Cook in meal') +elseif item_type == df.item_type.FISH_RAW then + add_use('Fishery', 'Prepare raw fish') +elseif item_type == df.item_type.EGG then + add_use('Kitchen', 'Cook in meal') + add_use('Nest box', 'Hatch (if fertile)') +elseif item_type == df.item_type.GLOB then + add_use('Kitchen', 'Render fat / Cook tallow') +elseif item_type == df.item_type.CHEESE then + add_use('Kitchen', 'Cook in meal') +elseif item_type == df.item_type.DRINK then + add_use('Tavern', 'Drink') +elseif item_type == df.item_type.BAR then + if has_flag('IS_METAL') then + add_use("Metalsmith's forge", 'Forge weapons/armor/items') + end + if has_flag('SOAP') then add_use('Hospital', 'Cleaning') end +elseif item_type == df.item_type.BLOCKS then + add_use('Construction', 'Build walls/floors/stairs') +end + +add_use('Trade depot', 'Trade with merchants') + +--------------------------------------------------------------------------- +-- 4. REACTION MATCHING (with proper filtering) +--------------------------------------------------------------------------- +for _, r in ipairs(df.global.world.raws.reactions.reactions) do + -- Find the primary (first non-container) reagent and match against it + local primary_ir = nil + for _, reagent in ipairs(r.reagents) do + if df.reaction_reagent_itemst:is_instance(reagent) then + local code = '' + pcall(function() code = reagent.code end) + if not skip_codes[code] then + primary_ir = reagent + break + end + end + end + if not primary_ir then goto next_reaction end + + do + local ir = primary_ir + + -- Item type check + if ir.item_type ~= -1 and ir.item_type ~= item_type then goto next_reaction end + + -- Item subtype check + if ir.item_subtype ~= -1 and ir.item_subtype ~= item_subtype then goto next_reaction end + + -- Material type check + if ir.mat_type ~= -1 and ir.mat_type ~= mat_type then goto next_reaction end + + -- Material index check + if ir.mat_index ~= -1 and ir.mat_index ~= mat_index then goto next_reaction end + + -- If BOTH item_type and mat_type are -1 (accepts anything), + -- require at least has_material_reaction_product or reaction_class + local has_hmrp = false + local hmrp_val = nil + pcall(function() + if ir.has_material_reaction_product and #ir.has_material_reaction_product > 0 then + has_hmrp = true + hmrp_val = ir.has_material_reaction_product + end + end) + + local has_rc = false + local rc_val = nil + pcall(function() + if ir.reaction_class and #ir.reaction_class > 0 then + has_rc = true + rc_val = ir.reaction_class + end + end) + + -- Skip overly generic reagents (both type and mat are wildcard, no extra filters) + if ir.item_type == -1 and ir.mat_type == -1 and not has_hmrp and not has_rc then + goto next_reaction + end + + -- Verify has_material_reaction_product + if has_hmrp then + if not mat_has_product(material, hmrp_val) then goto next_reaction end + end + + -- Verify reaction_class + if has_rc then + if not mat_has_class(material, rc_val) then goto next_reaction end + end + + -- Match! + add_use(get_workshop_name(r), r.name) + end + + ::next_reaction:: +end + +--------------------------------------------------------------------------- +-- 5. OUTPUT +--------------------------------------------------------------------------- +print('') +print(('=== Uses for: %s ==='):format(desc)) +print((' Type: %s | Material: %s'):format( + df.item_type[item_type], mi and mi:getToken() or '?')) +print('') + +local names = {} +for n in pairs(uses) do table.insert(names, n) end +table.sort(names) + +local total = 0 +for _, ws in ipairs(names) do + local tasks = uses[ws] + table.sort(tasks) + print((' %s:'):format(ws)) + for _, t in ipairs(tasks) do + print((' - %s'):format(t)) + total = total + 1 + end +end +print(('\n Total: %d uses across %d workshops'):format(total, #names)) From a6843e06bb2b41234f45b0058249729c62725d5d Mon Sep 17 00:00:00 2001 From: sizzlins Date: Sat, 25 Apr 2026 10:55:29 +0700 Subject: [PATCH 2/4] docs: Add inline help and .rst documentation for item-uses --- docs/item-uses.rst | 22 ++++++++++++++++++++++ item-uses.lua | 25 ++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 docs/item-uses.rst diff --git a/docs/item-uses.rst b/docs/item-uses.rst new file mode 100644 index 0000000000..68344917ac --- /dev/null +++ b/docs/item-uses.rst @@ -0,0 +1,22 @@ +item-uses +========= + +.. dfhack-tool:: + :summary: Lists all workshops and tasks where a specific item can be used. + :tags: fort inspection + +This script analyzes the selected item and determines exactly which workshops +can accept it as a reagent, and what reactions or tasks can be performed with it. +It automatically distinguishes between raw materials (like ores or logs) and finished +goods, as well as checking for applicable tasks like encrusting, melting, and milling. + +Usage +----- + +:: + + item-uses + +Select an item in the game UI (e.g. using the ``k`` cursor, or viewing an item +in a stockpile or inventory) and run the command. The script will output a +categorized list of all compatible workshops and their relevant tasks. diff --git a/item-uses.lua b/item-uses.lua index d1ecdc8516..4c7cc36c45 100644 --- a/item-uses.lua +++ b/item-uses.lua @@ -1,4 +1,27 @@ --- item-uses: Shows what workshops and tasks can use the selected item +-- Lists all workshops and tasks where a specific item can be used. +--[====[ + +item-uses +========= + +Tags: fort | inspection + +This script analyzes the selected item and determines exactly which workshops +can accept it as a reagent, and what reactions or tasks can be performed with it. +It automatically distinguishes between raw materials (like ores or logs) and finished +goods, as well as checking for applicable tasks like encrusting, melting, and milling. + +Usage +----- + + item-uses + +Select an item in the game UI (e.g. using the ``k`` cursor, or viewing an item +in a stockpile or inventory) and run the command. The script will output a +categorized list of all compatible workshops and their relevant tasks. + +]====] + local item = dfhack.gui.getSelectedItem(true) if not item then qerror('Select an item first!') end From da0fd2fc6a76883eed9fdb5505071d313d8bb310 Mon Sep 17 00:00:00 2001 From: Sizzle Date: Wed, 24 Jun 2026 08:17:12 +0700 Subject: [PATCH 3/4] Refactor item-uses.lua: Strip unnecessary pcall wrappers for performance --- item-uses.lua | 110 +++++++++++++++++++++----------------------------- 1 file changed, 46 insertions(+), 64 deletions(-) diff --git a/item-uses.lua b/item-uses.lua index 4c7cc36c45..7c0d0c6532 100644 --- a/item-uses.lua +++ b/item-uses.lua @@ -43,31 +43,24 @@ end -- Helper: check material reaction product local function mat_has_product(mat, pid) if not mat then return false end - local ok, r = pcall(function() - for i = 0, #mat.reaction_product.id - 1 do - if tostring(mat.reaction_product.id[i].value) == pid then return true end - end - return false - end) - return ok and r or false + for i = 0, #mat.reaction_product.id - 1 do + if tostring(mat.reaction_product.id[i].value) == pid then return true end + end + return false end -- Helper: check material reaction class local function mat_has_class(mat, cname) if not mat then return false end - local ok, r = pcall(function() - for i = 0, #mat.reaction_class - 1 do - if tostring(mat.reaction_class[i].value) == cname then return true end - end - return false - end) - return ok and r or false + for i = 0, #mat.reaction_class - 1 do + if tostring(mat.reaction_class[i].value) == cname then return true end + end + return false end local function has_flag(name) if not material then return false end - local ok, v = pcall(function() return material.flags[name] end) - return ok and v + return material.flags[name] end -- Map enum names to readable workshop names @@ -93,33 +86,30 @@ local BTYPE_WORKSHOP = tonumber(df.building_type.Workshop) local BTYPE_FURNACE = tonumber(df.building_type.Furnace) local function get_workshop_name(r) - local count = 0 - pcall(function() count = #r.building.type end) + local count = #r.building.type if count == 0 then return 'Unknown workshop' end for idx = 0, count - 1 do local name = nil - pcall(function() - local btype = tonumber(r.building.type[idx]) - local st = tonumber(r.building.subtype[idx]) - local custom = tonumber(r.building.custom[idx]) - - if btype == BTYPE_WORKSHOP then - local enum_name = df.workshop_type[st] - if enum_name and enum_name ~= 'Custom' then - name = workshop_readable[enum_name] or enum_name - elseif custom and custom >= 0 then - name = df.global.world.raws.buildings.all[custom].name - end - elseif btype == BTYPE_FURNACE then - local enum_name = df.furnace_type[st] - if enum_name and enum_name ~= 'Custom' then - name = furnace_readable[enum_name] or enum_name - elseif custom and custom >= 0 then - name = df.global.world.raws.buildings.all[custom].name - end + local btype = tonumber(r.building.type[idx]) + local st = tonumber(r.building.subtype[idx]) + local custom = tonumber(r.building.custom[idx]) + + if btype == BTYPE_WORKSHOP then + local enum_name = df.workshop_type[st] + if enum_name and enum_name ~= 'Custom' then + name = workshop_readable[enum_name] or enum_name + elseif custom and custom >= 0 then + name = df.global.world.raws.buildings.all[custom].name + end + elseif btype == BTYPE_FURNACE then + local enum_name = df.furnace_type[st] + if enum_name and enum_name ~= 'Custom' then + name = furnace_readable[enum_name] or enum_name + elseif custom and custom >= 0 then + name = df.global.world.raws.buildings.all[custom].name end - end) + end if name and #name > 0 then return name end end return 'Unknown workshop' @@ -201,8 +191,7 @@ end --------------------------------------------------------------------------- if mi and mi.plant then local function has_pflag(n) - local ok, v = pcall(function() return mi.plant.flags[n] end) - return ok and v + return mi.plant.flags[n] end -- These plant flags only apply to PLANT items (not growths) local is_plant = (item_type == df.item_type.PLANT) @@ -222,17 +211,15 @@ end -- Growth material checks if item_type == df.item_type.PLANT_GROWTH then - pcall(function() - local plant_raw = df.global.world.raws.plants.all[mat_index] - local growth = plant_raw.growths[item_subtype] - local gmi = dfhack.matinfo.decode(growth.mat_type, growth.mat_index) - if gmi and gmi.material then - -- DYE_MAT milling only applies to PLANT items, not growths - if mat_has_product(gmi.material, 'DRINK_MAT') then - add_use('Still', 'Brew drink from growth') - end + local plant_raw = df.global.world.raws.plants.all[mat_index] + local growth = plant_raw.growths[item_subtype] + local gmi = dfhack.matinfo.decode(growth.mat_type, growth.mat_index) + if gmi and gmi.material then + -- DYE_MAT milling only applies to PLANT items, not growths + if mat_has_product(gmi.material, 'DRINK_MAT') then + add_use('Still', 'Brew drink from growth') end - end) + end end --------------------------------------------------------------------------- @@ -293,8 +280,7 @@ for _, r in ipairs(df.global.world.raws.reactions.reactions) do local primary_ir = nil for _, reagent in ipairs(r.reagents) do if df.reaction_reagent_itemst:is_instance(reagent) then - local code = '' - pcall(function() code = reagent.code end) + local code = reagent.code if not skip_codes[code] then primary_ir = reagent break @@ -322,21 +308,17 @@ for _, r in ipairs(df.global.world.raws.reactions.reactions) do -- require at least has_material_reaction_product or reaction_class local has_hmrp = false local hmrp_val = nil - pcall(function() - if ir.has_material_reaction_product and #ir.has_material_reaction_product > 0 then - has_hmrp = true - hmrp_val = ir.has_material_reaction_product - end - end) + if ir.has_material_reaction_product and #ir.has_material_reaction_product > 0 then + has_hmrp = true + hmrp_val = ir.has_material_reaction_product + end local has_rc = false local rc_val = nil - pcall(function() - if ir.reaction_class and #ir.reaction_class > 0 then - has_rc = true - rc_val = ir.reaction_class - end - end) + if ir.reaction_class and #ir.reaction_class > 0 then + has_rc = true + rc_val = ir.reaction_class + end -- Skip overly generic reagents (both type and mat are wildcard, no extra filters) if ir.item_type == -1 and ir.mat_type == -1 and not has_hmrp and not has_rc then From e50bc81eb5d18f42fc5f03b925b9704486e94020 Mon Sep 17 00:00:00 2001 From: sizzlins Date: Wed, 24 Jun 2026 23:36:59 +0700 Subject: [PATCH 4/4] Refactor: Strip out redundant logic and reaction conditionals --- item-uses.lua | 54 +++++++-------------------------------------------- 1 file changed, 7 insertions(+), 47 deletions(-) diff --git a/item-uses.lua b/item-uses.lua index 7c0d0c6532..183a8a41e1 100644 --- a/item-uses.lua +++ b/item-uses.lua @@ -150,6 +150,7 @@ end if has_flag('WOOD') and is_raw then add_use("Carpenter's workshop", 'Make wooden items/furniture') add_use('Wood furnace', 'Make charcoal/ash') + add_use("Bowyer's workshop", 'Make crossbow') end if has_flag('IS_METAL') then if is_raw then add_use("Metalsmith's forge", 'Forge metal items') end @@ -225,47 +226,24 @@ end --------------------------------------------------------------------------- -- 3. ITEM TYPE USES --------------------------------------------------------------------------- -if item_type == df.item_type.PLANT then - add_use("Farmer's workshop", 'Process plant') -elseif item_type == df.item_type.SEEDS then +if item_type == df.item_type.SEEDS then add_use('Farm plot', 'Plant seeds') -elseif item_type == df.item_type.BOULDER then - add_use("Mason's workshop", 'Construct furniture') - add_use("Craftsdwarf's workshop", 'Make crafts') elseif item_type == df.item_type.ROUGH then add_use("Jeweler's workshop", 'Cut rough gem') elseif item_type == df.item_type.SMALLGEM then add_use("Jeweler's workshop", 'Encrust with gem') -elseif item_type == df.item_type.WOOD then - add_use("Carpenter's workshop", 'Make wooden furniture/items') - add_use('Wood furnace', 'Make charcoal/ash') - add_use("Bowyer's workshop", 'Make crossbow') elseif item_type == df.item_type.CLOTH then add_use("Clothier's shop", 'Make clothing') add_use("Dyer's shop", 'Dye cloth') elseif item_type == df.item_type.THREAD then add_use('Loom', 'Weave into cloth') add_use("Dyer's shop", 'Dye thread') -elseif item_type == df.item_type.SKIN_TANNED then - add_use('Leatherworks', 'Make leather items') -elseif item_type == df.item_type.MEAT then - add_use('Kitchen', 'Cook in meal') elseif item_type == df.item_type.FISH_RAW then add_use('Fishery', 'Prepare raw fish') elseif item_type == df.item_type.EGG then - add_use('Kitchen', 'Cook in meal') add_use('Nest box', 'Hatch (if fertile)') -elseif item_type == df.item_type.GLOB then - add_use('Kitchen', 'Render fat / Cook tallow') -elseif item_type == df.item_type.CHEESE then - add_use('Kitchen', 'Cook in meal') elseif item_type == df.item_type.DRINK then add_use('Tavern', 'Drink') -elseif item_type == df.item_type.BAR then - if has_flag('IS_METAL') then - add_use("Metalsmith's forge", 'Forge weapons/armor/items') - end - if has_flag('SOAP') then add_use('Hospital', 'Cleaning') end elseif item_type == df.item_type.BLOCKS then add_use('Construction', 'Build walls/floors/stairs') end @@ -306,34 +284,16 @@ for _, r in ipairs(df.global.world.raws.reactions.reactions) do -- If BOTH item_type and mat_type are -1 (accepts anything), -- require at least has_material_reaction_product or reaction_class - local has_hmrp = false - local hmrp_val = nil - if ir.has_material_reaction_product and #ir.has_material_reaction_product > 0 then - has_hmrp = true - hmrp_val = ir.has_material_reaction_product - end - - local has_rc = false - local rc_val = nil - if ir.reaction_class and #ir.reaction_class > 0 then - has_rc = true - rc_val = ir.reaction_class - end + local hmrp = ir.has_material_reaction_product ~= "" and ir.has_material_reaction_product or nil + local rc = ir.reaction_class ~= "" and ir.reaction_class or nil -- Skip overly generic reagents (both type and mat are wildcard, no extra filters) - if ir.item_type == -1 and ir.mat_type == -1 and not has_hmrp and not has_rc then + if ir.item_type == -1 and ir.mat_type == -1 and not hmrp and not rc then goto next_reaction end - -- Verify has_material_reaction_product - if has_hmrp then - if not mat_has_product(material, hmrp_val) then goto next_reaction end - end - - -- Verify reaction_class - if has_rc then - if not mat_has_class(material, rc_val) then goto next_reaction end - end + if hmrp and not mat_has_product(material, hmrp) then goto next_reaction end + if rc and not mat_has_class(material, rc) then goto next_reaction end -- Match! add_use(get_workshop_name(r), r.name)