ProgramCrafter

MoveItems

Aug 11th, 2021
28
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. local c = require "component"
  2. local e = require "event"
  3. local s = require "sides"
  4.  
  5. local selector = c.transposer
  6. local gpu = c.gpu
  7.  
  8. -------- Chests scheme -------- chest transposer
  9. -- C C C | C = chest | 1-3
  10. -- CTCTCTC | - = adapter | 4-7 1-3
  11. -- CTCTC | T = transposer | 8-10 4-5
  12. -- CTC-CTC |-----------------| 11-14 6-7
  13. -- CTCTC | 74 m3, 36 chs | 15-17 8-9
  14. -- CTCTCTC | | 18-21 10-12
  15. -- C C C | Usage sp 48.6% | 22-24
  16. -------------------------------
  17.  
  18. ------ Used transposer API --------
  19. -- getAllStacks(side) |
  20.  
  21. ------ Events format --------------
  22. -- "interrupted" ... |
  23. -- "touch" <address> <x> <y> ... |
  24.  
  25. local cur_id, cur_addr
  26. local function attach_transposer(id)
  27. if id == cur_id then return cur_addr end
  28.  
  29. if cur_id then
  30. selector.transferItem(s.down, s.north, 1, 1, cur_id)
  31. end
  32. if id then
  33. selector.transferItem(s.north, s.down, 1, id, 1)
  34. end
  35. cur_id = id
  36.  
  37. local cname = "transposer" -- inventory_controller doesn't work
  38. cur_addr = ({e.pull(1, "component_added", nil, cname)})[2]
  39. return cur_addr
  40. end
  41.  
  42. local function invoke_transposer(tid, ...)
  43. local addr = attach_transposer(tid)
  44. -- print("invoke", tid, ...)
  45. return c.invoke(addr, ...)
  46. end
  47.  
  48. local N,E,W,S = s.north, s.east, s.west, s.south
  49. local chests = {
  50. ["2 1 0"] = {{N, 1}},
  51. ["4 1 0"] = {{N, 2}},
  52. ["6 1 0"] = {{N, 3}},
  53. ["1 2 0"] = {{W, 1}},
  54. ["3 2 0"] = {{W, 2}, {E, 1}, {N, 4}},
  55. ["5 2 0"] = {{W, 3}, {E, 2}, {N, 5}},
  56. ["7 2 0"] = {{E, 3}},
  57. ["2 3 0"] = {{S, 1}, {W, 4}, {N, 6}},
  58. ["4 3 0"] = {{S, 2}, {W, 5}, {E, 4}},
  59. ["6 3 0"] = {{S, 3}, {E, 5}, {N, 7}},
  60. ["1 4 0"] = {{W, 6}},
  61. ["3 4 0"] = {{S, 4}, {E, 6}, {N, 8}},
  62. ["5 4 0"] = {{S, 5}, {W, 7}, {N, 9}},
  63. ["7 4 0"] = {{E, 7}},
  64. ["2 5 0"] = {{S, 6}, {W, 8}, {N, 10}},
  65. ["4 5 0"] = {{W, 9}, {E, 8}, {N, 11}},
  66. ["6 5 0"] = {{S, 7}, {E, 9}, {N, 12}},
  67. ["1 6 0"] = {{W, 10}},
  68. ["3 6 0"] = {{S, 8}, {W, 11}, {E, 10}},
  69. ["5 6 0"] = {{S, 9}, {W, 12}, {E, 11}},
  70. ["7 6 0"] = {{E, 12}},
  71. ["2 7 0"] = {{S, 10}},
  72. ["4 7 0"] = {{S, 11}},
  73. ["6 7 0"] = {{S, 12}},
  74. }
  75.  
  76. local filled = {
  77. [{0, 0}] = 0xFFFFFF,
  78. [{1, 7}] = 0xFFFF80,
  79. [{8, 64}] = 0xFF8080,
  80. [{65, 127}] = 0x808080,
  81. [{128, 191}] = 0x808000,
  82. [{192, 255}] = 0x800000,
  83. [{256, 1e9}] = 0x00FF00
  84. }
  85.  
  86. local displayed_items = {}
  87. local cx, cy, selected_item
  88.  
  89. local function draw_active_chest(x, y, color)
  90. gpu.setBackground(color or 0xFF0000)
  91. gpu.set(x * 2 + 2, y + 2, ' ')
  92. gpu.setBackground(0)
  93. end
  94.  
  95. local items_buf = {}
  96. local function update_chest_info(key, position)
  97. local items = invoke_transposer(position[1][2], "getAllStacks", position[1][1]).getAll()
  98.  
  99. local items_count = 0
  100. for i = 0, #items do
  101. items_count = items_count + (items[i].size or 0)
  102. end
  103.  
  104. items_buf[key] = items
  105. items_buf[key].total_count = items_count
  106.  
  107. for range, color in pairs(filled) do
  108. if items_count >= range[1] and items_count <= range[2] then
  109. draw_active_chest(tonumber(key:sub(1, 1)), tonumber(key:sub(3, 3)), color)
  110. break
  111. end
  112. end
  113. end
  114.  
  115. local function draw_chest_info(s, items)
  116. for h = 3, 25 do gpu.set(20, h, (' '):rep(60)) end
  117. gpu.set(20, 3, 'Chest at `' .. s .. '`')
  118.  
  119. if not items then return end
  120.  
  121. displayed_items = {}
  122. selected_item = nil
  123. local h = 4
  124. for i = 0, #items do
  125. if items[i].name then
  126. local desc = items[i].name .. ' - ' .. (items[i].label or '') .. ' - ' .. tostring(math.floor(items[i].size))
  127. gpu.set(20, h, desc)
  128. displayed_items[h] = {["slot"] = i, ["size"] = items[i].size, ["desc"] = desc}
  129. h = h + 1
  130. end
  131. end
  132.  
  133. return displayed_items, selected_item
  134. end
  135.  
  136. local chest_dirs = {}
  137. for k, transposers in pairs(chests) do
  138. chest_dirs[k] = {}
  139. for i, t in ipairs(transposers) do
  140. chest_dirs[k][t[1]] = t[2]
  141. end
  142. end
  143.  
  144. local tcache = {}
  145. local function mtc(tostr_return)
  146. return {
  147. __tostring = function(self)
  148. return tostr_return
  149. end
  150. }
  151. end
  152.  
  153. local function collapse(...) -- make {x,y,z} able to be used as table key
  154. local tkey = table.concat({...}, ' ')
  155. if not tcache[tkey] then tcache[tkey] = setmetatable({...}, mtc(tkey)) end
  156. return tcache[tkey]
  157. end
  158.  
  159. local function distance(x1, y1, z1, x2, y2, z2)
  160. return math.abs(x1 - x2) + math.abs(y1 - y2) + math.abs(z1 - z2)
  161. end
  162.  
  163. local function move_item(x1, y1, z1, x2, y2, z2, count, slot)
  164. if z1 ~= 0 or z2 ~= 0 then error('Moving items along Z is not supported') end
  165.  
  166. local cd = distance(x1, y1, z1, x2, y2, z2)
  167. if cd == 0 then return end
  168.  
  169. local stx, sty, stz = x1, y1, z1
  170.  
  171. local st = tostring(collapse(x2, y2, z2))
  172. if not chests[st] then error('Chest at `' .. st .. '` not found') end
  173.  
  174. local valid_targets = {}
  175. local visited = {}
  176. visited[collapse(x1, y1, z1)] = true
  177.  
  178. while x1 ~= x2 or y1 ~= y2 or z1 ~= z2 do
  179. local s = tostring(collapse(x1, y1, z1))
  180. if not chests[s] then error('Chest at `' .. s .. '` not found') end
  181.  
  182. draw_active_chest(x1, y1)
  183.  
  184. valid_targets = {}
  185. valid_targets[collapse(x1 - 2, y1, z1, E, W)] = chest_dirs[s][E]
  186. valid_targets[collapse(x1 + 2, y1, z1, W, E)] = chest_dirs[s][W]
  187. valid_targets[collapse(x1, y1 - 2, z1, S, N)] = chest_dirs[s][S]
  188. valid_targets[collapse(x1, y1 + 2, z1, N, S)] = chest_dirs[s][N]
  189.  
  190. valid_targets[collapse(x1 - 1, y1 - 1, z1, E, N)] = chest_dirs[s][E]
  191. valid_targets[collapse(x1 - 1, y1 + 1, z1, E, S)] = chest_dirs[s][E]
  192. valid_targets[collapse(x1 + 1, y1 + 1, z1, W, S)] = chest_dirs[s][W]
  193. valid_targets[collapse(x1 + 1, y1 - 1, z1, W, N)] = chest_dirs[s][W]
  194.  
  195. valid_targets[collapse(x1 - 1, y1 - 1, z1, S, W)] = chest_dirs[s][S]
  196. valid_targets[collapse(x1 - 1, y1 + 1, z1, N, W)] = chest_dirs[s][N]
  197. valid_targets[collapse(x1 + 1, y1 + 1, z1, N, E)] = chest_dirs[s][N]
  198. valid_targets[collapse(x1 + 1, y1 - 1, z1, S, E)] = chest_dirs[s][S]
  199.  
  200. local best_xyz, best_transposer, best_target_distance = {}, 0, 65535
  201. for xyz_uv, transposer in pairs(valid_targets) do
  202. local x, y, z, u, v = table.unpack(xyz_uv)
  203. local dist = distance(x2, y2, z2, x, y, z)
  204. if dist < best_target_distance and not visited[collapse(x, y, z)] then
  205. -- print('Valid transposer', transposer, 'with distance', dist)
  206. best_xyz = xyz_uv
  207. best_transposer = transposer
  208. best_target_distance = dist
  209. end
  210. end
  211.  
  212. if best_transposer == 0 then
  213. error('No moves available from ' .. s)
  214. end
  215.  
  216. os.sleep(0.3)
  217. draw_active_chest(x1, y1, 0xFFFF00)
  218.  
  219. local start_slot = (x1 == stx and y1 == sty and z1 == stz) and slot or 96
  220.  
  221. local s1, s2
  222. x1, y1, z1, s1, s2 = table.unpack(best_xyz)
  223.  
  224.  
  225. local invoke_result
  226. if x1 ~= x2 or y1 ~= y2 or z1 ~= z2 then
  227. invoke_result = invoke_transposer(best_transposer, "transferItem", s1, s2, count, start_slot, 96)
  228. else
  229. invoke_result = invoke_transposer(best_transposer, "transferItem", s1, s2, count, start_slot)
  230. end
  231.  
  232. if invoke_result <= 0.5 then
  233. error('Item not moved from ' .. s)
  234. end
  235.  
  236. local new_chest_s = tostring(collapse(x1, y1, z1))
  237. update_chest_info(s, {{s1, best_transposer}})
  238. update_chest_info(new_chest_s, {{s2, best_transposer}})
  239. displayed_items, selected_item = draw_chest_info(new_chest_s, items_buf[new_chest_s])
  240.  
  241. -- print('Moved to', x1, y1, z1)
  242. visited[collapse(x1, y1, z1)] = true
  243. end
  244.  
  245. draw_active_chest(x2, y2)
  246. end
  247.  
  248. os.execute('cls')
  249. gpu.set(1, 1, 'Please, wait for a while.')
  250.  
  251. selector.transferItem(s.down, s.north, 1) -- force detach MFU
  252.  
  253.  
  254. for k,v in pairs(chests) do
  255. update_chest_info(k, v)
  256. end
  257.  
  258. gpu.set(1, 1, 'Click on the chest to get information about it.')
  259.  
  260. while true do
  261. local evt = {e.pull()}
  262. if evt[1] == 'interrupted' then
  263. break
  264. elseif evt[1] == 'touch' then
  265. local x, y = evt[3], evt[4]
  266. local chest_x, chest_y = math.floor(x) // 2 - 1, math.floor(y) - 2
  267. local s = tostring(chest_x) .. ' ' .. tostring(chest_y) .. ' 0'
  268.  
  269. local items = items_buf[s]
  270. if items and selected_item then
  271. move_item(cx, cy, 0, chest_x, chest_y, 0, selected_item.size, selected_item.slot + 1)
  272. cx, cy = chest_x, chest_y
  273.  
  274. selected_item = nil
  275. elseif items then
  276. displayed_items, selected_item = draw_chest_info(s, items)
  277. cx, cy = chest_x, chest_y
  278. elseif x >= 20 and displayed_items[y] then
  279. selected_item = displayed_items[y]
  280.  
  281. gpu.setForeground(0x00FFFF)
  282. gpu.set(20, y, selected_item.desc)
  283. gpu.setForeground(0xFFFFFF)
  284. end
  285. end
  286. ::continue::
  287. end
  288.  
  289. attach_transposer(nil)
  290. os.execute('cls')
RAW Paste Data