Advertisement
Ubidibity

backpackloot.lua

May 28th, 2025 (edited)
1,948
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.12 KB | Gaming | 0 0
  1. -- Backpack Inventory Processor v1.21
  2. -- Configuration
  3. local trash = peripheral.wrap("up")
  4. local chest = peripheral.wrap("back")
  5. local modem = peripheral.wrap("top") -- Modem on top as per your setup
  6. local monitor = peripheral.find("monitor") -- Find the monitor through the modem
  7. local backpack_side = "front"
  8.  
  9. -- Constants
  10. local VERSION = "1.21"
  11. local BACKPACKS_PER_CYCLE = 9 -- Process exactly 9 backpacks per cycle
  12. local STUCK_LIMIT = 10
  13. local SLEEP_INTERVAL = 0.25
  14.  
  15. -- Global tables to track item quantities and frequencies (removed local so they're actually global for save functions)
  16. item_quantities = {} -- {item_name = total_quantity}
  17. item_frequencies = {} -- {item_name = number_of_stacks}
  18.  
  19. -- wtp imports
  20. local filefunctions = dofile("filefunctions.lua") -- https://pastebin.com/52b6wYBq
  21.  
  22. -- Append line of text to file at 'path'
  23. local function fileAppend(path, text)
  24.     local file = io.open(path, "a")
  25.     file:write(text .. "\n")
  26.     file:close()
  27. end
  28. -- Append a log entry with timestamp 'time' and text 'text' to the file at 'path'
  29. local function addLog(text)
  30.     fileAppend("log.txt", "[" .. os.time() .. "]"..text)
  31. end
  32.  
  33.  
  34. -- Initialize monitor with a specific text scale
  35. local function initMonitor(scale)
  36.     if not monitor then
  37.         error("Monitor not found. Ensure it's connected via the modem.")
  38.     end
  39.     monitor.clear()
  40.     monitor.setTextScale(scale)
  41.     monitor.setCursorPos(1, 1)
  42.     return monitor.getSize()
  43. end
  44.  
  45. -- Display items on monitor in two columns with progress bars at the bottom
  46. local function displayManifest(manifest, mon_width, mon_height, backpacks_processed, current_slot, backpack_size)
  47.     local mon_width, mon_height = initMonitor(1.4) -- Scale 1.4 for processing
  48.     monitor.clear()
  49.     monitor.setCursorPos(1, 1)
  50.    
  51.     -- Add version header
  52.     monitor.write("Version " .. VERSION)
  53.     monitor.setCursorPos(1, 2)
  54.     monitor.write("Backpack Contents")
  55.     monitor.setCursorPos(1, 3)
  56.    
  57.     local mid_point = math.ceil(mon_width / 2) -- Split the width for two columns
  58.     local max_item_length = mid_point - 1 -- Dynamically set max_item_length based on column width
  59.     local row = 3 -- Start below the headers
  60.     local col = 1
  61.     local item_display_height = mon_height - 2 -- Reserve last 2 lines for progress bars
  62.  
  63.     for _, item in ipairs(manifest) do
  64.         if row > item_display_height then
  65.             col = mid_point -- Second column starts at mid_point
  66.             row = 3 -- Reset row below the headers
  67.             monitor.setCursorPos(col, row)
  68.         end
  69.         -- Truncate item name to leave room for quantity (": XX" plus buffer)
  70.         local display_name = item.name
  71.         local name_length = max_item_length - 5 -- Reserve 5 characters for ": XX" and a buffer
  72.         if #display_name > name_length then
  73.             display_name = string.sub(display_name, 1, name_length - 3) .. "..."
  74.         end
  75.         monitor.write(display_name .. ": " .. item.count)
  76.         row = row + 1
  77.         monitor.setCursorPos(col, row)
  78.     end
  79.  
  80.     -- Draw backpack progress bar (Line mon_height - 1)
  81.     local backpack_percent = (backpacks_processed / BACKPACKS_PER_CYCLE) * 100
  82.     local filled_backpack = math.floor(backpack_percent * mon_width / 100)
  83.     monitor.setCursorPos(1, mon_height - 1)
  84.     monitor.setBackgroundColour(colors.green)
  85.     for i = 1, filled_backpack do
  86.         monitor.write(" ")
  87.     end
  88.     monitor.setBackgroundColour(colors.red)
  89.     for i = filled_backpack + 1, mon_width do
  90.         monitor.write(" ")
  91.     end
  92.     monitor.setBackgroundColour(colors.black)
  93.     monitor.setTextColour(colors.white)
  94.     monitor.setCursorPos(1, mon_height - 1)
  95.     monitor.write("Backpacks: " .. math.floor(backpack_percent) .. "%")
  96.  
  97.     -- Draw inventory progress bar (Line mon_height)
  98.     local inventory_percent = backpack_size > 0 and (current_slot / backpack_size) * 100 or 0
  99.     local filled_inventory = math.floor(inventory_percent * mon_width / 100)
  100.     monitor.setCursorPos(1, mon_height)
  101.     monitor.setBackgroundColour(colors.green)
  102.     for i = 1, filled_inventory do
  103.         monitor.write(" ")
  104.     end
  105.     monitor.setBackgroundColour(colors.red)
  106.     for i = filled_inventory + 1, mon_width do
  107.         monitor.write(" ")
  108.     end
  109.     monitor.setBackgroundColour(colors.black)
  110.     monitor.setTextColour(colors.white)
  111.     monitor.setCursorPos(1, mon_height)
  112.     monitor.write("Inventory: " .. math.floor(inventory_percent) .. "%")
  113. end
  114.  
  115. -- Check for backpack and handle stuck detection
  116. local function checkForBackpack()
  117.     local stuck = 0
  118.     while true do
  119.         local has_block, data = turtle.inspect()
  120.         if has_block then
  121.             return true
  122.         end
  123.         print("Waiting for backpack...")
  124.         sleep(SLEEP_INTERVAL)
  125.         stuck = stuck + 1
  126.         if stuck >= STUCK_LIMIT then
  127.             print("No more backpacks to process.")
  128.             return false
  129.         end
  130.     end
  131. end
  132.  
  133. -- Process backpack contents and track items imported
  134. local function processBackpack(backpack, chest, manifest, mon_width, mon_height, backpacks_processed)
  135.     local size = backpack.size()
  136.     local items_imported = 0 -- Track items imported in this backpack
  137.     for i = 1, size do
  138.         local slot = backpack.getItemDetail(i)
  139.         if slot then
  140.             addLog("Slot " .. i .. " contains " .. slot.name .. " (" .. slot.count .. ")")
  141.             table.insert(manifest, {name = slot.name, count = slot.count})
  142.             -- Update item quantities and frequencies
  143.             item_quantities[slot.name] = (item_quantities[slot.name] or 0) + slot.count
  144.             item_frequencies[slot.name] = (item_frequencies[slot.name] or 0) + 1
  145.             displayManifest(manifest, mon_width, mon_height, backpacks_processed, i, size)
  146.             local items = backpack.pushItems(peripheral.getName(chest), i, 64)
  147.             if items ~= slot.count then
  148.                 addLog(items .. " of " .. slot.count .. " transferred, chest full? Failing.")
  149.                 return false, items_imported
  150.             end
  151.             items_imported = items_imported + 1 -- Count each stack as an "item"
  152.         end
  153.     end
  154.     return true, items_imported
  155. end
  156.  
  157. -- Empty turtle inventory
  158. local function emptyTurtleInventory()
  159.     local success = true
  160.     local mon_width, mon_height = initMonitor(1.4) -- Scale 1.4 for dumping
  161.     monitor.clear()
  162.     monitor.setCursorPos(1, 1)
  163.     monitor.write("Version " .. VERSION)
  164.     monitor.setCursorPos(1, 2)
  165.     monitor.write("Dumping empty backpacks...")
  166.     turtle.turnRight()
  167.     local ok, err = pcall(function()
  168.         for slot = 1, BACKPACKS_PER_CYCLE do
  169.             turtle.select(slot)
  170.             if not turtle.drop() then
  171.                 addLog("Drop failed, likely had <" .. BACKPACKS_PER_CYCLE .. " backpacks.")
  172.                 success = false
  173.                 return
  174.             end
  175.         end
  176.     end)
  177.     turtle.turnLeft() -- Always turn back left
  178.     if not ok then
  179.         addLog("Error during dump: " .. tostring(err))
  180.         success = false
  181.     end
  182.     return success
  183. end
  184.  
  185. -- Helper function to sort items for display
  186. local function sortItemsForDisplay(item_table, sort_by_value, ascending)
  187.     local sorted_items = {}
  188.     for name, value in pairs(item_table) do
  189.         table.insert(sorted_items, {name = name, value = value})
  190.     end
  191.     table.sort(sorted_items, function(a, b)
  192.         if a.value == b.value then
  193.             return a.name < b.name -- Alphabetical order for ties
  194.         end
  195.         if ascending then
  196.             return a.value < b.value
  197.         else
  198.             return a.value > b.value
  199.         end
  200.     end)
  201.     return sorted_items
  202. end
  203.  
  204. -- Display waiting screen with top looted and rarest items
  205. local function displayWaitingScreen(mon_width, mon_height, total_backpacks_processed, total_items_imported)
  206.     monitor.clear()
  207.     monitor.setCursorPos(1, 1)
  208.     monitor.write("Version " .. VERSION)
  209.     monitor.setCursorPos(1, 2)
  210.     monitor.write("Waiting for backpacks...")
  211.     monitor.setCursorPos(1, 3)
  212.     monitor.write("Backpacks Processed: " .. total_backpacks_processed)
  213.     monitor.setCursorPos(1, 4)
  214.     monitor.write("Items Imported: " .. total_items_imported)
  215.  
  216.     -- Get top 10 looted items (by quantity)
  217.     -- I believe this passes a global reference that sort uses internally as a local
  218.  
  219.     local top_looted = sortItemsForDisplay(item_quantities, true, false)
  220.     local mid_point = math.ceil(mon_width / 2)
  221.     local max_item_length = mid_point - 5 -- Reserve space for ": XXXXX" (up to 5 digits)
  222.  
  223.     -- Display top 10 looted items in two columns
  224.  
  225.     monitor.setCursorPos(1, 7)
  226.     monitor.write("Top 10 Looted Items")
  227.     local row = 8 -- Start items on line 8
  228.     local col = 1
  229.     for i = 1, math.min(10, #top_looted) do
  230.         if i == 6 then
  231.             col = mid_point
  232.             row = 8 -- Reset row for second column
  233.         end
  234.         local item = top_looted[i]
  235.         local display_name = item.name
  236.         if #display_name > max_item_length then
  237.             display_name = string.sub(display_name, 1, max_item_length - 3) .. "..."
  238.         end
  239.         monitor.setCursorPos(col, row)
  240.         monitor.write(display_name .. ": " .. item.value)
  241.         row = row + 1
  242.     end
  243.  
  244.     -- Display 5 rarest items below with a blank line
  245.     local rarest_items = sortItemsForDisplay(item_frequencies, true, true)
  246.     monitor.setCursorPos(1, 14) -- Moved to line 14 with a blank line on 13
  247.     monitor.write("5 Rarest Items Seen")
  248.     row = 15 -- Start items on line 15
  249.     col = 1
  250.     for i = 1, math.min(5, #rarest_items) do
  251.         local item = rarest_items[i]
  252.         local display_name = item.name
  253.         if #display_name > max_item_length then
  254.             display_name = string.sub(display_name, 1, max_item_length - 3) .. "..."
  255.         end
  256.         monitor.setCursorPos(col, row)
  257.         monitor.write(display_name .. ": " .. item.value)
  258.         row = row + 1
  259.     end
  260. end
  261.  
  262. -- Main function
  263. local function main()
  264.     local mon_width, mon_height = initMonitor(1.4) -- Use 1.4 scale for waiting screen
  265.     local total_backpacks_processed = 0 -- Track total backpacks processed
  266.     local total_items_imported = 0 -- Track total items imported
  267.     while true do
  268.         -- Wait for redstone signal to indicate 9 backpacks (signal strength 15)
  269.         displayWaitingScreen(mon_width, mon_height, total_backpacks_processed, total_items_imported)
  270.         while redstone.getAnalogInput("bottom") ~= 15 do
  271.             sleep(1) -- Check every second to avoid excessive CPU usage
  272.             monitor.setCursorPos(1, 5)
  273.             monitor.write("Signal: " .. redstone.getAnalogInput("bottom"))
  274.             sleep(1) -- Update every second
  275.             displayWaitingScreen(mon_width, mon_height, total_backpacks_processed, total_items_imported)
  276.         end
  277.  
  278.         -- Process exactly 9 backpacks
  279.         local backpacks_processed = 0 -- Track backpacks processed in this cycle
  280.         local cycle_items_imported = 0 -- Track items imported in this cycle
  281.         for slot = 1, BACKPACKS_PER_CYCLE do
  282.             local manifest = {} -- Reset manifest for each backpack
  283.             turtle.select(slot)
  284.             redstone.setOutput("left", true)
  285.             if not checkForBackpack() then
  286.                 redstone.setOutput("left", false) -- Reset redstone on abort
  287.                 addLog("Fewer than 9 backpacks available, proceeding with " .. backpacks_processed .. " backpacks.")
  288.                 break
  289.             end
  290.             redstone.setOutput("left", false)
  291.             backpacks_processed = backpacks_processed + 1
  292.  
  293.             local backpack = peripheral.wrap(backpack_side)
  294.             local success, items_imported = processBackpack(backpack, chest, manifest, mon_width, mon_height, backpacks_processed)
  295.             if not success then
  296.                 redstone.setOutput("left", false) -- Ensure redstone is off
  297.                 addLog("Chest full, proceeding to dump backpacks.")
  298.                 break
  299.             end
  300.             cycle_items_imported = cycle_items_imported + items_imported
  301.             turtle.dig()
  302.             sleep(1) -- Brief pause to show the manifest
  303.         end
  304.  
  305.         -- Dump backpacks if any were processed
  306.         if backpacks_processed > 0 then
  307.             if not emptyTurtleInventory() then
  308.                 redstone.setOutput("left", false) -- Ensure redstone is off
  309.                 addLog("Dump failed, continuing to wait for next cycle.")
  310.                 -- Continue to waiting state instead of exiting
  311.             end
  312.             total_backpacks_processed = total_backpacks_processed + backpacks_processed
  313.             total_items_imported = total_items_imported + cycle_items_imported
  314.         end
  315.  
  316.         -- Return to waiting state
  317.         mon_width, mon_height = initMonitor(1.4) -- Reset to 1.4 scale for waiting
  318.         monitor.clear()
  319.         monitor.setCursorPos(1, 1)
  320.         monitor.write("Version " .. VERSION)
  321.         monitor.setCursorPos(1, 2)
  322.         monitor.write("Ready for next set of backpacks...")
  323.         monitor.setCursorPos(1, 3)
  324.         monitor.write("Backpacks Processed: " .. total_backpacks_processed)
  325.         monitor.setCursorPos(1, 4)
  326.         monitor.write("Items Imported: " .. total_items_imported)
  327.         sleep(2) -- Brief pause before returning to waiting state
  328.         filefunctions.saveStats("stats.dat") -- trying _G. out ,item_quantities,item_frequencies)
  329.         print("Logging stats...")
  330.         for k,v in pairs(item_quantities) do
  331.           addLog("Qty", k, v)
  332.         end
  333.         for k,v in pairs(item_frequencies) do
  334.           addLog("Freq", k, v)
  335.        end
  336.         addLog("Stats saved.")
  337.     end
  338. end
  339.  
  340. -- initialize log
  341. if fs.exists("log.txt") then
  342. else
  343.     file = io.open("logs.txt", "w")
  344.     file:write("")
  345.     file:close()
  346. end
  347.  
  348. -- load previous values (if any)
  349. -- item_quantities,item_frequencies=
  350. filefunctions.loadStats("stats.dat") -- _G. should be global so trying to revert back to not passing the tables
  351. -- for whatever reason my non local item_* variables seem to act local in scope and seems to need the following to work
  352. item_quantities=_G.item_quantities
  353. item_frequencies=_G.item_frequencies
  354.  
  355. print("checking stats...")
  356. for k,v in pairs(item_quantities) do
  357.     print("Qty", k, v)
  358. end
  359. for k,v in pairs(item_frequencies) do
  360.     print("Freq", k, v)
  361. end
  362.  
  363. -- Run the program
  364. main()
  365. -- Ensure redstone is off on program exit
  366. redstone.setOutput("left", false)
  367.  
  368. -- Mobs drop backpacks now.  Basic setup is AE2 export bus->ender chest, that 'combo' ender chest exports to a placer.
  369. -- The redstone signal on the left is letting a placer place the backpack (back-filled from an item
  370. -- pipe from the ender chest full of backpacks from the AE2) it's carried along the top of a block via redstone wire
  371. -- the chest referenced in code below is where the turtle dumps the inventory inside the backpacks
  372. -- finally after 16 backpacks it turns to the right and drops them all in a different ender chest that then drops them
  373. -- into a matter condenser for singularities.
  374. -- I've introduced a comparator and a couple ARS redstone relays linked one in front of the comparator, and one below the turtle.
  375. -- Alternative implementation by DesatKorun at https://pastebin.com/gkDnN8Fm
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement