opus/sys/extensions/tl3.lua

863 lines
19 KiB
Lua

if not turtle then
return
end
local function noop() end
turtle.point = { x = 0, y = 0, z = 0, heading = 0 }
turtle.status = 'idle'
turtle.abort = false
function turtle.getPoint()
return turtle.point
end
local state = {
moveAttack = noop,
moveDig = noop,
moveCallback = noop,
locations = {},
coordSystem = 'relative', -- type of coordinate system being used
}
function turtle.getState()
return state
end
function turtle.setPoint(pt)
turtle.point.x = pt.x
turtle.point.y = pt.y
turtle.point.z = pt.z
if pt.heading then
turtle.point.heading = pt.heading
end
return true
end
function turtle.reset()
turtle.point.x = 0
turtle.point.y = 0
turtle.point.z = 0
turtle.point.heading = 0
turtle.abort = false -- should be part of state
--turtle.status = 'idle' -- should be part of state
state.moveAttack = noop
state.moveDig = noop
state.moveCallback = noop
state.locations = {}
state.coordSystem = 'relative'
return true
end
local actions = {
up = {
detect = turtle.native.detectUp,
dig = turtle.native.digUp,
move = turtle.native.up,
attack = turtle.native.attackUp,
place = turtle.native.placeUp,
drop = turtle.native.dropUp,
suck = turtle.native.suckUp,
compare = turtle.native.compareUp,
inspect = turtle.native.inspectUp,
side = 'top'
},
down = {
detect = turtle.native.detectDown,
dig = turtle.native.digDown,
move = turtle.native.down,
attack = turtle.native.attackDown,
place = turtle.native.placeDown,
drop = turtle.native.dropDown,
suck = turtle.native.suckDown,
compare = turtle.native.compareDown,
inspect = turtle.native.inspectDown,
side = 'bottom'
},
forward = {
detect = turtle.native.detect,
dig = turtle.native.dig,
move = turtle.native.forward,
attack = turtle.native.attack,
place = turtle.native.place,
drop = turtle.native.drop,
suck = turtle.native.suck,
compare = turtle.native.compare,
inspect = turtle.native.inspect,
side = 'front'
},
back = {
detect = noop,
dig = noop,
move = turtle.native.back,
attack = noop,
place = noop,
suck = noop,
compare = noop,
side = 'back'
},
}
function turtle.getAction(direction)
return actions[direction]
end
-- [[ Heading data ]] --
local headings = {
[ 0 ] = { xd = 1, zd = 0, yd = 0, heading = 0, direction = 'east' },
[ 1 ] = { xd = 0, zd = 1, yd = 0, heading = 1, direction = 'south' },
[ 2 ] = { xd = -1, zd = 0, yd = 0, heading = 2, direction = 'west' },
[ 3 ] = { xd = 0, zd = -1, yd = 0, heading = 3, direction = 'north' },
[ 4 ] = { xd = 0, zd = 0, yd = 1, heading = 4, direction = 'up' },
[ 5 ] = { xd = 0, zd = 0, yd = -1, heading = 5, direction = 'down' }
}
local namedHeadings = {
east = headings[0],
south = headings[1],
west = headings[2],
north = headings[3],
up = headings[4],
down = headings[5]
}
function turtle.getHeadings()
return headings
end
function turtle.getHeadingInfo(heading)
if heading and type(heading) == 'string' then
return namedHeadings[heading]
end
heading = heading or turtle.point.heading
return headings[heading]
end
-- [[ Basic turtle actions ]] --
local function _attack(action)
if action.attack() then
repeat until not action.attack()
return true
end
return false
end
function turtle.attack() return _attack(actions.forward) end
function turtle.attackUp() return _attack(actions.up) end
function turtle.attackDown() return _attack(actions.down) end
local function _place(action, indexOrId)
local slot
if indexOrId then
slot = turtle.getSlot(indexOrId)
if not slot then
return false, 'No items to place'
end
end
if slot and slot.qty == 0 then
return false, 'No items to place'
end
return Util.tryTimes(3, function()
if slot then
turtle.select(slot.index)
end
local result = { action.place() }
if result[1] then
return true
end
if not state.moveDig(action) then
state.moveAttack(action)
end
return unpack(result)
end)
end
function turtle.place(slot) return _place(actions.forward, slot) end
function turtle.placeUp(slot) return _place(actions.up, slot) end
function turtle.placeDown(slot) return _place(actions.down, slot) end
local function _drop(action, count, indexOrId)
if indexOrId then
local slot = turtle.getSlot(indexOrId)
if not slot or slot.qty == 0 then
return false, 'No items to drop'
end
turtle.select(slot.index)
end
if not count then
return action.drop() -- wtf
end
return action.drop(count)
end
function turtle.drop(count, slot) return _drop(actions.forward, count, slot) end
function turtle.dropUp(count, slot) return _drop(actions.up, count, slot) end
function turtle.dropDown(count, slot) return _drop(actions.down, count, slot) end
--[[
function turtle.dig() return state.dig(actions.forward) end
function turtle.digUp() return state.dig(actions.up) end
function turtle.digDown() return state.dig(actions.down) end
--]]
function turtle.isTurtleAtSide(side)
local sideType = peripheral.getType(side)
return sideType and sideType == 'turtle'
end
turtle.attackPolicies = {
none = noop,
attack = function(action)
return _attack(action)
end,
}
turtle.digPolicies = {
none = noop,
dig = function(action)
return action.dig()
end,
turtleSafe = function(action)
if action.side == 'back' then
return false
end
if not turtle.isTurtleAtSide(action.side) then
return action.dig()
end
return Util.tryTimes(6, function()
-- if not turtle.isTurtleAtSide(action.side) then
-- return true --action.dig()
-- end
os.sleep(.25)
if not action.detect() then
return true
end
end)
end,
digAndDrop = function(action)
if action.detect() then
local slots = turtle.getInventory()
if action.dig() then
turtle.reconcileInventory(slots)
return true
end
end
return false
end
}
turtle.policies = {
none = { dig = turtle.digPolicies.none, attack = turtle.attackPolicies.none },
digOnly = { dig = turtle.digPolicies.dig, attack = turtle.attackPolicies.none },
attackOnly = { dig = turtle.digPolicies.none, attack = turtle.attackPolicies.attack },
digAttack = { dig = turtle.digPolicies.dig, attack = turtle.attackPolicies.attack },
turtleSafe = { dig = turtle.digPolicies.turtleSafe, attack = turtle.attackPolicies.attack },
}
function turtle.setPolicy(policy)
if type(policy) == 'string' then
policy = turtle.policies[policy]
end
if not policy then
return false, 'Invalid policy'
end
state.moveDig = policy.dig
state.moveAttack = policy.attack
return true
end
function turtle.setDigPolicy(policy)
state.moveDig = policy
end
function turtle.setAttackPolicy(policy)
state.moveAttack = policy
end
function turtle.setMoveCallback(cb)
state.moveCallback = cb
end
function turtle.clearMoveCallback()
state.moveCallback = noop
end
local function infoMoveCallback()
local pt = turtle.point
print(string.format('x:%d y:%d z:%d heading:%d', pt.x, pt.y, pt.z, pt.heading))
end
-- TESTING
--turtle.setMoveCallback(infoMoveCallback)
-- [[ Locations ]] --
function turtle.getLocation(name)
return state.locations[name]
end
function turtle.saveLocation(name, pt)
pt = pt or turtle.point
state.locations[name] = { x = pt.x, y = pt.y, z = pt.z }
end
function turtle.gotoLocation(name)
local pt = turtle.getLocation(name)
if pt then
return turtle.goto(pt.x, pt.z, pt.y, pt.heading)
end
end
function turtle.storeLocation(name, pt)
pt = pt or turtle.point
Util.writeTable(name .. '.pt', pt)
return true
end
function turtle.loadLocation(name)
return Util.readTable(name .. '.pt')
end
function turtle.gotoStoredLocation(name)
local pt = turtle.loadLocation(name)
if pt then
return turtle.gotoPoint(pt)
end
end
-- [[ Heading ]] --
function turtle.getHeading()
return turtle.point.heading
end
function turtle.turnRight()
turtle.setHeading(turtle.point.heading + 1)
return turtle.point
end
function turtle.turnLeft()
turtle.setHeading(turtle.point.heading - 1)
return turtle.point
end
function turtle.turnAround()
turtle.setHeading(turtle.point.heading + 2)
return turtle.point
end
function turtle.setNamedHeading(headingName)
local headingInfo = namedHeadings[headingName]
if headingInfo then
return turtle.setHeading(headingInfo.heading)
end
return false, 'Invalid heading'
end
function turtle.setHeading(heading)
if not heading then
return
end
heading = heading % 4
if heading ~= turtle.point.heading then
while heading < turtle.point.heading do
heading = heading + 4
end
if heading - turtle.point.heading == 3 then
turtle.native.turnLeft()
turtle.point.heading = turtle.point.heading - 1
else
local turns = heading - turtle.point.heading
while turns > 0 do
turns = turns - 1
turtle.point.heading = turtle.point.heading + 1
turtle.native.turnRight()
end
end
turtle.point.heading = turtle.point.heading % 4
state.moveCallback('turn', turtle.point)
end
return turtle.point
end
function turtle.headTowardsX(dx)
if turtle.point.x ~= dx then
if turtle.point.x > dx then
turtle.setHeading(2)
else
turtle.setHeading(0)
end
end
end
function turtle.headTowardsZ(dz)
if turtle.point.z ~= dz then
if turtle.point.z > dz then
turtle.setHeading(3)
else
turtle.setHeading(1)
end
end
end
function turtle.headTowards(pt)
local xd = math.abs(turtle.point.x - pt.x)
local zd = math.abs(turtle.point.z - pt.z)
if xd > zd then
turtle.headTowardsX(pt.x)
else
turtle.headTowardsZ(pt.z)
end
end
-- [[ move ]] --
local function _move(action)
while not action.move() do
if not state.moveDig(action) and not state.moveAttack(action) then
return false
end
end
return true
end
function turtle.up()
if _move(actions.up) then
turtle.point.y = turtle.point.y + 1
state.moveCallback('up', turtle.point)
return true, turtle.point
end
end
function turtle.down()
if _move(actions.down) then
turtle.point.y = turtle.point.y - 1
state.moveCallback('down', turtle.point)
return true, turtle.point
end
end
function turtle.forward()
if _move(actions.forward) then
turtle.point.x = turtle.point.x + headings[turtle.point.heading].xd
turtle.point.z = turtle.point.z + headings[turtle.point.heading].zd
state.moveCallback('forward', turtle.point)
return true, turtle.point
end
end
function turtle.back()
if _move(actions.back) then
turtle.point.x = turtle.point.x - headings[turtle.point.heading].xd
turtle.point.z = turtle.point.z - headings[turtle.point.heading].zd
state.moveCallback('back', turtle.point)
return true, turtle.point
end
end
function turtle.moveTowardsX(dx)
local direction = dx - turtle.point.x
local move
if direction == 0 then
return true
end
if direction > 0 and turtle.point.heading == 0 or
direction < 0 and turtle.point.heading == 2 then
move = turtle.forward
else
move = turtle.back
end
repeat
if not move() then
return false
end
until turtle.point.x == dx
return true
end
function turtle.moveTowardsZ(dz)
local direction = dz - turtle.point.z
local move
if direction == 0 then
return true
end
if direction > 0 and turtle.point.heading == 1 or
direction < 0 and turtle.point.heading == 3 then
move = turtle.forward
else
move = turtle.back
end
repeat
if not move() then
return false
end
until turtle.point.z == dz
return true
end
-- [[ go ]] --
-- 1 turn goto (going backwards if possible)
function turtle.gotoSingleTurn(dx, dz, dy, dh)
dy = dy or turtle.point.y
local function gx()
if turtle.point.x ~= dx then
turtle.moveTowardsX(dx)
end
if turtle.point.z ~= dz then
if dh and dh % 2 == 1 then
turtle.setHeading(dh)
else
turtle.headTowardsZ(dz)
end
end
end
local function gz()
if turtle.point.z ~= dz then
turtle.moveTowardsZ(dz)
end
if turtle.point.x ~= dx then
if dh and dh % 2 == 0 then
turtle.setHeading(dh)
else
turtle.headTowardsX(dx)
end
end
end
repeat
local x, z
local y = turtle.point.y
repeat
x, z = turtle.point.x, turtle.point.z
if turtle.point.heading % 2 == 0 then
gx()
gz()
else
gz()
gx()
end
until x == turtle.point.x and z == turtle.point.z
if turtle.point.y ~= dy then
turtle.gotoY(dy)
end
if turtle.point.x == dx and turtle.point.z == dz and turtle.point.y == dy then
return true
end
until x == turtle.point.x and z == turtle.point.z and y == turtle.point.y
return false
end
local function gotoEx(dx, dz, dy)
-- determine the heading to ensure the least amount of turns
-- first check is 1 turn needed - remaining require 2 turns
if turtle.point.heading == 0 and turtle.point.x <= dx or
turtle.point.heading == 2 and turtle.point.x >= dx or
turtle.point.heading == 1 and turtle.point.z <= dz or
turtle.point.heading == 3 and turtle.point.z >= dz then
-- maintain current heading
-- nop
elseif dz > turtle.point.z and turtle.point.heading == 0 or
dz < turtle.point.z and turtle.point.heading == 2 or
dx < turtle.point.x and turtle.point.heading == 1 or
dx > turtle.point.x and turtle.point.heading == 3 then
turtle.turnRight()
else
turtle.turnLeft()
end
if (turtle.point.heading % 2) == 1 then
if not turtle.gotoZ(dz) then return false end
if not turtle.gotoX(dx) then return false end
else
if not turtle.gotoX(dx) then return false end
if not turtle.gotoZ(dz) then return false end
end
if dy then
if not turtle.gotoY(dy) then return false end
end
return true
end
-- fallback goto - will turn around if was previously moving backwards
local function gotoMultiTurn(dx, dz, dy)
if gotoEx(dx, dz, dy) then
return true
end
local moved
repeat
local x, y, z = turtle.point.x, turtle.point.y, turtle.point.z
-- try going the other way
if (turtle.point.heading % 2) == 1 then
turtle.headTowardsX(dx)
else
turtle.headTowardsZ(dz)
end
if gotoEx(dx, dz, dy) then
return true
end
if dy then
turtle.gotoY(dy)
end
moved = x ~= turtle.point.x or y ~= turtle.point.y or z ~= turtle.point.z
until not moved
return false
end
function turtle.gotoPoint(pt)
return turtle.goto(pt.x, pt.z, pt.y, pt.heading)
end
-- go backwards - turning around if necessary to fight mobs / break blocks
function turtle.goback()
local hi = headings[turtle.point.heading]
return turtle.goto(turtle.point.x - hi.xd, turtle.point.z - hi.zd, turtle.point.y, turtle.point.heading)
end
function turtle.gotoYfirst(pt)
if turtle.gotoY(pt.y) then
if turtle.goto(pt.x, pt.z, nil, pt.heading) then
turtle.setHeading(pt.heading)
return true
end
end
end
function turtle.gotoYlast(pt)
if turtle.goto(pt.x, pt.z, nil, pt.heading) then
if turtle.gotoY(pt.y) then
turtle.setHeading(pt.heading)
return true
end
end
end
function turtle.goto(dx, dz, dy, dh)
if not turtle.gotoSingleTurn(dx, dz, dy, dh) then
if not gotoMultiTurn(dx, dz, dy) then
return false
end
end
turtle.setHeading(dh)
return true
end
function turtle.gotoX(dx)
turtle.headTowardsX(dx)
while turtle.point.x ~= dx do
if not turtle.forward() then
return false
end
end
return true
end
function turtle.gotoZ(dz)
turtle.headTowardsZ(dz)
while turtle.point.z ~= dz do
if not turtle.forward() then
return false
end
end
return true
end
function turtle.gotoY(dy)
while turtle.point.y > dy do
if not turtle.down() then
return false
end
end
while turtle.point.y < dy do
if not turtle.up() then
return false
end
end
return true
end
-- [[ Slot management ]] --
function turtle.getSlot(indexOrId, slots)
if type(indexOrId) == 'string' then
slots = slots or turtle.getInventory()
local _,c = string.gsub(indexOrId, ':', '')
if c == 2 then -- combined id and dmg .. ie. minecraft:coal:0
return Util.find(slots, 'iddmg', indexOrId)
end
return Util.find(slots, 'id', indexOrId)
end
local detail = turtle.getItemDetail(indexOrId)
if detail then
return {
qty = detail.count,
dmg = detail.damage,
id = detail.name,
iddmg = detail.name .. ':' .. detail.damage,
index = indexOrId,
}
end
return {
qty = 0,
index = indexOrId,
}
end
function turtle.selectSlot(indexOrId)
local s = turtle.getSlot(indexOrId)
if s then
turtle.select(s.index)
return s
end
return false, 'Inventory does not contain item'
end
function turtle.getInventory(slots)
slots = slots or { }
for i = 1, 16 do
slots[i] = turtle.getSlot(i)
end
return slots
end
function turtle.emptyInventory(dropAction)
dropAction = dropAction or turtle.drop
for i = 1, 16 do
turtle.emptySlot(i, dropAction)
end
end
function turtle.emptySlot(slot, dropAction)
dropAction = dropAction or turtle.drop
local count = turtle.getItemCount(slot)
if count > 0 then
turtle.select(slot)
return dropAction(count)
end
return false, 'No items to drop'
end
function turtle.getFilledSlots(startSlot)
startSlot = startSlot or 1
local slots = { }
for i = startSlot, 16 do
local count = turtle.getItemCount(i)
if count > 0 then
slots[i] = turtle.getSlot(i)
end
end
return slots
end
function turtle.eachFilledSlot(fn)
local slots = turtle.getFilledSlots()
for _,slot in pairs(slots) do
fn(slot)
end
end
function turtle.reconcileInventory(slots, dropAction)
dropAction = dropAction or turtle.native.drop
for _,s in pairs(slots) do
local qty = turtle.getItemCount(s.index)
if qty > s.qty then
turtle.select(s.index)
dropAction(qty-s.qty, s)
end
end
end
function turtle.selectSlotWithItems(startSlot)
startSlot = startSlot or 1
for i = startSlot, 16 do
if turtle.getItemCount(i) > 0 then
turtle.select(i)
return i
end
end
end
function turtle.selectOpenSlot(startSlot)
return turtle.selectSlotWithQuantity(0, startSlot)
end
function turtle.selectSlotWithQuantity(qty, startSlot)
startSlot = startSlot or 1
for i = startSlot, 16 do
if turtle.getItemCount(i) == qty then
turtle.select(i)
return i
end
end
end
function turtle.condense(startSlot)
startSlot = startSlot or 1
local aslots = turtle.getInventory()
for _,slot in ipairs(aslots) do
if slot.qty < 64 then
for i = slot.index + 1, 16 do
local fslot = aslots[i]
if fslot.qty > 0 then
if slot.qty == 0 or slot.iddmg == fslot.iddmg then
turtle.select(fslot.index)
turtle.transferTo(slot.index, 64)
local transferred = turtle.getItemCount(slot.index) - slot.qty
slot.qty = slot.qty + transferred
fslot.qty = fslot.qty - transferred
slot.iddmg = fslot.iddmg
if slot.qty == 64 then
break
end
end
end
end
end
end
end