Skip to content

Commit f900f71

Browse files
committed
Caravan overlay
1 parent 9b6340b commit f900f71

File tree

1 file changed

+276
-21
lines changed

1 file changed

+276
-21
lines changed

caravan.lua

Lines changed: 276 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,286 @@
1-
-- Adjusts properties of caravans
2-
--[====[
1+
-- Adjusts properties of caravans and provides overlay for enhanced trading
2+
--@ module = true
33

4-
caravan
5-
=======
4+
local gui = require('gui')
5+
local overlay = require('plugins.overlay')
6+
local widgets = require('gui.widgets')
67

7-
Adjusts properties of caravans on the map. See also `force` to create caravans.
8+
trader_selected_state = trader_selected_state or {}
9+
broker_selected_state = broker_selected_state or {}
10+
handle_ctrl_click_on_render = handle_ctrl_click_on_render or false
11+
handle_shift_click_on_render = handle_shift_click_on_render or false
812

9-
This script has multiple subcommands. Commands listed with the argument
10-
``[IDS]`` can take multiple caravan IDs (see ``caravan list``). If no IDs are
11-
specified, then the commands apply to all caravans on the map.
13+
dfhack.onStateChange.caravanTradeOverlay = function(code)
14+
if code == SC_WORLD_UNLOADED then
15+
trader_selected_state = {}
16+
broker_selected_state = {}
17+
handle_ctrl_click_on_render = false
18+
handle_shift_click_on_render = false
19+
end
20+
end
1221

13-
**Subcommands:**
22+
local GOODFLAG = {
23+
UNCONTAINED_UNSELECTED = 0,
24+
UNCONTAINED_SELECTED = 1,
25+
CONTAINED_UNSELECTED = 2,
26+
CONTAINED_SELECTED = 3,
27+
CONTAINER_COLLAPSED_UNSELECTED = 4,
28+
CONTAINER_COLLAPSED_SELECTED = 5,
29+
}
1430

15-
- ``list``: lists IDs and information about all caravans on the map.
16-
- ``extend [DAYS] [IDS]``: extends the time that caravans stay at the depot by
17-
the specified number of days (defaults to 7 if not specified). Also causes
18-
caravans to return to the depot if applicable.
19-
- ``happy [IDS]``: makes caravans willing to trade again (after seizing goods,
20-
annoying merchants, etc.). Also causes caravans to return to the depot if
21-
applicable.
22-
- ``leave [IDS]``: makes caravans pack up and leave immediately.
23-
- ``unload``: fixes endless unloading at the depot. Run this if merchant pack
24-
animals were startled and now refuse to come to the trade depot.
31+
function select_shift_clicked_container_items(new_state, old_state, list_index)
32+
-- if ctrl is also held, collapse the container too
33+
local also_collapse = dfhack.internal.getModifiers().ctrl
34+
local collapsed_item_count = 0
35+
for k, goodflag in ipairs(new_state) do
36+
if old_state[k] ~= goodflag then
37+
local next_item_flag = new_state[k + 1]
38+
local this_item_is_container = df.item_binst:is_instance(df.global.game.main_interface.trade.good[list_index][k])
39+
if this_item_is_container then
40+
local container_is_selected = goodflag == GOODFLAG.UNCONTAINED_SELECTED or goodflag == GOODFLAG.CONTAINER_COLLAPSED_SELECTED
41+
if container_is_selected then
42+
local collapsed_this_container = false
2543

26-
]====]
44+
if goodflag == GOODFLAG.UNCONTAINED_SELECTED and also_collapse then
45+
collapsed_this_container = true
46+
end
2747

28-
--@ module = true
48+
new_state[k] = also_collapse and GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED or GOODFLAG.UNCONTAINED_UNSELECTED
49+
local end_of_container_reached = false
50+
local contained_item_index = k + 1
51+
52+
if contained_item_index > #new_state - 1 then
53+
end_of_container_reached = true
54+
end
55+
56+
while not end_of_container_reached do
57+
new_state[contained_item_index] = GOODFLAG.CONTAINED_SELECTED
58+
59+
if collapsed_this_container then
60+
collapsed_item_count = collapsed_item_count + 1
61+
end
62+
63+
local next_item_index = contained_item_index + 1
64+
65+
if next_item_index > #new_state or new_state[next_item_index] < 2 or new_state[next_item_index] >= 4 then
66+
end_of_container_reached = true
67+
end
68+
contained_item_index = contained_item_index + 1
69+
end
70+
end
71+
end
72+
end
73+
end
74+
75+
if collapsed_item_count > 0 then
76+
df.global.game.main_interface.trade.i_height[list_index] = df.global.game.main_interface.trade.i_height[list_index] - collapsed_item_count * 3
77+
end
78+
end
79+
80+
function collapse_ctrl_clicked_containers(new_state, old_state, list_index)
81+
for k, goodflag in ipairs(new_state) do
82+
if old_state[k] ~= goodflag then
83+
local next_item_flag = new_state[k + 1]
84+
if next_item_flag == GOODFLAG.CONTAINED_UNSELECTED or next_item_flag == GOODFLAG.CONTAINED_SELECTED then
85+
local target_goodflag
86+
if goodflag == GOODFLAG.UNCONTAINED_SELECTED then
87+
target_goodflag = GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED
88+
elseif goodflag == GOODFLAG.UNCONTAINED_UNSELECTED then
89+
target_goodflag = GOODFLAG.CONTAINER_COLLAPSED_SELECTED
90+
end
91+
92+
if target_goodflag ~= nil then
93+
new_state[k] = target_goodflag
94+
-- changed a container state, items inside will be reset, return contained items to state before collapse
95+
local end_of_container_reached = false
96+
local contained_item_index = k + 1
97+
98+
if contained_item_index > #new_state - 1 then
99+
end_of_container_reached = true
100+
end
101+
102+
local num_items_collapsed = 0
103+
104+
while not end_of_container_reached do
105+
num_items_collapsed = num_items_collapsed + 1
106+
new_state[contained_item_index] = old_state[contained_item_index]
107+
108+
local next_item_index = contained_item_index + 1
109+
110+
if next_item_index > #new_state or new_state[next_item_index] < 2 or new_state[next_item_index] >= 4 then
111+
end_of_container_reached = true
112+
end
113+
contained_item_index = contained_item_index + 1
114+
end
115+
116+
if num_items_collapsed > 0 then
117+
df.global.game.main_interface.trade.i_height[list_index] = df.global.game.main_interface.trade.i_height[list_index] - num_items_collapsed * 3
118+
end
119+
end
120+
end
121+
end
122+
end
123+
end
124+
125+
126+
function collapseTypes(types_list, list_index)
127+
local type_on_count = 0
128+
129+
for k, type_open in ipairs(types_list) do
130+
local type_on = df.global.game.main_interface.trade.current_type_a_on[list_index][k]
131+
if type_on then
132+
type_on_count = type_on_count + 1
133+
end
134+
types_list[k] = false
135+
end
136+
137+
df.global.game.main_interface.trade.i_height[list_index] = type_on_count * 3
138+
end
139+
140+
function collapseAllTypes()
141+
collapseTypes(df.global.game.main_interface.trade.current_type_a_expanded[0], 0)
142+
collapseTypes(df.global.game.main_interface.trade.current_type_a_expanded[1], 1)
143+
-- reset scroll to top when collapsing types
144+
df.global.game.main_interface.trade.scroll_position_item[0] = 0
145+
df.global.game.main_interface.trade.scroll_position_item[1] = 0
146+
end
147+
148+
function collapseContainers(item_list, list_index)
149+
local num_items_collapsed = 0
150+
for k, goodflag in ipairs(item_list) do
151+
if goodflag ~= GOODFLAG.CONTAINED_UNSELECTED and goodflag ~= GOODFLAG.CONTAINED_SELECTED then
152+
local next_item_index = k + 1
153+
if next_item_index > #item_list - 1 then
154+
goto skip
155+
end
156+
157+
local next_item = item_list[next_item_index]
158+
local this_item_is_container = df.item_binst:is_instance(df.global.game.main_interface.trade.good[list_index][k])
159+
local collapsed_this_container = false
160+
if this_item_is_container then
161+
if goodflag == GOODFLAG.UNCONTAINED_SELECTED then
162+
item_list[k] = GOODFLAG.CONTAINER_COLLAPSED_SELECTED
163+
collapsed_this_container = true
164+
elseif goodflag == GOODFLAG.UNCONTAINED_UNSELECTED then
165+
item_list[k] = GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED
166+
collapsed_this_container = true
167+
end
168+
169+
if collapsed_this_container then
170+
num_items_collapsed = num_items_collapsed + #dfhack.items.getContainedItems(df.global.game.main_interface.trade.good[list_index][k])
171+
end
172+
end
173+
174+
::skip::
175+
end
176+
end
177+
178+
if num_items_collapsed > 0 then
179+
df.global.game.main_interface.trade.i_height[list_index] = df.global.game.main_interface.trade.i_height[list_index] - num_items_collapsed * 3
180+
end
181+
end
182+
183+
function collapseAllContainers()
184+
collapseContainers(df.global.game.main_interface.trade.goodflag[0], 0)
185+
collapseContainers(df.global.game.main_interface.trade.goodflag[1], 1)
186+
end
187+
188+
function collapseEverything()
189+
collapseAllContainers()
190+
collapseAllTypes()
191+
end
192+
193+
function copyGoodflagState()
194+
trader_selected_state = copyall(df.global.game.main_interface.trade.goodflag[0])
195+
broker_selected_state = copyall(df.global.game.main_interface.trade.goodflag[1])
196+
end
197+
198+
CaravanTradeOverlay = defclass(CaravanTradeOverlay, overlay.OverlayWidget)
199+
CaravanTradeOverlay.ATTRS{
200+
default_pos={x=-3,y=-12},
201+
default_enabled=true,
202+
viewscreens='dwarfmode/Trade',
203+
frame={w=27, h=13},
204+
frame_style=gui.MEDIUM_FRAME,
205+
frame_background=gui.CLEAR_PEN,
206+
}
207+
208+
function CaravanTradeOverlay:init()
209+
self:addviews{
210+
widgets.Label{
211+
frame={t=0, l=0},
212+
text={
213+
{text='Shift+Click checkbox:', pen=COLOR_LIGHTGREEN},
214+
NEWLINE,
215+
{text='select items inside bin', pen=COLOR_WHITE},
216+
},
217+
text_pen=COLOR_LIGHTGREEN,
218+
},
219+
widgets.Label{
220+
frame={t=3, l=0},
221+
text={
222+
{text='Ctrl+Click checkbox:', pen=COLOR_LIGHTGREEN},
223+
NEWLINE,
224+
{text='collapse single bin', pen=COLOR_WHITE},
225+
},
226+
text_pen=COLOR_LIGHTGREEN,
227+
},
228+
widgets.HotkeyLabel{
229+
frame={t=6, l=0},
230+
label='collapse bins',
231+
key='CUSTOM_CTRL_C',
232+
on_activate=collapseAllContainers,
233+
},
234+
widgets.HotkeyLabel{
235+
frame={t=7, l=0},
236+
label='collapse all',
237+
key='CUSTOM_CTRL_X',
238+
on_activate=collapseEverything,
239+
},
240+
widgets.Label{
241+
frame={t=9, l=0},
242+
text = 'Shift+Scroll:',
243+
text_pen=COLOR_LIGHTGREEN,
244+
},
245+
widgets.Label{
246+
frame={t=9, l=14},
247+
text = 'fast scroll',
248+
},
249+
}
250+
end
251+
252+
function CaravanTradeOverlay:render(dc)
253+
CaravanTradeOverlay.super.render(self, dc)
254+
if handle_shift_click_on_render then
255+
handle_shift_click_on_render = false
256+
select_shift_clicked_container_items(df.global.game.main_interface.trade.goodflag[0], trader_selected_state, 0)
257+
select_shift_clicked_container_items(df.global.game.main_interface.trade.goodflag[1], broker_selected_state, 1)
258+
elseif handle_ctrl_click_on_render then
259+
handle_ctrl_click_on_render = false
260+
collapse_ctrl_clicked_containers(df.global.game.main_interface.trade.goodflag[0], trader_selected_state, 0)
261+
collapse_ctrl_clicked_containers(df.global.game.main_interface.trade.goodflag[1], broker_selected_state, 1)
262+
end
263+
end
264+
265+
function CaravanTradeOverlay:onInput(keys)
266+
if keys._MOUSE_L_DOWN then
267+
if dfhack.internal.getModifiers().shift then
268+
handle_shift_click_on_render = true
269+
elseif dfhack.internal.getModifiers().ctrl then
270+
handle_ctrl_click_on_render = true
271+
end
272+
273+
if handle_ctrl_click_on_render or handle_shift_click_on_render then
274+
copyGoodflagState()
275+
end
276+
end
277+
CaravanTradeOverlay.super.onInput(self, keys)
278+
return false
279+
end
280+
281+
OVERLAY_WIDGETS = {
282+
tradeScreenExtension=CaravanTradeOverlay,
283+
}
29284

30285
INTERESTING_FLAGS = {
31286
casualty = 'Casualty',

0 commit comments

Comments
 (0)