looting.lua

main 432 lines · 13.3 KB Raw
Alex Alex Commit Client Upload Part 2 19/10/2026 00:06
1local context = G.botContext
2local Panels = context.Panels
3
4Panels.Looting = function(parent)
5 local ui = context.setupUI([[
6Panel
7 id: looting
8 height: 180
9
10 BotLabel
11 anchors.top: parent.top
12 anchors.left: parent.left
13 anchors.right: parent.right
14 text: Looting
15
16 ComboBox
17 id: config
18 anchors.top: prev.bottom
19 anchors.left: parent.left
20 margin-top: 5
21 text-offset: 3 0
22 width: 130
23
24 Button
25 id: enableButton
26 anchors.top: prev.top
27 anchors.left: prev.right
28 anchors.right: parent.right
29 margin-left: 5
30
31 Button
32 margin-top: 1
33 id: add
34 anchors.top: prev.bottom
35 anchors.left: parent.left
36 text: Add
37 width: 60
38 height: 17
39
40 Button
41 id: edit
42 anchors.top: prev.top
43 anchors.horizontalCenter: parent.horizontalCenter
44 text: Edit
45 width: 60
46 height: 17
47
48 Button
49 id: remove
50 anchors.top: prev.top
51 anchors.right: parent.right
52 text: Remove
53 width: 60
54 height: 17
55
56 ScrollablePanel
57 id: items
58 anchors.top: prev.bottom
59 anchors.right: parent.right
60 anchors.left: parent.left
61 vertical-scrollbar: scrollBar
62 margin-right: 5
63 margin-top: 2
64 height: 70
65 layout:
66 type: grid
67 cell-size: 34 34
68 flow: true
69
70 BotSmallScrollBar
71 id: scrollBar
72 anchors.top: prev.top
73 anchors.bottom: prev.bottom
74 anchors.right: parent.right
75 step: 10
76 pixels-scroll: true
77
78 BotLabel
79 anchors.top: prev.bottom
80 anchors.left: parent.left
81 anchors.right: parent.right
82 margin-top: 4
83 text: Loot Containers
84
85 ItemsRow
86 id: containers
87 anchors.top: prev.bottom
88 anchors.left: parent.left
89 anchors.right: parent.right
90 height: 33
91 margin-top: 2
92
93]], parent)
94
95 local lootContainers = { ui.containers.item1, ui.containers.item2, ui.containers.item3, ui.containers.item4, ui.containers.item5 }
96
97 if type(context.storage.looting) ~= "table" then
98 context.storage.looting = {}
99 end
100 if type(context.storage.looting.configs) ~= "table" then
101 context.storage.looting.configs = {}
102 end
103
104 local getConfigName = function(config)
105 local matches = regexMatch(config, [[name:\s*([^\n]*)$]])
106 if matches[1] and matches[1][2] then
107 return matches[1][2]:trim()
108 end
109 return nil
110 end
111
112 local items = {}
113 local itemsByKey = {}
114 local containers = {}
115 local commands = {}
116 local refreshConfig = nil -- declared later
117
118 local createNewConfig = function(focusedWidget)
119 if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
120 return
121 end
122
123 local tmpItems = {}
124 local tmpContainers = {}
125 local focusIndex = 0
126
127 local newConfig = ""
128 for i, text in ipairs(commands) do
129 newConfig = newConfig .. text .. "\n"
130 end
131 for i=1,ui.items:getChildCount() do
132 local widget = ui.items:getChildByIndex(i)
133 if widget and widget:getItemId() >= 100 then
134 if tmpItems[widget:getItemId()] == nil then
135 tmpItems[widget:getItemId()] = 1
136 newConfig = newConfig .. "\n" .. widget:getItemId()
137 end
138 end
139 if widget == focusedWidget then
140 focusIndex = i
141 end
142 end
143 for i, widget in ipairs(lootContainers) do
144 if widget:getItemId() >= 100 then
145 if tmpContainers[widget:getItemId()] == nil then
146 tmpContainers[widget:getItemId()] = 1 -- remove duplicates
147 newConfig = newConfig .. "\ncontainer:" .. widget:getItemId()
148 end
149 end
150 end
151
152 context.storage.looting.configs[context.storage.looting.activeConfig] = newConfig
153 refreshConfig(focusIndex)
154 end
155
156 local parseConfig = function(config)
157 items = {}
158 itemsByKey = {}
159 containers = {}
160 commands = {}
161 local matches = regexMatch(config, [[([^:^\n^\s]+)(:?)([^\n]*)]])
162 for i=1,#matches do
163 local command = matches[i][2]
164 local validation = (matches[i][3] == ":")
165 local text = matches[i][4]
166 local commandAsNumber = tonumber(command)
167 local textAsNumber = tonumber(text)
168 if commandAsNumber and commandAsNumber >= 100 then
169 table.insert(items, commandAsNumber)
170 itemsByKey[commandAsNumber] = 1
171 elseif command == "container" and validation and textAsNumber and textAsNumber >= 100 then
172 containers[textAsNumber] = 1
173 elseif validation then
174 table.insert(commands, command .. ":" .. text)
175 end
176 end
177
178 local itemsToShow = #items + 2
179 if itemsToShow % 5 ~= 0 then
180 itemsToShow = itemsToShow + 5 - itemsToShow % 5
181 end
182 if itemsToShow < 10 then
183 itemsToShow = 10
184 end
185
186 for i=1,itemsToShow do
187 local widget = g_ui.createWidget("BotItem", ui.items)
188 local itemId = 0
189 if i <= #items then
190 itemId = items[i]
191 end
192 widget:setItemId(itemId)
193 widget.onItemChange = createNewConfig
194 end
195
196 for i, widget in ipairs(lootContainers) do
197 widget:setItemId(0)
198 end
199 local containerIndex = 1
200 for containerId, i in pairs(containers) do
201 if lootContainers[containerIndex] then
202 lootContainers[containerIndex]:setItemId(containerId)
203 end
204 containerIndex = containerIndex + 1
205 end
206 for i, widget in ipairs(lootContainers) do
207 widget.onItemChange = createNewConfig
208 end
209 end
210
211 local ignoreOnOptionChange = true
212 refreshConfig = function(focusIndex)
213 ignoreOnOptionChange = true
214 if context.storage.looting.enabled then
215 ui.enableButton:setText("On")
216 ui.enableButton:setColor('#00AA00FF')
217 else
218 ui.enableButton:setText("Off")
219 ui.enableButton:setColor('#FF0000FF')
220 end
221
222 ui.config:clear()
223 for i, config in ipairs(context.storage.looting.configs) do
224 local name = getConfigName(config)
225 if not name then
226 name = "Unnamed config"
227 end
228 ui.config:addOption(name)
229 end
230
231 if (not context.storage.looting.activeConfig or context.storage.looting.activeConfig == 0) and #context.storage.looting.configs > 0 then
232 context.storage.looting.activeConfig = 1
233 end
234
235 ui.items:destroyChildren()
236 for i, widget in ipairs(lootContainers) do
237 widget.onItemChange = nil
238 widget:setItemId(0)
239 widget:setItemCount(0)
240 end
241
242 if context.storage.looting.activeConfig and context.storage.looting.configs[context.storage.looting.activeConfig] then
243 ui.config:setCurrentIndex(context.storage.looting.activeConfig)
244 parseConfig(context.storage.looting.configs[context.storage.looting.activeConfig])
245 end
246
247 context.saveConfig()
248 if focusIndex and focusIndex > 0 and ui.items:getChildByIndex(focusIndex) then
249 ui.items:focusChild(ui.items:getChildByIndex(focusIndex))
250 end
251
252 ignoreOnOptionChange = false
253 end
254
255 ui.config.onOptionChange = function(widget)
256 if not ignoreOnOptionChange then
257 context.storage.looting.activeConfig = widget.currentIndex
258 refreshConfig()
259 end
260 end
261 ui.enableButton.onClick = function()
262 if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
263 return
264 end
265 context.storage.looting.enabled = not context.storage.looting.enabled
266 refreshConfig()
267 end
268 ui.add.onClick = function()
269 modules.client_textedit.multilineEditor("Looting editor", "name:Config name", function(newText)
270 table.insert(context.storage.looting.configs, newText)
271 context.storage.looting.activeConfig = #context.storage.looting.configs
272 refreshConfig()
273 end)
274 end
275 ui.edit.onClick = function()
276 if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
277 return
278 end
279 modules.client_textedit.multilineEditor("Looting editor", context.storage.looting.configs[context.storage.looting.activeConfig], function(newText)
280 context.storage.looting.configs[context.storage.looting.activeConfig] = newText
281 refreshConfig()
282 end)
283 end
284 ui.remove.onClick = function()
285 if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
286 return
287 end
288 local questionWindow = nil
289 local closeWindow = function()
290 questionWindow:destroy()
291 end
292 local removeConfig = function()
293 closeWindow()
294 if not context.storage.looting.activeConfig or not context.storage.looting.configs[context.storage.looting.activeConfig] then
295 return
296 end
297 context.storage.looting.enabled = false
298 table.remove(context.storage.looting.configs, context.storage.looting.activeConfig)
299 context.storage.looting.activeConfig = 0
300 refreshConfig()
301 end
302 questionWindow = context.displayGeneralBox(tr('Remove config'), tr('Do you want to remove current looting config?'), {
303 { text=tr('Yes'), callback=removeConfig },
304 { text=tr('No'), callback=closeWindow },
305 anchor=AnchorHorizontalCenter}, removeConfig, closeWindow)
306 end
307 refreshConfig()
308
309 context.onContainerOpen(function(container, prevContainer)
310 if context.storage.attacking.enabled then
311 return
312 end
313 if prevContainer then
314 container.autoLooting = prevContainer.autoLooting
315 else
316 container.autoLooting = true
317 end
318 end)
319
320 context.macro(200, function()
321 if not context.storage.looting.enabled then
322 return
323 end
324 local candidates = {}
325 local lootContainersCandidates = {}
326 for containerId, container in pairs(g_game.getContainers()) do
327 local containerItem = container:getContainerItem()
328 if container.autoLooting and container:getItemsCount() > 0 and (not containerItem or containers[containerItem:getId()] == nil) then
329 table.insert(candidates, container)
330 elseif containerItem and containers[containerItem:getId()] ~= nil then
331 table.insert(lootContainersCandidates, container)
332 end
333 end
334 if #lootContainersCandidates == 0 then
335 for slot = InventorySlotFirst, InventorySlotLast do
336 local item = context.getInventoryItem(slot)
337 if item and item:isContainer() and containers[item:getId()] ~= nil then
338 table.insert(lootContainersCandidates, item)
339 end
340 end
341 if #lootContainersCandidates > 0 then
342 -- try to open inventory backpack
343 local target = lootContainersCandidates[math.random(1,#lootContainersCandidates)]
344 g_game.open(target, nil)
345 context.delay(200)
346 end
347 return
348 end
349
350 if #candidates == 0 then
351 return
352 end
353
354 local container = candidates[math.random(1,#candidates)]
355 local nextContainers = {}
356 local foundItem = nil
357 for i, item in ipairs(container:getItems()) do
358 if item:isContainer() then
359 table.insert(nextContainers, item)
360 elseif itemsByKey[item:getId()] ~= nil then
361 foundItem = item
362 break
363 end
364 end
365
366 -- found item to loot
367 if foundItem then
368 -- find backpack for it, first backpack with same items
369 for i, container in ipairs(lootContainersCandidates) do
370 if container:getItemsCount() < container:getCapacity() or foundItem:isStackable() then -- has space
371 for j, item in ipairs(container:getItems()) do
372 if item:getId() == foundItem:getId() then
373 if foundItem:isStackable() then
374 if item:getCount() ~= 100 then
375 g_game.move(foundItem, container:getSlotPosition(j - 1), foundItem:getCount())
376 return
377 end
378 else
379 g_game.move(foundItem, container:getSlotPosition(container:getItemsCount()), foundItem:getCount())
380 return
381 end
382 end
383 end
384 end
385 end
386 -- now any backpack with empty slot
387 for i, container in ipairs(lootContainersCandidates) do
388 if container:getItemsCount() < container:getCapacity() then -- has space
389 g_game.move(foundItem, container:getSlotPosition(container:getItemsCount()), foundItem:getCount())
390 return
391 end
392 end
393
394 -- can't find backpack, try to open new
395 for i, container in ipairs(lootContainersCandidates) do
396 local candidates = {}
397 for j, item in ipairs(container:getItems()) do
398 if item:isContainer() and containers[item:getId()] ~= nil then
399 table.insert(candidates, item)
400 end
401 end
402 if #candidates > 0 then
403 g_game.open(candidates[math.random(1,#candidates)], container)
404 return
405 end
406 -- full, close it
407 g_game.close(container)
408 return
409 end
410 return
411 end
412
413 -- open remaining containers
414 if #nextContainers == 0 then
415 return
416 end
417 local delay = 1
418 for i=2,#nextContainers do
419 -- if more than 1 container, open them in new window
420 context.schedule(delay, function()
421 g_game.open(nextContainers[i], nil)
422 end)
423 delay = delay + 250
424 end
425 context.schedule(delay, function()
426 g_game.open(nextContainers[1], container)
427 end)
428 context.delay(150 + delay)
429 end)
430end
431
432
Top