mathiaas

turtle_bot

Apr 20th, 2024 (edited)
327
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 10.57 KB | None | 0 0
  1. dofile("utils")
  2. dofile("constants")
  3. local ENV = loadEnv()
  4.  
  5. TurtleBot = {}
  6. TurtleBot.__index = TurtleBot
  7.  
  8. function TurtleBot.new(currentScript, direction, x, y, z)
  9.     local self = setmetatable({}, TurtleBot)
  10.     self.label = os.computerLabel()
  11.     self.fuelLevel = turtle.getFuelLevel()
  12.     self.x = x or 0
  13.     self.y = y or 0
  14.     self.z = z or 0
  15.     self.direction = direction or "North"
  16.     self.currentScript = currentScript or nil
  17.     self.status = STATUS.STARTING
  18.     return self
  19. end
  20.  
  21. -- API functions
  22. function TurtleBot:connect()
  23.     local apiClient = dofile("api_client")
  24.     self.apiClient = apiClient.new(ENV.API)
  25. end
  26.  
  27. function TurtleBot:fetchData()
  28.     -- Fetch data about the turtle based on its label
  29.     local path = "/turtles/" .. self.label
  30.     return self.apiClient:get(path)
  31. end
  32.  
  33. function TurtleBot:updateData()
  34.     -- Update the turtle data on the server
  35.     self.fuelLevel = turtle.getFuelLevel()
  36.     local data = {
  37.         label = self.label,
  38.         current_script = self.currentScript or "",
  39.         coordinate_x = self.x,
  40.         coordinate_y = self.y,
  41.         coordinate_z = self.z,
  42.         status = self.status,
  43.         fuel_lvl = self.fuelLevel
  44.     }
  45.     local path = "/turtles"
  46.     return pcall(function() self.apiClient:post(path, data) end)
  47. end
  48.  
  49. function TurtleBot:continousUpdate(interval)
  50.     if interval ~= nil then
  51.         typeCheck(interval, "number", "TurtleBot:continousUpdate")
  52.     end
  53.     local interval = interval or 30
  54.     while true do
  55.         self:updateData()
  56.         if inList(self.status, {STATUS.ERROR, STATUS.FINISHED}) then break end
  57.         sleep(interval)
  58.     end
  59. end
  60.  
  61.  
  62. function TurtleBot:execute(func, recovery)
  63.     typeCheck(func, "function", "TurtleBot:execute")
  64.     parallel.waitForAll(
  65.         function() self:continousUpdate() end,
  66.         function ()
  67.             self.status = STATUS.RUNNING
  68.             local status, err = pcall(func)
  69.             if not status then
  70.                 print("Error occured: " .. err)
  71.                 self.status = STATUS.ERROR
  72.                 if type(recovery) == "function" then recovery() end
  73.             else
  74.                 self.status = STATUS.FINISHED
  75.             end
  76.         end
  77.     )
  78. end
  79.  
  80.  
  81. -- Helper function for movement
  82. function TurtleBot:_movement(steps, func, changeFunc)
  83.     steps = steps or 1
  84.     typeCheck(steps, "number", "TurtleBot:_movement")
  85.     typeCheck(func, "function", "TurtleBot:_movement")
  86.  
  87.     for i = 1, steps do
  88.         local success = func()
  89.         if not success then
  90.             print("Movement function failed")
  91.             return false
  92.         end
  93.         if changeFunc then changeFunc(self) end
  94.     end
  95.     return true
  96. end
  97.  
  98. -- Helper function for turning
  99. function TurtleBot:_turn(directionModifier)
  100.     typeCheck(directionModifier, "number", "TurtleBot:_turn")
  101.  
  102.     local dirIndex = (findIndex(DIRECTIONS, self.direction) + directionModifier - 1) % #DIRECTIONS + 1
  103.     self.direction = DIRECTIONS[dirIndex]
  104. end
  105.  
  106.  
  107. function TurtleBot:updatePositionForward()
  108.     if self.direction == "North" then
  109.         self.z = self.z - 1
  110.     elseif self.direction == "South" then
  111.         self.z = self.z + 1
  112.     elseif self.direction == "East" then
  113.         self.x = self.x + 1
  114.     elseif self.direction == "West" then
  115.         self.x = self.x - 1
  116.     end
  117. end
  118.  
  119.  
  120. function TurtleBot:updatePositionBackward()
  121.     if self.direction == "North" then
  122.         self.z = self.z + 1
  123.     elseif self.direction == "South" then
  124.         self.z = self.z - 1
  125.     elseif self.direction == "East" then
  126.         self.x = self.x - 1
  127.     elseif self.direction == "West" then
  128.         self.x = self.x + 1
  129.     end
  130. end
  131.  
  132.  
  133. -- Instance methods for directional movement
  134. function TurtleBot:forward(steps)
  135.     return self:_movement(steps, turtle.forward, self.updatePositionForward)
  136. end
  137.  
  138. function TurtleBot:back(steps)
  139.     return self:_movement(steps, turtle.back, self.updatePositionBackward)
  140. end
  141.  
  142. function TurtleBot:down(steps)
  143.     return self:_movement(steps, turtle.down, function() self.y = self.y - 1 end)
  144. end
  145.  
  146. function TurtleBot:up(steps)
  147.     return self:_movement(steps, turtle.up, function() self.y = self.y + 1 end)
  148. end
  149.  
  150.  
  151. function TurtleBot:left(steps)
  152.     steps = steps or 1
  153.     for i = 1, steps do
  154.         turtle.turnLeft()
  155.         self:_turn(-1)
  156.     end
  157.     return true
  158. end
  159.  
  160. function TurtleBot:right(steps)
  161.     steps = steps or 1
  162.     for i = 1, steps do
  163.         turtle.turnRight()
  164.         self:_turn(1)
  165.     end
  166.     return true
  167. end
  168.  
  169.  
  170. -- Inventory
  171. function TurtleBot:inventory()
  172.     local inventory = {}
  173.     for slot = 1, 16 do
  174.         local itemDetail = turtle.getItemDetail(slot)
  175.         if itemDetail then
  176.             itemDetail.slot = slot
  177.             itemDetail.space = turtle.getItemSpace(slot)
  178.             table.insert(inventory, itemDetail)
  179.         end
  180.     end
  181.     return inventory
  182. end
  183.  
  184.  
  185. function TurtleBot:combine()
  186.     local inventory = self:inventory()
  187.     for i=1, #inventory do
  188.         local itemI = inventory[i]
  189.         if i == #inventory then break end -- Last item in the inventory does not have any existing slots to match
  190.         if itemI.count ~= 0 then --Skip slot if count is exhausted
  191.             for j=i + 1, #inventory do -- Loop through the remaining inventory to find more of this item
  192.                 local itemJ = inventory[j]
  193.                 if itemI.space == 0 then break end --ItemI has no more space left. Skipping to next item.
  194.                 if itemJ.count ~= 0  and itemJ.name == itemI.name and itemJ.damage == itemI.damage then -- Determining that the items are identical and that there are more items left.
  195.                     turtle.select(itemJ.slot) --Slot is selected because there are items here we want in another stack.
  196.                     local transferAmount = math.min(itemJ.count, itemI.space) -- We cant move more than itemJ has or itemI has space for
  197.                     turtle.transferTo(itemI.slot, transferAmount)
  198.                     itemI.space = itemI.space - transferAmount
  199.                     itemI.count = itemI.count + transferAmount
  200.                     itemJ.count = itemJ.count - transferAmount
  201.                     itemJ.space = itemJ.space + transferAmount
  202.                 end
  203.             end
  204.         end
  205.     end
  206. end
  207.  
  208. function TurtleBot:selectItem(itemName, itemDamage)
  209.     typeCheck(itemName, "string", "selectItem")
  210.     for _,v in pairs(self:inventory()) do
  211.         if v.name == itemName then
  212.             if itemDamage ~= nil and v.damage ~= itemDamage then return false end
  213.             turtle.select(v.slot)
  214.             return true
  215.         end
  216.     end
  217.     return false
  218. end
  219.  
  220.  
  221. function TurtleBot:dig(direction)
  222.     -- This could be expanded to track blocks dug
  223.     direction = direction or "front"
  224.     local digMethod = turtle.dig
  225.  
  226.     if direction == "front" then
  227.         digMethod = turtle.dig
  228.     elseif direction == "up" then
  229.         digMethod = turtle.digUp
  230.     elseif direction == "down" then
  231.         digMethod = turtle.digDown
  232.     else
  233.         error("Unexpected dig direction: " .. direction, 2)
  234.     end
  235.  
  236.     return digMethod()
  237. end
  238.  
  239.  
  240. function TurtleBot:detectBlock(blocks, direction)
  241.     direction = direction or "front"
  242.     local inspect_method = turtle.inspect
  243.  
  244.     if direction == "down" then
  245.         inspect_method = turtle.inspectDown
  246.     elseif direction == "up" then
  247.         inspect_method = turtle.inspectUp
  248.     end
  249.  
  250.     local success, data = inspect_method()
  251.  
  252.     if not success then return false end
  253.     if not inList(data.name, blocks) then return false end
  254.     return true
  255. end
  256.  
  257. -- Ender Storage
  258. function TurtleBot:enderPlaceChest(channel)
  259.     typeCheck(channel, "table", "TurtleBot:enderPlaceChest")
  260.     -- A bug/instability in computercraft or minecraft
  261.     -- causes peripheral.wrap to catch a java runtime error
  262.     local success, error = pcall(function()
  263.         local inspectResult, data = turtle.inspectUp()
  264.         -- Checking if there is already an enderchest placed.
  265.         -- If there is, all we do is change the channel
  266.         if not inspectResult or data.name ~= BLOCKS.enderChest.name then
  267.             if not self:selectItem(ITEMS.enderChest.name) then error("Missing Ender Chest", 2) end
  268.             self:dig("up")
  269.             turtle.placeUp()
  270.         end
  271.         local enderChest = peripheral.wrap("top")
  272.         enderChest.setColors(unpack(channel))
  273.     end)
  274.     if type(error) == "string" then
  275.         print("Caught an error while wrapping ender chest" .. error)
  276.         self:dig("up")
  277.     end
  278.     return success
  279. end
  280.  
  281.  
  282. function TurtleBot:enderCollect(channel, count)
  283.     typeCheck(channel, "table", "TurtleBot:enderCollect")
  284.     typeCheck(count, "number", "TurtleBot:enderCollect")
  285.  
  286.     if not self:enderPlaceChest(channel) then return false end
  287.     return turtle.suckUp(count)
  288. end
  289.  
  290.  
  291. function TurtleBot:enderRefuel(lowFuel, maxFuel, item)
  292.     typeCheck(lowFuel, "number", "TurtleBot:enderRefuel")
  293.     typeCheck(maxFuel, "number", "TurtleBot:enderRefuel")
  294.     typeCheck(item, "table", "TurtleBot:enderRefuel")
  295.  
  296.     local fuelLvl = turtle.getFuelLevel()
  297.     if fuelLvl > lowFuel then return end
  298.  
  299.     while turtle.getFuelLevel() < maxFuel do
  300.         self:enderCollect(COLOR_CHANNELS.fuel, 64)
  301.         self:selectItem(item.name, item.damage)
  302.         turtle.refuel()
  303.         sleep(1)
  304.     end
  305. end
  306.  
  307.  
  308. function TurtleBot:enderDeposit(channel, blacklist)
  309.     typeCheck(channel, "table", "TurtleBot:enderDeposit")
  310.     typeCheck(blacklist, "table", "TurtleBot:enderDeposit")
  311.     table.insert(blacklist, ITEMS.enderChest.name) -- Making sure we never deposit enderChest
  312.  
  313.     if not self:enderPlaceChest(channel) then return false end
  314.  
  315.     local result = true
  316.     for _, v in pairs(self:inventory()) do
  317.         if not inList(v.name, blacklist) then
  318.             turtle.select(v.slot)
  319.             result = turtle.dropUp()
  320.         end
  321.     end
  322.  
  323.     return result
  324. end
  325.  
  326.  
  327. function TurtleBot:awaitInventory(item)
  328.     dofile("timer")
  329.     local timer = Timer.new()
  330.     local timeout = 300
  331.    
  332.     timer:start()
  333.     while true do
  334.         timer:stop()
  335.         if timer.elapsedTime > timeout then
  336.             print("Timeout reached waiting for " .. item.name)
  337.             return false
  338.         end
  339.  
  340.         for _, v in pairs(self:inventory()) do
  341.             if v.name == item.name then
  342.                 print(item.name .. " is now stocked in inventory.")
  343.                 return true
  344.             end
  345.         end
  346.  
  347.         if math.floor(timer.elapsedTime) % 5 == 0 then  
  348.             shell.run("clear")
  349.             print("Waiting for " .. item.name .. " in inventory...")
  350.         end
  351.         sleep(1)
  352.     end
  353. end
Add Comment
Please, Sign In to add comment