diff --git a/library/lua/dfhack/buildings.lua b/library/lua/dfhack/buildings.lua index 9650dc4cd..eb0a6a5b5 100644 --- a/library/lua/dfhack/buildings.lua +++ b/library/lua/dfhack/buildings.lua @@ -2,6 +2,336 @@ local dfhack = dfhack local _ENV = dfhack.BASE_G local buildings = dfhack.buildings +local utils = require 'utils' + +--[[ Building input material tables. ]] + +local building_inputs = { + [df.building_type.Chair] = { { item_type=df.item_type.CHAIR, vector_id=df.job_item_vector_id.CHAIR } }, + [df.building_type.Bed] = { { item_type=df.item_type.BED, vector_id=df.job_item_vector_id.BED } }, + [df.building_type.Table] = { { item_type=df.item_type.TABLE, vector_id=df.job_item_vector_id.TABLE } }, + [df.building_type.Coffin] = { { item_type=df.item_type.COFFIN, vector_id=df.job_item_vector_id.COFFIN } }, + [df.building_type.FarmPlot] = { }, + [df.building_type.Furnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, + [df.building_type.TradeDepot] = { { flags2={ building_material=true, non_economic=true }, quantity=3 } }, + [df.building_type.Door] = { { item_type=df.item_type.DOOR, vector_id=df.job_item_vector_id.DOOR } }, + [df.building_type.Floodgate] = { + { + item_type=df.item_type.FLOODGATE, + vector_id=df.job_item_vector_id.FLOODGATE + } + }, + [df.building_type.Box] = { + { + flags1={ empty=true }, + item_type=df.item_type.BOX, + vector_id=df.job_item_vector_id.BOX + } + }, + [df.building_type.Weaponrack] = { + { + item_type=df.item_type.WEAPONRACK, + vector_id=df.job_item_vector_id.WEAPONRACK + } + }, + [df.building_type.Armorstand] = { + { + item_type=df.item_type.ARMORSTAND, + vector_id=df.job_item_vector_id.ARMORSTAND + } + }, + [df.building_type.Workshop] = { { flags2={ building_material=true, non_economic=true } } }, + [df.building_type.Cabinet] = { + { item_type=df.item_type.CABINET, vector_id=df.job_item_vector_id.CABINET } + }, + [df.building_type.Statue] = { { item_type=df.item_type.STATUE, vector_id=df.job_item_vector_id.STATUE } }, + [df.building_type.WindowGlass] = { { item_type=df.item_type.WINDOW, vector_id=df.job_item_vector_id.WINDOW } }, + [df.building_type.WindowGem] = { + { + item_type=df.item_type.SMALLGEM, + quantity=3, + vector_id=df.job_item_vector_id.ANY_GENERIC35 + } + }, + [df.building_type.Well] = { + { + item_type=df.item_type.BLOCKS, + vector_id=df.job_item_vector_id.ANY_GENERIC35 + }, + { + name='bucket', + flags2={ lye_milk_free=true }, + item_type=df.item_type.BUCKET, + vector_id=df.job_item_vector_id.BUCKET + }, + { + name='chain', + item_type=df.item_type.CHAIN, + vector_id=df.job_item_vector_id.CHAIN + }, + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + }, + [df.building_type.Bridge] = { { flags2={ building_material=true, non_economic=true }, quantity=-1 } }, + [df.building_type.RoadDirt] = { }, + [df.building_type.RoadPaved] = { { flags2={ building_material=true, non_economic=true }, quantity=-1 } }, + [df.building_type.AnimalTrap] = { + { + flags1={ empty=true }, + item_type=df.item_type.ANIMALTRAP, + vector_id=df.job_item_vector_id.ANIMALTRAP + } + }, + [df.building_type.Support] = { { flags2={ building_material=true, non_economic=true } } }, + [df.building_type.ArcheryTarget] = { { flags2={ building_material=true, non_economic=true } } }, + [df.building_type.Chain] = { { item_type=df.item_type.CHAIN, vector_id=df.job_item_vector_id.CHAIN } }, + [df.building_type.Cage] = { { item_type=df.item_type.CAGE, vector_id=df.job_item_vector_id.CAGE } }, + [df.building_type.Weapon] = { { vector_id=df.job_item_vector_id.ANY_SPIKE } }, + [df.building_type.ScrewPump] = { + { + item_type=df.item_type.BLOCKS, + vector_id=df.job_item_vector_id.ANY_GENERIC35 + }, + { + name='screw', + flags2={ screw=true }, + item_type=df.item_type.TRAPCOMP, + vector_id=df.job_item_vector_id.ANY_WEAPON + }, + { + name='pipe', + item_type=df.item_type.PIPE_SECTION, + vector_id=df.job_item_vector_id.PIPE_SECTION + } + }, + [df.building_type.Construction] = { { flags2={ building_material=true, non_economic=true } } }, + [df.building_type.Hatch] = { + { + item_type=df.item_type.HATCH_COVER, + vector_id=df.job_item_vector_id.HATCH_COVER + } + }, + [df.building_type.GrateWall] = { { item_type=df.item_type.GRATE, vector_id=df.job_item_vector_id.GRATE } }, + [df.building_type.GrateFloor] = { { item_type=df.item_type.GRATE, vector_id=df.job_item_vector_id.GRATE } }, + [df.building_type.BarsVertical] = { + { item_type=df.item_type.BAR, vector_id=df.job_item_vector_id.ANY_GENERIC35 } + }, + [df.building_type.BarsFloor] = { + { item_type=df.item_type.BAR, vector_id=df.job_item_vector_id.ANY_GENERIC35 } + }, + [df.building_type.GearAssembly] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + }, + [df.building_type.AxleHorizontal] = { + { item_type=df.item_type.WOOD, vector_id=df.job_item_vector_id.WOOD, quantity=-1 } + }, + [df.building_type.AxleVertical] = { { item_type=df.item_type.WOOD, vector_id=df.job_item_vector_id.WOOD } }, + [df.building_type.WaterWheel] = { + { + item_type=df.item_type.WOOD, + quantity=3, + vector_id=df.job_item_vector_id.WOOD + } + }, + [df.building_type.Windmill] = { + { + item_type=df.item_type.WOOD, + quantity=4, + vector_id=df.job_item_vector_id.WOOD + } + }, + [df.building_type.TractionBench] = { + { + item_type=df.item_type.TRACTION_BENCH, + vector_id=df.job_item_vector_id.TRACTION_BENCH + } + }, + [df.building_type.Slab] = { { item_type=df.item_type.SLAB } }, + [df.building_type.NestBox] = { { has_tool_use=df.tool_uses.NEST_BOX, item_type=df.item_type.TOOL } }, + [df.building_type.Hive] = { { has_tool_use=df.tool_uses.HIVE, item_type=df.item_type.TOOL } } +} + +local furnace_inputs = { + [df.furnace_type.WoodFurnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, + [df.furnace_type.Smelter] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, + [df.furnace_type.GlassFurnace] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, + [df.furnace_type.Kiln] = { { flags2={ building_material=true, fire_safe=true, non_economic=true } } }, + [df.furnace_type.MagmaSmelter] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } }, + [df.furnace_type.MagmaGlassFurnace] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } }, + [df.furnace_type.MagmaKiln] = { { flags2={ building_material=true, magma_safe=true, non_economic=true } } } +} + +local workshop_inputs = { + [df.workshop_type.Carpenters] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Farmers] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Masons] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Craftsdwarfs] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Jewelers] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.MetalsmithsForge] = { + { + name='anvil', + flags2={ fire_safe=true }, + item_type=df.item_type.ANVIL, + vector_id=df.job_item_vector_id.ANVIL + }, + { flags2={ building_material=true, fire_safe=true, non_economic=true } } + }, + [df.workshop_type.MagmaForge] = { + { + name='anvil', + flags2={ magma_safe=true }, + item_type=df.item_type.ANVIL, + vector_id=df.job_item_vector_id.ANVIL + }, + { flags2={ building_material=true, magma_safe=true, non_economic=true } } + }, + [df.workshop_type.Bowyers] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Mechanics] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Siege] = { { flags2={ building_material=true, non_economic=true }, quantity=3 } }, + [df.workshop_type.Butchers] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Leatherworks] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Tanners] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Clothiers] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Fishery] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Still] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Loom] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Quern] = { { item_type=df.item_type.QUERN, vector_id=df.job_item_vector_id.QUERN } }, + [df.workshop_type.Kennels] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Kitchen] = { { flags2={ building_material=true, non_economic=true } } }, + [df.workshop_type.Ashery] = { + { + item_type=df.item_type.BLOCKS, + vector_id=df.job_item_vector_id.ANY_GENERIC35 + }, + { + name='barrel', + flags1={ empty=true }, + item_type=df.item_type.BARREL, + vector_id=df.job_item_vector_id.BARREL + }, + { + name='bucket', + flags2={ lye_milk_free=true }, + item_type=df.item_type.BUCKET, + vector_id=df.job_item_vector_id.BUCKET + } + }, + [df.workshop_type.Dyers] = { + { + name='barrel', + flags1={ empty=true }, + item_type=df.item_type.BARREL, + vector_id=df.job_item_vector_id.BARREL + }, + { + name='bucket', + flags2={ lye_milk_free=true }, + item_type=df.item_type.BUCKET, + vector_id=df.job_item_vector_id.BUCKET + } + }, + [df.workshop_type.Millstone] = { + { + item_type=df.item_type.MILLSTONE, + vector_id=df.job_item_vector_id.MILLSTONE + }, + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + } +} + +local trap_inputs = { + [df.trap_type.StoneFallTrap] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + }, + [df.trap_type.WeaponTrap] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + }, + { vector_id=df.job_item_vector_id.ANY_WEAPON } + }, + [df.trap_type.Lever] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + }, + [df.trap_type.PressurePlate] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + }, + [df.trap_type.CageTrap] = { + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS + } + } +} + +local function get_inputs_by_type(type,subtype,custom) + if type == df.building_type.Workshop then + return workshop_inputs[subtype] + elseif type == df.building_type.Furnace then + return furnace_inputs[subtype] + elseif type == df.building_type.Trap then + return trap_inputs[subtype] + else + return building_inputs[type] + end +end + +local function augment_input(input, argtable) + local rv = {} + local arg = argtable[input.name or 'material'] + + if arg then + utils.assign(rv, arg) + end + + utils.assign(rv, input) + + if rv.mat_index and safe_index(rv, 'flags2', 'non_economic') then + rv.flags2.non_economic = false + end + + rv.new = true + rv.name = nil + return rv +end + +function buildings.getFiltersByType(argtable,type,subtype,custom) + local inputs = get_inputs_by_type(type,subtype,custom) + if not inputs then + return nil + end + local rv = {} + for i,v in ipairs(inputs) do + rv[i] = augment_input(v, argtable) + end + return rv +end + --[[ Wraps all steps necessary to create a building with a construct job into one function. @@ -30,6 +360,10 @@ local buildings = dfhack.buildings filter = { { ... }, { ... }... } -- OR abstract = true + -- OR + material = { filter_properties... } + mechanism = { filter_properties... } + barrel, bucket, chain, anvil, screw, pipe } Returns: the created building, or 'nil, error' @@ -39,14 +373,18 @@ function buildings.constructBuilding(info) local btype = info.type local subtype = info.subtype or -1 local custom = info.custom or -1 + local filters = info.filters if not (info.pos or info.x) then error('position is required') end - if not (info.abstract or info.items or info.filters) then - error('one of items, filters or abstract are required') - elseif info.filters then - for _,v in ipairs(info.filters) do + if not (info.abstract or info.items or filters) then + filters = buildings.getFiltersByType(info,btype,subtype,custom) + if not filters then + error('one of items, filters or abstract is required') + end + elseif filters then + for _,v in ipairs(filters) do v.new = true end end @@ -84,7 +422,7 @@ function buildings.constructBuilding(info) elseif info.items then ok = buildings.constructWithItems(instance, info.items) else - ok = buildings.constructWithFilters(instance, info.filters) + ok = buildings.constructWithFilters(instance, filters) end if not ok then return nil, "could not construct the building" diff --git a/library/lua/utils.lua b/library/lua/utils.lua index 00e42f9da..3eeb0cc6f 100644 --- a/library/lua/utils.lua +++ b/library/lua/utils.lua @@ -111,4 +111,29 @@ function make_sort_order(data,ordering) return index end +--[[ + Recursively assign data into a table. +--]] +function assign(tgt,src) + if df.isvalid(tgt) == 'ref' then + df.assign(tgt, src) + elseif type(tgt) == 'table' then + for k,v in pairs(src) do + if type(v) == 'table' or df.isvalid(v) == 'ref' then + local cv = tgt[k] + if cv == nil then + cv = {} + tgt[k] = cv + end + assign(cv, v) + else + tgt[k] = v + end + end + else + error('Invalid assign target type: '..tostring(tgt)) + end + return tgt +end + return _ENV \ No newline at end of file