Advertisement
ZO125

Sorter.lua

Nov 14th, 2020 (edited)
191
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ---#############################---
  2. ---# INVENTORY SORTER PROGRAM  #---
  3. ---# AUTHOR: ZO125             #---
  4. ---# 30.04.2020             sZo#---
  5. ---#############################---
  6.  
  7. local component = require "component"
  8. local computer = require "computer"
  9. local event = require "event"
  10. local unicode = require "unicode"
  11.  
  12. local transposer = component.transposer
  13. local gpu = component.gpu
  14.  
  15. local CONFIG_FILE = "filter_v2.txt"
  16. local SORT_PERIOD = 20
  17. local BLINK_PERIOD = 20
  18.  
  19. local edit_handle
  20. local inventories = {}
  21. local properity_state = 0
  22. local filters_pos = 0
  23. local properities_pos = 0
  24. local filters = {}
  25.  
  26. local screen_x, screen_y = gpu.getResolution()
  27.  
  28. ------------------------------
  29. --   UTILITY FUNCTIONS      --
  30. ------------------------------
  31.  
  32. local function inLimits(n, min, max)
  33.   if n < min then
  34.     n = max
  35.   elseif n > max then
  36.     n = min
  37.   end
  38.   return n
  39. end
  40.  
  41. local function rightTab(text, length)
  42.   return text .. string.rep(" ", length - unicode.len(text))
  43. end
  44.  
  45. local function leftTab(text, length)
  46.   return string.rep(" ", length - unicode.len(text)) .. text
  47. end
  48.  
  49. local function compareItemWithFilter(filter_name, filter_metadata, item_name, item_metadata)
  50.   if not (filter_name == "" or filter_name == item_name) then
  51.     return false
  52.   end
  53.   if not (filter_metadata == 0 or (filter_metadata - 1) == item_metadata) then
  54.     return false
  55.   end
  56.   return true
  57. end
  58.  
  59. local function compareItemWithItem(name_1, metadata_1, name_2, metadata_2)
  60.   if name_1 == name_2 and metadata_1 == metadata_2 then
  61.     return true
  62.   end
  63.   return false
  64. end
  65.  
  66. ------------------------------
  67. --   OBJECT DEFENTIONS      --
  68. ------------------------------
  69.  
  70. local function Filter(enabled, input_side, output_side, input_slot_start, input_slot_end, output_slot_start, output_slot_end, name, metadata, count)
  71.   local filter = {
  72.     enabled = enabled or false,
  73.     input_side = input_side or 0,
  74.     output_side = output_side or 0,
  75.     input_slot_start = input_slot_start or 0,
  76.     input_slot_end = input_slot_end or 1,
  77.     output_slot_start = output_slot_start or 0,
  78.     output_slot_end = output_slot_end or 1,
  79.     name = name or "",
  80.     metadata = metadata or 0,
  81.     count = count or 64
  82.   }
  83.   return filter
  84. end
  85.  
  86. ------------------------------
  87. --   ADD/EDIT FUNCTIONS     --
  88. ------------------------------
  89.  
  90. local drawFilter, drawInfo
  91.  
  92. local function getProperity(pos)
  93.   local pos = pos or pos or properities_pos
  94.   local filter = filters[filters_pos]
  95.   local input_inventory = inventories[filter.input_side]
  96.   local output_inventory = inventories[filter.output_side]
  97.   -- Returns state, name, min_state, max_state, default_state, fieldname; otherwise returns nil
  98.   if pos == 1 then
  99.     return filter["enabled"], "Активен", nil, nil, false, "enabled"
  100.   elseif pos == 2 then -- INPUT
  101.     return filter["input_side"], "Вход", 0, 5, 0, "input_side"
  102.   elseif pos == 3 then
  103.     local max_state = 0
  104.     if input_inventory then
  105.       max_state = input_inventory.size
  106.     end
  107.     return filter["input_slot_start"], "\226\148\156Слот начало", 0, max_state, 1, "input_slot_start"
  108.   elseif pos == 4 then
  109.     local max_state = 0
  110.     if input_inventory then
  111.       max_state = input_inventory.size
  112.     end
  113.     return filter["input_slot_end"], "\226\148\148Слот конец", 1, max_state, max_state, "input_slot_end"
  114.   elseif pos == 5 then -- OUTPUT
  115.     return filter["output_side"], "Выход", 0, 5, 0, "output_side"
  116.   elseif pos == 6 then
  117.     local max_state = 0
  118.     if output_inventory then
  119.       max_state = output_inventory.size
  120.     end
  121.     return filter["output_slot_start"], "\226\148\156Слот начало", 0, max_state, 0, "output_slot_start"
  122.   elseif pos == 7 then
  123.     local max_state = 0
  124.     if output_inventory then
  125.       max_state = output_inventory.size
  126.     end
  127.     return filter["output_slot_end"], "\226\148\148Слот конец", 1, max_state, max_state, "output_slot_end"
  128.   elseif pos == 8 then
  129.     return filter["name"], "Имя предмета", nil, 255, "", "name"
  130.   elseif pos == 9 then
  131.     return filter["metadata"], "Мета (-1)", 0, 16, 0, "metadata"
  132.   elseif pos == 10 then
  133.     local max_state = (math.floor(filter.output_slot_end - filter.output_slot_start) + 1) * 64
  134.     return filter["count"], "Количество", 0, max_state, max_state, "count"
  135.   end
  136. end
  137.  
  138. local function setProperity(state, pos)
  139.   local pos = pos or pos or properities_pos
  140.   local prev_state, name, min_state, max_state, default_state, fieldname = getProperity(pos)
  141.   local filter = filters[filters_pos]
  142.   if type(prev_state) == "number" then
  143.     state = tonumber(state)
  144.     if not state or state < min_state or state > max_state then
  145.       return
  146.     end
  147.   elseif type(prev_state) == "string" then
  148.     state = tostring(state)
  149.     if not state or #state > max_state then
  150.       return
  151.     end
  152.   elseif type(prev_state) == "boolean" then
  153.     if type(state) ~= "boolean" then
  154.       return
  155.     end
  156.   end
  157.   --error(fieldname)
  158.   filter[fieldname] = state
  159. end
  160.  
  161. local function drawAdditional()
  162.   if filters_pos == 0 then
  163.     drawInfo()
  164.   else
  165.     drawFilter()
  166.   end
  167. end
  168.  
  169. local function addFilter()
  170.   local filter = Filter()
  171.   table.insert(filters, filter)
  172.   filters_pos = #filters
  173.   drawAdditional()
  174. end
  175.  
  176. local function delFilter()
  177.   local count = #filters
  178.   filters[filters_pos] = nil
  179.   for i = filters_pos + 1, count, 1 do
  180.     filters[i - 1] = filters[i]
  181.   end
  182.   filters[count] = nil
  183.   if filters_pos >= count then
  184.     filters_pos = #filters
  185.   end
  186.   drawAdditional()
  187. end
  188.  
  189. local function moveUpFilter() -- F4
  190.  
  191. end
  192.  
  193. local function moveDownFilter() -- F3
  194.  
  195. end
  196.  
  197. ------------------------------
  198. --   DRAWING FUNCTIONS      --
  199. ------------------------------
  200.  
  201. local function drawBase()
  202.   gpu.setBackground(0x000000)
  203.   gpu.setForeground(0xFFFFFF)
  204.   gpu.fill(1, 1, screen_x, screen_y, " ")
  205.   gpu.set(1, 1, "Перемещение клавишами со стрелками.")
  206.   gpu.set(1, 2, "ENTER редактирует текст.")
  207.   gpu.set(1, 3, "INSERT добавляет фильтр.")
  208.   gpu.set(1, 4, "DELETE удаляет фильтр.")
  209. end
  210.  
  211. local function drawProperity(pos, clc)
  212.   if clc then
  213.     gpu.setBackground(0x000000)
  214.     gpu.setForeground(0xFFFFFF)
  215.   end
  216.   local state, name = getProperity(pos)
  217.   state = tostring(state)
  218.   gpu.set(2, pos + 6, rightTab(name, 12) .. "\226\148\130" .. state .. string.rep(" ", screen_x - 14 - #state))
  219. end
  220.  
  221. function drawFilter()
  222.     gpu.setBackground(0x000000)
  223.     gpu.setForeground(0xFFFFFF)
  224.     gpu.set(1, 5,
  225.       string.rep("\226\148\129", 13) .. "\226\148\175" ..
  226.       string.rep("\226\148\129", screen_x)
  227.     )
  228.     gpu.fill(1, 6, screen_x, screen_y - 6, " ")
  229.     gpu.set(2, 6, "ID"  ..string.rep(" ", 10) .. "\226\148\130" .. tostring(filters_pos))
  230.     for i = 1, 10 do -- #PROPERITIES
  231.       drawProperity(i, false)
  232.     end
  233.     gpu.set(1, properities_pos + 6, ">")
  234. end
  235.  
  236. function drawInfo()
  237.   gpu.setBackground(0x000000)
  238.   gpu.setForeground(0xFFFFFF)
  239.   gpu.fill(1, 5, screen_x, screen_y - 6, " ")
  240.   gpu.set(1, 5, "SPACE сохранит настройки.")
  241.   gpu.set(1, 6, "BACKSPACE закроет программу.")
  242.   gpu.set(1, 7, "Сортировщик предметов от ZO125")
  243.   gpu.set(1, 8, string.rep("\226\148\129", screen_x))
  244.   gpu.set(1, 9, "Транспозер:" .. transposer.address)
  245.   gpu.set(1, 10,
  246.     string.rep("\226\148\128", 7) .. "\226\148\172" ..
  247.     string.rep("\226\148\128", 7) .. "\226\148\172" ..
  248.     string.rep("\226\148\128", screen_x - 16)
  249.   )
  250.   gpu.set(1, 11, "Сторона\226\148\130Размер \226\148\130Инвентарь")
  251.   local c = 11
  252.   for side = 0, 5 do
  253.     local inventory = inventories[side]
  254.     if inventory then
  255.       c = c + 1
  256.       gpu.set(1, c, leftTab(side, 7) .. "\226\148\130" .. leftTab(inventory.size, 7) .. "\226\148\130" .. inventory.name)
  257.     end
  258.   end
  259. end
  260.  
  261. ------------------------------
  262. --   INTERFACE NAVIGATION   --
  263. ------------------------------
  264.  
  265. local function moveFilters(n)
  266.   filters_pos = inLimits(filters_pos + n, 0, #filters)
  267.   properities_pos = 0
  268.   drawAdditional()
  269. end
  270.  
  271. local function editProperity(n)
  272.   local filter = filters[filters_pos]
  273.   local state, _, min_state, max_state = getProperity()
  274.   if type(state) == "boolean" then
  275.     state = not state
  276.   elseif type(state) == "number" then
  277.     state = inLimits(state + n, min_state, max_state)
  278.   end
  279.   setProperity(state)
  280.   drawProperity(properities_pos, true)
  281. end
  282.  
  283. local function startEdit()
  284.   local properity_text = getProperity()
  285.   if type(properity_text) ~= "string" and type(properity_text) ~= "number" then
  286.     return
  287.   end
  288.   local text = tostring(properity_text)
  289.   local x = 15
  290.   local y = properities_pos + 6
  291.   local cursor = unicode.len(text)
  292.   local blink_stage = 0
  293.   local blink_state = false
  294.   gpu.setBackground(0xFFFFFF)
  295.   gpu.setForeground(0x000000)
  296.   gpu.set(x, y, rightTab(text, screen_x - 14))
  297.   -- Text input processing function
  298.   edit_handle = function(mode, key, char)
  299.     if mode then
  300.       local len = unicode.len(text)
  301.       local prev_text = unicode.sub(text, 1, cursor)
  302.       local next_text = unicode.sub(text, cursor + 1, len)
  303.       if key == 28 then -- Enter
  304.         setProperity(text)
  305.         drawProperity(properities_pos, true)
  306.         edit_handle = nil
  307.         return
  308.       elseif key == 14 then -- Backspace
  309.         if cursor >= 1 then
  310.           prev_text = unicode.sub(prev_text, 1, cursor - 1)
  311.           cursor = cursor - 1
  312.         end
  313.       elseif key == 203 then -- Left arrrow
  314.         if cursor > 1 then
  315.           cursor = cursor - 1
  316.         end
  317.       elseif key == 205 then -- Right arrow
  318.         if cursor < len then
  319.           cursor = cursor + 1
  320.         end
  321.       else
  322.         if char >= 32 then
  323.           local chr = unicode.char(char)
  324.           prev_text = prev_text .. chr
  325.           cursor = cursor + 1
  326.         end
  327.       end
  328.       text = prev_text .. next_text
  329.       gpu.setBackground(0xFFFFFF)
  330.       gpu.setForeground(0x000000)
  331.       gpu.set(x, y, rightTab(text, screen_x - 14))
  332.     else
  333.       if blink_stage >= BLINK_PERIOD then
  334.         blink_stage = 0
  335.         local smb = unicode.sub(text, cursor + 1, cursor + 1)
  336.         smb = smb == "" and " " or smb
  337.         blink_state = not blink_state
  338.         if blink_state then
  339.           gpu.setBackground(0xFFFFFF)
  340.           gpu.setForeground(0x000000)
  341.         else
  342.           gpu.setBackground(0x000000)
  343.           gpu.setForeground(0xFFFFFF)
  344.         end
  345.         gpu.set(x + cursor, y, smb)
  346.       end
  347.       blink_stage = blink_stage + 1
  348.     end
  349.   end
  350. end
  351.  
  352.  
  353.  
  354. local function moveRL(n)
  355.   if properities_pos == 0 then
  356.     moveFilters(n)
  357.   else
  358.     if filters_pos ~= 0 then
  359.       editProperity(n)
  360.     end
  361.   end
  362. end
  363.  
  364. local function moveUD(n)
  365.   if filters_pos ~= 0 then
  366.     gpu.setBackground(0x000000)
  367.     gpu.setForeground(0xFFFFFF)
  368.     gpu.set(1, properities_pos + 6, " ")
  369.     properities_pos = inLimits(properities_pos + n, 0, 10) -- #PROPERITIES
  370.     properity_state = 0
  371.     gpu.set(1, properities_pos + 6, ">")
  372.   end
  373. end
  374.  
  375. ------------------------------
  376. --   SAVING/LOADING BLOCK   --
  377. ------------------------------
  378.  
  379. local function saveProperities()
  380.   local bytes = ""
  381.   for i = 1, #filters, 1 do
  382.     local filter = filters[i]
  383.     bytes = bytes .. string.char(
  384.       filter.enabled and 1 or 0,
  385.       filter.input_side,
  386.       filter.output_side,
  387.       filter.input_slot_start,
  388.       filter.input_slot_end,
  389.       filter.output_slot_start,
  390.       filter.output_slot_end,
  391.       #filter.name
  392.     ) .. filter.name .. string.char(
  393.       filter.metadata,
  394.       filter.count
  395.     )
  396.   end
  397.   local file = io.open(CONFIG_FILE, "wb")
  398.   if file then
  399.     file:write(bytes)
  400.     file:close()
  401.   end
  402. end
  403.  
  404. local function loadProperities()
  405.   local file = io.open(CONFIG_FILE, "rb")
  406.   if file then
  407.     local bytes = file:read("*a")
  408.     file:close()
  409.     if #bytes > 0 then
  410.       local pos = 1
  411.       repeat
  412.         local enabled, input_side, output_side, input_slot_start, input_slot_end, output_slot_start, output_slot_end, name_length = bytes:byte(pos, pos + 7)
  413.         pos = pos + 8
  414.         local name = bytes:sub(pos, pos + name_length - 1)
  415.         pos = pos + name_length
  416.         local metadata, count = bytes:byte(pos, pos + 1)
  417.         pos = pos + 2
  418.         table.insert(filters, Filter(enabled == 1, input_side, output_side, input_slot_start, input_slot_end, output_slot_start, output_slot_end, name, metadata, count))
  419.       until pos >= #bytes
  420.     end
  421.   end
  422. end
  423.  
  424. ------------------------------
  425. --   ITEM TRANSPORT BLOCK   --
  426. ------------------------------
  427.  
  428. local function initInventories()
  429.   for side = 0, 5 do
  430.     local name = transposer.getInventoryName(side)
  431.     if name then
  432.       local size = transposer.getInventorySize(side)
  433.       inventories[side] = {
  434.         name = name,
  435.         size = size,
  436.         stacks = {}
  437.       }
  438.     end
  439.   end
  440. end
  441.  
  442. local function getStacks()
  443.   for side = 0, 5 do
  444.     local inventory = inventories[side]
  445.     if inventory then
  446.       inventory.stacks = transposer.getAllStacks(side)
  447.     end
  448.   end
  449. end
  450.  
  451. local function getSlotItem(inventory, slot)
  452.   local stack = inventory.stacks[slot]
  453.   if stack then
  454.     return stack.name, stack.damage, stack.size, stack.maxSize
  455.   end
  456.   return "", 0, 0, 64
  457. end
  458.  
  459. local function getInputSlot(inventory, input_slot_start, input_slot_end, filter_name, filter_metadata)
  460.   local stacks = inventory.stacks
  461.   if input_slot_start == 0 then
  462.     input_slot_start = 1
  463.     input_slot_end = inventory.size
  464.   end
  465.   for i = input_slot_start, input_slot_end do
  466.     local stack = stacks[i]
  467.     if stack then
  468.       local item_name = stack.name
  469.       local item_metadata = stack.damage
  470.       if compareItemWithFilter(filter_name, filter_metadata, item_name, item_metadata) then
  471.         return i
  472.       end
  473.     end
  474.   end
  475.   return 0
  476. end
  477.  
  478. local function getOutputSlot(inventory, output_slot_start, output_slot_end, item_name, item_metadata)
  479.   local stacks = inventory.stacks
  480.   if output_slot_start == 0 then
  481.     output_slot_start = 1
  482.     output_slot_end = inventory.size
  483.   end
  484.   for i = output_slot_start, output_slot_end do
  485.     local stack = stacks[i]
  486.     if not stack then
  487.       return i
  488.     else
  489.       local stack_item_name = stack.name
  490.       local stack_item_metadata = stack.damage
  491.       if compareItemWithItem(item_name, item_metadata, stack_item_name, stack_item_metadata) and stack.size < stack.maxSize then
  492.         return i
  493.       end
  494.     end
  495.   end
  496.   return 0
  497. end
  498.  
  499. local function separateItems()
  500.   for i = 1, #filters, 1 do
  501.     local filter = filters[i]
  502.     if filter.enabled then
  503.       local input_side = filter.input_side
  504.       local output_side = filter.output_side
  505.       local input_inventory = inventories[input_side]
  506.       local output_inventory = inventories[output_side]
  507.       if input_inventory and output_inventory then
  508.         local filter_name, filter_metadata, filter_count = filter.name, filter.metadata, filter.count
  509.         local input_slot_start = filter.input_slot_start
  510.         local input_slot_end = filter.input_slot_end
  511.         input_slot_start = getInputSlot(input_inventory, input_slot_start, input_slot_end, filter_name, filter_metadata)
  512.        
  513.         if input_slot_start ~= 0 then
  514.           local item_name, item_metadata, item_count, item_max_count = getSlotItem(input_inventory, input_slot_start)
  515.           if compareItemWithFilter(filter_name, filter_metadata, item_name, item_metadata) then
  516.             local output_slot_start = filter.output_slot_start
  517.             local output_slot_end = filter.output_slot_end
  518.             output_slot_start = getOutputSlot(output_inventory, output_slot_start, output_slot_end, item_name, item_metadata)
  519.            
  520.             if output_slot_start ~= 0 then
  521.               _, _, out_count, out_max_count = getSlotItem(output_inventory, output_slot_start)
  522.               local transfer_count = math.min(filter_count - out_count, out_max_count, item_count)
  523.               transposer.transferItem(input_side, output_side, transfer_count, input_slot_start, output_slot_start)
  524.             end
  525.           end
  526.         end
  527.       end
  528.     end
  529.   end
  530. end
  531.  
  532. ------------------------------
  533. --   WORKING PROCESS        --
  534. ------------------------------
  535.  
  536. local function init()
  537.   initInventories()
  538.   loadProperities()
  539.   getStacks()
  540.   separateItems()
  541.   drawBase()
  542.   drawInfo()
  543.   local prev_uptime = computer.uptime()
  544.   local counter = 0
  545.   while true do
  546.     local signal = {event.pull(0.05)}
  547.     local current_uptime = computer.uptime()
  548.     local s = signal[1]
  549.     -- Timer
  550.     if current_uptime - prev_uptime > 0.05 then
  551.       if counter % SORT_PERIOD == 0 then
  552.         getStacks()
  553.         separateItems()
  554.         prev_uptime = current_uptime
  555.       end
  556.       if edit_handle then
  557.         edit_handle(false)
  558.       end
  559.       counter = counter + 1
  560.     end
  561.     -- Handler
  562.     if s == "key_down" then
  563.       local key = signal[4]
  564.       local byte = signal[3]
  565.       if edit_handle then
  566.         edit_handle(true, key, byte)
  567.       else
  568.         if key == 203 then -- Left arrow
  569.           moveRL(-1)
  570.         elseif key == 205 then -- Right arrow
  571.           moveRL(1)
  572.         elseif key == 200 then -- Up arrow
  573.           moveUD(-1)
  574.         elseif key == 208 then -- Down arrow
  575.           moveUD(1)
  576.         elseif key == 210 then -- Insert
  577.           addFilter()
  578.         elseif key == 211 then -- Delete
  579.           delFilter()
  580.         elseif key == 28 then -- Enter
  581.           startEdit()
  582.         elseif key == 14 then -- Backspace
  583.           saveProperities()
  584.           gpu.setBackground(0x000000)
  585.           gpu.setForeground(0xFFFFFF)
  586.           gpu.fill(1, 1, screen_x, screen_y, " ")
  587.           return
  588.         elseif key == 57 then -- Space
  589.           if filters_pos == 0 then
  590.             saveProperities()
  591.           end
  592.         end
  593.       end
  594.     end
  595.   end
  596. end
  597.  
  598. --init()
  599.  
  600. local ok, msg  = pcall(init)
  601. if not ok then
  602.   print("Error: " .. tostring(msg))
  603. end
  604.  
Advertisement
RAW Paste Data Copied
Advertisement