|
| 1 | +-- List and pull levers |
| 2 | + |
| 3 | +local gui = require('gui') |
| 4 | +local guidm = require('gui.dwarfmode') |
| 5 | +local utils = require('utils') |
| 6 | +local widgets = require('gui.widgets') |
| 7 | + |
| 8 | +local lever_script = reqscript('lever') |
| 9 | + |
| 10 | +local REFRESH_MS = 1000 |
| 11 | + |
| 12 | +local function get_levers() |
| 13 | + local levers = {} |
| 14 | + for _, building in ipairs(df.global.world.buildings.other.TRAP) do |
| 15 | + if building.trap_type == df.trap_type.Lever then |
| 16 | + table.insert(levers, building) |
| 17 | + end |
| 18 | + end |
| 19 | + return levers |
| 20 | +end |
| 21 | + |
| 22 | +local function get_lever_label(lever) |
| 23 | + local status = (lever.state == 1) and 'Pulled' or 'Not Pulled' |
| 24 | + local name = utils.getBuildingName(lever) |
| 25 | + local queued = 0 |
| 26 | + for _, job in ipairs(lever.jobs) do |
| 27 | + if job.job_type == df.job_type.PullLever then |
| 28 | + queued = queued + 1 |
| 29 | + end |
| 30 | + end |
| 31 | + local queued_text = queued > 0 and (' (queued: %d)'):format(queued) or '' |
| 32 | + return ('[%s] %s (#%d)%s'):format(status, name, lever.id, queued_text) |
| 33 | +end |
| 34 | + |
| 35 | +local function get_queued_count(levers) |
| 36 | + local queued = 0 |
| 37 | + for _, lever in ipairs(levers) do |
| 38 | + for _, job in ipairs(lever.jobs) do |
| 39 | + if job.job_type == df.job_type.PullLever then |
| 40 | + queued = queued + 1 |
| 41 | + end |
| 42 | + end |
| 43 | + end |
| 44 | + return queued |
| 45 | +end |
| 46 | + |
| 47 | +LeverWindow = defclass(LeverWindow, widgets.Window) |
| 48 | +LeverWindow.ATTRS{ |
| 49 | + frame_title = 'Lever Tasks', |
| 50 | + frame = {w=60, h=18, r=2}, |
| 51 | +} |
| 52 | + |
| 53 | +function LeverWindow:init() |
| 54 | + local _, screen_height = dfhack.screen.getWindowSize() |
| 55 | + if screen_height then |
| 56 | + self.frame.t = math.max(0, math.floor((screen_height - self.frame.h) / 2)) |
| 57 | + end |
| 58 | + self.next_refresh_ms = dfhack.getTickCount() + REFRESH_MS |
| 59 | + self.filter_text = '' |
| 60 | + self:addviews{ |
| 61 | + widgets.EditField{ |
| 62 | + view_id='search', |
| 63 | + frame={t=0, l=0, r=0}, |
| 64 | + label_text='Search: ', |
| 65 | + on_change=self:callback('set_filter'), |
| 66 | + }, |
| 67 | + widgets.List{ |
| 68 | + view_id='lever_list', |
| 69 | + frame={t=1, l=0, r=0, b=4}, |
| 70 | + on_submit=self:callback('queue_pull'), |
| 71 | + on_select=self:callback('focus_lever'), |
| 72 | + }, |
| 73 | + widgets.Label{ |
| 74 | + view_id='empty_message', |
| 75 | + frame={t=1, l=0, r=0}, |
| 76 | + text='No levers found.', |
| 77 | + visible=false, |
| 78 | + }, |
| 79 | + widgets.HotkeyLabel{ |
| 80 | + frame={b=3, l=0}, |
| 81 | + label='Pull selected lever', |
| 82 | + key='CUSTOM_P', |
| 83 | + on_activate=self:callback('queue_pull'), |
| 84 | + }, |
| 85 | + widgets.HotkeyLabel{ |
| 86 | + frame={b=2, l=0}, |
| 87 | + label='Remove queued pulls', |
| 88 | + key='CUSTOM_X', |
| 89 | + on_activate=self:callback('remove_queued_pulls'), |
| 90 | + }, |
| 91 | + widgets.Label{ |
| 92 | + view_id='queued_count', |
| 93 | + frame={b=3, r=0}, |
| 94 | + text='Queued pulls: 0', |
| 95 | + auto_width=true, |
| 96 | + }, |
| 97 | + widgets.HotkeyLabel{ |
| 98 | + frame={b=1, l=0}, |
| 99 | + label='Refresh list', |
| 100 | + key='CUSTOM_R', |
| 101 | + on_activate=self:callback('refresh_list'), |
| 102 | + }, |
| 103 | + } |
| 104 | + |
| 105 | + self:refresh_list() |
| 106 | +end |
| 107 | + |
| 108 | +function LeverWindow:set_filter(text) |
| 109 | + self.filter_text = text or '' |
| 110 | + self:refresh_list() |
| 111 | +end |
| 112 | + |
| 113 | +function LeverWindow:refresh_list() |
| 114 | + local list = self.subviews.lever_list |
| 115 | + local selected_id |
| 116 | + if list then |
| 117 | + local _, selected = list:getSelected() |
| 118 | + if selected and selected.data then |
| 119 | + selected_id = selected.data.id |
| 120 | + end |
| 121 | + end |
| 122 | + |
| 123 | + local choices = {} |
| 124 | + local levers = get_levers() |
| 125 | + table.sort(levers, function(a, b) |
| 126 | + if a.state == b.state then |
| 127 | + return a.id < b.id |
| 128 | + end |
| 129 | + return a.state > b.state |
| 130 | + end) |
| 131 | + local filter = (self.filter_text or ''):lower() |
| 132 | + local filtered_levers = {} |
| 133 | + if filter == '' then |
| 134 | + filtered_levers = levers |
| 135 | + else |
| 136 | + for _, lever in ipairs(levers) do |
| 137 | + local name = utils.getBuildingName(lever) |
| 138 | + if name:lower():find(filter, 1, true) then |
| 139 | + table.insert(filtered_levers, lever) |
| 140 | + end |
| 141 | + end |
| 142 | + end |
| 143 | + local selected_idx = 1 |
| 144 | + for idx, lever in ipairs(filtered_levers) do |
| 145 | + table.insert(choices, {text=get_lever_label(lever), data=lever}) |
| 146 | + if selected_id and lever.id == selected_id then |
| 147 | + selected_idx = idx |
| 148 | + end |
| 149 | + end |
| 150 | + list:setChoices(choices, selected_idx) |
| 151 | + self.subviews.empty_message.visible = #choices == 0 |
| 152 | + self.subviews.queued_count:setText(('Queued pulls: %d'):format(get_queued_count(levers))) |
| 153 | +end |
| 154 | + |
| 155 | +function LeverWindow:queue_pull() |
| 156 | + local _, choice = self.subviews.lever_list:getSelected() |
| 157 | + if not choice then |
| 158 | + return |
| 159 | + end |
| 160 | + lever_script.leverPullJob(choice.data, false) |
| 161 | + self:refresh_list() |
| 162 | +end |
| 163 | + |
| 164 | +function LeverWindow:remove_queued_pulls() |
| 165 | + local _, choice = self.subviews.lever_list:getSelected() |
| 166 | + if not choice then |
| 167 | + return |
| 168 | + end |
| 169 | + local jobs = {} |
| 170 | + for _, job in ipairs(choice.data.jobs) do |
| 171 | + if job.job_type == df.job_type.PullLever then |
| 172 | + table.insert(jobs, job) |
| 173 | + end |
| 174 | + end |
| 175 | + for _, job in ipairs(jobs) do |
| 176 | + dfhack.job.removeJob(job) |
| 177 | + end |
| 178 | + self:refresh_list() |
| 179 | +end |
| 180 | + |
| 181 | +function LeverWindow:onRenderFrame(dc, rect) |
| 182 | + LeverWindow.super.onRenderFrame(self, dc, rect) |
| 183 | + |
| 184 | + local list = self.subviews.lever_list |
| 185 | + local hover_idx = list:getIdxUnderMouse() |
| 186 | + if hover_idx and hover_idx ~= self.hover_index then |
| 187 | + self.hover_index = hover_idx |
| 188 | + list:setSelected(hover_idx) |
| 189 | + local _, choice = list:getSelected() |
| 190 | + if choice then |
| 191 | + self:focus_lever(nil, choice) |
| 192 | + end |
| 193 | + end |
| 194 | +end |
| 195 | + |
| 196 | +function LeverWindow:onRenderBody() |
| 197 | + if dfhack.getTickCount() >= self.next_refresh_ms then |
| 198 | + self.next_refresh_ms = dfhack.getTickCount() + REFRESH_MS |
| 199 | + self:refresh_list() |
| 200 | + end |
| 201 | +end |
| 202 | + |
| 203 | +function LeverWindow:focus_lever(_, choice) |
| 204 | + if not choice then |
| 205 | + return |
| 206 | + end |
| 207 | + local lever = choice.data |
| 208 | + local pos = {x=lever.centerx, y=lever.centery, z=lever.z} |
| 209 | + dfhack.gui.revealInDwarfmodeMap(pos, true, true) |
| 210 | + guidm.setCursorPos(pos) |
| 211 | +end |
| 212 | + |
| 213 | +LeverScreen = defclass(LeverScreen, gui.ZScreen) |
| 214 | +LeverScreen.ATTRS{focus_path='lever'} |
| 215 | + |
| 216 | +function LeverScreen:init() |
| 217 | + self:addviews{LeverWindow{}} |
| 218 | +end |
| 219 | + |
| 220 | +function LeverScreen:onDismiss() |
| 221 | + view = nil |
| 222 | +end |
| 223 | + |
| 224 | +if not dfhack.isMapLoaded() then |
| 225 | + qerror('gui/lever requires a map to be loaded') |
| 226 | +end |
| 227 | + |
| 228 | +view = view and view:raise() or LeverScreen{}:show() |
0 commit comments