From 8f517802a6ce36860d954790a783797e0928153b Mon Sep 17 00:00:00 2001 From: Nicholas McDaniel Date: Wed, 5 Nov 2025 00:17:58 -0500 Subject: [PATCH 1/2] Add support for new siege update buildings --- docs/changelog.txt | 2 ++ library/lua/dfhack/buildings.lua | 26 +++++++++++++++- plugins/lua/buildingplan/planneroverlay.lua | 34 +++++++++++++++++++-- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/docs/changelog.txt b/docs/changelog.txt index 537686fb9a..0037d2515a 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -59,8 +59,10 @@ Template for new versions: ## New Features ## Fixes +- `buildingplan`: Building costs for reinforced walls are now correct. ## Misc Improvements +- `buildingplan`: Added support for bolt throwers and siege engine rotation. ## Documentation diff --git a/library/lua/dfhack/buildings.lua b/library/lua/dfhack/buildings.lua index 7fff00dcf2..5ea3b1f758 100644 --- a/library/lua/dfhack/buildings.lua +++ b/library/lua/dfhack/buildings.lua @@ -130,7 +130,6 @@ local building_inputs = { 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, @@ -353,6 +352,26 @@ local siegeengine_input = { quantity=3 } }, + [df.siegeengine_type.BoltThrower] = { + { + item_type=df.item_type.BOLT_THROWER_PARTS, + vector_id=df.job_item_vector_id.BOLT_THROWER_PARTS, + }, + { + item_type=df.item_type.BIN, + vector_id=df.job_item_vector_id.BIN, + }, + { + name='mechanism', + item_type=df.item_type.TRAPPARTS, + vector_id=df.job_item_vector_id.TRAPPARTS, + }, + { + name='chain', + item_type=df.item_type.CHAIN, + vector_id=df.job_item_vector_id.CHAIN + }, + }, } --[[ Functions for lookup in tables. ]] @@ -380,6 +399,11 @@ local function get_inputs_by_type(type,subtype,custom) return trap_inputs[subtype] elseif type == df.building_type.SiegeEngine then return siegeengine_input[subtype] + elseif type == df.building_type.Construction then + if subtype == df.construction_type.ReinforcedWall then + return { { flags2={ building_material=true, non_economic=true }, quantity=2 }, { flags3={ metal=true }, item_type=df.item_type.BAR, vector_id=df.job_item_vector_id.BAR } } + end + return { { flags2={ building_material=true, non_economic=true } } } else return building_inputs[type] end diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index f92dc95d84..72987590fb 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -119,6 +119,10 @@ local function is_construction() return uibs.building_type == df.building_type.Construction end +local function is_siege_engine() + return uibs.building_type == df.building_type.SiegeEngine +end + local function tile_is_construction(pos) local tt = dfhack.maps.getTileType(pos) if not tt then return false end @@ -605,7 +609,7 @@ function PlannerOverlay:init() local main_panel = widgets.Panel{ view_id='main', - frame={t=1, l=0, r=0, h=14}, + frame={t=1, l=0, r=0, h=15}, frame_style=gui.FRAME_INTERIOR_MEDIUM, frame_background=gui.CLEAR_PEN, visible=self:callback('is_not_minimized'), @@ -743,6 +747,24 @@ function PlannerOverlay:init() buildingplan.setSpecial(uibs.building_type, uibs.building_subtype, uibs.custom_type, 'engraved', val) end, }, + widgets.CycleHotkeyLabel { + view_id='siege_facing', + frame = {b=4, l=1, w=28}, + key='CUSTOM_T', + key_back='CUSTOM_SHIFT_T', + label='Facing:', + visible=is_siege_engine, + options={ + {label='North',value=0}, + {label='Northeast',value=1}, + {label='East',value=2}, + {label='Southeast',value=3}, + {label='South',value=4}, + {label='Southwest',value=5}, + {label='West',value=6}, + {label='Northwest',value=7}, + }, + }, widgets.ToggleHotkeyLabel { view_id='empty', frame={b=4, l=1, w=22}, @@ -829,7 +851,7 @@ function PlannerOverlay:init() } local divider_widget = widgets.Divider{ - frame={t=10, l=0, r=0, h=1}, + frame={t=11, l=0, r=0, h=1}, frame_style=gui.FRAME_INTERIOR_MEDIUM, visible=self:callback('is_not_minimized'), } @@ -1307,10 +1329,16 @@ function PlannerOverlay:place_building(placement_data, chosen_items) if is_stairs() then subtype = self:get_stairs_subtype(pos, pd) end + local fields = {} + if is_siege_engine() then + local val = self.subviews.siege_facing:getOptionValue() + fields.facing = val + fields.resting_orientation = val + end local bld, err = dfhack.buildings.constructBuilding{pos=pos, type=uibs.building_type, subtype=subtype, custom=uibs.custom_type, width=pd.width, height=pd.height, - direction=uibs.direction, filters=filters} + direction=uibs.direction, filters=filters, fields=fields} if err then -- it's ok if some buildings fail to build goto continue From dc7057fb22722fe06be5bdd4742064ac1ea0449d Mon Sep 17 00:00:00 2001 From: Nicholas McDaniel Date: Wed, 5 Nov 2025 08:42:18 -0500 Subject: [PATCH 2/2] Use vanilla direction interface for siege engine facing direction --- plugins/lua/buildingplan/planneroverlay.lua | 29 +++++---------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/plugins/lua/buildingplan/planneroverlay.lua b/plugins/lua/buildingplan/planneroverlay.lua index 72987590fb..f0fbe17de4 100644 --- a/plugins/lua/buildingplan/planneroverlay.lua +++ b/plugins/lua/buildingplan/planneroverlay.lua @@ -239,6 +239,7 @@ local direction_panel_types = utils.invert{ df.building_type.WaterWheel, df.building_type.AxleHorizontal, df.building_type.Rollers, + df.building_type.SiegeEngine, } local function has_direction_panel() @@ -609,7 +610,7 @@ function PlannerOverlay:init() local main_panel = widgets.Panel{ view_id='main', - frame={t=1, l=0, r=0, h=15}, + frame={t=1, l=0, r=0, h=14}, frame_style=gui.FRAME_INTERIOR_MEDIUM, frame_background=gui.CLEAR_PEN, visible=self:callback('is_not_minimized'), @@ -747,24 +748,6 @@ function PlannerOverlay:init() buildingplan.setSpecial(uibs.building_type, uibs.building_subtype, uibs.custom_type, 'engraved', val) end, }, - widgets.CycleHotkeyLabel { - view_id='siege_facing', - frame = {b=4, l=1, w=28}, - key='CUSTOM_T', - key_back='CUSTOM_SHIFT_T', - label='Facing:', - visible=is_siege_engine, - options={ - {label='North',value=0}, - {label='Northeast',value=1}, - {label='East',value=2}, - {label='Southeast',value=3}, - {label='South',value=4}, - {label='Southwest',value=5}, - {label='West',value=6}, - {label='Northwest',value=7}, - }, - }, widgets.ToggleHotkeyLabel { view_id='empty', frame={b=4, l=1, w=22}, @@ -851,7 +834,7 @@ function PlannerOverlay:init() } local divider_widget = widgets.Divider{ - frame={t=11, l=0, r=0, h=1}, + frame={t=10, l=0, r=0, h=1}, frame_style=gui.FRAME_INTERIOR_MEDIUM, visible=self:callback('is_not_minimized'), } @@ -1331,9 +1314,9 @@ function PlannerOverlay:place_building(placement_data, chosen_items) end local fields = {} if is_siege_engine() then - local val = self.subviews.siege_facing:getOptionValue() - fields.facing = val - fields.resting_orientation = val + local facing = df.global.buildreq.direction + fields.facing = facing + fields.resting_orientation = facing end local bld, err = dfhack.buildings.constructBuilding{pos=pos, type=uibs.building_type, subtype=subtype, custom=uibs.custom_type,