Myros27

newVillageBot

May 25th, 2025 (edited)
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 32.67 KB | None | 0 0
  1. --[[
  2. Minecraft Turtle Delivery Script
  3. --]]
  4.  
  5. -- ========= CONFIGURATION =========
  6. local ENABLE_FILE_LOGGING = true
  7. local LOG_FILE_NAME = "turtle_delivery.log"
  8. local BUILDER_CHEST_CLEAR_TRIPS = 4 -- How many trips to make to try and clear builder's chest
  9. -- ===============================
  10.  
  11. local AUTOMATA_TYPE = "endAutomata"
  12. local ME_BRIDGE_TYPE = "meBridge"
  13. local COLONY_INTEGRATOR_TYPE = "colonyIntegrator"
  14. local ME_BRIDGE_BLOCK_ID = "advancedperipherals:me_bridge"
  15. local SLOT_NUMBER_FILE = "builder_slot.txt"
  16. local HOME_POINT_NAME = "home"
  17. local DELIVERY_POINT_NAME = "deliveryPoint"
  18. local BUILDER_HUT_TYPE_PATTERN = "builder"
  19.  
  20. local automata
  21. local meBridge, colonyIntegrator
  22.  
  23. local function getTimestamp() return os.date("%Y-%m-%d %H:%M:%S") end
  24.  
  25. local function log(message)
  26.     print(message)
  27.     if ENABLE_FILE_LOGGING then
  28.         local fileMessage = "[" .. getTimestamp() .. "] " .. message
  29.         local file, err = fs.open(LOG_FILE_NAME, "a")
  30.         if file then
  31.             file.writeLine(fileMessage)
  32.             file.close()
  33.         else
  34.             print("ERROR: Could not write to log file '" .. LOG_FILE_NAME .. "': " .. tostring(err))
  35.         end
  36.     end
  37. end
  38.  
  39. if ENABLE_FILE_LOGGING then
  40.     local initialFileMessage = "[" .. getTimestamp() .. "] --- Turtle script started. Logging enabled. ---"
  41.     local file, err = fs.open(LOG_FILE_NAME, "a")
  42.     if file then
  43.         file.writeLine(initialFileMessage)
  44.         file.close()
  45.     else
  46.         print("ERROR: Could not write initial message to log file '" .. LOG_FILE_NAME .. "': " .. tostring(err))
  47.     end
  48. end
  49.  
  50. local function setupEssentialPeripherals()
  51.     log("Setting up essential peripherals...")
  52.     automata = peripheral.find(AUTOMATA_TYPE)
  53.     if not automata then log("CRITICAL ERROR: End Automata ('" .. AUTOMATA_TYPE .. "') not found!"); return false end
  54.     log("End Automata found.")
  55.     if not rs then log("CRITICAL ERROR: Redstone API (rs) not available!"); return false end
  56.     log("Redstone API found.")
  57.     log("Essential peripherals setup complete.")
  58.     return true
  59. end
  60.  
  61. local function setupOperationalPeripherals()
  62.     log("Setting up operational peripherals (at home base)...")
  63.     local meBridgeFound = false
  64.     meBridge = peripheral.find(ME_BRIDGE_TYPE)
  65.     if not meBridge then log("ERROR: ME Bridge ('" .. ME_BRIDGE_TYPE .. "') not found at home base!")
  66.     else log("ME Bridge found."); meBridgeFound = true end
  67.     colonyIntegrator = peripheral.find(COLONY_INTEGRATOR_TYPE)
  68.     if not colonyIntegrator then log("WARN: Colony Integrator ('" .. COLONY_INTEGRATOR_TYPE .. "') not found.")
  69.     else log("Colony Integrator found.") end
  70.     return meBridgeFound
  71. end
  72.  
  73. local function warpTo(pointName)
  74.     if not automata then log("Automata not available for warp."); return false end
  75.     log("Attempting warp to " .. pointName .. "...")
  76.     local currentFuel = turtle.getFuelLevel()
  77.     if type(currentFuel) == "number" and currentFuel < 50 and pointName ~= HOME_POINT_NAME then
  78.         log("WARN: Low fuel ("..currentFuel..") before warp to "..pointName..".")
  79.     elseif currentFuel == 0 and pointName ~= HOME_POINT_NAME then
  80.          log("ERROR: No fuel to attempt warp to "..pointName..".")
  81.          return false
  82.     end
  83.  
  84.     local success, reasonOrResult = pcall(automata.warpToPoint, pointName)
  85.    
  86.     if success then
  87.         if reasonOrResult == true or (type(reasonOrResult) == "string" and string.match(string.lower(reasonOrResult), "success")) then
  88.             log("Warp to " .. pointName .. " successful.")
  89.             return true
  90.         elseif reasonOrResult == nil then
  91.              log("Warp to " .. pointName .. " successful (returned true, nil).")
  92.              return true
  93.         else
  94.             log("ERROR: Warp to " .. pointName .. " call succeeded but peripheral reported failure: " .. tostring(reasonOrResult))
  95.             return false
  96.         end
  97.     else
  98.         log("ERROR: Warp to " .. pointName .. " pcall failed: " .. tostring(reasonOrResult))
  99.         return false
  100.     end
  101. end
  102.  
  103. local function orientToMEBridge()
  104.     log("Orienting to ME Bridge block ('" .. ME_BRIDGE_BLOCK_ID .. "')...")
  105.     for i = 1, 4 do
  106.         local s, data = turtle.inspect()
  107.         if s and data and data.name == ME_BRIDGE_BLOCK_ID then log("ME Bridge block found in front."); return true end
  108.         log("Not found in front. Turning right...")
  109.         turtle.turnRight(); os.sleep(0.5)
  110.     end
  111.     log("ERROR: ME Bridge block not found in front after 4 turns.")
  112.     return false
  113. end
  114.  
  115. -- findEmptySlot is no longer strictly needed for item collection but might be useful for other things like refueling.
  116. local function findEmptySlot()
  117.     for i = 1, 16 do if turtle.getItemCount(i) == 0 then return i end end
  118.     return nil
  119. end
  120.  
  121. local function clearTurtleInventoryToME()
  122.     if not meBridge then log("ME Bridge not found for inv clear."); return false end
  123.     log("Clearing turtle inventory to ME...")
  124.     local anyFailed = false
  125.     for slot = 1, 16 do
  126.         local itemDetail = turtle.getItemDetail(slot)
  127.         if itemDetail then
  128.             turtle.select(slot)
  129.             if turtle.dropUp(itemDetail.count) then
  130.                 local itemToImport = {name=itemDetail.name, count=itemDetail.count, nbt=itemDetail.nbt}
  131.                 local importedCount, err = meBridge.importItem(itemToImport, "up")
  132.                 if importedCount and importedCount > 0 then
  133.                     if importedCount < itemDetail.count then log("WARN: Not all "..itemDetail.name.." imported from chest. ".. (itemDetail.count - importedCount) .. " left?"); anyFailed = true end
  134.                 elseif importedCount == 0 then log("WARN: Imported 0 of " .. itemDetail.name .. " from chest. ME full/not accepted? Stuck in chest."); anyFailed = true
  135.                 else log("ERROR importing " .. itemDetail.name .. " from chest: " .. (err or "unknown")); anyFailed = true end
  136.             else log("WARN: Failed to dropUp item from slot " .. slot .. " ("..itemDetail.name..")"); anyFailed = true end
  137.         end
  138.     end
  139.     if anyFailed then log("Turtle inv clear had issues.") else log("Turtle inv clear done.") end
  140.     return not anyFailed    
  141. end
  142.  
  143. local function clearChestAboveToME()
  144.     if not meBridge then log("ME Bridge not found for chest clear."); return false end
  145.     log("Clearing chest above to ME...")
  146.     local tempSlot = findEmptySlot()
  147.     if not tempSlot then log("No empty turtle slot for chest clear. Skipping."); return end
  148.     local originalSelectedSlot = turtle.getSelectedSlot()
  149.     turtle.select(tempSlot)
  150.     local emptySuckCycles = 0
  151.     while emptySuckCycles < 3 do
  152.         local itemsFoundThisCycle = false
  153.         while turtle.suckUp(1) do
  154.             itemsFoundThisCycle = true; emptySuckCycles = 0
  155.             local itemDetail = turtle.getItemDetail(tempSlot)
  156.             if not itemDetail then log("ERROR: Sucked item, no detail. Breaking."); turtle.select(originalSelectedSlot); return end
  157.             if turtle.dropUp(1) then
  158.                 local itemToImport = {name=itemDetail.name, nbt=itemDetail.nbt}
  159.                 local totalImportedThisType = 0
  160.                 for _=1,10 do
  161.                     local imported, err = meBridge.importItem(itemToImport, "up")
  162.                     if imported and imported > 0 then totalImportedThisType = totalImportedThisType + imported
  163.                     else if err then log("Error importing "..itemDetail.name.." from chest: " .. err) end; break end
  164.                     if imported < 64 then break end; os.sleep(0.1)
  165.                 end
  166.                 if totalImportedThisType > 0 then log("Imported total " .. totalImportedThisType .. " of " .. itemDetail.name .. " from chest.") end
  167.             else
  168.                 log("ERROR: Sucked item, failed dropUp. Importing from turtle slot " ..tempSlot)
  169.                 meBridge.importItem({name=itemDetail.name, count=itemDetail.count, nbt=itemDetail.nbt}, "up")
  170.             end
  171.             if turtle.getItemCount(tempSlot) > 0 then
  172.                 log("WARN: Temp slot " ..tempSlot.. " not empty: " .. turtle.getItemDetail(tempSlot).name .. ". Clearing.")
  173.                 local leftover = turtle.getItemDetail(tempSlot)
  174.                 if leftover then turtle.dropUp(leftover.count); meBridge.importItem({name=leftover.name, count=leftover.count, nbt=leftover.nbt}, "up") end
  175.             end; os.sleep(0.1)
  176.         end
  177.         if not itemsFoundThisCycle then emptySuckCycles = emptySuckCycles + 1; if emptySuckCycles == 3 then log("Chest above appears empty (checked 3x).") end end
  178.         os.sleep(0.2)
  179.     end
  180.     turtle.select(originalSelectedSlot)
  181. end
  182.  
  183. local function ensureFuel()
  184.     if not meBridge then log("ME Bridge not found for refueling."); return false end
  185.     local fuelLevelStr = turtle.getFuelLevel()
  186.     if fuelLevelStr == "unlimited" then log("Fuel is unlimited."); return true end
  187.     local currentFuelNum = tonumber(fuelLevelStr) or 0
  188.     local maxFuelNum = tonumber(turtle.getFuelLimit()) or 0
  189.     if maxFuelNum == 0 then log("Max fuel is 0. Cannot refuel."); return true end
  190.     if currentFuelNum >= maxFuelNum * 0.8 then return true end
  191.     log("Refueling needed. Current: " .. tostring(currentFuelNum) .. "/" .. tostring(maxFuelNum))
  192.     local fuelItems = {
  193.         {name="minecraft:coal_block", count=16}, {name="minecraft:charcoal_block", count=16},
  194.         {name="minecraft:coal", count=64}, {name="minecraft:charcoal", count=64}
  195.     }
  196.     local refueledOverall = false
  197.     for _, fuelItemSpec in ipairs(fuelItems) do
  198.         log("Attempting to get " .. fuelItemSpec.name .. " (max " .. fuelItemSpec.count .. ").")
  199.         local numExported, err_export = meBridge.exportItem(fuelItemSpec, "up")
  200.         if numExported and numExported > 0 then
  201.             log("Exported " .. numExported .. " " .. fuelItemSpec.name .. " to chest.")
  202.             local originalSlot = turtle.getSelectedSlot()
  203.             local tempSlot = findEmptySlot()
  204.             if not tempSlot then
  205.                 log("No empty slot for fuel. Returning to ME.")
  206.                 meBridge.importItem({name=fuelItemSpec.name, count=numExported}, "up"); turtle.select(originalSlot)
  207.                 goto continue_fuel_loop
  208.             end
  209.             turtle.select(tempSlot)
  210.             local suckedCount = 0
  211.             for _=1, numExported do if turtle.suckUp(1) then suckedCount = suckedCount + 1 else break end end
  212.             if suckedCount > 0 then
  213.                 log("Sucked " .. suckedCount .. " of " .. fuelItemSpec.name .. ".")
  214.                 local fuelBefore = tonumber(turtle.getFuelLevel()) or 0
  215.                 local refuelOpRes = turtle.refuel()
  216.                 local fuelAfter = tonumber(turtle.getFuelLevel()) or 0
  217.                 local gained = 0
  218.                 if type(refuelOpRes) == "number" then gained = refuelOpRes elseif refuelOpRes == true then gained = fuelAfter - fuelBefore; if gained < 0 then gained = 0 end end
  219.                 log("Refuel op. Gained: " .. tostring(gained) .. " points.")
  220.                 if gained > 0 then refueledOverall = true end
  221.             else log("Failed to suck any " .. fuelItemSpec.name .. " from chest (" .. numExported .. " exported).") end
  222.             if suckedCount < numExported then
  223.                 log("Returning " .. (numExported - suckedCount) .. " unused " .. fuelItemSpec.name .. " (chest) to ME.")
  224.                 meBridge.importItem({name=fuelItemSpec.name, count=(numExported - suckedCount)}, "up")
  225.             end
  226.             if turtle.getItemCount(tempSlot) > 0 then
  227.                 log("Returning unused " .. fuelItemSpec.name .. " (turtle slot " ..tempSlot.. ") to ME.")
  228.                 local remaining = turtle.getItemDetail(tempSlot)
  229.                 if remaining then turtle.dropUp(remaining.count); meBridge.importItem({name=remaining.name, count=remaining.count, nbt=remaining.nbt}, "up")
  230.                 else log("WARN: Could not get item detail for remaining fuel in slot " .. tempSlot) end
  231.             end
  232.             turtle.select(originalSlot)
  233.             local currentFuelCheck = tonumber(turtle.getFuelLevel()) or 0
  234.             if currentFuelCheck >= maxFuelNum * 0.8 then log("Fuel target reached."); break end
  235.         else
  236.             if err_export then log("Error exporting " .. fuelItemSpec.name .. ": " .. tostring(err_export))
  237.             else log("ME system has no " .. fuelItemSpec.name .. " or export failed (0 exported).") end
  238.         end
  239.         ::continue_fuel_loop::
  240.     end
  241.     local finalFuel = tonumber(turtle.getFuelLevel()) or 0
  242.     if not refueledOverall and finalFuel < maxFuelNum * 0.8 then log("WARN: Refueling done, fuel might still be low: " .. tostring(finalFuel) .. "/" .. tostring(maxFuelNum)) end
  243.     log("Fuel check finished. Current fuel: " .. tostring(finalFuel))
  244.     return true
  245. end
  246.  
  247. local function waitForLever()
  248.     log("Waiting for lever (back=true)...")
  249.     while not rs.getInput("back") do log("Lever off. Waiting 60s..."); os.sleep(60) end
  250.     log("Lever ON.")    
  251. end
  252.  
  253. local function getActualBuilderHuts()
  254.     if not colonyIntegrator then log("Colony Integrator not found for huts list."); return {} end
  255.     log("Fetching builder huts list...")
  256.     local ok, buildings = pcall(colonyIntegrator.getBuildings)
  257.     if not ok then log("Could not get buildings (pcall error): " .. tostring(buildings)); return {} end
  258.     if type(buildings) ~= "table" then log("Could not get buildings (not table): " .. tostring(buildings)); return {} end
  259.     local huts = {}
  260.     for _, b in ipairs(buildings) do if b and b.type and string.match(string.lower(b.type), string.lower(BUILDER_HUT_TYPE_PATTERN)) then table.insert(huts, b) end end
  261.     log("Found " .. #huts .. " builder huts (pattern: '" .. BUILDER_HUT_TYPE_PATTERN .. "').")
  262.     return huts    
  263. end
  264.  
  265. local function performInfoRetrievalMode()
  266.     log("--- Info Retrieval Mode ---")
  267.     if not colonyIntegrator then log("Colony Integrator not found for info mode."); return end
  268.     local huts = getActualBuilderHuts()
  269.     if #huts == 0 then log("No Builder's Huts found.")
  270.     else
  271.         log("Available Builder's Huts:")
  272.         for i, h in ipairs(huts) do
  273.             if h and h.location then log(string.format("[%d] %s (L%d) @ %.0f,%.0f,%.0f (%s)", i, h.name or "Unnamed", h.level or 0, h.location.x, h.location.y, h.location.z, h.type or "N/A"))
  274.             else log("["..i.."] Invalid hut data.") end
  275.         end
  276.         log("To assign, create '" .. SLOT_NUMBER_FILE .. "' with hut number.")
  277.     end
  278.     log("---------------------------")    
  279. end
  280.  
  281. local function getAssignedBuilderSlot()
  282.     if fs.exists(SLOT_NUMBER_FILE) then
  283.         local file, err = fs.open(SLOT_NUMBER_FILE, "r")
  284.         if file then
  285.             local line = file.readLine(); file.close()
  286.             if line then
  287.                 local num = tonumber(line)
  288.                 if num then return num end; log("WARN: Content of " .. SLOT_NUMBER_FILE .. " not number: '" .. line .. "'")
  289.             else log("WARN: " .. SLOT_NUMBER_FILE .. " is empty.") end
  290.         else log("ERROR: Could not open " .. SLOT_NUMBER_FILE .. ": " .. tostring(err)) end
  291.     end
  292.     return nil    
  293. end
  294.  
  295. local function requestAndCollectItems(resources)
  296.     if not meBridge then log("ME Bridge not found for item request."); return false end
  297.     if not resources then log("WARN: Resources table is nil."); return false end
  298.     if #resources == 0 then log("No resources requested by builder."); return true end
  299.    
  300.     log("Requesting & collecting items... Found " .. #resources .. " resource entries.")
  301.     local overallSuccess = true
  302.     local anyValidResourceFound = false
  303.  
  304.     for i, res_entry in ipairs(resources) do
  305.         log("Processing resource entry #" .. i .. ": " .. textutils.serialize(res_entry, {compact=true}))
  306.  
  307.         local currentItemName = nil
  308.         if res_entry.item and type(res_entry.item) == "table" and res_entry.item.name then
  309.             currentItemName = res_entry.item.name
  310.         elseif type(res_entry.item) == "string" then -- Fallback based on documentation
  311.             currentItemName = res_entry.item
  312.         end
  313.  
  314.         local initialTotalNeededRaw = res_entry.needs
  315.         local initialTotalNeeded = tonumber(initialTotalNeededRaw)
  316.        
  317.         local currentItemNBT = nil
  318.         if res_entry.item and type(res_entry.item) == "table" and res_entry.item.nbt then
  319.             currentItemNBT = res_entry.item.nbt
  320.         end
  321.        
  322.         if currentItemName == nil then
  323.             log("WARN: Entry #" .. i .. " is missing valid 'item' name. Skipping.")
  324.         elseif initialTotalNeeded == nil then
  325.             log("WARN: Entry #" .. i .. " 'needs' ('" .. tostring(initialTotalNeededRaw) .. "') not num. Item: " .. tostring(currentItemName) .. ". Skipping.")
  326.         elseif initialTotalNeeded <= 0 then
  327.             log("Skipping " .. currentItemName .. ": needed amount is zero or invalid (" .. tostring(initialTotalNeededRaw) .. ")")
  328.         else
  329.             anyValidResourceFound = true
  330.             log(string.format("Need %d of %s", initialTotalNeeded, currentItemName))
  331.             local collectedForItem = 0
  332.            
  333.             local loopIteration = 0
  334.             while true do
  335.                 loopIteration = loopIteration + 1
  336.                 local loopTotalNeededForThisIteration = tonumber(res_entry.needs) or 0
  337.                
  338.                 log(string.format("PRE-COMPARE (Item: %s, Pass: %d): collectedForItem=%s (type: %s), res_entry.needs=%s (type: %s), loopTotalNeededForThisIteration=%s (type: %s)",
  339.                     currentItemName, loopIteration,
  340.                     tostring(collectedForItem), type(collectedForItem),
  341.                     tostring(res_entry.needs), type(res_entry.needs),
  342.                     tostring(loopTotalNeededForThisIteration), type(loopTotalNeededForThisIteration)
  343.                 ))
  344.  
  345.                 if collectedForItem == nil or loopTotalNeededForThisIteration == nil then
  346.                      log("CRITICAL DEBUG: A value IS NIL right before comparison! collectedForItem type: "..type(collectedForItem)..", loopTotalNeededForThisIteration type: "..type(loopTotalNeededForThisIteration))
  347.                 end
  348.  
  349.                 if collectedForItem >= loopTotalNeededForThisIteration then
  350.                     log("DEBUG: Condition met (collected >= needed). Breaking while loop for " .. currentItemName)
  351.                     break
  352.                 end
  353.                 if loopTotalNeededForThisIteration == 0 then
  354.                     log("WARN: total needed for " .. currentItemName .. " became 0 or invalid mid-collection. Breaking loop.")
  355.                     break
  356.                 end
  357.                
  358.                 -- Simplified slot finding: just try to suck. Turtle will place it.
  359.                 -- We need to know the currently selected slot before sucking to check if it's full *for that item type*
  360.                 -- However, the main constraint will be turtle.suckUp() failing if inventory is truly full.
  361.                
  362.                 local amountToRequestThisPass = math.min(loopTotalNeededForThisIteration - collectedForItem, 64) -- Request up to a stack or remaining
  363.                
  364.                 if amountToRequestThisPass <= 0 then
  365.                     log("Cannot request "..currentItemName..": amountToRequestThisPass is "..amountToRequestThisPass .. ". Breaking loop.")
  366.                     break
  367.                 end
  368.                
  369.                 log(string.format("Requesting %d %s", amountToRequestThisPass, currentItemName))
  370.                 local itemFilter = {name=currentItemName, count=amountToRequestThisPass, nbt=currentItemNBT}
  371.                 local exportedCount, err_export = meBridge.exportItem(itemFilter, "up")
  372.                
  373.                 if not exportedCount or exportedCount == 0 then
  374.                     log("ME has no "..currentItemName.." or export failed. (Err: "..tostring(err_export).."). Breaking loop.")
  375.                     break
  376.                 end
  377.                 log("ME exported "..exportedCount.." "..currentItemName.." to chest.")
  378.                
  379.                 local suckedThisPass=0
  380.                 local canStillSuck = true
  381.                 for _=1,exportedCount do
  382.                     local emptySlotBeforeSuck = findEmptySlot() -- Check if there's any empty slot at all
  383.                     if not emptySlotBeforeSuck and turtle.getItemCount(turtle.getSelectedSlot()) > 0 and turtle.getItemSpace(turtle.getSelectedSlot()) == 0 then
  384.                         -- If no absolutely empty slot, and current slot is full (or can't stack more of this type)
  385.                         local isFullForAllTypes = true
  386.                         for s_check = 1, 16 do
  387.                             if turtle.getItemCount(s_check) == 0 then isFullForAllTypes = false; break end
  388.                             local detail = turtle.getItemDetail(s_check)
  389.                             if detail and detail.name == currentItemName and detail.count < (detail.maxStackSize or 64) then
  390.                                 isFullForAllTypes = false; break
  391.                             end
  392.                         end
  393.                         if isFullForAllTypes then
  394.                             log("Turtle inventory appears completely full for new items or this type. Breaking suck loop.")
  395.                             canStillSuck = false
  396.                             break
  397.                         end
  398.                     end
  399.  
  400.                     if turtle.suckUp(1) then
  401.                         suckedThisPass=suckedThisPass+1
  402.                     else
  403.                         log("turtle.suckUp(1) failed. Chest empty or inv full for this item.")
  404.                         canStillSuck = false
  405.                         break
  406.                     end
  407.                 end
  408.                 log("Sucked "..suckedThisPass.." "..currentItemName..".")
  409.                 collectedForItem = collectedForItem + suckedThisPass
  410.                
  411.                 if suckedThisPass < exportedCount then
  412.                     log((exportedCount-suckedThisPass).." "..currentItemName.." left in chest. Returning.")
  413.                     meBridge.importItem({name=currentItemName, count=(exportedCount-suckedThisPass), nbt=currentItemNBT},"up")
  414.                 end
  415.                 if not canStillSuck or (suckedThisPass==0 and exportedCount>0) then
  416.                     log("WARN: Could not suck items or inventory became full. Breaking loop for "..currentItemName)
  417.                     break
  418.                 end
  419.                 os.sleep(0.2)
  420.             end
  421.             log(string.format("Collected %d / %d of %s", collectedForItem, initialTotalNeeded, currentItemName))
  422.             if collectedForItem < initialTotalNeeded then overallSuccess = false end
  423.         end
  424.     end
  425.  
  426.     if not anyValidResourceFound and #resources > 0 then
  427.         log("ERROR: Processed " .. #resources .. " resource entries, but none were valid for processing.")
  428.         return false
  429.     end
  430.     return overallSuccess    
  431. end
  432.  
  433. local function attemptClearBuilderChest()
  434.     log("Attempting to clear builder's chest at delivery point...")
  435.     local itemsMovedFromBuilderChest = 0
  436.     for trip = 1, BUILDER_CHEST_CLEAR_TRIPS do
  437.         log("Builder chest clear: Trip " .. trip .. "/" .. BUILDER_CHEST_CLEAR_TRIPS)
  438.         if not warpTo(DELIVERY_POINT_NAME) then
  439.             log("Builder chest clear: Failed to warp to delivery point for trip " .. trip .. ". Aborting clear.")
  440.             return itemsMovedFromBuilderChest > 0
  441.         end
  442.  
  443.         -- Move down to ensure we are above the chest
  444.         local movesDown = 0
  445.         while not turtle.detectDown() and movesDown < 10 do
  446.             if not turtle.down() then log("Builder chest clear: Failed to move down. Blocked?"); break end
  447.             movesDown = movesDown + 1; os.sleep(0.2)
  448.         end
  449.         if not turtle.detectDown() and movesDown > 0 then
  450.             log("Builder chest clear: Could not find surface below after moving down. Aborting clear for this trip.")
  451.             goto next_clear_trip -- Continue to next trip, maybe issue resolves
  452.         end
  453.  
  454.         local suckedThisTrip = 0
  455.         local anySuckedThisPass
  456.         repeat
  457.             anySuckedThisPass = false
  458.             for slot = 1, 16 do -- Try to fill turtle inventory
  459.                 if turtle.getItemCount(slot) == 0 then -- Found an empty slot in turtle
  460.                     if turtle.suckDown(1) then
  461.                         suckedThisTrip = suckedThisTrip + 1
  462.                         anySuckedThisPass = true
  463.                         itemsMovedFromBuilderChest = itemsMovedFromBuilderChest + 1
  464.                         local suckedItem = turtle.getItemDetail(slot)
  465.                         if suckedItem then log("Builder chest clear: Sucked 1x " .. suckedItem.name)
  466.                         else log("Builder chest clear: Sucked an item, but no details.") end
  467.                         os.sleep(0.1)
  468.                     else
  469.                         -- suckDown failed, slot might be full for this item type or chest empty for this pull
  470.                         goto end_suck_loop_for_trip -- Break inner, then outer if needed
  471.                     end
  472.                 end
  473.             end
  474.         until not anySuckedThisPass
  475.         ::end_suck_loop_for_trip::
  476.  
  477.         if suckedThisTrip > 0 then
  478.             log("Builder chest clear: Sucked " .. suckedThisTrip .. " items this trip. Returning to home to deposit.")
  479.             if not warpTo(HOME_POINT_NAME) then
  480.                 log("Builder chest clear: CRITICAL - Failed to warp home with items from builder. Items might be lost. Aborting.")
  481.                 return itemsMovedFromBuilderChest > 0 -- Return status so far
  482.             end
  483.             if not orientToMEBridge() then
  484.                  log("Builder chest clear: CRITICAL - Failed to orient at home. Cannot deposit items. Aborting.")
  485.                  return itemsMovedFromBuilderChest > 0
  486.             end
  487.             if not meBridge then
  488.                 if not setupOperationalPeripherals() or not meBridge then
  489.                     log("Builder chest clear: CRITICAL - ME Bridge lost and cannot be reacquired. Items stuck in turtle. Aborting.")
  490.                     return itemsMovedFromBuilderChest > 0
  491.                 end
  492.             end
  493.             clearTurtleInventoryToME() -- Deposit everything sucked
  494.         else
  495.             log("Builder chest clear: Sucked no items this trip. Assuming builder chest is empty or inaccessible.")
  496.             break -- Exit the trip loop
  497.         end
  498.         ::next_clear_trip::
  499.     end
  500.     log("Builder chest clearing attempt finished. Total items moved: " .. itemsMovedFromBuilderChest)
  501.     return itemsMovedFromBuilderChest > 0
  502. end
  503.  
  504. local function performAssignedBuilderDelivery()
  505.     log("--- Assigned Builder Delivery Mode ---")
  506.     if not (colonyIntegrator and meBridge) then log("Integrator or ME Bridge missing. Cannot perform delivery."); return end
  507.     local slotNum = getAssignedBuilderSlot()
  508.     if not slotNum then log("Builder slot file missing/invalid."); return end
  509.     log("Assigned to builder slot: " .. slotNum)
  510.     local allHuts = getActualBuilderHuts()
  511.     if #allHuts == 0 then log("No builder huts in colony."); return end
  512.     if slotNum <= 0 or slotNum > #allHuts then log("ERROR: Slot " .. slotNum .. " out of range (1-" .. #allHuts .. ")."); return end
  513.     local targetHut = allHuts[slotNum]
  514.     if not (targetHut and targetHut.location) then log("ERROR: Target hut data invalid for slot " .. slotNum); return end
  515.     log(string.format("Targeting: %s @ %.0f,%.0f,%.0f", targetHut.name or "Unnamed", targetHut.location.x, targetHut.location.y, targetHut.location.z))
  516.    
  517.     local resources, err_res = colonyIntegrator.getBuilderResources(targetHut.location)
  518.     if not resources then
  519.         log("Could not get resources for builder: " .. tostring(err_res)); return
  520.     end
  521.    
  522.     local collectionSuccessfulOrNotNeeded = true
  523.     if #resources > 0 then
  524.         collectionSuccessfulOrNotNeeded = requestAndCollectItems(resources)
  525.         if not collectionSuccessfulOrNotNeeded then log("WARN: Item collection phase reported issues.")
  526.         else log("Item collection process reported success.") end
  527.     else
  528.         log("Builder needs no resources according to getBuilderResources().")
  529.     end
  530.  
  531.     local anyItemsInInventory = false
  532.     for s=1,16 do if turtle.getItemCount(s) > 0 then anyItemsInInventory = true; break end end
  533.  
  534.     if not anyItemsInInventory then
  535.         log("No items in inventory to deliver. Skipping warp to delivery.")
  536.         return
  537.     end
  538.    
  539.     log("Proceeding to delivery point: " .. DELIVERY_POINT_NAME)
  540.    
  541.     if warpTo(DELIVERY_POINT_NAME) then
  542.         log("Arrived at delivery. Moving down for drop...")
  543.         local movesDown = 0
  544.         while not turtle.detectDown() and movesDown < 10 do if not turtle.down() then log("Move down failed."); break end; movesDown=movesDown+1; os.sleep(0.2) end
  545.        
  546.         if turtle.detectDown() or movesDown == 0 then
  547.             log("Surface detected/assumed. Dumping inventory down.")
  548.             local deliveryFailedDueToFullChest = false
  549.             for s=1,16 do
  550.                 if turtle.getItemCount(s)>0 then
  551.                     turtle.select(s);
  552.                     local d=turtle.getItemDetail(s);
  553.                     if d then
  554.                         log("Dumping "..d.count.."x "..d.name.." down.");
  555.                         if not turtle.dropDown() then
  556.                            log("WARN: dropDown() failed for "..d.name..". Builder's chest might be full.")
  557.                            deliveryFailedDueToFullChest = true
  558.                            break -- Stop trying to drop more if one fails
  559.                         end;
  560.                         os.sleep(0.1)
  561.                     else log("WARN: Slot "..s.." has items but getItemDetail returned nil. Skipping.") end
  562.                 end
  563.             end
  564.  
  565.             if deliveryFailedDueToFullChest then
  566.                 log("Delivery failed, builder's chest likely full. Initiating reverse and clear process.")
  567.                 if warpTo(HOME_POINT_NAME) then
  568.                     if orientToMEBridge() and meBridge then
  569.                         clearTurtleInventoryToME() -- Return what couldn't be delivered
  570.                         attemptClearBuilderChest() -- This will try to empty builder's chest
  571.                         -- After this, the main loop will continue, effectively retrying the whole process.
  572.                     else
  573.                         log("CRITICAL: Could not return to home base ME setup after failed delivery. Items stuck.")
  574.                     end
  575.                 else
  576.                     log("CRITICAL: Could not warp home after failed delivery. Items stuck at builder.")
  577.                 end
  578.             else
  579.                 log("Inventory dump (down) complete.")
  580.             end
  581.         else log("No surface below after " .. movesDown .. " moves. Items remain.") end
  582.     else log("Failed warp to delivery. Items remain.") end    
  583. end
  584.  
  585. local function main()
  586.     term.clear(); term.setCursorPos(1,1)
  587.     log("Main script execution started.")
  588.  
  589.     if not setupEssentialPeripherals() then log("Critical essential peripheral setup failed. Exiting."); return end
  590.  
  591.     while true do
  592.         log("=== New Cycle Started ===")
  593.         if not warpTo(HOME_POINT_NAME) then log("CRITICAL: Failed warp to home. Cycle cannot continue. Waiting 60s."); os.sleep(60); goto continue_loop end
  594.         if not orientToMEBridge() then log("Failed orient to ME Bridge at home. Waiting 60s."); os.sleep(60); goto continue_loop end
  595.         local opReady = setupOperationalPeripherals()
  596.         if opReady then clearTurtleInventoryToME(); os.sleep(0.5); clearChestAboveToME(); os.sleep(0.5); ensureFuel(); os.sleep(0.5)
  597.         else log("ME Bridge not found at home. Skipping ME tasks."); os.sleep(10) end
  598.         waitForLever(); os.sleep(0.5)
  599.         local slotFileExists = fs.exists(SLOT_NUMBER_FILE)
  600.         if slotFileExists then
  601.             if opReady and colonyIntegrator then performAssignedBuilderDelivery()
  602.             else
  603.                 log("Slot file exists, but ME Bridge or Integrator missing.")
  604.                 if colonyIntegrator then log("Integrator found, attempting info mode."); performInfoRetrievalMode()
  605.                 else log("Integrator also missing, cannot run info mode.") end
  606.                 log("Delivery prereqs not met. Wait 60s.")
  607.                 os.sleep(60)
  608.             end
  609.         else
  610.             log("Slot file '"..SLOT_NUMBER_FILE.."' not found.")
  611.             log("Proceeding to info mode (if Integrator available).")
  612.             if colonyIntegrator then performInfoRetrievalMode() else log("Integrator missing, cannot run info mode.") end
  613.             log("Info mode done. Wait 60s."); os.sleep(60)
  614.         end
  615.         log("Cycle complete. Returning home..."); os.sleep(2)
  616.         ::continue_loop::
  617.     end
  618. end
  619.  
  620. main()
  621.  
  622. log("Script main function has unexpectedly exited the main loop.")
Add Comment
Please, Sign In to add comment