Myros27

colonySupplier V3.9

May 16th, 2025 (edited)
35
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 33.28 KB | None | 0 0
  1. --[[
  2. MineColonies Builder Request Fulfillment Bot
  3. Version 3.9
  4.  
  5. Changelog v3.9:
  6. - Fixed `goto` Scope Error in `getItemsFromME`:
  7.   - Ensured all per-item-request local variables (including `exportFilter`,
  8.     `exportedAmount`, `exportReason`) are declared at the beginning of each
  9.     iteration of the item request loop, preventing `goto` from bypassing their
  10.     initialization on subsequent iterations.
  11. - Previous fixes from v3.8 remain.
  12.  
  13. Changelog v3.8:
  14. - Simplified Home Base ME Interaction (Reverted from v3.7 turns):
  15.   - Turtle now only performs: turtle.up() then turtle.down() for IO Chest interaction.
  16.   - Uses turtle.drop() / turtle.suck() when at the higher Y-level.
  17. - Retained `goto` scope error fix from v3.7 (initial attempt).
  18. - Previous fixes from v3.6 remain.
  19.  
  20. Setup: (Same as v2.0)
  21. Physical ME Interaction Setup at HOME_WARP_POINT (Side View):
  22.   Level +1: [ IO Chest      ]  (After turtle.up(), turtle is at Level +1, in front of IO Chest)
  23.   Level  0: [ Turtle        ]  (Turtle resting spot)
  24.             [ ME Bridge     ]  (Adjacent to turtle or turtle is on it. IO Chest is "up" from bridge)
  25.   Level -1: [ Coal Chest    ]  (Coal chest below turtle's resting spot)
  26. ]]
  27.  
  28. -- Configuration
  29. local ME_BRIDGE_NAME = "meBridge"
  30. local END_AUTOMATA_NAME = "endAutomata"
  31. local COLONY_INTEGRATOR_NAME = "colonyIntegrator"
  32. local HOME_WARP_POINT = "home"
  33. local COLONY_WARP_POINT = "colony"
  34. local NAMED_BUILDING_STORAGE_SUFFIX = "_storage"
  35. local COAL_SLOT = 16
  36. local COAL_RESERVE_AMOUNT = 64
  37. local LOW_FUEL_THRESHOLD = 200
  38. local DETAILED_RESOURCE_LOG_FILE = "builder_bot_resource_details.log"
  39. local MAIN_LOG_FILE = "builder_bot_main.log"
  40.  
  41. -- DEBUG FLAG (v3.4)
  42. local DEBUG_MODE = true -- Set to true for detailed file logging and longer waits, false for production
  43.  
  44. local automata = nil
  45.  
  46. -- [[ Logging Function (Modified v3.4) ]]
  47. local function log(message, isDebugMessage)
  48.     local timestamp = textutils.formatTime(os.time(), false)
  49.     local logMessage = "[" .. timestamp .. "] " .. tostring(message)
  50.     print(logMessage)
  51.     if DEBUG_MODE then
  52.         if not isDebugMessage or (isDebugMessage and DEBUG_MODE) then
  53.             local file = fs.open(MAIN_LOG_FILE, "a")
  54.             if file then file.writeLine(logMessage) file.close()
  55.             else print("ERROR: Could not write to main log file: " .. MAIN_LOG_FILE) end
  56.         end
  57.     end
  58. end
  59. local function debugLog(message) if DEBUG_MODE then log("DEBUG: " .. tostring(message), true) end end
  60. local function debugNbtLog(message) if DEBUG_MODE then log("DEBUG NBT: " .. tostring(message), true) end end
  61.  
  62. -- [[ HELPER FUNCTIONS ]]
  63. local function initialPeripheralCheck()
  64.     automata = peripheral.find(END_AUTOMATA_NAME)
  65.     if not automata then log("CRITICAL ERROR: End Automata ('" .. END_AUTOMATA_NAME .. "') not found!") return false end
  66.     log("End Automata found.")
  67.     return true
  68. end
  69.  
  70. local function ensureFuel(atHome)
  71.     log("Ensuring fuel. Turtle is " .. (atHome and "at home." or "not at home."))
  72.     if turtle.getFuelLevel() < LOW_FUEL_THRESHOLD then
  73.         turtle.select(COAL_SLOT)
  74.         if turtle.getItemCount(COAL_SLOT) > 0 then log("Fuel is low (" .. turtle.getFuelLevel() .. "). Refueling from coal slot...") turtle.refuel() log("Fuel level after attempting refuel: " .. turtle.getFuelLevel())
  75.         else log("Fuel is low, but no coal in slot " .. COAL_SLOT .. " to use.") end
  76.     end
  77.     if atHome then
  78.         log("At home: Attempting to stock coal and maximize fuel.")
  79.         turtle.select(COAL_SLOT)
  80.         local currentCoal = turtle.getItemCount(COAL_SLOT)
  81.         if currentCoal < COAL_RESERVE_AMOUNT then
  82.             local amountToSuck = COAL_RESERVE_AMOUNT - currentCoal
  83.             log("Coal slot " .. COAL_SLOT .. " has " .. currentCoal .. "/" .. COAL_RESERVE_AMOUNT .. ". Sucking up to " .. amountToSuck .. " from coal chest below...")
  84.             local suckResult = turtle.suckDown(amountToSuck)
  85.             if type(suckResult) == "number" then
  86.                 if suckResult > 0 then log("Sucked " .. suckResult .. " additional coal (numeric return).")
  87.                 else log("Sucked 0 items (numeric return; chest empty or other issue).") end
  88.             elseif suckResult == true then log("suckDown returned true. Assuming " .. amountToSuck .. " (or available) coal sucked.")
  89.             elseif suckResult == false then log("Failed to suck coal (suckDown returned false).")
  90.             else log("WARN: turtle.suckDown returned unexpected type: " .. type(suckResult) .. ", value: " .. tostring(suckResult)) end
  91.         else log("Coal slot " .. COAL_SLOT .. " is already full or has sufficient reserve.") end
  92.         turtle.select(COAL_SLOT)
  93.         if turtle.getItemCount(COAL_SLOT) > 0 then log("Refueling turtle at home base from slot " .. COAL_SLOT .. "...") turtle.refuel() log("Fuel level after home refueling: " .. turtle.getFuelLevel())
  94.         else log("No coal in slot " .. COAL_SLOT .. " to perform home refueling.") end
  95.     end
  96.     if turtle.getFuelLevel() == 0 then log("CRITICAL: Turtle has zero fuel!") return false end
  97.     log("Fuel check complete. Current level: " .. turtle.getFuelLevel())
  98.     return true
  99. end
  100.  
  101. local function warpTo(pointName)
  102.     log("Attempting to warp to '" .. pointName .. "'...")
  103.     if turtle.getFuelLevel() == 0 then log("  ERROR: Zero fuel. Cannot warp.") return false end
  104.     local success, reason = automata.warpToPoint(pointName)
  105.     if success then log("Warp successful to " .. pointName) os.sleep(2) return true
  106.     else log("ERROR: Failed to warp to " .. pointName .. ": " .. (reason or "unknown error")) return false end
  107. end
  108.  
  109. local function findEmptySlot() for i = 1, 15 do if turtle.getItemCount(i) == 0 then return i end end return nil end
  110. local function consolidateInventory() log("Consolidating inventory...") for i = 1, 15 do turtle.select(i) if turtle.getItemCount(i) > 0 then for j = i + 1, 15 do if turtle.compareTo(j) then local space = turtle.getItemSpace(i) if space > 0 then turtle.select(j) turtle.transferTo(i, math.min(space, turtle.getItemCount(j))) end end end end end turtle.select(1) end
  111.  
  112. local function returnUnusedItemsToME(bridge)
  113.     if not bridge then log("ERROR (returnUnused): ME Bridge not provided!") return end
  114.     log("Returning unused items to ME system (at home)...")
  115.     log("  Moving turtle UP to interact with IO Chest (Level +1)...")
  116.     if not turtle.up() then
  117.         log("  CRITICAL ERROR: Failed to move turtle.up(). Cannot return items.")
  118.         return
  119.     end
  120.     local itemsAttemptedToDrop = {}
  121.     for i = 1, 15 do
  122.         turtle.select(i)
  123.         local itemDetail = turtle.getItemDetail(i)
  124.         if itemDetail and itemDetail.count > 0 then
  125.             local displayNameToPrint = itemDetail.displayName or itemDetail.name or "UnknownItemInTurtle"
  126.             log("  Attempting to drop " .. itemDetail.count .. " of " .. displayNameToPrint .. " (Slot " .. i .. ") into IO Chest (in front at Level +1).")
  127.             if turtle.drop() then
  128.                 log("    Dropped successfully into IO Chest.")
  129.                 table.insert(itemsAttemptedToDrop, itemDetail)
  130.             else
  131.                 log("    WARN: Failed to drop " .. displayNameToPrint .. " into IO Chest from slot " .. i .. ". Item remains in turtle.")
  132.             end
  133.         end
  134.     end
  135.     log("  Moving turtle DOWN to resting position (Level 0)...")
  136.     if not turtle.down() then
  137.         log("  WARN: Failed to move turtle.down() after attempting to drop items.")
  138.     end
  139.     if #itemsAttemptedToDrop == 0 then
  140.         log("  No items were successfully dropped into IO chest by turtle to return via bridge.")
  141.         local itemsStillInTurtle = false
  142.         for i = 1, 15 do if turtle.getItemCount(i) > 0 then itemsStillInTurtle = true; break; end end
  143.         if not itemsStillInTurtle then log("  Turtle inventory (slots 1-15) is clear.")
  144.         else log("  Some items remain in turtle inventory that could not be dropped.") end
  145.         return
  146.     end
  147.     log("  Attempting to import items from IO chest (up from bridge) into ME system:")
  148.     for _, itemToImport in ipairs(itemsAttemptedToDrop) do
  149.         local displayNameToImport = itemToImport.displayName or itemToImport.name or "UnknownItemToImport"
  150.         local importFilter = {name=itemToImport.name, count=itemToImport.count}
  151.         if itemToImport.nbt then importFilter.nbt = itemToImport.nbt end
  152.         log("    Bridge: Attempting to import " .. itemToImport.count .. " of " .. displayNameToImport .. " from IO Chest (up).")
  153.         local importedAmount, importError = bridge.importItem(importFilter, "up")
  154.         if importError then log("    ERROR importing " .. displayNameToImport .. " via bridge: " .. importError)
  155.         elseif importedAmount > 0 then
  156.             log("    Successfully imported " .. importedAmount .. " of " .. displayNameToImport .. " via bridge.")
  157.             if importedAmount < itemToImport.count then log("    WARN: Bridge imported " .. importedAmount .. "/" .. itemToImport.count) end
  158.         else log("    Bridge imported 0 of " .. displayNameToImport .. ". (No error, 0 moved, or item not found/match)") end
  159.         os.sleep(0.2)
  160.     end
  161.     log("Finished returning items from IO Chest via bridge.")
  162. end
  163.  
  164. -- Fixed v3.9 for goto scope issues
  165. local function getItemsFromME(bridge, itemsToGet)
  166.     if not bridge then log("ERROR (getItems): ME Bridge not provided!") return {} end
  167.     log("Requesting items from ME system (at home):")
  168.     local successfullyFetched = {}
  169.  
  170.     for itemReqIdx, itemReq in ipairs(itemsToGet) do
  171.         -- Initialize all per-item-request locals here
  172.         local currentRequestMainItemSlot = nil
  173.         local itemActuallySuckedThisRequest = 0
  174.         local unexpectedItemWasSucked = false
  175.         local contaminationCheckSlotUsed = nil
  176.         local exportFilter = nil               -- v3.9: Declare here
  177.         local exportedAmount = 0             -- v3.9: Initialize
  178.         local exportReason = nil             -- v3.9: Initialize
  179.  
  180.         local itemName = "" ; local itemDisplayName = ""
  181.         if type(itemReq.item) == "table" then itemName = itemReq.item.name or "UnknownItemRegistry"
  182.         elseif type(itemReq.item) == "string" then itemName = itemReq.item else itemName = "InvalidItemRegistry" end
  183.         if type(itemReq.displayName) == "table" then itemDisplayName = itemReq.displayName.name or itemReq.displayName.displayName or itemName
  184.         elseif type(itemReq.displayName) == "string" then itemDisplayName = itemReq.displayName else itemDisplayName = itemName end
  185.         local neededCount = itemReq.needs
  186.  
  187.         if neededCount == nil or type(neededCount) ~= "number" or neededCount <= 0 then
  188.             log("  Skipping item " .. itemDisplayName .. " for ME fetch (invalid neededCount: " .. tostring(neededCount) .. ")")
  189.             goto continue_me_req_v39
  190.         end
  191.  
  192.         currentRequestMainItemSlot = findEmptySlot()
  193.         if not currentRequestMainItemSlot then
  194.             log("  No empty turtle slot for main item. Skipping further ME requests this cycle.")
  195.             break
  196.         end
  197.         turtle.select(currentRequestMainItemSlot)
  198.  
  199.         exportFilter = {name=itemName, count=neededCount} -- v3.9: Assign here
  200.         if itemReq.nbt then exportFilter.nbt = itemReq.nbt end
  201.  
  202.         log("  Requesting " .. neededCount .. " of " .. itemDisplayName .. " (NBT: " ..tostring(itemReq.nbt).. ") from ME to IO Chest (up from bridge).")
  203.         exportedAmount, exportReason = bridge.exportItem(exportFilter, "up") -- v3.9: Use declared vars
  204.  
  205.         if not exportedAmount or exportedAmount == 0 then
  206.             log("  Export failed for " .. itemDisplayName .. ": " .. (exportReason or "0 items exported to IO Chest"))
  207.             -- No items to suck, so itemActuallySuckedThisRequest remains 0. Flow to end of loop.
  208.         else
  209.             log("  ME system reported exporting " .. exportedAmount .. ". Preparing to suck from IO Chest.")
  210.             log("  Moving turtle UP to interact with IO Chest (Level +1)...")
  211.             if not turtle.up() then
  212.                 log("  CRITICAL: Failed turtle.up(). Cannot retrieve " .. itemDisplayName .. ". Items may be in IO chest.")
  213.                 goto continue_me_req_v39 -- Skips rest of *this* item, goes to next in itemsToGet
  214.             end
  215.  
  216.             log("  Attempting to suck " .. exportedAmount .. " of " .. itemDisplayName .. " into slot " .. currentRequestMainItemSlot .. " from IO Chest (in front at Level +1).")
  217.             for _ = 1, exportedAmount do
  218.                 if turtle.suck(1) then
  219.                     itemActuallySuckedThisRequest = itemActuallySuckedThisRequest + 1
  220.                 else
  221.                     debugLog("  WARN: Expected to suck " .. exportedAmount .. " but only got " .. itemActuallySuckedThisRequest .. ". IO Chest empty or slot full.")
  222.                     break
  223.                 end
  224.             end
  225.  
  226.             if itemActuallySuckedThisRequest > 0 then
  227.                 local detail = turtle.getItemDetail(currentRequestMainItemSlot)
  228.                 log("  Sucked " .. itemActuallySuckedThisRequest .. " of " .. (detail.displayName or detail.name) .. " (intended: " .. itemDisplayName .. ")")
  229.                 if itemActuallySuckedThisRequest < neededCount then
  230.                     log("  WARN: Sucked " ..itemActuallySuckedThisRequest.."/"..neededCount .. " (exported: " .. exportedAmount .. ")")
  231.                 end
  232.             elseif exportedAmount > 0 then
  233.                 log("  ME reported export of " .. exportedAmount .. " but failed to suck any " .. itemDisplayName .. " from IO chest.")
  234.             end
  235.  
  236.             log("  Performing post-suck check for unexpected items in IO Chest...")
  237.             local contaminationCheckSlot = nil -- This local is fine, specific to this block
  238.             if turtle.getItemCount(currentRequestMainItemSlot) == 0 then
  239.                 contaminationCheckSlot = currentRequestMainItemSlot
  240.             else
  241.                 contaminationCheckSlot = findEmptySlot()
  242.             end
  243.             contaminationCheckSlotUsed = contaminationCheckSlot
  244.  
  245.             if contaminationCheckSlot then
  246.                 local originalSelectedSlotForCheck = turtle.getSelectedSlot()
  247.                 turtle.select(contaminationCheckSlot)
  248.                 if turtle.suck(1) then
  249.                     unexpectedItemWasSucked = true
  250.                     local unexpectedItemDetail = turtle.getItemDetail(contaminationCheckSlot)
  251.                     log("  CRITICAL: Unexpected item found in IO Chest! First: " .. (unexpectedItemDetail.displayName or unexpectedItemDetail.name) .. " x" .. unexpectedItemDetail.count .. " in slot " .. contaminationCheckSlot)
  252.                     while turtle.getItemCount(contaminationCheckSlot) < turtle.getItemSpace(contaminationCheckSlot) and turtle.suck(1) do end
  253.                     unexpectedItemDetail = turtle.getItemDetail(contaminationCheckSlot)
  254.                     if unexpectedItemDetail then log("  Total unexpected in slot " .. contaminationCheckSlot .. ": " .. (unexpectedItemDetail.displayName or unexpectedItemDetail.name) .. " x" .. unexpectedItemDetail.count) end
  255.                 else
  256.                     log("  Post-suck check: IO Chest is clear of further items.")
  257.                 end
  258.                 turtle.select(originalSelectedSlotForCheck)
  259.             else
  260.                 log("  WARN: No empty slot for contamination check.")
  261.             end
  262.  
  263.             log("  Moving turtle DOWN to resting position (Level 0)...")
  264.             if not turtle.down() then log("  WARN: Failed turtle.down() when returning to resting spot.") end
  265.         end
  266.  
  267.         if unexpectedItemWasSucked then
  268.             log("  IO Chest contamination detected. Items for '"..itemDisplayName.."' (slot " .. currentRequestMainItemSlot ..") and unexpected (slot " .. contaminationCheckSlotUsed .. ") remain in turtle.")
  269.             log("  Skipping current item request '"..itemDisplayName.."' for this WO.")
  270.         elseif itemActuallySuckedThisRequest > 0 then
  271.              local detail = turtle.getItemDetail(currentRequestMainItemSlot)
  272.              table.insert(successfullyFetched, {
  273.                 slot = currentRequestMainItemSlot, name = detail.name, displayName = detail.displayName,
  274.                 count = detail.count, nbt = detail.nbt
  275.             })
  276.         else
  277.             debugLog("  No items were successfully sucked for '"..itemDisplayName.."' this attempt (or export failed).")
  278.         end
  279.  
  280.         ::continue_me_req_v39::
  281.         os.sleep(0.1)
  282.     end
  283.     consolidateInventory()
  284.     return successfullyFetched
  285. end
  286.  
  287.  
  288. local function nbtValuesMatch(itemNbtFromTurtle, requestedNbtFromColony)
  289.     if requestedNbtFromColony == nil or (type(requestedNbtFromColony) == "table" and next(requestedNbtFromColony) == nil) then
  290.         debugNbtLog("Colony requested no specific NBT. Match TRUE.")
  291.         return true
  292.     end
  293.     local directMatch = (itemNbtFromTurtle == requestedNbtFromColony)
  294.     debugNbtLog("Colony requested specific NBT. Turtle NBT: '" .. tostring(itemNbtFromTurtle) .. "', Requested NBT: '" .. tostring(requestedNbtFromColony) .. "'. Direct Match: " .. tostring(directMatch))
  295.     return directMatch
  296. end
  297.  
  298. local function deliverItems(deliveryWarpPointName, itemsExpectedToDeliver)
  299.     log("Attempting delivery to " .. deliveryWarpPointName)
  300.     log("Delivering items by dropping down:")
  301.     local allItemsFullyDelivered = true
  302.     local remainingToDeliver = {}
  303.     for _, itemDetail in ipairs(itemsExpectedToDeliver) do
  304.         table.insert(remainingToDeliver, {
  305.             name = itemDetail.name, displayName = itemDetail.displayName or itemDetail.name or "UnknownItem",
  306.             nbt = itemDetail.nbt, count = itemDetail.count
  307.         })
  308.     end
  309.     local deliveryAttempted = false
  310.     for deliveryPass = 1, 2 do
  311.         if deliveryAttempted and deliveryPass == 2 then
  312.             local anyLeft = false; for _, item in ipairs(remainingToDeliver) do if item.count > 0 then anyLeft = true break end end
  313.             if not anyLeft then break end
  314.             log("    Some items remain. Trying alternative drop spot...")
  315.             turtle.turnLeft() turtle.forward()
  316.         elseif deliveryPass == 2 then break end
  317.         deliveryAttempted = true
  318.         for i = 1, #remainingToDeliver do
  319.             local wantedItem = remainingToDeliver[i]
  320.             if wantedItem.count > 0 then
  321.                 local wantedItemDisplayName = wantedItem.displayName
  322.                 log("  Seeking to deliver " .. wantedItem.count .. " of " .. wantedItemDisplayName .. " (Req NBT type: " .. type(wantedItem.nbt) .. ", val: " .. tostring(wantedItem.nbt) .. ")")
  323.                 for slot = 1, 15 do
  324.                     turtle.select(slot)
  325.                     local itemInSlot = turtle.getItemDetail(slot)
  326.                     if itemInSlot and itemInSlot.name == wantedItem.name then
  327.                         debugLog("Checking slot " .. slot .. ": Item '" .. itemInSlot.name .. "', Count: " .. itemInSlot.count .. ", Slot NBT type: " .. type(itemInSlot.nbt) .. ", val: " .. tostring(itemInSlot.nbt))
  328.                         if nbtValuesMatch(itemInSlot.nbt, wantedItem.nbt) then
  329.                             debugLog("NBTs MATCH for slot " .. slot .. " for item " .. wantedItemDisplayName)
  330.                             local amountToDrop = math.min(itemInSlot.count, wantedItem.count)
  331.                             if amountToDrop > 0 then
  332.                                 log("    Found " .. itemInSlot.count .. " of " .. wantedItemDisplayName .. " in slot " .. slot .. ". Attempting to dropDown " .. amountToDrop)
  333.                                 if turtle.dropDown(amountToDrop) then
  334.                                     log("      DroppedDown " .. amountToDrop .. " successfully.")
  335.                                     wantedItem.count = wantedItem.count - amountToDrop
  336.                                     if wantedItem.count == 0 then log("      Requirement for " .. wantedItemDisplayName .. " met.") break end
  337.                                 else
  338.                                     log("      DropDown failed for " .. amountToDrop .. " of " .. wantedItemDisplayName .. " from slot " .. slot .. ".")
  339.                                     allItemsFullyDelivered = false
  340.                                 end
  341.                             end
  342.                         else
  343.                              debugLog("NBTs DO NOT MATCH for slot " .. slot .. " for item " .. wantedItemDisplayName)
  344.                         end
  345.                     end
  346.                 end
  347.             end
  348.         end
  349.         local allMetThisPass = true
  350.         for _, item in ipairs(remainingToDeliver) do if item.count > 0 then allMetThisPass = false break end end
  351.         if allMetThisPass then log("    All item requirements met in this delivery pass.") break end
  352.         if deliveryPass == 2 then turtle.back() turtle.turnRight() end
  353.     end
  354.     for _, item in ipairs(remainingToDeliver) do
  355.         if item.count > 0 then log("  WARN: Could not deliver " .. item.count .. " of " .. item.displayName .. ".") allItemsFullyDelivered = false end
  356.     end
  357.     if allItemsFullyDelivered then log("All requested items appear fully delivered to " .. deliveryWarpPointName)
  358.     else log("Some items were not fully delivered to " .. deliveryWarpPointName) end
  359.     local waitTime = DEBUG_MODE and 30 or 10
  360.     log("Waiting " .. waitTime .. " seconds at builder's hut: " .. deliveryWarpPointName .. (DEBUG_MODE and " (DEBUG MODE)" or ""))
  361.     os.sleep(waitTime)
  362.     return allItemsFullyDelivered
  363. end
  364.  
  365. function prepareAtHomeBase()
  366.     log("Ensuring turtle is prepared at home base...")
  367.     if not warpTo(HOME_WARP_POINT) then log("CRITICAL: Cannot reach " .. HOME_WARP_POINT) return false, nil end
  368.     local bridge = peripheral.find(ME_BRIDGE_NAME)
  369.     if not bridge then log("ERROR: ME Bridge ('"..ME_BRIDGE_NAME.."') not found at " .. HOME_WARP_POINT .. "!") return false, nil end
  370.     log("ME Bridge found at home base.")
  371.     if not ensureFuel(true) then log("CRITICAL: Failed to ensure fuel at " .. HOME_WARP_POINT .. ".") return false, bridge end
  372.     returnUnusedItemsToME(bridge)
  373.     log("Turtle is at home and prepared.")
  374.     return true, bridge
  375. end
  376.  
  377. -- MAIN SCRIPT EXECUTION
  378. log("Builder Bot v3.9 Initializing..." .. (DEBUG_MODE and " (DEBUG MODE ENABLED)" or ""))
  379. if not initialPeripheralCheck() then log("Essential End Automata not found. Exiting.") return end
  380. if DEBUG_MODE then
  381.     local resLogClearFile = fs.open(DETAILED_RESOURCE_LOG_FILE, "w")
  382.     if resLogClearFile then resLogClearFile.close() log("Cleared " .. DETAILED_RESOURCE_LOG_FILE) end
  383. end
  384. local homePrepared, currentBridge = prepareAtHomeBase()
  385. if not homePrepared then
  386.     log("Initial home preparation failed. Waiting 5 minutes...") os.sleep(300)
  387.     homePrepared, currentBridge = prepareAtHomeBase()
  388.     if not homePrepared then log("Second home prep failed. Exiting.") return end
  389. end
  390. log("Successfully initialized. Starting main loop.")
  391.  
  392. while true do
  393.     log("\n--- Starting New Cycle ---")
  394.     local workOrderFoundAndTargeted = false ; local actualDeliveryWarpPoint = nil
  395.     local originalTargetBuildingName = nil ; local currentWorkOrderItems = nil
  396.     local detailedResFile = nil
  397.  
  398.     log("Going to colony for work orders...")
  399.     if not ensureFuel(false) then log("Warn: Low fuel before leaving home.") end
  400.     if not warpTo(COLONY_WARP_POINT) then
  401.         log("Failed to reach colony.") ; homePrepared, currentBridge = prepareAtHomeBase()
  402.         if not homePrepared then log("CRIT: No recovery. Wait 5m...") os.sleep(300) end
  403.         goto continue_main_loop_v39
  404.     end
  405.     local colony = peripheral.find(COLONY_INTEGRATOR_NAME)
  406.     if not colony then
  407.         log("ERROR: CI not found at "..COLONY_WARP_POINT) ; homePrepared, currentBridge = prepareAtHomeBase()
  408.         if not homePrepared then os.sleep(300) end
  409.         goto continue_main_loop_v39
  410.     end
  411.     log("CI found. Checking work orders...")
  412.     local workOrders = colony.getWorkOrders()
  413.     local suitableWorkOrders = {}
  414.  
  415.     if not workOrders or #workOrders == 0 then log("No active work orders.")
  416.     else
  417.         log("Found " .. #workOrders .. " WOs. Evaluating...")
  418.         for _, wo in ipairs(workOrders) do
  419.             if not wo or wo.id == nil then log("  WARN: Invalid WO entry.") goto continue_eval_wo_loop_v39 end
  420.             log("\n  Evaluating WO ID: " .. wo.id .. " for: " .. (wo.buildingName or "N/A"))
  421.             if DEBUG_MODE then
  422.                 detailedResFile = fs.open(DETAILED_RESOURCE_LOG_FILE, "a")
  423.                 if detailedResFile then detailedResFile.write("\n--- Eval WO ID: " .. wo.id .. " for: " .. (wo.buildingName or "N/A") .. ", Type: " .. (wo.workOrderType or "N/A") .. " ---\n") end
  424.             end
  425.             local itemsNeededForThisOrder = {} ; local needsSomething = false
  426.             local resources = colony.getWorkOrderResources(wo.id)
  427.             if resources then
  428.                 if #resources > 0 then
  429.                     local headerMsg = "    Found " .. #resources .. " resource entries:"
  430.                     if detailedResFile then detailedResFile.write(headerMsg .. "\n") elseif DEBUG_MODE then log(headerMsg) end
  431.                     for resIdx, res in ipairs(resources) do
  432.                         local sResItem = "UnknownItemRegistry"
  433.                         if type(res.item) == "table" then sResItem = res.item.name or (res.item.displayName or "UnknownInTable") elseif type(res.item) == "string" then sResItem = res.item end
  434.                         local sResDisplayName = sResItem
  435.                         if type(res.displayName) == "table" then sResDisplayName = res.displayName.name or (res.displayName.displayName or sResItem) elseif type(res.displayName) == "string" then sResDisplayName = res.displayName end
  436.                         local res_needs_val = res.needs ; local resAvailable = res.available
  437.                         local resStatus = res.status ; local resDelivering = res.delivering
  438.                         local logEntry = ("      Res #" .. resIdx .. ": '" .. sResDisplayName .. "' (Reg: " .. sResItem .. ")\n") ..
  439.                                        ("        Raw needs: " .. tostring(res_needs_val) .. " (Type: " .. type(res_needs_val) .. ")\n") ..
  440.                                        ("        Raw Avail: " .. tostring(resAvailable) .. " (Type: " .. type(resAvailable) .. ")\n") ..
  441.                                        ("        Raw Status: " .. tostring(resStatus) .. " (Type: " .. type(resStatus) .. ")\n") ..
  442.                                        ("        Raw Deliver: " .. tostring(resDelivering) .. " (Type: " .. type(resDelivering) .. ")\n") ..
  443.                                        ("        Raw NBT: " .. tostring(res.nbt) .. " (Type: " .. type(res.nbt) .. ")\n")
  444.                         if detailedResFile then detailedResFile.write(logEntry) elseif DEBUG_MODE then log(logEntry) end
  445.                         if res_needs_val == nil then
  446.                             local skipMsg = "        SKIP: 'needs' is nil."
  447.                             if detailedResFile then detailedResFile.write(skipMsg .. "\n") elseif DEBUG_MODE then log(skipMsg) end
  448.                             goto continue_res_loop_v39
  449.                         end
  450.                         local isStillNeeded = false ; local evalMsg = ""
  451.                         if type(res_needs_val) == "number" and res_needs_val > 0 then
  452.                             if resAvailable == false then isStillNeeded = true evalMsg = "        EVAL: Need (avail false).\n"
  453.                             elseif resStatus and string.lower(resStatus) ~= "met" and string.lower(resStatus) ~= "fulfilled" and string.lower(resStatus) ~= "not_needed" then
  454.                                 isStillNeeded = true evalMsg = "        EVAL: Need (status '"..tostring(resStatus).."').\n"
  455.                             else evalMsg = "        EVAL: Not Need (avail true or status met/fulfilled/not_needed). Needs: "..tostring(res_needs_val)..", Avail: "..tostring(resAvailable)..", Status: "..tostring(resStatus).."\n" end
  456.                         else evalMsg = "        EVAL: Not Need (needs not >0 or not num). Needs: "..tostring(res_needs_val).."\n" end
  457.                         if detailedResFile then detailedResFile.write(evalMsg) elseif DEBUG_MODE then log(evalMsg) end
  458.                         if isStillNeeded then
  459.                             local actionMsg = "        ACTION: Add " .. res_needs_val .. " of " .. sResDisplayName .. " to list. (WO NBT Type: " .. type(res.nbt) .. ", Val: " .. tostring(res.nbt) .. ")\n"
  460.                             if detailedResFile then detailedResFile.write(actionMsg) elseif DEBUG_MODE then log(actionMsg) end
  461.                             table.insert(itemsNeededForThisOrder, {item = sResItem, displayName = sResDisplayName, needs = res_needs_val, nbt = res.nbt})
  462.                             needsSomething = true
  463.                         end
  464.                         ::continue_res_loop_v39::
  465.                     end
  466.                 else
  467.                     local noResMsg = "    WO " .. wo.id .. " no resource entries."
  468.                     if detailedResFile then detailedResFile.write(noResMsg .. "\n") else log(noResMsg) end
  469.                 end
  470.             else
  471.                 local failGetResMsg = "  WARN: Failed to get resources for WO " .. wo.id
  472.                 if detailedResFile then detailedResFile.write(failGetResMsg .. "\n") else log(failGetResMsg) end
  473.             end
  474.             if detailedResFile then detailedResFile.close() detailedResFile = nil end
  475.             if needsSomething then
  476.                 local tempDeliveryWarpPoint = nil
  477.                 if wo.buildingName and not string.match(string.lower(wo.buildingName), "com.minecolonies.building.") and not string.match(string.lower(wo.buildingName), "minecolonies:") then
  478.                     tempDeliveryWarpPoint = wo.buildingName .. NAMED_BUILDING_STORAGE_SUFFIX
  479.                 elseif wo.builder and wo.builder.x ~= nil then tempDeliveryWarpPoint = wo.builder.x .. "," .. wo.builder.y .. "," .. wo.builder.z
  480.                 end
  481.                 if tempDeliveryWarpPoint then
  482.                     log("  WO ID " .. wo.id .. " for " .. (wo.buildingName or "N/A") .. " is suitable and has a valid warp point.")
  483.                     table.insert(suitableWorkOrders, { woData = wo, items = itemsNeededForThisOrder, warpPoint = tempDeliveryWarpPoint })
  484.                 else
  485.                     log("  WO ID " .. wo.id .. " for " .. (wo.buildingName or "N/A") .. " needs items, but no valid warp point determined. Skipping as suitable.")
  486.                 end
  487.             else log("  WO " .. wo.id .. " no items needed based on eval.") end
  488.             ::continue_eval_wo_loop_v39::
  489.         end
  490.     end
  491.     colony = nil
  492.     if #suitableWorkOrders > 0 then
  493.         local randomIndex = math.random(1, #suitableWorkOrders)
  494.         local chosenTask = suitableWorkOrders[randomIndex]
  495.         log("Found " .. #suitableWorkOrders .. " suitable work order(s). Randomly selected task for WO ID: " .. chosenTask.woData.id)
  496.         originalTargetBuildingName = chosenTask.woData.buildingName or "Unknown Target"
  497.         currentWorkOrderItems = chosenTask.items
  498.         actualDeliveryWarpPoint = chosenTask.warpPoint
  499.         workOrderFoundAndTargeted = true
  500.     else
  501.         log("No suitable work orders found this cycle that require items and have valid warp points.")
  502.     end
  503.     if not workOrderFoundAndTargeted then log("No suitable WOs at colony.") log("Wait 10s at colony...") os.sleep(10) end
  504.  
  505.     if workOrderFoundAndTargeted and actualDeliveryWarpPoint and currentWorkOrderItems then
  506.         log("WO targeted. Home for items: " .. originalTargetBuildingName)
  507.         if not warpTo(HOME_WARP_POINT) then log("Failed warp home.") goto continue_main_loop_v39 end
  508.         currentBridge = peripheral.find(ME_BRIDGE_NAME)
  509.         if not currentBridge then log("ERROR: ME Bridge missing at home!") goto continue_main_loop_v39 end
  510.         if not ensureFuel(true) then log("Fuel fail home.") goto continue_main_loop_v39 end
  511.         local fetchedItems = getItemsFromME(currentBridge, currentWorkOrderItems)
  512.         if not fetchedItems then fetchedItems = {} end
  513.         log("ME fetch attempt complete. Fetched " .. #fetchedItems .. " types of items for delivery. Delivering to " .. originalTargetBuildingName .. " via " .. actualDeliveryWarpPoint)
  514.         if not ensureFuel(false) then log("Warn: Low fuel pre-delivery.") end
  515.         if warpTo(actualDeliveryWarpPoint) then
  516.             if #fetchedItems > 0 then
  517.                 deliverItems(actualDeliveryWarpPoint, fetchedItems)
  518.             else
  519.                 log("No items were successfully fetched from ME (or all were compromised by contamination). Nothing to deliver for this WO.")
  520.                 local waitTimeNoItems = DEBUG_MODE and 30 or 10
  521.                 log("Waiting " .. waitTimeNoItems .. " seconds at builder's hut (even with no items): " .. actualDeliveryWarpPoint .. (DEBUG_MODE and " (DEBUG MODE)" or ""))
  522.                 os.sleep(waitTimeNoItems)
  523.             end
  524.         else log("Failed warp to " .. actualDeliveryWarpPoint .. ". Items (if any) remain in turtle.") end
  525.         log("Delivery attempt for " .. originalTargetBuildingName .. " done.")
  526.     else log("No WO targeted this cycle. Home if not there.") end
  527.     log("Cycle end. Prep for next.")
  528.     homePrepared, currentBridge = prepareAtHomeBase()
  529.     if not homePrepared then log("Home prep fail. Wait 5m...") os.sleep(300) homePrepared, currentBridge = prepareAtHomeBase()
  530.         if not homePrepared then log("2nd home prep fail. Exit.") return end
  531.     end
  532.     log("Wait 10s before next cycle...") os.sleep(10)
  533.     ::continue_main_loop_v39::
  534. end
Add Comment
Please, Sign In to add comment