Advertisement
Multirezonator

Extended robot library

Sep 20th, 2016
374
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.34 KB | None | 0 0
  1. --[[Extended functionality for OpenComputes robot,
  2.     for easy movement, navigation and manipulation with inventory.
  3.  
  4.     Author: Multirez ]]--
  5.  
  6. local sides = require("sides")
  7. local component = require("component")
  8.  
  9. local robotExt = require("robot")
  10. local help = { } -- help texts for robotExt will be wrapped at the end
  11.  
  12. -- region Movement
  13. local pos = { ["x"] = 0, ["y"] = 0, ["z"] = 0 }
  14. setmetatable(pos, {
  15.     __add = function(pos, add)
  16.         -- the addition of two vectors
  17.         pos.x = pos.x + add.x
  18.         pos.y = pos.y + add.y
  19.         pos.z = pos.z + add.z
  20.         return pos
  21.     end,
  22.     __sub = function(pos, sub)
  23.         -- the subtraction of two vectors
  24.         pos.x = pos.x - sub.x
  25.         pos.y = pos.y - sub.y
  26.         pos.z = pos.z - sub.z
  27.         return pos
  28.     end,
  29.     __mul = function(pos, m)
  30.         -- multiplication of vector by a number
  31.         pos.x = pos.x * m
  32.         pos.y = pos.y * m
  33.         pos.z = pos.z * m
  34.         return pos
  35.     end
  36. } )
  37. local direction = sides.forward
  38.  
  39. help.setOrigin = "function() - robot resets its navigation state to (0,0,0), so that the current position as the origin."
  40. function robotExt.setOrigin()
  41.     pos.x, pos.y, pos.z = 0, 0, 0
  42.     direction = sides.forward
  43. end
  44.  
  45. help.getDir = "function():number - returns value from sides as direction relative the start."
  46. function robotExt.getDir()
  47.     return direction
  48. end
  49.  
  50. local rotateMap = {
  51.     sides.forward,sides.right,sides.back,sides.left,
  52.     front = 0,
  53.     right = 1,
  54.     back = 2,
  55.     left = - 1
  56. } -- the help table for the sides conversion
  57.  
  58. help.transformDirection = "function(localDirection:number):number - transforms direction from local space to start space."
  59. function robotExt.transformDirection(localDirection)
  60.     if ((direction == sides.forward) or(localDirection < 2)) then
  61.         return localDirection
  62.     end
  63.     local mapIndex = rotateMap[sides[direction]]
  64.     -- self index in rotateMap
  65.     local localMapIndex = rotateMap[sides[localDirection]]
  66.     return rotateMap[(mapIndex + localMapIndex + 8) % 4 + 1]
  67. end
  68.  
  69. help.inverseTransformDirection = "function(startDirection:number):number - transforms direction from start space to local space."
  70. function robotExt.inverseTransformDirection(startDirection)
  71.     if (startDirection < 2) then
  72.         return startDirection
  73.     end
  74.     local deltaMapIndex = rotateMap[sides[startDirection]] - rotateMap[sides[direction]]
  75.     return rotateMap[(deltaMapIndex + 8) % 4 + 1]
  76. end
  77.  
  78. help.rotate = "function(side: number[, isStartSpace:bool]):number - rotate robot to the [side] " ..
  79. "relative the current rotation by default or (if [isStartSpace]=true) relative start rotation. " ..
  80. "The robot can not rotate to [up] or [down] sides, so function returns the result side for interaction, " ..
  81. "it's one of {sides.front, sides.up, sides.down}."
  82. function robotExt.rotate(side, isStartSpace)
  83.     if (side < 2) then
  84.         -- error("Wrong [side] for rotation. The robot can not rotate to [up] or [down] sides.")       
  85.         return side
  86.     end
  87.     if (isStartSpace) then
  88.         -- convert to local space
  89.         side = robotExt.inverseTransformDirection(side)
  90.     end
  91.     -- rotate robot
  92.     local i = rotateMap[sides[side]]
  93.     local di = i < 0 and 1 or -1
  94.     while (i ~= 0) do
  95.         component.robot.turn(i > 0)
  96.         i = i + di
  97.     end
  98.     direction = robotExt.transformDirection(side)
  99.     -- update direction
  100.  
  101.     return sides.front
  102. end
  103.  
  104. help.getPos = "function():table{x, y, z} - returns table contains current robot coordinates relative the start position, " ..
  105. "where x - right, y - up, z - forward directional axes."
  106. -- x = "number - value of X axis of current robot position."}
  107. function robotExt.getPos()
  108.     return { x = pos.x, y = pos.y, z = pos.z }
  109. end
  110.  
  111. local side2vector = {
  112.     [sides.right] = { x = 1, y = 0, z = 0 },
  113.     [sides.left] = { x = - 1, y = 0, z = 0 },
  114.     [sides.up] = { x = 0, y = 1, z = 0 },
  115.     [sides.down] = { x = 0, y = - 1, z = 0 },
  116.     [sides.front] = { x = 0, y = 0, z = 1 },
  117.     [sides.back] = { x = 0, y = 0, z = - 1 }
  118. }
  119.  
  120. help.move = "function(direction:number, distance:number[, isStartSpace:bool]):bool[, number, string] " ..
  121. "- robot try to move at [distance] of blocks to the [direction] as sides value. " ..
  122. "If [isStartSpace]=true robot will move relative its rotation at the start, otherwise relative the current rotation. " ..
  123. "Returns [true] if final movement point has been reached, otherwise [false] and how many blocks has passed and " ..
  124. "describing why moving failed, which will either be 'impossible move', 'not enough energy' or " ..
  125. "the description of the obstacle as robot.detect would return."
  126. function robotExt.move(direction, distance, isStartSpace)
  127.     local moveSide = robotExt.rotate(direction, isStartSpace)
  128.     local moveVector = moveSide < 2 and side2vector[moveSide] or side2vector[robotExt.getDir()]
  129.     local i, isMoved, reason = 0, true, nil
  130.     while (i < distance and isMoved) do
  131.         isMoved, reason = component.robot.move(moveSide)
  132.         i = i + 1
  133.         pos = pos + moveVector
  134.     end
  135.     if (not isMoved) then
  136.         i = i - 1
  137.         pos = pos - moveVector
  138.         return false, i, reason
  139.     end
  140.     return true
  141. end
  142.  
  143. -- endregion
  144.  
  145. -- region World interaction
  146.  
  147. help.swingAt = "function(side:number[, targetSide:number [, sneaky:bool]]):bool[, string] - Makes the robot use the item " ..
  148. "currently in the tool slot against the block or space immediately in [side] of the robot " ..
  149. "in the same way as if a player would make a left-click. [targetSide] - if given the robot will try to 'left-click' " ..
  150. "only on the surface as specified by side, otherwise the robot will try all possible sides. " ..
  151. "Returns [true] if the robot could interact with the block or entity, [false] otherwise. " ..
  152. "If successful the secondary parameter describes what the robot interacted with " ..
  153. "and will be one of 'entity', 'block' or 'fire'."
  154. function robotExt.swingAt(side, targetSide, sneaky)
  155.     local backDir = direction
  156.     side = robotExt.rotate(side)
  157.     isSwing, desrciption = component.robot.swing(side, targetSide, sneaky ~= nil and sneaky ~= false)
  158.     robotExt.rotate(backDir, true)
  159.  
  160.     return isSwing, desrciption
  161. end
  162.  
  163. help.detectAt = "function(side:number): bool, string - detects what is directly in [side] of the robot. " ..
  164. "Returns: [true] if the robot if whatever is in [side] of the robot would prevent him from moving " ..
  165. "(a block or an entity), [false] otherwise. The second parameter describes what is in [side] in general " ..
  166. "and is one of either 'entity', 'solid', 'replaceable', 'liquid', 'passable' or 'air'."
  167. function robotExt.detectAt(side)
  168.     local backDir = direction
  169.     side = robotExt.rotate(side)
  170.     local isObstacle, desrciption = component.robot.detect(side)
  171.     robotExt.rotate(backDir, true)
  172.  
  173.     return isObstacle, desrciption
  174. end
  175.  
  176. help.mine = "function(direction:number, distance:number[, isStartSpace:bool]):bool[, number, string] " ..
  177. "- robot try to mine at [distance] of blocks to the [direction] as sides value. " ..
  178. "If [isStartSpace]=true robot will mine relative its rotation at the start, otherwise relative the current rotation. " ..
  179. "Returns [true] if final point has been reached, otherwise [false] and how many blocks has passed and " ..
  180. "describing why moving failed, which will either be 'impossible move', 'not enough energy' or " ..
  181. "the description of the obstacle as robot.detect would return."
  182. function robotExt.mine(direction, distance, isStartSpace)
  183.     local moveSide = robotExt.rotate(direction, isStartSpace)
  184.     local moveVector = moveSide < 2 and side2vector[moveSide] or side2vector[robotExt.getDir()]
  185.     local i, isMoved, reason, isDetect = 0, true, nil, false
  186.     while (i < distance and isMoved) do
  187.         isMoved, reason = component.robot.move(moveSide)
  188.         if (not isMoved) then
  189.             if (robotExt.swingAt(moveSide)) then
  190.                 isDetect, reason = component.robot.detectAt(moveSide)
  191.                 isMoved = not isDetect
  192.             end
  193.         else
  194.             i = i + 1
  195.             pos = pos + moveVector
  196.         end
  197.     end
  198.     if (i < distance) then
  199.         return false, i, reason
  200.     end
  201.     return true
  202. end
  203.  
  204. help.find = "function(filter:string[, warn:bool]):bool, number - scans the blocks around until finds one by filter. " ..
  205. "Returns [true] if found, and side for interaction. Require geolayzer for working. " ..
  206. "If warn is [true] - throws error if geolyzer not available."
  207. function robotExt.find(filter, warn)
  208.     if (not component.isAvailable("geolyzer")) then
  209.         if (warn) then
  210.             error("Error! robotExt.find(filter) require geolyzer for working.")
  211.         end
  212.         return false
  213.     end
  214.     local geo = component.geolyzer
  215.     local function check(side)
  216.         local blockData = geo.analyze(side)
  217.         return blockData ~= nill and string.find(blockData.name, filter) ~= nil
  218.     end
  219.  
  220.     local checkList = { sides.front, sides.down, sides.up }
  221.     for _, v in pairs(checkList) do
  222.         if (check(v)) then
  223.             return true, v
  224.         end
  225.     end
  226.     for i = 1, 3 do
  227.         robotExt.rotate(sides.right)
  228.         if (check(sides.front)) then
  229.             return true, sides.front
  230.         end
  231.     end
  232.  
  233.     robotExt.rotate(sides.right)
  234.     return false
  235. end
  236.  
  237. help.findChest = "function([minSize:number[, onlyEmptyCells:bool]]):bool, number - checks blocks around " ..
  238. "until finds one with inventory size more or equal [minSize], 5 by default. Returns [true] if found one " ..
  239. "and side for interaction with inventory. Requires inventory_controller for working. " ..
  240. "If [onlyEmptyCells] is specified - will found block with empty slots quantity more or equal [minSize]."
  241. function robotExt.findChest(minSize, onlyEmptyCells)
  242.     if (not component.isAvailable("inventory_controller")) then
  243.         print("Error! robotExt.findChest() require inventory_controller for working.")
  244.         return false
  245.     end
  246.     minSize = minSize or 5
  247.     -- minimum size for chest by default
  248.     local ic = component.inventory_controller
  249.     local size = 0
  250.     local function check(side)
  251.         size = ic.getInventorySize(side)
  252.         if (not onlyEmptyCells) then
  253.             return size ~= nill and size >= minSize
  254.         end
  255.         -- count empty slots
  256.         size = size or 0
  257.         local empty = 0
  258.         for i = 1, size do
  259.             empty = ic.getSlotStackSize(side, i) == 0 and(empty + 1) or empty
  260.         end
  261.         return empty >= minSize
  262.     end
  263.  
  264.     local checkList = { sides.front, sides.down, sides.up }
  265.     for _, v in pairs(checkList) do
  266.         if (check(v)) then
  267.             return true, v
  268.         end
  269.     end
  270.     for i = 1, 3 do
  271.         robotExt.rotate(sides.right)
  272.         if (check(sides.front)) then
  273.             return true, sides.front
  274.         end
  275.     end
  276.  
  277.     robotExt.rotate(sides.right)
  278.     return false
  279. end
  280. -- endregion
  281.  
  282. -- region Override the functions of movement from the robot API for the position tracking
  283.  
  284. help.forward = "robot.forward(): boolean[, string] - Tries to move the robot forward. " ..
  285. "Returns: true if the robot successfully moved, nil otherwise. If movement fails a secondary result " ..
  286. "will be returned describing why it failed, which will either be 'impossible move', 'not enough energy' " ..
  287. "or the description of the obstacle as robot.detect would return."
  288. function robotExt.forward()
  289.     local isMoved, _, reason = robotExt.move(sides.front, 1)
  290.     return isMoved, reason
  291. end
  292.  
  293. help.back = "robot.back(): boolean[, string] - As robot.forward() except that the robot tries to move backward."
  294. function robotExt.back()
  295.     -- does not rotate, only moving
  296.     local isMoved, reason = component.robot.move(sides.back)
  297.     if (isMoved) then
  298.         -- update pos
  299.         pos = pos + side2vector[robotExt.transformDirection(sides.back)]
  300.     end
  301.     return isMoved, reason
  302. end
  303.  
  304. help.up = "robot.up(): boolean[, string] - As robot.forward() except that the robot tries to move upwards."
  305. function robotExt.up()
  306.     local isMoved, _, reason = robotExt.move(sides.up, 1)
  307.     return isMoved, reason
  308. end
  309.  
  310. help.down = "robot.down(): boolean[, string] - As robot.forward() except that the robot tries to move downwards."
  311. function robotExt.down()
  312.     local isMoved, _, reason = robotExt.move(sides.down, 1)
  313.     return isMoved, reason
  314. end
  315.  
  316. help.turnLeft = "robot.turnLeft() - Turns the robot 90° to the left. Note that this can only fail " ..
  317. "if the robot has not enough energy to perform the turn but has not yet shut down because of it."
  318. function robotExt.turnLeft()
  319.     robotExt.rotate(sides.left)
  320.     return true
  321. end
  322.  
  323. help.turnRight = "robot.turnRight() - As robot.turnLeft except that the robot turns 90° to the right."
  324. function robotExt.turnRight()
  325.     robotExt.rotate(sides.right)
  326.     return true
  327. end
  328.  
  329. help.turnAround = "robot.turnAround() - This is the same as calling robot.turnRight twice."
  330. function robotExt.turnAround()
  331.     robotExt.rotate(sides.back)
  332.     return true
  333. end
  334. -- endregion
  335.  
  336. -- region Debug
  337. help.reload = "function():robotExt - reload module 'robotExt' from lib folder. Uses only for debug purposes, example: myRobotExt = myRobotExt.reload()."
  338. function robotExt.reload()
  339.     package.loaded["robotExt"] = nil
  340.     _G["robotExt"] = nil
  341.     return require("robotExt")
  342. end
  343.  
  344. help.getInfo = "function(self):string - returns help information for functions of this library."
  345. function robotExt.getInfo(self, ident)
  346.     ident = ident or ""
  347.     result = "{"
  348.     ident = ident .. " "
  349.     newLine = ""
  350.     for n, v in pairs(self) do
  351.         if (type(v) == "table" and(not getmetatable(v) or not getmetatable(v).__tostring)) then
  352.             result = result .. newLine .. n .. "=" .. robotExt.getInfo(v, ident)
  353.         else
  354.             result = result .. newLine .. n .. "=" .. tostring(v)
  355.         end
  356.         newLine = ",\n" .. ident
  357.     end
  358.     result = result .. "}"
  359.     return result
  360. end
  361.  
  362. local function dropHelp(table)
  363.     -- remove help texts
  364.     for n, v in pairs(table) do
  365.         if (type(v) == "table") then
  366.             robotExt.dropHelp(v)
  367.         end
  368.     end
  369.     local mt = getmetatable(table)
  370.     if (mt) then
  371.         mt.__tostring = nil
  372.     end
  373. end
  374.  
  375. help.dropHelp = "function(self) - removes help texts, all meta __tostring() functions will be nil, uses GC at the end."
  376. function robotExt.dropHelp(self)
  377.     dropHelp(self)
  378.     os.sleep(0)
  379.     -- collectgarbage("collect")
  380. end
  381. -- endregion
  382.  
  383. -- region Help wrapper
  384. local function wrapFn(fn, desc)
  385.     return setmetatable( { }, {
  386.         __call = function(_, ...) return fn(...) end,
  387.         __tostring = function() return desc end
  388.     } )
  389. end
  390.  
  391. local function wrapTable(table, helpTable)
  392.     if (type(helpTable) ~= "table") then
  393.         print("Error! HelpTable must have same structure like the wrap table")
  394.         return
  395.     end
  396.     local mt
  397.     -- temp metatable
  398.     for n, v in pairs(table) do
  399.         if (type(v) == "table") then
  400.             if (type(helpTable[n]) == "table") then
  401.                 wrapTable(v, helpTable[n])
  402.             end
  403.             if (type(helpTable[n]) == "string") then
  404.                 mt = getmetatable(v)
  405.                 if (not mt) then
  406.                     mt = { }
  407.                     setmetatable(v, mt)
  408.                 end
  409.                 mt.__tostring = function() return helpTable[n] end
  410.             end
  411.         elseif (type(v) == "function") then
  412.             if (type(helpTable[n]) == "string") then
  413.                 table[n] = wrapFn(v, helpTable[n])
  414.             end
  415.         end
  416.     end
  417. end
  418.  
  419. wrapTable(robotExt, help)
  420.  
  421. -- endregion
  422.  
  423. return robotExt
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement