Shaka01

Crafter 2024

Dec 3rd, 2024 (edited)
35
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.06 KB | None | 0 0
  1. local turtleName = "turtle_2"
  2.  
  3.  
  4.  
  5. -- Load dependancies
  6. if not fs.exists("list") then
  7.     shell.run("pastebin", "get", "NnxPk3Dt", "list")
  8. end
  9.  
  10. -- Load dependancies
  11. if not fs.exists("recipeRecorder") then
  12.     shell.run("pastebin", "get", "wfvywJmg", "recipeRecorder")
  13. end
  14.  
  15. -- Load API
  16. if not fs.exists("API") then
  17.     shell.run("pastebin", "get", "EzkfU5ZM", "API")
  18. end
  19. shaka = require("API")
  20.  
  21. -- Variables for peripherals
  22. local wirelessModem = nil
  23. local wiredModem = nil
  24. local monitor = nil
  25. local fromInventories = {}
  26. local itemName = nil
  27. local listExist = false
  28.  
  29.  
  30. if not fs.exists("items_list.lua") then
  31.     shaka.clearScreen()
  32.     term.setTextColor(colors.green)
  33.     print("Welcome!")
  34.     sleep(1)
  35.     term.setCursorPos(1, 3)
  36.     term.setTextColor(colors.yellow)
  37.     textutils.slowPrint("Please add crafting recipes...", 10)
  38.     sleep(1)
  39.     shell.run("recipeRecorder")
  40. else
  41.     listExist = true
  42. end
  43.  
  44. -- Connect peripherals on direct sides
  45. local function connectDirectPeripherals()
  46.     local sides = {"left", "right", "top", "bottom", "front", "back"}
  47.     for _, side in ipairs(sides) do
  48.         local device = peripheral.getType(side)
  49.         if device == "modem" then
  50.             local modem = peripheral.wrap(side)
  51.             if modem.isWireless() then
  52.                 wirelessModem = modem
  53.                 rednet.open(side)
  54.             else
  55.                 wiredModem = modem
  56.                 rednet.close(side)
  57.             end
  58.         elseif device == "monitor" then
  59.             monitor = peripheral.wrap(side)
  60.         end
  61.     end
  62. end
  63.  
  64. -- Function to check if a table contains an item
  65. local function tableContains(t, item)
  66.     for _, value in pairs(t) do
  67.         if value == item then
  68.             return true
  69.         end
  70.     end
  71.     return false
  72. end
  73.  
  74. -- Connect and bind inventory peripherals via modem
  75. local function getInventories()
  76.     if not wiredModem then
  77.         print("No wired modem detected. Cannot fetch inventories.")
  78.         return
  79.     end
  80.  
  81.     local remotePeripherals = wiredModem.getNamesRemote()
  82.     for _, inventoryName in ipairs(remotePeripherals) do
  83.         if inventoryName and not shaka.stringFind(inventoryName, "turtle") and not shaka.stringFind(inventoryName, "computer") then
  84.             if not tableContains(fromInventories, inventoryName) then
  85.                 table.insert(fromInventories, inventoryName)
  86.             end
  87.         end
  88.     end
  89.     shaka.writeFile("inventories", fromInventories)
  90. end
  91.  
  92. local function startup()
  93.     connectDirectPeripherals()
  94.     getInventories()
  95.  
  96.     if monitor then
  97.         monitor.write("Setup Complete. Inventories saved.")
  98.     else
  99.         -- print("Setup Complete. Inventories saved.")
  100.     end
  101. end
  102. startup()
  103.  
  104. -- Function to search an individual inventory for the desired item and return a list of slots with amounts
  105. local function searchInventoryForSlots(inventoryName, itemName)
  106.     local inventory = peripheral.wrap(inventoryName)
  107.     local slots = {}
  108.  
  109.     if inventory then
  110.         local items = inventory.list()
  111.         for slot, details in pairs(items) do
  112.             if details.name == itemName then
  113.                 table.insert(slots, {slot = slot, count = details.count})
  114.             end
  115.         end
  116.     end
  117.    
  118.     return slots
  119.    
  120. end
  121.  
  122. -- Function to search all inventories and return a list of slots with the desired item
  123. local function searchAllInventories(itemName)
  124.     local indexedSlots = {}
  125.  
  126.     local tasks = {}
  127.     for _, inventoryName in ipairs(fromInventories) do
  128.         table.insert(tasks, function()
  129.             local slots = searchInventoryForSlots(inventoryName, itemName)
  130.             if #slots > 0 then
  131.                 indexedSlots[inventoryName] = slots
  132.             end
  133.             -- Print inventory and slot info for debugging
  134.             for _, slotInfo in ipairs(slots) do
  135.                 -- print(inventoryName .. " has " .. slotInfo.count .. " of " .. itemName .. " in slot " .. slotInfo.slot)
  136.             end
  137.         end)
  138.     end
  139.  
  140.     parallel.waitForAll(unpack(tasks))
  141.     -- shaka.writeFile("lol", indexedSlots)
  142.     return indexedSlots
  143. end
  144.  
  145. -- Function to pull items from the indexed slots
  146. local function pullItemsFromSlots(indexedSlots, quantity, toSlot)
  147.     local totalPulled = 0
  148.     local remainingToPull = quantity
  149.     local pullTasks = {}
  150.  
  151.     -- Create parallel tasks to pull from each slot
  152.     for inventoryName, slots in pairs(indexedSlots) do
  153.         for _, slotInfo in ipairs(slots) do
  154.             local toPull = math.min(slotInfo.count, remainingToPull)
  155.             table.insert(pullTasks, function()
  156.                 local inventory = peripheral.wrap(inventoryName)
  157.                 local pulled = 0
  158.                 if inventory then
  159.                     pulled = inventory.pushItems(turtleName, slotInfo.slot, toPull, toSlot)
  160.                     totalPulled = totalPulled + pulled
  161.                     remainingToPull = remainingToPull - pulled
  162.                     -- print("Pulled " .. pulled .. " from " .. inventoryName .. " (slot " .. slotInfo.slot .. ")")
  163.                 end
  164.             end)
  165.             remainingToPull = remainingToPull - toPull
  166.             if remainingToPull <= 0 then
  167.                 break
  168.             end
  169.         end
  170.     end
  171.  
  172.     -- Run all the tasks in parallel
  173.     parallel.waitForAll(unpack(pullTasks))
  174.  
  175.     if totalPulled >= quantity then
  176.         -- print("Successfully pulled " .. totalPulled .. " of " .. itemName)
  177.         return true
  178.     else
  179.         print("Failed to pull enough items. Pulled " .. totalPulled .. " out of " .. quantity)
  180.         return false
  181.     end
  182. end
  183.  
  184.  
  185. function findAvailableDrop(slot)
  186.   local tasks = {}
  187.   for i = 1, #fromInventories do
  188.     local invWrap = peripheral.wrap(fromInventories[i])
  189.     tasks[#tasks+1] = function()
  190.       invWrap.pullItems(turtleName, slot)
  191.     end
  192.   end
  193.   parallel.waitForAll(unpack(tasks))
  194. end
  195.  
  196.  
  197. function dropInventory()
  198.   local tasks = {}
  199.   for i = 1, 16 do
  200.     if turtle.getItemCount(i) > 0 then
  201.       tasks[#tasks+1] = function()
  202.         repeat
  203.           findAvailableDrop(i)
  204.         until turtle.getItemCount(i) == 0
  205.       end
  206.     end
  207.   end
  208.   parallel.waitForAll(unpack(tasks))
  209. end
  210.  
  211. -- Load saved recipes
  212. local function loadRecipes()
  213.     local filePath = "recipes/recipes.lua"
  214.     local recipes = {}
  215.    
  216.     if fs.exists(filePath) then
  217.         local file = fs.open(filePath, "r")
  218.         local content = file.readAll()
  219.         file.close()
  220.         if content and content ~= "" then
  221.             recipes = textutils.unserialize(content) or {}
  222.         end
  223.     end
  224.    
  225.     return recipes
  226. end
  227.  
  228. -- Find a recipe by output item
  229. local function findRecipeByOutput(outputName)
  230.     local recipes = loadRecipes()
  231.     for _, recipe in ipairs(recipes) do
  232.         if recipe.output[1].name == outputName then
  233.             -- print(outputName)
  234.             -- shaka.writeFile("showMe", recipe)
  235.             return recipe
  236.         end
  237.     end
  238.     return nil
  239. end
  240.  
  241.  
  242. local function checkAvailability(itemName, amount)
  243.     -- Step 1: Search all inventories for available slots for the item
  244.     local indexedSlots = searchAllInventories(itemName)
  245.     local totalAvailable = 0
  246.  
  247.     -- Step 2: Check if there are enough items in the inventories
  248.     for _, slots in pairs(indexedSlots) do
  249.         for _, slotInfo in ipairs(slots) do
  250.             totalAvailable = totalAvailable + slotInfo.count
  251.         end
  252.     end
  253.  
  254.     -- Step 3: If not enough items, return false
  255.     if totalAvailable < amount then
  256.         -- print("Not enough " .. itemName .. " available! Only " .. totalAvailable .. "/" .. amount .. " found.")
  257.         local missingCount = amount - totalAvailable
  258.         -- print(missingCount)
  259.         -- sleep(2)
  260.         return missingCount
  261.     end
  262.  
  263.     -- Step 4: If enough items are available, return true
  264.     -- print("There are enough " .. itemName .. " available: " .. totalAvailable .. "/" .. amount)
  265.     return true
  266. end
  267.  
  268. local function checkRecipeAvailability(outputName, amount)
  269.     -- Step 1: Find the recipe for the given output item
  270.     local recipe = findRecipeByOutput(outputName)
  271.     if not recipe then
  272.         print("Recipe for " .. outputName .. " not found.")
  273.         return false, nil, nil
  274.     end
  275.    
  276.     -- Get the number of items produced by the recipe (e.g., 1 chest)
  277.     local craftOutput = recipe.output[1].count
  278.    
  279.     -- Step 2: Calculate the total amount of ingredients required based on the amount to craft
  280.     local totalIngredients = {} -- To store the total required quantity of each input item
  281.  
  282.     for _, inputItem in ipairs(recipe.input) do
  283.         if inputItem.type ~= "empty" then
  284.             -- Calculate the required amount of the input item (scaled by amount if needed)
  285.             local requiredAmount = math.ceil((amount or 1)/craftOutput)
  286.             totalIngredients[inputItem.name] = (totalIngredients[inputItem.name] or 0) + requiredAmount
  287.         end
  288.     end
  289.    
  290.     -- Step 3: Check if there are enough ingredients available in the inventories
  291.     for itemName, requiredAmount in pairs(totalIngredients) do
  292.         -- print("Checking availability for " .. itemName .. ": " .. requiredAmount .. " needed.")
  293.         -- Check if the required amount of the ingredient is available in the inventories
  294.         local missing = checkAvailability(itemName, requiredAmount)
  295.         if missing ~= true then
  296.             -- print("Missing " .. missing .. " of " .. itemName)
  297.             -- sleep(2)
  298.             return itemName, missing, requiredAmount
  299.         end
  300.     end
  301.  
  302.     -- Step 4: If all ingredients are available, return true
  303.     -- print("All ingredients are available for " .. outputName)
  304.     return true, amount, nil
  305. end
  306.  
  307. -- Main pull
  308. local function finalPull(item_Name, amount, toSlot)
  309.     itemName = item_Name
  310.  
  311.     -- Step 1: Search all inventories for available slots and check if there are enough items
  312.     local indexedSlots = searchAllInventories(item_Name)
  313.     local totalAvailable = 0
  314.     for _, slots in pairs(indexedSlots) do
  315.         for _, slotInfo in ipairs(slots) do
  316.             totalAvailable = totalAvailable + slotInfo.count
  317.         end
  318.     end
  319.     -- print(totalAvailable, amount)
  320.  
  321.     -- Step 2: If not enough items, abort the pull
  322.     if totalAvailable < amount then
  323.         -- print("Not enough items available! Only " .. totalAvailable .. "/"..amount.." of " .. item_Name .. " found.")
  324.         return false
  325.     end
  326.  
  327.     -- Step 3: Proceed to pull items from indexed slots
  328.     return pullItemsFromSlots(indexedSlots, math.min(amount, 64), toSlot)
  329. end
  330.  
  331.  
  332. -- Function to check if all items in the list can be found in the connected inventories with the specified amount
  333. local function checkAllItemsAvailability(itemsList)
  334.     -- Loop through each item in the list
  335.     for _, item in ipairs(itemsList) do
  336.         local itemName = item.name
  337.         local requiredAmount = item.amount
  338.        
  339.         -- Check if the item is available in the inventories
  340.         local missingAmount = checkAvailability(itemName, requiredAmount)
  341.        
  342.         -- If the checkAvailability function returns a number (missing amount), that means we don't have enough items
  343.         if missingAmount ~= true then
  344.             -- print("Not enough " .. itemName .. " available. Missing: " .. missingAmount)
  345.             return itemName, missingAmount
  346.         end
  347.     end
  348.    
  349.     -- If all items are available with the required amount
  350.     -- print("All items are available with the required amount.")
  351.     return true, true
  352. end
  353.  
  354.  
  355. -- Function to craft an item based on a requested output
  356. local function craftItem(outputName, amount)
  357.     -- local amount = math.min(amount, 64)
  358.     -- Step 1: Check availability of ingredients for the requested item
  359.     local name, missAmount, requiredAmount = checkRecipeAvailability(outputName, amount)
  360.     -- print("MISS: ", name, missAmount)
  361.     -- sleep(2)
  362.     if name ~= true then
  363.         if findRecipeByOutput(outputName) ~= nil then
  364.             -- print("Not enough " .. name .. " (" .. missAmount .. "/" .. requiredAmount .. "). Crafting required.")
  365.             craftItem(name, missAmount) -- Recursively craft missing item
  366.             return
  367.             craftItem(outputName, amount)
  368.         else
  369.             print("Missing ", name, " and can't craft ", outputName,", aborting")
  370.             return
  371.         end
  372.     end
  373.    
  374.     -- Step 2: Find the recipe for the given output item
  375.     local recipe = findRecipeByOutput(outputName)
  376.     if not recipe then
  377.         term.setTextColor(colors.red)
  378.         print("Recipe for " .. outputName .. " not found.")
  379.         sleep(1)
  380.         return
  381.     end
  382.     local craftOutput = recipe.output[1].count
  383.  
  384.     -- Step 3: Pull the input items using the finalPull function
  385.     -- print("Pulling input items for " .. outputName .. "...")
  386.     for _, inputItem in ipairs(recipe.input) do
  387.         if inputItem.type ~= "empty" then
  388.             -- Pull each item to the turtle's inventory
  389.             local success = finalPull(inputItem.name, math.ceil(amount/craftOutput), inputItem.slot)
  390.             if not success then
  391.                 term.setTextColor(colors.red)
  392.                 print("Failed to pull item: " .. inputItem.name)
  393.                 return
  394.             end
  395.         end
  396.     end
  397.    
  398.     -- Step 4: Craft the item
  399.     -- print("Crafting " .. outputName .. "...")
  400.     turtle.craft()
  401.    
  402.     -- Step 5: Check if crafting succeeded
  403.     local output = turtle.getItemDetail(1)
  404.     if output and output.name == outputName then
  405.         term.setTextColor(colors.green)
  406.         print("Crafted " .. outputName)
  407.     else
  408.         term.setTextColor(colors.red)
  409.         print("Crafting failed. Check inputs.")
  410.     end
  411.  
  412.     -- Drop excess items
  413.     -- sleep(2)
  414.     dropInventory()
  415. end
  416.  
  417. -- Define key mappings for the desired programs
  418. local keyMappings = {
  419.     c = "recipeRecorder", -- Program to switch to when 'c' is pressed
  420.     f = "list"            -- Program to switch to when 'f' is pressed
  421. }
  422.  
  423. -- Function to map keys to program switches
  424. function programSwitcher()
  425.     shaka.clearScreen()
  426.     term.setTextColor(colors.gray)
  427.     print("[c] to switch to recipeRecorder\n[f] to view list")
  428.    
  429.     while true do
  430.         local event, keyCode = os.pullEvent("key") -- Wait for a key press
  431.         local keyName = keys.getName(keyCode)     -- Get the name of the key pressed
  432.  
  433.         if keyMappings[keyName] then
  434.             print("Switching to program: " .. keyMappings[keyName])
  435.             shell.run(keyMappings[keyName])      -- Run the specified program
  436.             break -- Exit loop after launching the program
  437.         else
  438.             -- print("Unmapped key pressed: " .. (keyName or "unknown"))
  439.         end
  440.     end
  441. end
  442.  
  443.  
  444.  
  445. function doitall()
  446.     local wantedItems = shaka.readFile("items_list.lua")
  447.  
  448.     while true do
  449.         local wantedName, wantedAmount = checkAllItemsAvailability(wantedItems)
  450.  
  451.         if wantedName ~= true then
  452.             craftItem(wantedName, wantedAmount)
  453.         end
  454.         sleep(1)
  455.     end
  456. end
  457.  
  458. function finish()
  459.     if listExist then
  460.         parallel.waitForAny(doitall, programSwitcher)
  461.     end
  462. end
  463.  
  464.  
  465.  
  466.  
  467. finish()
  468. -- dropInventory()
  469. -- finalPull("minecraft:oak_planks", 3, 5)
  470. -- craftItem("minecraft:piston", 1)
  471. -- findRecipeByOutput("minecraft:chest")
  472. -- searchAllInventories("minecraft:chest")
  473. -- checkAvailability("minecraft:chest", 3)
  474. -- checkRecipeAvailability("minecraft:spruce_slab", 6)
Add Comment
Please, Sign In to add comment