-
Notifications
You must be signed in to change notification settings - Fork 4
/
control.lua
311 lines (269 loc) · 10.6 KB
/
control.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
require "util"
local INVENTORY_COLUMNS = 10
-- Name of the row headers we put in the UI
local filterUiElementName = "FilterFillRow"
local requestUiElementName = "RequestRow"
-- Button click dispatching
local Buttons = {}
local dispatch = {}
-- Initializes the world
function startup()
script.on_event(defines.events.on_tick, checkOpened)
initButtons()
end
-- Listener for events clicked
function handleButton(event)
local handler = dispatch[event.element.name]
if handler then
handler(game.players[event.player_index])
end
end
-- Initializes event handlers and button names
function initButtons()
function register(category, name, func)
local buttonName = category .. '_' .. name
Buttons[category][name] = buttonName
dispatch[buttonName] = func
end
-- Filtering
Buttons.Filter = {}
register('Filter', 'All', filter_fillAll)
register('Filter', 'Right', filter_fillRight)
register('Filter', 'Down', filter_fillDown)
register('Filter', 'Clear', filter_clearAll)
register('Filter', 'Set', filter_setAll)
-- Logistic Requests
Buttons.Requests = {}
register('Requests', 'x2', requests_x2)
register('Requests', 'x5', requests_x5)
register('Requests', 'x10', requests_x10)
register('Requests', 'Fill', requests_fill)
register('Requests', 'Blueprint', requests_blueprint)
script.on_event(defines.events.on_gui_click, handleButton)
end
function canFilter(obj)
return obj.get_output_inventory() ~= nil and obj.get_output_inventory().supports_filters()
end
function canRequest(obj)
return obj.request_slot_count > 0
end
-- See if an applicable container is opened and show/hide the UI accordingly.
-- Some delay is imperceptible here, so only check this once every few ticks
-- to avoid performance impact
-- Initialize this to a nonzero number so every mod in the universe doesn't
-- do its expensive work on the same tick
local tickCounter = 6;
function checkOpened()
tickCounter = tickCounter + 1
-- 1/5 second delay doesn't seem to be noticeable
if tickCounter == 12 then
tickCounter = 0
else
return
end
for _, player in pairs(game.players) do
showOrHideFilterUI(player, player.opened ~= nil and canFilter(player.opened))
showOrHideRequestUI(player, player.opened ~= nil and canRequest(player.opened))
end
end
-- Gets the name of the item at the given position, or nil if there
-- is no item at that position
function getItemAtPosition(player, n)
local inv = player.opened.get_output_inventory()
local isEmpty = not inv[n].valid_for_read
if isEmpty then
return nil
else
return inv[n].name
end
end
-- Returns either the item at a position, or the filter
-- at the position if there isn't an item there
function getItemOrFilterAtPosition(player, n)
local filter = player.opened.get_filter(n)
if filter ~= nil then
return filter
else
return getItemAtPosition(player, n)
end
end
-- Filtering: Clear all filters in the opened container
function filter_clearAll(player)
local op = player.opened.get_output_inventory();
local size = #op
for i = 1, size do
op.set_filter(i, nil)
end
end
-- Filtering: Set the filters of the opened container to the
-- contents of each cell
function filter_setAll(player)
local op = player.opened.get_output_inventory();
local size = #op
for i = 1, size do
local desired = getItemAtPosition(player, i)
op.set_filter(i, desired)
end
end
-- Filtering: Filter all cells of the opened container with the
-- contents of the player's cursor stack, or the first item in the container,
-- or the first filter in the container
function filter_fillAll(player)
-- Get the contents of the player's cursor stack, or the first cell
local desired = (player.cursor_stack.valid_for_read and player.cursor_stack.name) or getItemOrFilterAtPosition(player, 1)
local op = player.opened.get_output_inventory();
local size = #op
for i = 1, size do
local current = getItemAtPosition(player, i)
if current and desired and current ~= desired then
player.print({"", 'Skipped setting a filter on the cell occupied by ', {'item-name.' .. current}})
else
op.set_filter(i, desired or nil)
end
end
end
-- Filtering: Copies the filter settings of each cell to the cell(s) to the right of it
function filter_fillRight(player)
local op = player.opened.get_output_inventory()
local size = #op
local rows = math.ceil(size / INVENTORY_COLUMNS)
for r = 1, rows do
local desired = getItemOrFilterAtPosition(player, 1 + (r - 1) * INVENTORY_COLUMNS)
for c = 1, INVENTORY_COLUMNS do
local i = c + (r - 1) * INVENTORY_COLUMNS
if i <= size then
desired = getItemAtPosition(player, i) or desired
op.set_filter(i, desired)
end
end
end
end
-- Filtering: Copies the filter settings of each cell to the cell(s) below it
function filter_fillDown(player)
local size = #player.opened.get_output_inventory()
local rows = math.ceil(size / INVENTORY_COLUMNS)
for c = 1, INVENTORY_COLUMNS do
local desired = getItemOrFilterAtPosition(player, c)
for r = 1, rows do
local i = c + (r - 1) * INVENTORY_COLUMNS
if i <= size then
desired = getItemAtPosition(player, i) or desired
op.set_filter(c + (r - 1) * INVENTORY_COLUMNS, desired)
end
end
end
end
function multiply_filter(player, factor)
for i = 1, player.opened.request_slot_count do
local existing = player.opened.get_request_slot(i)
if existing ~= nil then
player.opened.set_request_slot({ name = existing.name, count = math.floor(existing.count * factor) }, i)
end
end
end
function requests_x2(player)
multiply_filter(player, 2)
end
function requests_x5(player)
multiply_filter(player, 5)
end
function requests_x10(player)
multiply_filter(player, 10)
end
function requests_fill(player)
local inv = player.opened.get_output_inventory()
local inventorySize = #inv
local totalStackRequests = 0
-- Add up how many total stacks we need here
for i = 1, player.opened.request_slot_count do
local item = player.opened.get_request_slot(i)
if item ~= nil then
totalStackRequests = totalStackRequests + item.count / game.item_prototypes[item.name].stack_size
end
end
local factor = inventorySize / totalStackRequests
-- Go back and re-set each thing according to its rounded-up stack size
for i = 1, player.opened.request_slot_count do
local item = player.opened.get_request_slot(i)
if item ~= nil then
stacksToRequest = math.ceil(item.count / game.item_prototypes[item.name].stack_size)
numberToRequest = stacksToRequest * game.item_prototypes[item.name].stack_size
player.opened.set_request_slot({ name = item.name, count = numberToRequest }, i)
end
end
end
function requests_blueprint(player)
-- Get some blueprint details
-- Note: 1 entry per item
-- game.local_player.opened.get_output_inventory()[1].get_blueprint_entities()[5].name
local blueprint = nil;
if player.cursor_stack.valid_for_read and player.cursor_stack.name == "blueprint" then
blueprint = player.cursor_stack;
elseif player.opened.get_output_inventory()[1].valid_for_read and player.opened.get_output_inventory()[1].name == "blueprint" then
blueprint = player.opened.get_output_inventory()[1];
else
player.print('You must be holding a blueprint or have a blueprint in the first chest slot to use this button')
return
end
-- Clear out all existing requests
for i = 1, player.opened.request_slot_count do
player.opened.clear_request_slot(i)
end
local bp = blueprint.get_blueprint_entities()
if bp == nil then
player.print('Blueprint has no pattern. Please use blueprint with pattern.')
return
end
if #bp > player.opened.request_slot_count then
-- BP has too many items to fit in the request set!
player.print('Blueprint has more required items than would fit in the logistic request slots of this chest')
return
end
-- Make a mapping from item name -> quantity needed
local lookup = {}
for k, v in ipairs(bp) do
lookup[v.name] = (lookup[v.name] or 0) + 1
end
-- Set the requests in the chest
local i = 1
for k, v in pairs(lookup) do
player.opened.set_request_slot({name = k, count = v}, i)
i = i + 1
end
end
-- UI management
function showOrHideUI(player, show, name, showFunc)
local exists = player.gui.top[name] ~= nil;
if exists ~= show then
if show then
player.gui.top.add({ type = "flow", name = name, direction = "horizontal" });
showFunc(player.gui.top[name])
else
player.gui.top[name].destroy()
end
end
end
function showOrHideFilterUI(player, show)
showOrHideUI(player, show, 'FilterRowName', showFilterUI)
end
function showFilterUI(myRow)
myRow.add( { type = "button", caption = "Filters: " } )
myRow.add( { type = "button", name = Buttons.Filter.All, caption = "Fill All" } )
myRow.add( { type = "button", name = Buttons.Filter.Right, caption = "Fill Right" } )
myRow.add( { type = "button", name = Buttons.Filter.Down, caption = "Fill Down" } )
myRow.add( { type = "button", name = Buttons.Filter.Clear, caption = "Clear All" } )
myRow.add( { type = "button", name = Buttons.Filter.Set, caption = "Set All" } )
end
function showOrHideRequestUI(player, show)
showOrHideUI(player, show, 'RequestRowName', showRequestUI)
end
function showRequestUI(myRow)
myRow.add( { type = "button", caption = "Requests: " } )
myRow.add( { type = "button", name = Buttons.Requests.x2, caption = "x2" } )
myRow.add( { type = "button", name = Buttons.Requests.x5, caption = "x5" } )
myRow.add( { type = "button", name = Buttons.Requests.x10, caption = "x10" } )
myRow.add( { type = "button", name = Buttons.Requests.Fill, caption = "Fill" } )
myRow.add( { type = "button", name = Buttons.Requests.Blueprint, caption = "Blueprint" } )
end
script.on_init(startup)
script.on_load(startup)