Created
October 17, 2012 10:36
-
-
Save Xavura/3904888 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- XAPI v0.1 | |
-- A turtle API | |
-- By Xavura | |
--[[ | |
TODO: | |
* Figure out if we can reliably overwrite turtle methods with our own. | |
]]-- | |
local _turtle = {} | |
local backups = {} | |
local methods = { | |
'craft', | |
'turnLeft', 'turnRight', | |
'refuel', 'getFuelLevel', | |
'dig', 'digUp', 'digDown', | |
'drop', 'dropUp', 'dropDown', | |
'suck', 'suckUp', 'suckDown', | |
'up', 'down', 'back', 'forward', | |
'place', 'placeUp', 'placeDown', | |
'attack', 'attackUp', 'attackDown', | |
'detect', 'detectUp', 'detectDown', | |
'select', 'getItemCount', 'getItemSpace', | |
'compare', 'compareUp', 'compareDown', 'compareTo' | |
} | |
for k, v in ipairs(methods) do | |
backups[v] = turtle[v] | |
end | |
local _turtleMeta = { | |
__index = function(tbl, key) | |
if backups[key] then | |
return backups[key] | |
end | |
end | |
} | |
setmetatable(_turtle, _turtleMeta) | |
-- Constants | |
local DEBUG = true | |
local VERSION = {0, 1, 0} | |
local NORTH = 1 | |
local EAST = 2 | |
local SOUTH = 3 | |
local WEST = 4 | |
local UP = 5 | |
local DOWN = 6 | |
local UPDATE_IDENT = '3924697' | |
local SOURCE_IDENT = '3904888' | |
local DIRECTIONS = {[1]="north", [2]="east", | |
[3]="south", [4]="west", | |
[5]="up", [6]="down"} | |
local GPS_TIMEOUT = 3 | |
--[[ | |
local MOVE_MODES = { | |
REMOVE_BLOCKS = 1, -- Remove BLOCKS that block path(s) | |
ATTACK_ENTITIES = 2, -- Attack ENTITIES that block path(s) | |
FIND_ALTERNATE = 4 -- Attempt to find an alternate route that avoids blocks and entities. | |
} | |
local MOVE_OPTIONS = { | |
REMOVE_ATTEMPTS = 2, -- Attempts before giving up removal (because e.g. Bedrock) | |
ATTACK_ATTEMPTS = 6, -- Attempts before giving up attacking | |
REMOVE_ONLY = {} -- Remove only blocks in these inventory slots. | |
} | |
--]] | |
-- Math Helpers | |
function vEqual(v1, v2) | |
return v1[1] == v2[1] and v1[2] == v2[2] and v1[3] == v2[3] | |
end | |
-- State | |
local position = {nil, nil, nil} | |
local direction = nil | |
--local movementMode = bit.band(MOVE_MODE['REMOVE_BLOCKS'], MOVE_MODE['ATTACK_ENTITIES']) | |
-- State Access | |
function getPos() | |
return position | |
end | |
function getDir() | |
return direction | |
end | |
-- Debugging | |
function debugVec(vec, str) | |
str = str or "Vector" | |
if type(vec) ~= "table" then | |
return print(str..": Not a vector.") | |
end | |
print(str..": "..vec[1]..", "..vec[2]..", "..vec[3]) | |
end | |
function debugPos(pos) | |
return debugVec(position, "Position") | |
end | |
function debugDir(dir) | |
if dir == nil then | |
dir = direction | |
end | |
if DIRECTIONS[dir] then | |
print("Direction: "..DIRECTIONS[dir]) | |
else | |
print("Direction: INVALID ("..dir..")") | |
end | |
end | |
-- Setup | |
local function locate() | |
rednet.open("right") | |
local cX, cY, cZ = gps.locate(GPS_TIMEOUT) | |
rednet.close("right") | |
if cX == nil then | |
return false | |
end | |
return {cX, cY, cZ} | |
end | |
local function determineCardinalDirection() | |
_turtle.forward() | |
local facing = 0 | |
local newPosition = locate() | |
if newPosition == false then | |
print("Fatal: Unable to connect to GPS @ determineCardinalDirection()") | |
return shell.exit() | |
end | |
if newPosition[1] > position[1] then | |
facing = EAST | |
elseif newPosition[1] < position[1] then | |
facing = WEST | |
elseif newPosition[3] > position[3] then | |
facing = SOUTH | |
elseif newPosition[3] < position[3] then | |
facing = NORTH | |
end | |
_turtle.back() | |
--for i = 1, 2 do turtle.turnRight() end | |
--turtle.forward() | |
--for i = 1, 2 do turtle.turnLeft() end | |
return facing | |
end | |
local function locateManually() | |
local pos = {} | |
local dir = nil | |
local tmp = nil | |
local line = 1 | |
term.clear() | |
term.setCursorPos(1, 1) | |
textutils.slowPrint('Unable to connect to GPS network!') | |
textutils.slowPrint('Please enter turtle coordinates.') | |
textutils.slowPrint('Note: F3 Y shows head position.') | |
textutils.slowPrint('Subtract 1 to get foot position!') | |
os.sleep(2) | |
term.clear() | |
for i = 1, 3 do | |
repeat | |
term.clearLine() | |
term.setCursorPos(1, line) | |
print(string.char(i+119)..': ') | |
term.setCursorPos(4, line) | |
tmp = tonumber(io.read()) | |
if tmp == nil then | |
term.setCursorPos(4, line) | |
term.write('Invalid input, please try again.') | |
os.sleep(1) | |
end | |
until tmp ~= nil | |
table.insert(pos, math.floor(tmp)) | |
line = line + 1 | |
end | |
term.clear() | |
term.setCursorPos(1, 1) | |
textutils.slowPrint('Please enter direction.') | |
textutils.slowPrint('Use debug (F3) screen, F value.') | |
os.sleep(2) | |
term.clear() | |
term.setCursorPos(1, 1) | |
repeat | |
print('f: ') | |
term.setCursorPos(4, 1) | |
tmp = tonumber(io.read()) | |
if tmp == nil then | |
term.setCursorPos(4, 1) | |
term.write('Invalid input, please try again.') | |
os.sleep(1) | |
end | |
until tmp >= 0 and tmp <= 4 | |
-- Convert F value 2, 3, 0, 1 -> 1, 2, 3, 4 | |
dir = math.ceil( (tmp + 3) % 4.1) | |
return pos, dir | |
end | |
local function setup() | |
position = locate() | |
if position == false then | |
position, direction = locateManually() | |
else | |
direction = determineCardinalDirection() | |
end | |
debugPos() | |
debugDir() | |
end | |
local function updateCheck() | |
local request = http.get('https://raw.github.com/gist/'..UPDATE_IDENT) | |
local response = nil | |
local version = nil | |
term.clear() | |
term.setCursorPos(1, 1) | |
if request then | |
response = request.readAll() | |
request.close() | |
version = (function() | |
local result = {} | |
string.gsub(response, '([^,]+)', function(m) table.insert(result, tonumber(m)) end) | |
return result | |
end)() | |
print('Current Version: v'..table.concat(VERSION, '.')) | |
if version[1] > VERSION[1] or version[2] > VERSION[2] or version[3] > VERSION[3] then | |
print('A new version (v'..table.concat(version, '.')..') is available!') | |
print('Update now (y/n): ') | |
term.setCursorPos(19, 3) | |
if io.read() == 'y' then | |
shell.run('gist', 'get', 'xapi.tmp', SOURCE_IDENT) | |
os.unloadAPI('xapi') | |
shell.run('rm', 'xapi') | |
shell.run('mv', 'xapi.tmp', 'xapi') | |
os.loadAPI('xapi') | |
end | |
else | |
print('You are running the latest version.') | |
end | |
else | |
print('Error: Unable to check for updates.') | |
end | |
end | |
-- Turning | |
local function updateDirection(change) | |
direction = direction + change | |
if direction == 0 then | |
direction = 4 | |
elseif direction == 5 then | |
direction = 1 | |
end | |
end | |
function turnLeft(count) | |
count = count or 1 | |
if count > 1 then | |
for i = 1, count do | |
turnLeft(1) | |
end | |
end | |
_turtle.turnLeft() | |
updateDirection(-1) | |
end | |
function turnRight() | |
count = count or 1 | |
if count > 1 then | |
for i = 1, count do | |
turnRight(1) | |
end | |
end | |
_turtle.turnRight() | |
updateDirection(1) | |
end | |
local function faceDirection(dir) | |
if dir == direction then | |
return | |
end | |
local reversed = false | |
if dir - direction == 3 or direction - dir == 3 then | |
reversed = true | |
end | |
while true do | |
if dir > direction then | |
if reversed == false then | |
turnRight() | |
else | |
turnLeft() | |
end | |
else | |
if reversed == false then | |
turnLeft() | |
else | |
turnRight() | |
end | |
end | |
if dir == direction then | |
break | |
end | |
end | |
--debugDir() | |
end | |
function faceNorth() | |
return faceDirection(1) | |
end | |
function faceEast() | |
return faceDirection(2) | |
end | |
function faceSouth() | |
return faceDirection(3) | |
end | |
function faceWest() | |
return faceDirection(4) | |
end | |
-- Basic Movement | |
local function updatePosition(backwards) | |
--backwards = backwards or false | |
local dir = direction | |
local index = 0 | |
local change = 1 | |
--[[if backwards then | |
dir = (dir + 2) % 4 | |
if dir == 0 then | |
dir = 4 | |
end | |
end]]-- | |
if dir == 1 or dir == 4 then | |
change = -1 | |
end | |
if dir == 2 or dir == 4 then | |
index = 1 | |
else | |
index = 3 | |
end | |
position[index] = position[index] + change | |
--debugPos() | |
end | |
function up(count, force) | |
count = count or 1 | |
force = force or false | |
if count > 1 then | |
for i = 1, count do | |
up(1, force) | |
end | |
return | |
end | |
if force and _turtle.detectUp() then | |
_turtle.digUp() | |
end | |
local success = _turtle.up() | |
if success then | |
position[2] = position[2] + 1 | |
end | |
return success | |
end | |
function down(count, force) | |
count = count or 1 | |
force = force or false | |
if count > 1 then | |
for i = 1, count do | |
down(1, force) | |
end | |
return | |
end | |
if force and _turtle.detectDown() then | |
_turtle.digDown() | |
end | |
local success = _turtle.down() | |
if success then | |
position[2] = position[2] - 1 | |
end | |
return success | |
end | |
function forward(count, force) | |
count = count or 1 | |
force = force or false | |
if count > 1 then | |
for i = 1, count do | |
forward(1, force) | |
end | |
return | |
end | |
local dug = nil | |
if force and _turtle.detect() then | |
dug = _turtle.dig() | |
end | |
local success = _turtle.forward() | |
if not success then | |
repeat | |
_turtle.attack() | |
success = _turtle.forward() | |
until success | |
end | |
if success then | |
updatePosition() | |
end | |
return success | |
end | |
function back(count, force) | |
count = count or 1 | |
force = force or false | |
if count > 1 then | |
for i = 1, count do | |
back(1, force) | |
end | |
return | |
end | |
--[[ Meh solution: | |
if force then | |
rotate(2) | |
if turtle.detect() then | |
turtle.dig() | |
end | |
rotate(2) | |
end | |
--]] | |
local success = _turtle.back() | |
if success then | |
updatePosition(true) | |
end | |
return success | |
end | |
-- Advanced Movement | |
local function getMovementVector(movement) | |
local vector = {0, 0, 0} | |
if movement[1] > 0 then | |
vector[1] = WEST | |
else | |
vector[1] = EAST | |
end | |
if movement[2] > 0 then | |
vector[2] = DOWN | |
else | |
vector[2] = UP | |
end | |
if movement[3] > 0 then | |
vector[3] = NORTH | |
else | |
vector[3] = SOUTH | |
end | |
return vector | |
end | |
function move(target, force) -- Relative movement. | |
end | |
function goto(target, force) -- Absolute movement. | |
local move = nil | |
local force = force or false | |
local moves = {position[1] - target[1], | |
position[2] - target[2], | |
position[3] - target[3]} | |
local movement = getMovementVector(moves) | |
--print("Face "..DIRECTIONS[movement[1]].." then move "..math.abs(moves[1])) | |
--print("Move "..DIRECTIONS[movement[2]].." "..math.abs(moves[2]).. " times") | |
--print("Face "..DIRECTIONS[movement[3]].." then move "..math.abs(moves[3])) | |
if movement[1] == WEST then | |
faceWest() | |
else | |
faceEast() | |
end | |
for i = 1, math.abs(moves[1]) do | |
move = forward(1, force) | |
if not move then | |
return print("Aborted in east-west loop.") | |
end | |
end | |
for i = 1, math.abs(moves[2]) do | |
if movement[2] == UP then | |
move = up(1, force) | |
else | |
move = down(1, force) | |
end | |
if not move then | |
return print("Aborted in up-down loop.") | |
end | |
end | |
if movement[3] == NORTH then | |
faceNorth() | |
else | |
faceSouth() | |
end | |
for i = 1, math.abs(moves[3]) do | |
move = forward(1, force) | |
if not move then | |
return print("Aborted in north-south loop.") | |
end | |
end | |
end | |
if shell ~= nil then | |
local args = {...} | |
if args[1] == 'update' then | |
updateCheck() | |
end | |
else | |
setup() | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment