@@ -2,8 +2,12 @@ local gui = require('gui')
22local widgets = require (' gui.widgets' )
33local textures = require (' gui.textures' )
44
5- local function activity_button_split (ascii , pens , x , y )
5+ --
6+ -- Button label definitions
7+ --
8+ local function make_button (ascii , pens , x , y )
69 local out = {}
10+ -- Grid of 3x3 tiles
711 for i = 1 ,3 do
812 local tmp = {}
913 for j = 1 ,3 do
@@ -37,8 +41,8 @@ local function make_activity_button(ch, color, border_color, border_acolor, x, y
3741 end
3842
3943 return {
40- inactive = activity_button_split (ascii , make_pens (border_color , color ), x , y ),
41- active = activity_button_split (ascii , make_pens (border_acolor , color ), ax , ay )
44+ inactive = make_button (ascii , make_pens (border_color , color ), x , y ),
45+ active = make_button (ascii , make_pens (border_acolor , color ), ax , ay )
4246 }
4347end
4448
@@ -65,7 +69,7 @@ local goto_button_color = {
6569 {COLOR_LIGHTCYAN , COLOR_LIGHTRED , COLOR_LIGHTCYAN },
6670 {COLOR_LIGHTCYAN , COLOR_LIGHTCYAN , COLOR_LIGHTCYAN },
6771}
68- local goto_button = activity_button_split (goto_button_ascii , goto_button_color , 32 , 0 )
72+ local goto_button = make_button (goto_button_ascii , goto_button_color , 32 , 0 )
6973
7074
7175-- TODO: The usage of these icons requires the ability to adjust screentexpos_flag
151155-- Obtain a list of siege engine buildings on the map with specific information
152156local function get_siege_engines ()
153157 local siege_list = {}
154- for _ , building in ipairs (df .global .world .buildings .all ) do
158+ for _ , building in ipairs (df .global .world .buildings .other . IN_PLAY ) do
155159 if not df .building_siegeenginest :is_instance (building ) then goto continue end
156160 if not building .flags .exists then goto continue end
157161
@@ -174,8 +178,8 @@ local function get_siege_engines()
174178 or job .job_type == df .job_type .FireBallista
175179 or job .job_type == df .job_type .FireBoltThrower then
176180 -- Display `Ready` instead of firing when in standby mode
177- -- as the same jobtype is used when actively firing and waiting.
178- -- This is to reduce confusion as no projectiles are made
181+ -- as the same job_type is used when actively firing and waiting.
182+ -- This is to reduce confusion as no projectiles are fired
179183 active_job = building .action == 2 and ' Ready' or ' Firing'
180184 end
181185 end
@@ -198,9 +202,9 @@ local function get_siege_engines()
198202 return siege_list
199203end
200204
201- -- Set siegeengine action, returning false if the building wasn 't found
205+ -- Set siegeengine action, returning false if the building isn 't found
202206local function set_siege_engine_action (id , action )
203- for _ , building in ipairs (df .global .world .buildings .all ) do
207+ for _ , building in ipairs (df .global .world .buildings .other . IN_PLAY ) do
204208 if building .id == id then
205209 if not df .building_siegeenginest :is_instance (building ) then return false end
206210 building .action = action
214218SiegeEngineList = defclass (SiegeEngineList , widgets .Panel )
215219SiegeEngineList .ATTRS = {
216220 view_id = ' list' ,
217- frame = {l = 0 , r = 0 , t = 3 , b = 3 },
221+ frame = {l = 0 , r = 0 , t = 1 , b = 5 },
218222 frame_style = gui .FRAME_INTERIOR ,
219223
220224 -- Filters by siegeengine_type, -1 being all
@@ -224,7 +228,7 @@ SiegeEngineList.ATTRS = {
224228function SiegeEngineList :init ()
225229 self :refresh_data ()
226230
227- self .refresh_rate = 60
231+ self .refresh_rate = 30
228232 self .refresh_timer = 0
229233
230234 self .button_start_x = 24
@@ -240,9 +244,10 @@ function SiegeEngineList:init()
240244 self :refresh_view (true )
241245end
242246
247+ -- Used to manage how often the ui data refreshes
243248function SiegeEngineList :onRenderBody ()
244249 self .refresh_timer = self .refresh_timer + 1
245- if (self .refresh_timer < self .refresh_rate ) then
250+ if (self .refresh_timer > self .refresh_rate ) then
246251 self .refresh_timer = 0
247252 self :refresh_data ()
248253 end
@@ -260,15 +265,18 @@ local function concat_tables(to, from)
260265 end
261266end
262267
263- -- TODO: Replace constants with df.siegeengine_action enum once merged
268+ -- TODO: Replace constants with df.siegeengine_action enum once structures merged
264269local action_button_order = {3 , 4 , 2 , 1 , 0 }
270+ local action_button_keybinds = {' CUSTOM_SHIFT_F' , ' CUSTOM_SHIFT_T' , ' CUSTOM_SHIFT_P' , ' CUSTOM_SHIFT_L' , ' CUSTOM_SHIFT_N' }
265271
266- local function add_multiline (to , from )
267- concat_tables (to [1 ], from [1 ])
268- concat_tables (to [2 ], from [2 ])
269- concat_tables (to [3 ], from [3 ])
272+ -- Add a multiline label definition from `from` to `to` starting at y=y_start or 0
273+ local function add_multiline (to , from , y_start )
274+ for i , item in pairs (from ) do
275+ concat_tables (to [i + (y_start or 0 )], from [i ])
276+ end
270277end
271278
279+ -- Label string callbacks, used to update the display without resetting the scrolling List
272280local action_text_pen = dfhack .pen .parse ({ fg = COLOR_GREEN })
273281function SiegeEngineList :get_action_text (id )
274282 return self .engines [id ].active_job or ' '
@@ -294,6 +302,7 @@ function SiegeEngineList:get_activity_button_tile(id, action, x, y)
294302 return activity_buttons [action ][self .engines [id ].action == action and ' active' or ' inactive' ][y ][x ].tile
295303end
296304
305+ -- Generate the multiline Label display for an engine
297306function SiegeEngineList :make_entry_text (engine )
298307 local lines = {
299308 {{text = self :callback (' get_name_text' , engine .id ), width = self .button_start_x }},
@@ -308,10 +317,10 @@ function SiegeEngineList:make_entry_text(engine)
308317 -- Goto Position Button
309318 add_multiline (lines , goto_button )
310319
311- -- Blank
320+ -- Padding following goto button
312321 add_multiline (lines , {{{text = ' ' , width = 3 }},{{text = ' ' , width = 3 }},{{text = ' ' , width = 3 }}})
313322
314- -- FireAtWill, PracticeFire, PrepareToFire, KeepLoaded, NotInUse
323+ -- Siege Engine activity selection buttons
315324 for _ , button_action in ipairs (action_button_order ) do
316325 for y = 1 ,3 do
317326 for x = 1 ,3 do
@@ -320,6 +329,7 @@ function SiegeEngineList:make_entry_text(engine)
320329 end
321330 end
322331
332+ -- Transform multiline label into a single label with newlines
323333 local out_tokens = {}
324334 for i = 1 ,3 do
325335 concat_tables (out_tokens , lines [i ])
@@ -329,6 +339,8 @@ function SiegeEngineList:make_entry_text(engine)
329339 return out_tokens
330340end
331341
342+ -- Refresh the engine information being displayed, but not the list.
343+ -- Updating data here *does not* add or remove new/deleted engines.
332344function SiegeEngineList :refresh_data ()
333345 local old_engines = self .engines
334346 self .engines = get_siege_engines ()
@@ -350,19 +362,40 @@ function SiegeEngineList:refresh_data()
350362 end
351363end
352364
365+ -- Refresh the engine list, updating to display new/deleted engines correctly.
353366function SiegeEngineList :refresh_view (refresh_data )
354367 if refresh_data then self :refresh_data () end
355368 local choices = {}
356369 for _ , data in pairs (self .engines ) do
357370 table.insert (choices , {
358371 text = self :make_entry_text (data ),
359372 search_key = " " ,
360- data = data
373+ data = data . id
361374 });
362375 end
363376 self .subviews .list :setChoices (choices )
364377end
365378
379+ function SiegeEngineList :reveal_selected ()
380+ local _ , selected = self .subviews .list :getSelected ()
381+ if selected ~= nil then
382+ dfhack .gui .revealInDwarfmodeMap (self .engines [selected .data ].pos , true , true )
383+ end
384+ end
385+
386+ function SiegeEngineList :set_selected_action (action )
387+ local _ , selected = self .subviews .list :getSelected ()
388+
389+ local successful = set_siege_engine_action (selected .data , action )
390+ if not successful then
391+ self :refresh_view (true )
392+ return
393+ end
394+
395+ -- Successfully updated, just update the cached state
396+ self .engines [selected .data ].action = action
397+ end
398+
366399function SiegeEngineList :onInput (keys )
367400 if not keys ._MOUSE_L then
368401 SiegeEngineList .super .onInput (self , keys )
@@ -382,13 +415,13 @@ function SiegeEngineList:onInput(keys)
382415 return
383416 end
384417
385- self . subviews . list :setSelected (idx )
418+ list :setSelected (idx )
386419
387- local engine = list : getChoices ()[ idx ]. data
420+ -- 0 is goto, 1 is blank, following are action buttons
388421 local button_pressed = math.ceil ((x - self .button_start_x + 1 )/ 3 )- 1
389422
390423 if button_pressed == 0 then
391- dfhack . gui . revealInDwarfmodeMap ( engine . pos , true , true )
424+ self : reveal_selected ( )
392425 return
393426 end
394427
@@ -398,14 +431,7 @@ function SiegeEngineList:onInput(keys)
398431 end
399432
400433 local action = action_button_order [button_pressed - 1 ]
401- local successful = set_siege_engine_action (engine .id , action )
402- if not successful then
403- self :refresh_view (true )
404- return
405- end
406-
407- -- Successfully updated, just update the cached state
408- self .engines [engine .id ].action = action
434+ self :set_selected_action (action )
409435end
410436
411437-- SiegeManager
@@ -423,7 +449,7 @@ function SiegeManager:init()
423449 self :addviews ({
424450 SiegeEngineList {},
425451 widgets .CycleHotkeyLabel {
426- frame = {b = 1 },
452+ frame = {b = 3 },
427453 key = ' CUSTOM_T' ,
428454 on_change = self :callback (' set_type_filter' ),
429455 label = ' Show Types:' ,
@@ -435,12 +461,38 @@ function SiegeManager:init()
435461 },
436462 initial_option = 1 ,
437463 },
464+ widgets .HotkeyLabel {
465+ frame = {b = 0 },
466+ key = ' CUSTOM_CTRL_C' ,
467+ label = ' Reveal in World' ,
468+ on_activate = self :callback (' reveal_selected' )
469+ },
438470 })
471+
472+ for i , action_button in ipairs (action_button_order ) do
473+ self :addviews ({
474+ widgets .HotkeyLabel {
475+ frame = {b = 1 , l = (i - 1 )* 2 },
476+ key = action_button_keybinds [i ],
477+ key_sep = i == # action_button_order and ' : ' or ' ' ,
478+ label = i == # action_button_order and ' Set Action' or ' ' ,
479+ on_activate = self :callback (' set_action' , action_button )
480+ }
481+ })
482+ end
439483end
440484
441485function SiegeManager :set_type_filter (new )
442486 self .subviews .list .type_filter = new
443- self .subviews .list :hard_refresh ()
487+ self .subviews .list :refresh_view (true )
488+ end
489+
490+ function SiegeManager :reveal_selected ()
491+ self .subviews .list :reveal_selected ()
492+ end
493+
494+ function SiegeManager :set_action (action )
495+ self .subviews .list :set_selected_action (action )
444496end
445497
446498-- SiegeManagerScreen
@@ -459,5 +511,4 @@ if not dfhack.isMapLoaded() then
459511 qerror (' requires a map to be loaded' )
460512end
461513
462- view = SiegeManagerScreen {}:show ()
463- -- view = view and view:raise() or SiegeManagerScreen{}:show()
514+ view = view and view :raise () or SiegeManagerScreen {}:show ()
0 commit comments