local turtleUtils = require('libs/turtle-utils') local config = require('config/harvesting') local VERSION = "0.5.0" local IDLE_TIME = 2 local WAIT_ITEM_IDLE_TIME = 5 local MIN_FUEL_NEEDED = (100 + config.firstCropZ + config.length) * 2 local MIN_FREE_SLOTS_BEFORE_COMPACT = 4 local function mystical(essenceName) return 'mysticalagriculture:' .. essenceName .. '_seeds' end local fakeRemotePlan = { mystical('coal'), mystical('diamond'), mystical('nether_quartz'), mystical('dye'), mystical('inferium'), mystical('inferium'), mystical('inferium'), mystical('inferium') } -- a table with the list of current crops local localPlan = nil -- UTILS local function difference(a, b) local aa = {} for k,v in pairs(a) do aa[v]=true end for k,v in pairs(b) do aa[v]=nil end local ret = {} local n = 0 for k,v in pairs(a) do if aa[v] then n=n+1 ret[n]=v end end return ret end local function shallowClone(t) local res = {} for k,v in pairs(t) do res[k] = v end return res end local function removeFirst(t, x) local res = {} local removed = false for k,v in pairs(t) do if not removed and x == v then removed = true else table.insert(res, v) end end return res end local function find(t, predicate) for k,v in pairs(t) do if predicate(v, k) then return v, k end end end local function getIndexedCount(t) local res = {} for k,v in pairs(t) do local count = res[v] or 0 res[v] = count + 1 end return res end local function assertEnoughFuel() if turtle.getFuelLevel() < MIN_FUEL_NEEDED then error('Not enough fuel') end end local function retrieveChestFuelOrientation() local inventoryFound = false for i=1, 4, 1 do if turtleUtils.getInventory('front') then inventoryFound = true break end turtle.turnRight() end if not inventoryFound then error('chest fuel not found') end end local function getSeedNameFromCropName(cropName) if not cropName then return cropName end return string.gsub(cropName, 'crop', 'seeds') end function isSeed(item) local tags = item and item.tags or {} return tags['forge:seeds'] or tags['mysticalagriculture:seeds'] or false end -- Inventory utils -- the slot of the item is returned local function waitForItem(inventory, itemName, count, sleepTime) sleepTime = sleepTime or 5 -- TODO: DEFAULT_IDLE_TIME while true do for slot, item in pairs(inventory.list()) do if item.name == itemName and item.count >= count then return slot end end os.sleep(IDLE_TIME) end end -- Implementations local function retrieveHomePositionProcedure() if turtleUtils.getInventory('bottom') then retrieveChestFuelOrientation() return end turtle.turnRight() if turtle.inspect() then turtle.turnRight() end while true do if turtleUtils.getInventory('bottom') then turtle.turnLeft() return end if turtle.inspect() then break else turtle.forward() end end turtle.turnLeft() turtle.turnLeft() while true do if turtleUtils.getInventory('bottom') then break end if turtle.inspect() then error('Cannot retrieve home position') else turtle.forward() end end turtle.turnLeft() end local function dropAllProcedure() for i=1, 16, 1 do local count = turtle.getItemCount(i) if count > 0 then turtleUtils.waitForInventory('bottom', IDLE_TIME) while not turtleUtils.dropSlot(i, turtle.dropDown) do os.sleep(IDLE_TIME) end end end end local function refuelProcedure() local refuelOk, refuelErr = turtleUtils.refuelWithBuffer('bottom', 'front', MIN_FUEL_NEEDED, IDLE_TIME) if not refuelOk then error('Cannot refuel the turtle: "' .. refuelErr .. '"') end end local function goToHarvestPoint() turtle.turnLeft() for i=1, config.firstCropZ, 1 do turtle.forward() end end local function goBackToHome() for i=1, config.firstCropZ, 1 do turtle.forward() end turtle.turnLeft() end local function compactIfNeeded() if turtleUtils.countFreeSlots() < MIN_FREE_SLOTS_BEFORE_COMPACT then turtleUtils.compactInventory() end end local function harvestDown() local cropName = turtleUtils.waitForMatureCrop(turtle.inspectDown, IDLE_TIME) local seedName = getSeedNameFromCropName(cropName) if not turtle.digDown() then error('turtle cannot harvest crop') end if not turtleUtils.selectItemByName(seedName) then error('turtle cannot find any crop to place') end if not turtle.placeDown() then error('turtle cannot place crop') end compactIfNeeded() end local function forward() if not turtle.forward() then error('turtle is blocked') end end local function harvestProcedure() for i=1, config.length, 1 do harvestDown() if i ~= config.length then forward() end end turtle.turnLeft() turtle.turnLeft() for i=1, config.length, 1 do if config.energySaving then harvestDown() end if i ~= config.length then forward() end end end local function retrieveLocalPlan() goToHarvestPoint() localPlan = {} for i=1, config.length, 1 do local ok, block = turtle.inspectDown() local blockName = block and block.name localPlan[i] = getSeedNameFromCropName(blockName) if i ~= config.length then forward() end end turtle.turnLeft() turtle.turnLeft() for i=1, config.length, 1 do if i ~= config.length then forward() end end goBackToHome() end local function fetchRemotePlan() -- TODO return fakeRemotePlan end local function removeSeeds(seeds) goToHarvestPoint() local stateSeeds = seeds for i=1, config.length, 1 do local ok, block = turtle.inspectDown() local blockName = block and block.name local found = find(stateSeeds, function(seedName) return seedName == getSeedNameFromCropName(blockName) end) if found then local digOk = turtle.digDown() compactIfNeeded() if not digOk then error('cannot remove seed') end stateSeeds = removeFirst(stateSeeds, found) end if i ~= config.length then forward() end end turtle.turnLeft() turtle.turnLeft() for i=1, config.length, 1 do if i ~= config.length then forward() end end goBackToHome() dropAllProcedure() end local function retrieveSeeds(seeds) local seedsCount = getIndexedCount(seeds) local storageInventory = turtleUtils.waitForInventory('bottom', WAIT_ITEM_IDLE_TIME) local bufferInventory = turtleUtils.waitForInventory('front', WAIT_ITEM_IDLE_TIME) for seedName, count in pairs(seedsCount) do local slot = waitForItem(storageInventory, seedName, count, WAIT_ITEM_IDLE_TIME) local pushOk = storageInventory.pushItems(peripheral.getName(bufferInventory), slot, count) if not pushOk then error('retrieveSeeds error: cannot pushItems from storage to buffer') end local suckOk = turtle.suck() if not suckOk then error('retrieveSeeds error: cannot suck items from buffer') end end end local function replantSeeds() goToHarvestPoint() for i=1, config.length, 1 do local ok, block = turtle.inspectDown() if not ok then turtleUtils.selectItemBy(function(slot) local item = turtle.getItemDetail(slot, true) return isSeed(item) end) turtle.placeDown() end if i ~= config.length then forward() end end turtle.turnLeft() turtle.turnLeft() for i=1, config.length, 1 do if i ~= config.length then forward() end end goBackToHome() end local function replantProcedure() if localPlan == nil then retrieveLocalPlan() end if localPlan == nil then error('cannot retrieve the local plan') end local remotePlan = fetchRemotePlan() if remotePlan == nil then error('cannot fetch the remote plan') end local seedsToRemove = difference(localPlan, remotePlan) local seedsToPlant = difference(remotePlan, localPlan) if #seedsToRemove > 0 then print('nb seeds to remove: ' .. #seedsToRemove) removeSeeds(seedsToRemove) end if #seedsToPlant > 0 then print('nb seeds to plants: ' .. #seedsToPlant) retrieveSeeds(seedsToPlant) replantSeeds() end end -- Main procedure local function main() print("Starting Trap's inferium harvester v" .. VERSION) turtleUtils.refuelAllFromInventory() assertEnoughFuel() print("> retrieving home position") retrieveHomePositionProcedure() print("> start the harvesting process") while true do dropAllProcedure() refuelProcedure() replantProcedure() goToHarvestPoint() harvestProcedure() goBackToHome() end end main()