treefarm + turtle improvements + cleanup

This commit is contained in:
kepler155c@gmail.com 2017-09-12 23:04:44 -04:00
parent e50e6da700
commit 9aca96cc3e
21 changed files with 3652 additions and 1190 deletions

View File

@ -1,5 +1,3 @@
local Util = require('util')
local Event = {
uid = 1, -- unique id for handlers
routines = { }, -- coroutines
@ -26,7 +24,7 @@ end
function Routine:resume(event, ...)
if not self.co then
error('Cannot resume a dead routine\n' .. Util.tostring(self))
error('Cannot resume a dead routine')
end
if not self.filter or self.filter == event or event == "terminate" then
@ -41,7 +39,7 @@ function Routine:resume(event, ...)
end
if not s and event ~= 'terminate' then
error('\n' .. (m or 'Error processing event') .. '\n' .. Util.tostring(self))
error('\n' .. (m or 'Error processing event'))
end
return s, m

View File

@ -1,3 +1,5 @@
local Util = require('util')
local Point = { }
function Point.copy(pt)
@ -10,6 +12,14 @@ function Point.same(pta, ptb)
pta.z == ptb.z
end
function Point.above(pt)
return { x = pt.x, y = pt.y + 1, z = pt.z, heading = pt.heading }
end
function Point.below(pt)
return { x = pt.x, y = pt.y - 1, z = pt.z, heading = pt.heading }
end
function Point.subtract(a, b)
a.x = a.x - b.x
a.y = a.y - b.y
@ -123,30 +133,17 @@ function Point.closest(reference, pts)
return lpt
end
-- find the closest block
-- * favor same plane
-- * going backwards only if the dest is above or below
function Point.closest2(reference, pts)
local lpt, lm -- lowest
for _,pt in pairs(pts) do
local m = Point.turtleDistance(reference, pt)
local h = Point.calculateHeading(reference, pt)
local t = Point.calculateTurns(reference.heading, h)
if pt.y ~= reference.y then -- try and stay on same plane
m = m + .01
end
if t ~= 2 or pt.y == reference.y then
m = m + t
if t > 0 then
m = m + .01
end
end
if not lm or m < lm then
lpt = pt
lm = m
function Point.eachClosest(spt, ipts, fn)
local pts = Util.shallowCopy(ipts)
while #pts > 0 do
local pt = Point.closest(spt, pts)
local r = fn(pt)
if r then
return r
end
Util.removeByValue(pts, pt)
end
return lpt
end
function Point.adjacentPoints(pt)
@ -159,26 +156,28 @@ function Point.adjacentPoints(pt)
return pts
end
return Point
--[[
function Point.toBox(pt, width, length, height)
return { ax = pt.x,
ay = pt.y,
az = pt.z,
bx = pt.x + width - 1,
by = pt.y + height - 1,
bz = pt.z + length - 1
}
function Point.normalizeBox(box)
return {
x = math.min(box.x, box.ex),
y = math.min(box.y, box.ey),
z = math.min(box.z, box.ez),
ex = math.max(box.x, box.ex),
ey = math.max(box.y, box.ey),
ez = math.max(box.z, box.ez),
}
end
function Point.inBox(pt, box)
return pt.x >= box.ax and
pt.z >= box.az and
pt.x <= box.bx and
pt.z <= box.bz
return pt.x >= box.x and
pt.y >= box.y and
pt.z >= box.z and
pt.x <= box.ex and
pt.z <= box.ez
end
return Point
--[[
Box = { }
function Box.contain(boundingBox, containedBox)

View File

@ -1,121 +0,0 @@
local Util = require('util')
local Process = { }
function Process:init(args)
self.args = { }
self.uid = 0
self.threads = { }
Util.merge(self, args)
self.name = self.name or 'Thread:' .. self.uid
end
function Process:isDead()
return coroutine.status(self.co) == 'dead'
end
function Process:terminate()
print('terminating ' .. self.name)
self:resume('terminate')
end
function Process:threadEvent(...)
for _,key in pairs(Util.keys(self.threads)) do
local thread = self.threads[key]
if thread then
thread:resume(...)
end
end
end
function Process:addThread(fn, ...)
return self:newThread(nil, fn, ...)
end
-- deprecated
function Process:newThread(name, fn, ...)
self.uid = self.uid + 1
local thread = { }
setmetatable(thread, { __index = Process })
thread:init({
fn = fn,
name = name,
uid = self.uid,
})
local args = { ... }
thread.co = coroutine.create(function()
local s, m = pcall(function() fn(unpack(args)) end)
if not s and m then
if m == 'Terminated' then
--printError(thread.name .. ' terminated')
else
printError(m)
end
end
--print('thread died ' .. thread.name)
self.threads[thread.uid] = nil
thread:threadEvent('terminate')
return s, m
end)
self.threads[thread.uid] = thread
thread:resume()
return thread
end
function Process:resume(event, ...)
-- threads get a chance to process the event regardless of the main process filter
self:threadEvent(event, ...)
if not self.filter or self.filter == event or event == "terminate" then
local ok, result = coroutine.resume(self.co, event, ...)
if ok then
self.filter = result
end
return ok, result
end
return true, self.filter
end
-- confusing...
-- pull either one event if no filter or until event matches filter
-- or until terminated (regardless of filter)
function Process:pullEvent(filter)
while true do
local e = { os.pullEventRaw() }
self:threadEvent(unpack(e))
if not filter or e[1] == filter or e[1] == 'terminate' then
return unpack(e)
end
end
end
-- pull events until either the filter is matched or terminated
function Process:pullEvents(filter)
while true do
local e = { os.pullEventRaw() }
self:threadEvent(unpack(e))
if (filter and e[1] == filter) or e[1] == 'terminate' then
return unpack(e)
end
end
end
local process = { }
setmetatable(process, { __index = Process })
process:init({ name = 'Main', co = coroutine.running() })
return process

View File

@ -1,58 +0,0 @@
local function resolveFile(filename, dir, lua_path)
local ch = string.sub(filename, 1, 1)
if ch == "/" then
return filename
end
if dir then
local path = fs.combine(dir, filename)
if fs.exists(path) and not fs.isDir(path) then
return path
end
end
if lua_path then
for dir in string.gmatch(lua_path, "[^:]+") do
local path = fs.combine(dir, filename)
if fs.exists(path) and not fs.isDir(path) then
return path
end
end
end
end
local modules = { }
return function(filename)
local dir = DIR
if not dir and shell and type(shell.dir) == 'function' then
dir = shell.dir()
end
local fname = resolveFile(filename:gsub('%.', '/') .. '.lua',
dir or '', LUA_PATH or '/sys/apis')
if not fname or not fs.exists(fname) then
error('Unable to load: ' .. filename, 2)
end
local rname = fname:gsub('%/', '.'):gsub('%.lua', '')
local module = modules[rname]
if not module then
local f, err = loadfile(fname)
if not f then
error(err)
end
setfenv(f, getfenv(1))
module = f(rname)
modules[rname] = module
end
return module
end

166
sys/apis/turtle/craft.lua Normal file
View File

@ -0,0 +1,166 @@
local itemDB = require('itemDB')
local Util = require('util')
local Craft = { }
local function clearGrid(chestProvider)
for i = 1, 16 do
local count = turtle.getItemCount(i)
if count > 0 then
chestProvider:insert(i, count)
if turtle.getItemCount(i) ~= 0 then
return false
end
end
end
return true
end
local function splitKey(key)
local t = Util.split(key, '(.-):')
local item = { }
if #t[#t] > 2 then
item.nbtHash = table.remove(t)
end
item.damage = tonumber(table.remove(t))
item.name = table.concat(t, ':')
return item
end
local function getItemCount(items, key)
local item = splitKey(key)
for _,v in pairs(items) do
if v.name == item.name and
v.damage == item.damage and
v.nbtHash == item.nbtHash then
return v.count
end
end
return 0
end
local function turtleCraft(recipe, qty, chestProvider)
clearGrid(chestProvider)
for k,v in pairs(recipe.ingredients) do
local item = splitKey(v)
chestProvider:provide({ id = item.name, dmg = item.damage, nbt_hash = item.nbtHash }, qty, k)
if turtle.getItemCount(k) == 0 then -- ~= qty then
-- FIX: ingredients cannot be stacked
return false
end
end
return turtle.craft()
end
function Craft.craftRecipe(recipe, count, chestProvider)
local items = chestProvider:listItems()
local function sumItems(items)
-- produces { ['minecraft:planks:0'] = 8 }
local t = {}
for _,item in pairs(items) do
t[item] = (t[item] or 0) + 1
end
return t
end
count = math.ceil(count / recipe.count)
local maxCount = recipe.maxCount or math.floor(64 / recipe.count)
local summedItems = sumItems(recipe.ingredients)
for key,icount in pairs(summedItems) do
local itemCount = getItemCount(items, key)
if itemCount < icount * count then
local irecipe = Craft.recipes[key]
if irecipe then
Util.print('Crafting %d %s', icount * count - itemCount, key)
if not Craft.craftRecipe(irecipe,
icount * count - itemCount,
chestProvider) then
turtle.select(1)
return
end
end
end
end
repeat
if not turtleCraft(recipe, math.min(count, maxCount), chestProvider) then
turtle.select(1)
return false
end
count = count - maxCount
until count <= 0
turtle.select(1)
return true
end
-- given a certain quantity, return how many of those can be crafted
function Craft.getCraftableAmount(recipe, count, items)
local function sumItems(recipe, items, summedItems, count)
local canCraft = 0
for i = 1, count do
for _,item in pairs(recipe.ingredients) do
local summedItem = summedItems[item] or getItemCount(items, item)
local irecipe = Craft.recipes[item]
if irecipe and summedItem <= 0 then
summedItem = summedItem + sumItems(irecipe, items, summedItems, 1)
end
if summedItem <= 0 then
return canCraft
end
summedItems[item] = summedItem - 1
end
canCraft = canCraft + recipe.count
end
return canCraft
end
return sumItems(recipe, items, { }, math.ceil(count / recipe.count))
end
function Craft.canCraft(item, count, items)
return Craft.getCraftableAmount(Craft.recipes[item], count, items) == count
end
function Craft.setRecipes(recipes)
Craft.recipes = recipes
end
function Craft.getCraftableAmountTest()
local results = { }
Craft.setRecipes(Util.readTable('sys/etc/recipes.db'))
local items = {
{ name = 'minecraft:planks', damage = 0, count = 5 },
{ name = 'minecraft:log', damage = 0, count = 2 },
}
results[1] = { item = 'chest', expected = 1, got = Craft.getCraftableAmount(Craft.recipes['minecraft:chest:0'], 2, items) }
items = {
{ name = 'minecraft:log', damage = 0, count = 1 },
{ name = 'minecraft:coal', damage = 1, count = 1 },
}
results[2] = { item = 'torch', expected = 4, got = Craft.getCraftableAmount(Craft.recipes['minecraft:torch:0'], 4, items) }
return results
end
function Craft.craftRecipeTest(name, count)
local ChestProvider = require('chestProvider18')
local chestProvider = ChestProvider({ wrapSide = 'top', direction = 'down' })
Craft.setRecipes(Util.readTable('usr/etc/recipes.db'))
return { Craft.craftRecipe(Craft.recipes[name], count, chestProvider) }
end
return Craft

160
sys/apis/turtle/level.lua Normal file
View File

@ -0,0 +1,160 @@
local Point = require('point')
local Util = require('util')
local checkedNodes = { }
local nodes = { }
local box = { }
local oldCallback
local function toKey(pt)
return table.concat({ pt.x, pt.y, pt.z }, ':')
end
local function addNode(node)
for i = 0, 5 do
local hi = turtle.getHeadingInfo(i)
local testNode = { x = node.x + hi.xd, y = node.y + hi.yd, z = node.z + hi.zd }
if Point.inBox(testNode, box) then
local key = toKey(testNode)
if not checkedNodes[key] then
nodes[key] = testNode
end
end
end
end
local function dig(action)
local directions = {
top = 'up',
bottom = 'down',
}
-- convert to up, down, north, south, east, west
local direction = directions[action.side] or
turtle.getHeadingInfo(turtle.point.heading).direction
local hi = turtle.getHeadingInfo(direction)
local node = { x = turtle.point.x + hi.xd, y = turtle.point.y + hi.yd, z = turtle.point.z + hi.zd }
if Point.inBox(node, box) then
local key = toKey(node)
checkedNodes[key] = true
nodes[key] = nil
if action.dig() then
addNode(node)
repeat until not action.dig() -- sand, etc
return true
end
end
end
local function move(action)
if action == 'turn' then
dig(turtle.getAction('forward'))
elseif action == 'up' then
dig(turtle.getAction('up'))
elseif action == 'down' then
dig(turtle.getAction('down'))
elseif action == 'back' then
dig(turtle.getAction('up'))
dig(turtle.getAction('down'))
end
if oldCallback then
oldCallback(action)
end
end
-- find the closest block
-- * favor same plane
-- * going backwards only if the dest is above or below
function closestPoint(reference, pts)
local lpt, lm -- lowest
for _,pt in pairs(pts) do
local m = Point.turtleDistance(reference, pt)
local h = Point.calculateHeading(reference, pt)
local t = Point.calculateTurns(reference.heading, h)
if pt.y ~= reference.y then -- try and stay on same plane
m = m + .01
end
if t ~= 2 or pt.y == reference.y then
m = m + t
if t > 0 then
m = m + .01
end
end
if not lm or m < lm then
lpt = pt
lm = m
end
end
return lpt
end
local function getAdjacentPoint(pt)
local t = { }
table.insert(t, pt)
for i = 0, 5 do
local hi = turtle.getHeadingInfo(i)
local heading
if i < 4 then
heading = (hi.heading + 2) % 4
end
table.insert(t, { x = pt.x + hi.xd, z = pt.z + hi.zd, y = pt.y + hi.yd, heading = heading })
end
return closestPoint(turtle.getPoint(), t)
end
return function(startPt, endPt, firstPt, verbose)
checkedNodes = { }
nodes = { }
box = { }
box.x = math.min(startPt.x, endPt.x)
box.y = math.min(startPt.y, endPt.y)
box.z = math.min(startPt.z, endPt.z)
box.ex = math.max(startPt.x, endPt.x)
box.ey = math.max(startPt.y, endPt.y)
box.ez = math.max(startPt.z, endPt.z)
turtle.pathfind(firstPt)
turtle.setPolicy("attack", { dig = dig }, "assuredMove")
oldCallback = turtle.getMoveCallback()
turtle.setMoveCallback(move)
repeat
local key = toKey(turtle.point)
checkedNodes[key] = true
nodes[key] = nil
dig(turtle.getAction('down'))
dig(turtle.getAction('up'))
dig(turtle.getAction('forward'))
if verbose then
print(string.format('%d nodes remaining', Util.size(nodes)))
end
if Util.size(nodes) == 0 then
break
end
local node = closestPoint(turtle.point, nodes)
node = getAdjacentPoint(node)
if not turtle.gotoPoint(node) then
break
end
until turtle.abort
turtle.resetState()
turtle.setMoveCallback(oldCallback)
end

View File

@ -1,22 +1,19 @@
if not turtle or turtle.pathfind then
return
end
requireInjector(getfenv(1))
local Grid = require ("jumper.grid")
local Pathfinder = require ("jumper.pathfinder")
local Point = require('point')
local Util = require('util')
local WALKABLE = 0
local function createMap(dim)
local map = { }
for z = 0, dim.ez do
for z = 1, dim.ez do
local row = {}
for x = 0, dim.ex do
for x = 1, dim.ex do
local col = { }
for y = 0, dim.ey do
for y = 1, dim.ey do
table.insert(col, WALKABLE)
end
table.insert(row, col)
@ -65,12 +62,21 @@ local function mapDimensions(dest, blocks, boundingBox)
end
-- expand one block out in all directions
sx = math.max(sx - 1, boundingBox.sx)
sz = math.max(sz - 1, boundingBox.sz)
sy = math.max(sy - 1, boundingBox.sy)
ex = math.min(ex + 1, boundingBox.ex)
ez = math.min(ez + 1, boundingBox.ez)
ey = math.min(ey + 1, boundingBox.ey)
if boundingBox then
sx = math.max(sx - 1, boundingBox.x)
sz = math.max(sz - 1, boundingBox.z)
sy = math.max(sy - 1, boundingBox.y)
ex = math.min(ex + 1, boundingBox.ex)
ez = math.min(ez + 1, boundingBox.ez)
ey = math.min(ey + 1, boundingBox.ey)
else
sx = sx - 1
sz = sz - 1
sy = sy - 1
ex = ex + 1
ez = ez + 1
ey = ey + 1
end
return {
ex = ex - sx + 1,
@ -138,22 +144,14 @@ local function addSensorBlocks(blocks, sblocks)
end
end
local function pathTo(dest, blocks, maxRadius)
local function pathTo(dest, options)
blocks = blocks or { }
maxRadius = maxRadius or 1000000
local blocks = options.blocks or { }
local allDests = options.dest or { } -- support alternative destinations
local lastDim = nil
local map = nil
local grid = nil
local boundingBox = {
sx = math.min(turtle.point.x, dest.x) - maxRadius,
sy = math.min(turtle.point.y, dest.y) - maxRadius,
sz = math.min(turtle.point.z, dest.z) - maxRadius,
ex = math.max(turtle.point.x, dest.x) + maxRadius,
ey = math.max(turtle.point.y, dest.y) + maxRadius,
ez = math.max(turtle.point.z, dest.z) + maxRadius,
}
-- Creates a pathfinder object
local myFinder = Pathfinder(grid, 'ASTAR', walkable)
@ -164,7 +162,7 @@ local function pathTo(dest, blocks, maxRadius)
while turtle.point.x ~= dest.x or turtle.point.z ~= dest.z or turtle.point.y ~= dest.y do
-- map expands as we encounter obstacles
local dim = mapDimensions(dest, blocks, boundingBox)
local dim = mapDimensions(dest, blocks, options.box)
-- reuse map if possible
if not lastDim or not dimsAreEqual(dim, lastDim) then
@ -189,23 +187,33 @@ local function pathTo(dest, blocks, maxRadius)
local path = myFinder:getPath(startPt.x, startPt.y, startPt.z, turtle.point.heading, endPt.x, endPt.y, endPt.z, dest.heading)
if not path then
return false, 'failed to recalculate'
end
Util.removeByValue(allDests, dest)
dest = Point.closest(turtle.point, allDests)
for node, count in path:nodes() do
local pt = nodeToPoint(dim, node)
if turtle.abort then
return false, 'aborted'
if not dest then
return false, 'failed to recalculate'
end
else
-- use single turn method so the turtle doesn't turn around when encountering obstacles
if not turtle.gotoSingleTurn(pt.x, pt.z, pt.y) then
for node, count in path:nodes() do
local pt = nodeToPoint(dim, node)
if turtle.abort then
return false, 'aborted'
end
-- use single turn method so the turtle doesn't turn around
-- when encountering obstacles -- IS THIS RIGHT ??
if not turtle.gotoSingleTurn(pt.x, pt.z, pt.y, node.heading) then
table.insert(blocks, pt)
if #allDests > 0 then
dest = Point.closest(turtle.point, allDests)
end
--if device.turtlesensorenvironment then
-- addSensorBlocks(blocks, device.turtlesensorenvironment.sonicScan())
--end
break
break
end
end
end
end
@ -213,12 +221,13 @@ local function pathTo(dest, blocks, maxRadius)
if dest.heading then
turtle.setHeading(dest.heading)
end
return true
return dest
end
turtle.pathfind = function(dest, blocks, maxRadius)
if not blocks and turtle.gotoPoint(dest) then
return true
return function(dest, options)
options = options or { }
if not options.blocks and turtle.gotoPoint(dest) then
return dest
end
return pathTo(dest, blocks, maxRadius)
return pathTo(dest, options)
end

View File

@ -343,7 +343,7 @@ function Manager:click(button, x, y)
if button == 1 then
local c = os.clock()
if self.doubleClickTimer and (c - self.doubleClickTimer < 1.5) and
if self.doubleClickTimer and (c - self.doubleClickTimer < 1.9) and
self.doubleClickX == x and self.doubleClickY == y and
self.doubleClickElement == clickEvent.element then
button = 3

View File

@ -243,6 +243,15 @@ function Util.size(list)
return 0
end
function Util.removeByValue(t, e)
for k,v in pairs(t) do
if v == e then
table.remove(t, k)
break
end
end
end
function Util.each(list, func)
for index, value in pairs(list) do
func(value, index, list)

View File

@ -1,107 +0,0 @@
requireInjector(getfenv(1))
local Config = require('config')
local Event = require('event')
local UI = require('ui')
local Util = require('util')
multishell.setTitle(multishell.getCurrent(), 'PIM')
local inventory = { }
local mode = 'sync'
if not device.pim then
error('PIM not attached')
end
local page = UI.Page({
menu = UI.Menu({
centered = true,
y = 2,
menuItems = {
{ prompt = 'Learn', event = 'learn', help = '' },
},
}),
statusBar = UI.StatusBar({
columns = {
{ 'Status', 'status', UI.term.width - 7 },
{ 'Mode', 'mode', 7 }
}
}),
accelerators = {
q = 'quit',
},
})
local function learn()
if device.pim.getInventorySize() > 0 then
local stacks = device.pim.getAllStacks(false)
Config.update('pim', stacks)
mode = 'sync'
page.statusBar:setValue('status', 'Learned inventory')
end
page.statusBar:setValue('mode', mode)
page.statusBar:draw()
end
function page:eventHandler(event)
if event.type == 'learn' then
mode = 'learn'
learn()
elseif event.type == 'quit' then
Event.exitPullEvents()
end
return UI.Page.eventHandler(self, event)
end
local function inInventory(s)
for _,i in pairs(inventory) do
if i.id == s.id then
return true
end
end
end
local function pimWatcher()
local playerOn = false
while true do
if device.pim.getInventorySize() > 0 and not playerOn then
playerOn = true
if mode == 'learn' then
learn()
else
local stacks = device.pim.getAllStacks(false)
for k,stack in pairs(stacks) do
if not inInventory(stack) then
device.pim.pushItem('down', k, stack.qty)
end
end
page.statusBar:setValue('status', 'Synchronized')
page.statusBar:draw()
end
elseif device.pim.getInventorySize() == 0 and playerOn then
page.statusBar:setValue('status', 'No player')
page.statusBar:draw()
playerOn = false
end
os.sleep(1)
end
end
Config.load('pim', inventory)
if Util.empty(inventory) then
mode = 'learn'
end
page.statusBar:setValue('mode', mode)
UI:setPage(page)
Event.pullEvents(pimWatcher)
UI.term:reset()

View File

@ -2,6 +2,7 @@ requireInjector(getfenv(1))
local ChestProvider = require('chestProvider18')
local Config = require('config')
local Craft = require('turtle.craft')
local Event = require('event')
local itemDB = require('itemDB')
local Peripheral = require('peripheral')
@ -34,13 +35,15 @@ local chestProvider = ChestProvider({ direction = 'west', wrapSide = 'back' })
local turtleChestProvider = ChestProvider({ direction = 'up', wrapSide = 'bottom' })
local RESOURCE_FILE = 'usr/etc/resources.db'
local RECIPES_FILE = 'usr/etc/recipes.db'
local RECIPES_FILE = 'sys/etc/recipes.db'
local jobListGrid
local craftingPaused = false
local recipes = Util.readTable(RECIPES_FILE) or { }
local resources = Util.readTable(RESOURCE_FILE) or { }
Craft.setRecipes(recipes)
for _,r in pairs(resources) do
r.maxDamage = nil
r.displayName = nil
@ -70,6 +73,17 @@ local function getItem(items, inItem, ignoreDamage)
end
end
local function splitKey(key)
local t = Util.split(key, '(.-):')
local item = { }
if #t[#t] > 2 then
item.nbtHash = table.remove(t)
end
item.damage = tonumber(table.remove(t))
item.name = table.concat(t, ':')
return item
end
local function getItemQuantity(items, item)
item = getItem(items, item)
if item then
@ -114,7 +128,8 @@ local function mergeResources(t)
end
end
for _,v in pairs(recipes) do
for k in pairs(recipes) do
local v = splitKey(k)
local item = getItem(t, v)
if not item then
item = Util.shallowCopy(v)
@ -149,8 +164,8 @@ end
local function sumItems3(ingredients, items, summedItems, count)
local canCraft = 0
for _,item in pairs(ingredients) do
local key = uniqueKey(item)
for _,key in pairs(ingredients) do
local item = splitKey(key)
local summedItem = summedItems[key]
if not summedItem then
summedItem = Util.shallowCopy(item)
@ -167,52 +182,6 @@ local function sumItems3(ingredients, items, summedItems, count)
end
end
local function sumItems2(ingredients, items, summedItems, count)
local canCraft = 0
for i = 1, count do
for _,item in pairs(ingredients) do
local key = uniqueKey(item)
local summedItem = summedItems[key]
if not summedItem then
summedItem = Util.shallowCopy(item)
summedItem.recipe = recipes[key]
summedItem.count = getItemQuantity(items, summedItem)
summedItems[key] = summedItem
end
if summedItem.recipe and summedItem.count <= 0 then
summedItem.count = sumItems2(summedItem.recipe.ingredients, items, summedItems, 1)
end
if summedItem.count <= 0 then
return canCraft
end
summedItem.count = summedItem.count - item.count
end
canCraft = canCraft + 1
end
return canCraft
end
local function sumItems(items)
local t = {}
for _,item in pairs(items) do
local key = uniqueKey(item)
local summedItem = t[key]
if summedItem then
summedItem.count = summedItem.count + item.count
else
summedItem = Util.shallowCopy(item)
summedItem.recipe = recipes[key]
t[key] = summedItem
end
end
return t
end
local function isGridClear()
for i = 1, 16 do
if turtle.getItemCount(i) ~= 0 then
@ -235,32 +204,6 @@ local function clearGrid()
return true
end
local function turtleCraft(recipe, originalItem, qty)
for k,v in pairs(recipe.ingredients) do
chestProvider:provide({ id = v.name, dmg = v.damage, nbt_hash = v.nbtHash }, v.count * qty, k)
if turtle.getItemCount(k) ~= v.count * qty then
clearGrid()
originalItem.status = v.name .. ' (extract failed)'
return false
end
end
if not turtle.craft() then
clearGrid()
return false
end
--for k,ingredient in pairs(recipe.ingredients) do
-- local item = getItem(items, ingredient)
-- item.count = item.count - ingredient.count
--end
clearGrid()
return true
end
local function addCraftingRequest(item, craftList, count)
local key = uniqueKey(item)
local request = craftList[key]
@ -272,64 +215,30 @@ local function addCraftingRequest(item, craftList, count)
request.count = request.count + count
end
local function craftRecipe(recipe, items, originalItem, count)
local maxCount = recipe.maxCount
if not maxCount then -- temporary
local cItem = itemDB:get(itemDB:makeKey(recipe))
if cItem then
maxCount = cItem.maxCount
else
maxCount = 1
end
end
local summedItems = sumItems(recipe.ingredients)
for key,ingredient in pairs(summedItems) do
local details = getItemDetails(items, ingredient)
maxCount = math.min(details.maxCount, maxCount)
if details.count < ingredient.count * count then
if ingredient.recipe then
if not craftRecipe(ingredient.recipe, items, originalItem, ingredient.count * count - details.count) then
return
end
end
end
end
repeat
if not turtleCraft(recipe, originalItem, math.min(count, maxCount)) then
return false
end
count = count - maxCount
until count < 0
return true
end
local function craftItem(recipe, items, originalItem, craftList, count)
if craftingPaused or not device.workbench or not isGridClear() then
return
end
count = math.ceil(count / recipe.count)
local toCraft = sumItems2(recipe.ingredients, items, { }, count)
local toCraft = Craft.getCraftableAmount(recipe, count, items)
if toCraft > 0 then
craftRecipe(recipe, items, originalItem, toCraft)
Craft.craftRecipe(recipe, toCraft, chestProvider)
clearGrid()
items = chestProvider:listItems()
end
count = count - toCraft
local summedItems = { }
sumItems3(recipe.ingredients, items, summedItems, count)
if count > 0 then
local summedItems = { }
sumItems3(recipe.ingredients, items, summedItems, math.ceil(count / recipe.count))
for key,ingredient in pairs(summedItems) do
if not ingredient.recipe and ingredient.count < 0 then
addCraftingRequest(ingredient, craftList, -ingredient.count)
for key,ingredient in pairs(summedItems) do
if not ingredient.recipe and ingredient.count < 0 then
addCraftingRequest(ingredient, craftList, -ingredient.count)
end
end
end
end

View File

@ -1,535 +0,0 @@
requireInjector(getfenv(1))
local Peripheral = require('peripheral')
local RefinedProvider = require('refinedProvider')
local Terminal = require('terminal')
local UI = require('ui')
local Util = require('util')
local controller = RefinedProvider()
if not controller:isValid() then
error('Refined storage controller not found')
end
multishell.setTitle(multishell.getCurrent(), 'Storage Manager')
function getItem(items, inItem, ignoreDamage)
for _,item in pairs(items) do
if item.name == inItem.name then
if ignoreDamage then
return item
elseif item.damage == inItem.damage and item.nbtHash == inItem.nbtHash then
return item
end
end
end
end
local function uniqueKey(item)
return table.concat({ item.name, item.damage, item.nbtHash }, ':')
end
function mergeResources(t)
local resources = Util.readTable('resource.limits') or { }
for _,v in pairs(resources) do
v.low = tonumber(v.low) -- backwards compatibility
local item = getItem(t, v)
if item then
item.low = v.low
item.auto = v.auto
item.ignoreDamage = v.ignoreDamage
item.rsControl = v.rsControl
item.rsDevice = v.rsDevice
item.rsSide = v.rsSide
else
v.count = 0
table.insert(t, v)
end
end
for _,v in pairs(t) do
v.lname = v.displayName:lower()
end
end
function filterItems(t, filter)
if filter then
local r = {}
filter = filter:lower()
for k,v in pairs(t) do
if string.find(v.lname, filter) then
table.insert(r, v)
end
end
return r
end
return t
end
function craftItems(itemList, allItems)
for _,item in pairs(itemList) do
local cItem = getItem(allItems, item)
if controller:isCrafting(item) then
item.status = '(crafting)'
elseif item.rsControl then
item.status = 'Activated'
elseif not cItem then
item.status = '(no recipe)'
else
local count = item.count
while count >= 1 do -- try to request smaller quantities until successful
local s, m = pcall(function()
item.status = '(no recipe)'
if not controller:craft(cItem, count) then
item.status = '(missing ingredients)'
error('failed')
end
item.status = '(crafting)'
end)
if s then
break -- successfully requested crafting
end
count = math.floor(count / 2)
end
end
end
end
function getAutocraftItems()
local t = Util.readTable('resource.limits') or { }
local itemList = { }
for _,res in pairs(t) do
if res.auto then
res.count = 4 -- this could be higher to increase autocrafting speed
table.insert(itemList, res)
end
end
return itemList
end
local function getItemWithQty(items, res, ignoreDamage)
local item = getItem(items, res, ignoreDamage)
if item then
if ignoreDamage then
local count = 0
for _,v in pairs(items) do
if item.name == v.name and item.nbtHash == v.nbtHash then
if item.maxDamage > 0 or item.damage == v.damage then
count = count + v.count
end
end
end
item.count = count
end
end
return item
end
function watchResources(items)
local itemList = { }
local t = Util.readTable('resource.limits') or { }
for k, res in pairs(t) do
res.low = tonumber(res.low) -- backwards compatibility
local item = getItemWithQty(items, res, res.ignoreDamage)
if not item then
item = {
damage = res.damage,
nbtHash = res.nbtHash,
name = res.name,
displayName = res.displayName,
count = 0
}
end
if res.low and item.count < res.low then
if res.ignoreDamage then
item.damage = 0
end
table.insert(itemList, {
damage = item.damage,
nbtHash = item.nbtHash,
count = res.low - item.count,
name = item.name,
displayName = item.displayName,
status = '',
rsControl = res.rsControl,
})
end
if res.rsControl and res.rsDevice and res.rsSide then
pcall(function()
device[res.rsDevice].setOutput(res.rsSide, item.count < res.low)
end)
end
end
return itemList
end
itemPage = UI.Page {
backgroundColor = colors.lightGray,
titleBar = UI.TitleBar {
title = 'Limit Resource',
previousPage = true,
event = 'form_cancel',
backgroundColor = colors.green
},
displayName = UI.Window {
x = 5, y = 2, width = UI.term.width - 10, height = 3,
},
form = UI.Form {
x = 4, y = 4, height = 10, rex = -4,
[1] = UI.TextEntry {
width = 7,
backgroundColor = colors.gray,
backgroundFocusColor = colors.gray,
formLabel = 'Min', formKey = 'low', help = 'Craft if below min'
},
[2] = UI.Chooser {
width = 7,
formLabel = 'Autocraft', formKey = 'auto',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Craft until out of ingredients'
},
[3] = UI.Chooser {
width = 7,
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Ignore damage of item'
},
[4] = UI.Chooser {
width = 7,
formLabel = 'RS Control', formKey = 'rsControl',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Control via redstone'
},
[5] = UI.Chooser {
width = 25,
formLabel = 'RS Device', formKey = 'rsDevice',
--choices = devices,
help = 'Redstone Device'
},
[6] = UI.Chooser {
width = 10,
formLabel = 'RS Side', formKey = 'rsSide',
--nochoice = 'No',
choices = {
{ name = 'up', value = 'up' },
{ name = 'down', value = 'down' },
{ name = 'east', value = 'east' },
{ name = 'north', value = 'north' },
{ name = 'west', value = 'west' },
{ name = 'south', value = 'south' },
},
help = 'Output side'
},
},
statusBar = UI.StatusBar { }
}
function itemPage.displayName:draw()
local item = self.parent.item
local str = string.format('Name: %s\nDamage: %d', item.displayName, item.damage)
if item.nbtHash then
str = str .. string.format('\nNBT: %s\n', item.nbtHash)
end
self:setCursorPos(1, 1)
self:print(str)
end
function itemPage:enable(item)
self.item = item
self.form:setValues(item)
self.titleBar.title = item.name
local devices = self.form[5].choices
Util.clear(devices)
for _,device in pairs(device) do
if device.setOutput then
table.insert(devices, { name = device.name, value = device.name })
end
end
if Util.size(devices) == 0 then
table.insert(devices, { name = 'None found', values = '' })
end
UI.Page.enable(self)
self:focusFirst()
end
function itemPage:eventHandler(event)
if event.type == 'form_cancel' then
UI:setPreviousPage()
elseif event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help)
self.statusBar:draw()
elseif event.type == 'form_complete' then
local values = self.form.values
local t = Util.readTable('resource.limits') or { }
local keys = { 'name', 'displayName', 'auto', 'low', 'damage',
'maxDamage', 'nbtHash', 'ignoreDamage',
'rsControl', 'rsDevice', 'rsSide', }
local filtered = { }
for _,key in pairs(keys) do
filtered[key] = values[key]
end
filtered.low = tonumber(filtered.low)
filtered.ignoreDamage = filtered.ignoreDamage == true
filtered.auto = filtered.auto == true
filtered.rsControl = filtered.rsControl == true
if filtered.ignoreDamage then
filtered.damage = 0
end
t[uniqueKey(filtered)] = filtered
Util.writeTable('resource.limits', t)
UI:setPreviousPage()
else
return UI.Page.eventHandler(self, event)
end
return true
end
listingPage = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = 'Forget', event = 'forget' },
},
},
grid = UI.Grid {
y = 2, height = UI.term.height - 2,
columns = {
{ heading = 'Name', key = 'displayName', width = UI.term.width - 14 },
{ heading = 'Qty', key = 'count', width = 5 },
{ heading = 'Min', key = 'low', width = 4 },
},
sortColumn = 'lname',
},
statusBar = UI.StatusBar {
backgroundColor = colors.gray,
width = UI.term.width,
filterText = UI.Text {
x = 2, width = 6,
value = 'Filter',
},
filter = UI.TextEntry {
x = 9, rex = -12,
limit = 50,
},
refresh = UI.Button {
rx = -9, width = 8,
text = 'Refresh',
event = 'refresh',
},
},
accelerators = {
r = 'refresh',
q = 'quit',
}
}
function listingPage.grid:getRowTextColor(row, selected)
if row.is_craftable then -- not implemented
return colors.yellow
end
return UI.Grid:getRowTextColor(row, selected)
end
function listingPage.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
row.count = Util.toBytes(row.count)
if row.low then
row.low = Util.toBytes(row.low)
end
return row
end
function listingPage.statusBar:draw()
return UI.Window.draw(self)
end
function listingPage.statusBar.filter:eventHandler(event)
if event.type == 'mouse_rightclick' then
self.value = ''
self:draw()
local page = UI:getCurrentPage()
page.filter = nil
page:applyFilter()
page.grid:draw()
page:setFocus(self)
end
return UI.TextEntry.eventHandler(self, event)
end
function listingPage:eventHandler(event)
if event.type == 'quit' then
UI:exitPullEvents()
elseif event.type == 'grid_select' then
local selected = event.selected
UI:setPage('item', selected)
elseif event.type == 'refresh' then
self:refresh()
self.grid:draw()
self.statusBar.filter:focus()
elseif event.type == 'forget' then
local item = self.grid:getSelected()
if item then
local resources = Util.readTable('resource.limits') or { }
resources[uniqueKey(item)] = nil
Util.writeTable('resource.limits', resources)
self.statusBar:timedStatus('Forgot: ' .. item.name, 3)
self:refresh()
self.grid:draw()
end
elseif event.type == 'text_change' then
self.filter = event.text
if #self.filter == 0 then
self.filter = nil
end
self:applyFilter()
self.grid:draw()
self.statusBar.filter:focus()
else
UI.Page.eventHandler(self, event)
end
return true
end
function listingPage:enable()
self:refresh()
self:setFocus(self.statusBar.filter)
UI.Page.enable(self)
end
function listingPage:refresh()
self.allItems = controller:listItems()
mergeResources(self.allItems)
self:applyFilter()
end
function listingPage:applyFilter()
local t = filterItems(self.allItems, self.filter)
self.grid:setValues(t)
end
local function jobMonitor(jobList)
local mon = Peripheral.getByType('monitor')
if mon then
mon = UI.Device({
device = device.monitor,
textScale = .5,
})
else
mon = UI.Device({
device = Terminal.getNullTerm(term.current())
})
end
jobListGrid = UI.Grid {
parent = mon,
sortColumn = 'displayName',
columns = {
{ heading = 'Qty', key = 'count', width = 6 },
{ heading = 'Crafting', key = 'displayName', width = mon.width / 2 - 10 },
{ heading = 'Status', key = 'status', width = mon.width - 10 },
},
}
return jobListGrid
end
UI:setPages({
listing = listingPage,
item = itemPage,
})
UI:setPage(listingPage)
listingPage:setFocus(listingPage.statusBar.filter)
local jobListGrid = jobMonitor()
jobListGrid:draw()
jobListGrid:sync()
function craftingThread()
while true do
os.sleep(5)
--pcall(function()
local items = controller:listItems()
if not controller:isOnline() then
jobListGrid.parent:clear()
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'Power failure')
jobListGrid:sync()
elseif Util.size(items) == 0 then
jobListGrid.parent:clear()
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system')
jobListGrid:sync()
else
local itemList = watchResources(items)
jobListGrid:setValues(itemList)
jobListGrid:draw()
jobListGrid:sync()
craftItems(itemList, items)
--jobListGrid:update()
jobListGrid:draw()
jobListGrid:sync()
itemList = getAutocraftItems() -- autocrafted items don't show on job monitor
craftItems(itemList, items)
end
--end)
end
end
UI:pullEvents(craftingThread)
UI.term:reset()
jobListGrid.parent:reset()

708
sys/apps/treefarm.lua Normal file
View File

@ -0,0 +1,708 @@
requireInjector(getfenv(1))
--[[
Requirements:
Place turtle against an oak tree or oak sapling
Area around turtle must be flat and can only be dirt or grass
(9 blocks in each direction from turtle)
Turtle must have: crafting table, chest
Turtle must have a pick equipped on the left side
Optional:
Add additional sapling types that can grow with a single sapling
Notes:
If the turtle does not get any saplings from the initial tree, place
down another sapling in front of the turtle.
The program will be able to survive server restarts as long as it has
created the cobble line. If the program is stopped before that time,
place the turtle in the original position before restarting the program.
]]--
local ChestProvider = require('chestProvider18')
local Craft = require('turtle.craft')
local Level = require('turtle.level')
local Point = require('point')
local Util = require('util')
local FUEL_BASE = 0
local FUEL_DIRE = FUEL_BASE + 10
local FUEL_GOOD = FUEL_BASE + 2000
local MIN_CHARCOAL = 24
local MAX_SAPLINGS = 32
local GRID_WIDTH = 8
local GRID_LENGTH = 10
local GRID = {
TL = { x = 8, y = 0, z = -8 },
TR = { x = 8, y = 0, z = 8 },
BL = { x = -10, y = 0, z = -8 },
BR = { x = -10, y = 0, z = 8 },
}
local HOME_PT = { x = 0, y = 0, z = 0, heading = 0 }
local DIG_BLACKLIST = {
[ 'minecraft:furnace' ] = true,
[ 'minecraft:lit_furnace' ] = true,
[ 'minecraft:chest' ] = true,
}
local COBBLESTONE = 'minecraft:cobblestone:0'
local CHARCOAL = 'minecraft:coal:1'
local OAK_LOG = 'minecraft:log:0'
local OAK_PLANK = 'minecraft:planks:0'
local CHEST = 'minecraft:chest:0'
local FURNACE = 'minecraft:furnace:0'
local SAPLING = 'minecraft:sapling:0'
local STONE = 'minecraft:stone:0'
local TORCH = 'minecraft:torch:0'
local DIRT = 'minecraft:dirt:0'
local APPLE = 'minecraft:apple:0'
local STICK = 'minecraft:stick:0'
local ALL_SAPLINGS = {
SAPLING
}
local state = Util.readTable('usr/config/treefarm') or {
trees = {
{ x = 1, y = 0, z = 0 }
}
}
local clock = os.clock()
local recipes = Util.readTable('sys/etc/recipes.db') or { }
Craft.setRecipes(recipes)
local function inspect(fn)
local s, item = fn()
if s and item then
return item.name .. ':' .. item.metadata
end
return 'minecraft:air:0'
end
local function setState(key, value)
state[key] = value
Util.writeTable('usr/config/treefarm', state)
end
local function refuel()
if turtle.getFuelLevel() < FUEL_GOOD then
local charcoal = turtle.getItemCount(CHARCOAL)
if charcoal > 1 then
turtle.refuel(CHARCOAL, math.min(charcoal - 1, MIN_CHARCOAL / 2))
print('fuel: ' .. turtle.getFuelLevel())
end
end
return true
end
local function safePlaceBlock(item)
if turtle.placeUp(item) then
return true
end
local s, m = turtle.inspectUp()
if s and not DIG_BLACKLIST[m.name] then
turtle.digUp()
return turtle.placeUp(item)
end
turtle.forward()
return turtle.placeUp(item)
end
local function craftItem(item, qty)
local success
if safePlaceBlock(CHEST) then
if turtle.equip('left', 'minecraft:crafting_table') then
local chestProvider = ChestProvider({
wrapSide = 'top',
direction = 'down',
})
if not chestProvider:isValid() then
print('invalid chestProvider')
read()
end
-- turtle.emptyInventory(turtle.dropUp)
Util.print('Crafting %d %s', (qty or 1), item)
success = Craft.craftRecipe(recipes[item], qty or 1, chestProvider)
repeat until not turtle.suckUp()
end
turtle.equip('left', 'minecraft:diamond_pickaxe')
turtle.digUp()
end
return success
end
local function cook(item, count, result, fuel, fuelCount)
setState('cooking', true)
fuel = fuel or CHARCOAL
fuelCount = fuelCount or math.ceil(count / 8)
Util.print('Making %d %s', count, result)
turtle.dropForwardAt(state.furnace, fuel, fuelCount)
turtle.dropDownAt(state.furnace, item, count)
count = count + turtle.getItemCount(result)
turtle.select(1)
turtle.pathfind(Point.below(state.furnace))
repeat
os.sleep(1)
turtle.suckUp()
until turtle.getItemCount(result) >= count
setState('cooking')
end
local function makeSingleCharcoal()
local slots = turtle.getSummedInventory()
if not state.furnace or
slots[CHARCOAL] or
not slots[OAK_LOG] or
slots[OAK_LOG].count < 2 then
return true
end
turtle.faceAgainst(state.furnace)
if craftItem(OAK_PLANK) then
cook(OAK_LOG, 1, CHARCOAL, OAK_PLANK, 1)
turtle.refuel(OAK_PLANK)
end
return true
end
local function makeCharcoal()
local slots = turtle.getSummedInventory()
if not state.furnace or
not slots[CHARCOAL] or
slots[CHARCOAL].count >= MIN_CHARCOAL then
return true
end
local function getLogSlot(slots)
local maxslot = { count = 0 }
for k,slot in pairs(slots) do
if string.match(k, 'minecraft:log') then
if slot.count > maxslot.count then
maxslot = slot
end
end
end
return maxslot
end
repeat
local slots = turtle.getSummedInventory()
local charcoal = slots[CHARCOAL].count
local slot = getLogSlot(slots)
if slot.count < 8 then
break
end
local toCook = math.min(charcoal, math.floor(slot.count / 8))
toCook = math.min(toCook, math.floor((MIN_CHARCOAL + 8 - charcoal) / 8))
toCook = toCook * 8
cook(slot.key, toCook, CHARCOAL)
until charcoal + toCook >= MIN_CHARCOAL
return true
end
local function emptyFurnace()
if state.cooking then
print('Emptying furnace')
turtle.suckDownAt(state.furnace)
turtle.suckForwardAt(state.furnace)
turtle.suckUpAt(state.furnace)
setState('cooking')
end
end
local function getCobblestone(count)
local slots = turtle.getSummedInventory()
if not slots[COBBLESTONE] or slots[COBBLESTONE].count < count then
print('Collecting cobblestone')
slots[COBBLESTONE] = true
slots[DIRT] = true
local pt = Point.copy(GRID.BR)
pt.x = GRID.BR.x + 2
pt.z = GRID.BR.z - 2
turtle.pathfind(pt)
repeat
turtle.select(1)
turtle.digDown()
turtle.down()
for i = 1, 3 do
if inspect(turtle.inspect) == STONE then
turtle.dig()
end
turtle.turnRight()
end
for item in pairs(turtle.getSummedInventory()) do
if not slots[item] then
turtle.drop(item)
end
end
until turtle.getItemCount(COBBLESTONE) >= count
turtle.gotoPoint(pt)
turtle.placeDown(DIRT)
turtle.drop(DIRT)
end
end
local function createFurnace()
if not state.furnace then
if turtle.getFuelLevel() < FUEL_BASE + 100 then
return true -- try again later
end
print('Adding a furnace')
getCobblestone(8)
if craftItem(FURNACE) then
turtle.drop(COBBLESTONE)
local furnacePt = { x = GRID.BL.x + 2, y = 1, z = GRID.BL.z + 2 }
turtle.placeAt(furnacePt, FURNACE)
setState('furnace', furnacePt)
end
end
end
local function createPerimeter()
if not state.perimeter then
if not state.furnace or
turtle.getFuelLevel() < FUEL_BASE + 500 or
turtle.getItemCount(OAK_LOG) == 0 or
not craftItem(OAK_PLANK, 2) then
return true
end
print('Creating a perimeter')
getCobblestone(GRID_WIDTH * 2 + 1)
cook(COBBLESTONE, 2, STONE, OAK_PLANK, 2)
turtle.refuel(OAK_PLANK)
turtle.pathfind(GRID.BL)
turtle.digDown()
turtle.placeDown(STONE)
turtle.setMoveCallback(function()
local target = COBBLESTONE
if math.abs(turtle.point.x) == GRID_LENGTH and
math.abs(turtle.point.z) == GRID_WIDTH then
target = STONE
end
if inspect(turtle.inspectDown) ~= target then
turtle.digDown()
turtle.placeDown(target)
end
end)
turtle.pathfind(GRID.BR)
turtle.clearMoveCallback()
turtle.drop(COBBLESTONE)
turtle.drop(DIRT)
setState('perimeter', true)
end
end
local function createChests()
if state.chest_1 then
return false
end
if state.perimeter and
turtle.getFuelLevel() > FUEL_BASE + 100 and
Craft.canCraft(CHEST, 4, turtle.getSummedInventory()) then
print('Adding storage')
if craftItem(CHEST, 4) then
local pt = Point.copy(GRID.BL)
pt.x = pt.x + 1
pt.y = pt.y - 1
for i = 1, 2 do
pt.z = pt.z + 1
turtle.digDownAt(pt)
turtle.placeDown(CHEST)
pt.z = pt.z + 1
turtle.digDownAt(pt)
turtle.placeDown(CHEST)
setState('chest_' .. i, Util.shallowCopy(pt))
pt.z = pt.z + 1
end
turtle.drop(DIRT)
turtle.refuel(OAK_PLANK)
end
end
return true
end
local function dropOffItems()
if state.chest_1 then
local slots = turtle.getSummedInventory()
if state.chest_1 and
slots[CHARCOAL] and
slots[CHARCOAL].count >= MIN_CHARCOAL and
(turtle.getItemCount('minecraft:log') > 0 or
turtle.getItemCount('minecraft:log2') > 0) then
print('Storing logs')
turtle.pathfind(state.chest_1)
turtle.dropDown('minecraft:log')
turtle.dropDown('minecraft:log2')
end
if slots[APPLE] then
print('Storing apples')
turtle.dropDownAt(state.chest_2, APPLE)
end
end
return true
end
local function eatSaplings()
local slots = turtle.getSummedInventory()
for _, sapling in pairs(ALL_SAPLINGS) do
if slots[sapling] and slots[sapling].count > MAX_SAPLINGS then
turtle.refuel(sapling, slots[sapling].count - MAX_SAPLINGS)
end
end
return true
end
local function placeTorches()
if state.torches then
return
end
if Craft.canCraft(TORCH, 4, turtle.getSummedInventory()) and
turtle.getFuelLevel() > 100 then
print('Placing torches')
if craftItem(TORCH, 4) then
local pts = { }
for x = -4, 4, 8 do
for z = -4, 4, 8 do
table.insert(pts, { x = x, y = 0, z = z })
end
end
Point.eachClosest(turtle.point, pts, function(pt)
turtle.placeAt(pt, TORCH)
end)
turtle.refuel(STICK)
turtle.refuel(OAK_PLANK)
setState('torches', true)
end
end
return true
end
local function randomSapling()
local sapling = SAPLING
if #state.trees > 1 then
ALL_SAPLINGS = { }
local slots = turtle.getFilledSlots()
for _, slot in pairs(slots) do
if slot.name == 'minecraft:sapling' then
table.insert(ALL_SAPLINGS, slot.key)
end
end
sapling = ALL_SAPLINGS[math.random(1, #ALL_SAPLINGS)]
end
return sapling
end
local function fellTree(pt)
local function desparateRefuel(min)
if turtle.getFuelLevel() < min then
local logs = turtle.getItemCount(OAK_LOG)
if logs > 0 then
if craftItem(OAK_PLANK, math.min(8, logs * 4)) then
turtle.refuel(OAK_PLANK)
print('fuel: ' .. turtle.getFuelLevel())
end
end
end
end
turtle.setMoveCallback(function() desparateRefuel(FUEL_DIRE) end)
desparateRefuel(FUEL_DIRE)
if turtle.digUpAt(Point.above(pt)) then
Level(
{ x = GRID_WIDTH-1, y = 1, z = GRID_WIDTH-1 },
{ x = -(GRID_WIDTH-1), y = 50, z = -(GRID_WIDTH-1) },
Point.above(pt))
end
desparateRefuel(FUEL_BASE + 100)
turtle.clearMoveCallback()
turtle.setPolicy("attack")
return true
end
local function fell()
local pts = Util.shallowCopy(state.trees)
local pt = table.remove(pts, math.random(1, #pts))
if not turtle.faceAgainst(pt) or
not string.match(inspect(turtle.inspect), 'minecraft:log') then
return true
end
print('Chopping')
local fuel = turtle.getFuelLevel()
table.insert(pts, 1, pt)
Point.eachClosest(turtle.point, pts, function(pt)
if turtle.faceAgainst(pt) and
string.match(inspect(turtle.inspect), 'minecraft:log') then
turtle.dig()
fellTree(pt)
end
turtle.placeAt(pt, randomSapling())
end)
print('Used ' .. (fuel - turtle.getFuelLevel()) .. ' fuel')
return true
end
local function moreTrees()
if #state.trees > 1 then
return
end
if not state.chest_1 or turtle.getItemCount(SAPLING) < 9 then
return true
end
print('Adding more trees')
local singleTree = state.trees[1]
state.trees = { }
for x = -2, 2, 2 do
for z = -2, 2, 2 do
table.insert(state.trees, { x = x, y = 0, z = z })
end
end
turtle.digAt(singleTree)
fellTree(singleTree)
setState('trees', state.trees)
Point.eachClosest(turtle.point, state.trees, function(pt)
turtle.placeDownAt(pt, randomSapling())
end)
end
function getTurtleFacing(block)
local directions = {
[5] = 2,
[3] = 3,
[4] = 0,
[2] = 1,
}
if not safePlaceBlock(block) then
error('unable to place chest above')
end
local _, bi = turtle.inspectUp()
turtle.digUp()
return directions[bi.metadata]
end
function saveTurtleFacing()
if not state.facing then
setState('facing', getTurtleFacing(CHEST))
end
end
local function findGround()
print('Locating ground level')
turtle.setPoint(HOME_PT)
while true do
local s, block = turtle.inspectDown()
if not s then block = { name = 'minecraft:air', metadata = 0 } end
b = block.name .. ':' .. block.metadata
if b == 'minecraft:dirt:0' or
b == 'minecraft:grass:0' or
block.name == 'minecraft:chest' then
break
end
if b == COBBLESTONE or b == STONE then
error('lost')
end
if b == TORCH or b == FURNACE then
turtle.forward()
else
turtle.digDown()
turtle.down()
end
if turtle.point.y < -20 then
error('lost')
end
end
turtle.setPoint(HOME_PT)
end
local function findHome()
if not state.perimeter then
return
end
print('Determining location')
turtle.point.heading = getTurtleFacing(CHEST)
turtle.setHeading(state.facing)
turtle.point.heading = 0
local pt = Point.copy(turtle.point)
while inspect(turtle.inspectDown) ~= COBBLESTONE do
pt.x = pt.x - 1
turtle.pathfind(pt)
if pt.x < -16 then
error('lost')
end
end
while inspect(turtle.inspectDown) == COBBLESTONE do
pt.z = pt.z - 1
turtle.pathfind(pt)
if pt.z < -16 then
error('lost')
end
end
turtle.setPoint({
x = -(GRID_LENGTH),
y = 0,
z = -GRID_WIDTH,
heading = turtle.point.heading
})
end
local function updateClock()
local ONE_HOUR = 50
if os.clock() - clock > ONE_HOUR then
clock = os.clock()
else
print('sleeping for ' .. math.floor(ONE_HOUR - (os.clock() - clock)))
os.sleep(ONE_HOUR - (os.clock() - clock))
clock = os.clock()
end
return true
end
local tasks = {
{ desc = 'Finding ground', fn = findGround },
{ desc = 'Determine facing', fn = saveTurtleFacing },
{ desc = 'Finding home', fn = findHome },
{ desc = 'Adding trees', fn = moreTrees },
{ desc = 'Chopping', fn = fell },
{ desc = 'Snacking', fn = eatSaplings },
{ desc = 'Creating chest', fn = createChests },
{ desc = 'Creating furnace', fn = createFurnace },
{ desc = 'Emptying furnace', fn = emptyFurnace },
{ desc = 'Making charcoal', fn = makeSingleCharcoal },
{ desc = 'Making charcoal', fn = makeCharcoal },
{ desc = 'Creating perimeter', fn = createPerimeter },
{ desc = 'Placing torches', fn = placeTorches },
{ desc = 'Refueling', fn = refuel },
{ desc = 'Dropping off items', fn = dropOffItems },
{ desc = 'Condensing', fn = turtle.condense },
{ desc = 'Sleeping', fn = updateClock },
}
turtle.run(function()
turtle.setPolicy("attack")
while not turtle.abort do
print('fuel: ' .. turtle.getFuelLevel())
for _,task in ipairs(Util.shallowCopy(tasks)) do
--print(task.desc)
turtle.status = task.desc
turtle.select(1)
if not task.fn() then
Util.filterInplace(tasks, function(v) return v.fn ~= task.fn end)
end
end
end
end)

View File

@ -1,17 +1,16 @@
if turtle and device.wireless_modem then
local s, m = turtle.run(function()
local homePt = turtle.loadLocation('gpsHome')
if homePt then
requireInjector(getfenv(1))
requireInjector(getfenv(1))
local Config = require('config')
local config = {
destructive = false,
}
Config.load('gps', config)
local Config = require('config')
local config = {
destructive = false,
}
Config.load('gps', config)
if config.home then
local s = turtle.enableGPS(2)
if not s then
@ -30,7 +29,7 @@ if turtle and device.wireless_modem then
turtle.setPolicy('turtleSafe')
end
if not turtle.pathfind(homePt) then
if not turtle.pathfind(config.home) then
error('Failed to return home')
end
end

2146
sys/etc/recipes.db Normal file

File diff suppressed because it is too large Load Diff

View File

@ -46,7 +46,7 @@ local function follow(id)
addBlocks(pt)
addBlocks({ x = pt.x, z = pt.z, y = pt.y + 1 })
if turtle.pathfind(cpt, blocks) then
if turtle.pathfind(cpt, { blocks = blocks }) then
turtle.headTowards(pt)
end
following = false

View File

@ -23,7 +23,7 @@ turtle.run(function()
error('turtle: No GPS response')
end
if not turtle.pathfind(pt, nil, 64) then
if not turtle.pathfind(pt) then
error('Unable to go to location')
end
end)

View File

@ -1,86 +0,0 @@
if not turtle or turtle.abortAction then
return
end
requireInjector(getfenv(1))
local Util = require('util')
local Scheduler = {
uid = 0,
queue = { },
idle = true,
}
function turtle.abortAction()
if turtle.status ~= 'idle' then
turtle.abort = true
os.queueEvent('turtle_abort')
end
Util.clear(Scheduler.queue)
os.queueEvent('turtle_ticket', 0, true)
end
local function getTicket(fn, ...)
Scheduler.uid = Scheduler.uid + 1
if Scheduler.idle then
Scheduler.idle = false
turtle.status = 'busy'
os.queueEvent('turtle_ticket', Scheduler.uid)
else
table.insert(Scheduler.queue, Scheduler.uid)
end
return Scheduler.uid
end
local function releaseTicket(id)
for k,v in ipairs(Scheduler.queue) do
if v == id then
table.remove(Scheduler.queue, k)
return
end
end
local id = table.remove(Scheduler.queue, 1)
if id then
os.queueEvent('turtle_ticket', id)
else
Scheduler.idle = true
turtle.status = 'idle'
end
end
function turtle.run(fn, ...)
local ticketId = getTicket()
if type(fn) == 'string' then
fn = turtle[fn]
end
while true do
local e, id, abort = os.pullEventRaw('turtle_ticket')
if e == 'terminate' then
releaseTicket(ticketId)
os.queueEvent('turtle_response')
error('Terminated')
end
if abort then
-- the function was queued, but the queue was cleared
os.queueEvent('turtle_response')
return false, 'aborted'
end
if id == ticketId then
turtle.abort = false
turtle.resetState()
local args = { ... }
local s, m = pcall(function() fn(unpack(args)) end)
turtle.abort = false
releaseTicket(ticketId)
os.queueEvent('turtle_response')
if not s and m then
printError(m)
end
return s, m
end
end
end

View File

@ -3,7 +3,9 @@ if not turtle or turtle.enableGPS then
end
requireInjector(getfenv(1))
local GPS = require('gps')
local GPS = require('gps')
local Config = require('config')
function turtle.enableGPS(timeout)
if turtle.point.gps then
@ -18,17 +20,23 @@ function turtle.enableGPS(timeout)
end
function turtle.gotoGPSHome()
local homePt = turtle.loadLocation('gpsHome')
if homePt then
local config = { }
Config.load('gps', config)
if config.home then
if turtle.enableGPS() then
turtle.pathfind(homePt)
turtle.pathfind(config.home)
end
end
end
function turtle.setGPSHome()
local config = { }
Config.load('gps', config)
if turtle.enableGPS() then
turtle.storeLocation('gpsHome', turtle.point)
config.home = turtle.point
Config.update('gps', config)
turtle.gotoPoint(turtle.point)
end
end

View File

@ -4,7 +4,10 @@ end
requireInjector(getfenv(1))
local Util = require('util')
local Point = require('point')
local synchronized = require('sync')
local Util = require('util')
turtle.pathfind = require('turtle.pathfind')
local function noop() end
@ -147,6 +150,29 @@ function turtle.getHeadingInfo(heading)
end
-- [[ Basic turtle actions ]] --
local function inventoryAction(fn, name, qty)
local slots = turtle.getFilledSlots()
local s
for _,slot in pairs(slots) do
if slot.key == name or slot.name == name then
turtle.native.select(slot.index)
if not qty then
s = fn()
else
s = fn(math.min(qty, slot.count))
qty = qty - slot.count
if qty < 0 then
break
end
end
end
end
if not s then
return false, 'No items found'
end
return s
end
local function _attack(action)
if action.attack() then
repeat until not action.attack()
@ -193,25 +219,24 @@ 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)
local function _drop(action, qtyOrName, qty)
if not qtyOrName or type(qtyOrName) == 'number' then
return action.drop(qtyOrName)
end
if not count then
return action.drop() -- wtf
end
return action.drop(count)
return inventoryAction(action.drop, qtyOrName, qty)
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.refuel(qtyOrName, qty)
if not qtyOrName or type(qtyOrName) == 'number' then
return turtle.native.refuel(qtyOrName)
end
return inventoryAction(turtle.native.refuel, qtyOrName, qty)
end
--[[
function turtle.dig() return state.dig(actions.forward) end
function turtle.digUp() return state.dig(actions.up) end
@ -277,7 +302,7 @@ turtle.movePolicies = {
return false
end
local oldStatus = turtle.status
print('stuck')
print('assured move: stuck')
turtle.status = 'stuck'
repeat
os.sleep(1)
@ -328,6 +353,7 @@ function turtle.setDigPolicy(policy) state.digPolicy = policy end
function turtle.setAttackPolicy(policy) state.attackPolicy = policy end
function turtle.setMoveCallback(cb) state.moveCallback = cb end
function turtle.clearMoveCallback() state.moveCallback = noop end
function turtle.getMoveCallback() return state.moveCallback end
-- [[ Locations ]] --
function turtle.getLocation(name)
@ -751,31 +777,47 @@ function turtle.getSlot(indexOrId, slots)
local detail = turtle.getItemDetail(indexOrId)
if detail then
return {
name = detail.name,
damage = detail.damage,
count = detail.count,
key = detail.name .. ':' .. detail.damage,
index = indexOrId,
-- deprecate
qty = detail.count,
dmg = detail.damage,
id = detail.name,
iddmg = detail.name .. ':' .. detail.damage,
index = indexOrId,
}
end
return {
qty = 0,
qty = 0, -- deprecate
count = 0,
index = indexOrId,
}
end
function turtle.selectSlot(indexOrId)
function turtle.select(indexOrId)
if type(indexOrId) == 'number' then
return turtle.native.select(indexOrId)
end
local s = turtle.getSlot(indexOrId)
if s then
turtle.select(s.index)
turtle.native.select(s.index)
return s
end
return false, 'Inventory does not contain item'
end
function turtle.selectSlot(indexOrId) -- deprecated
return turtle.select(indexOrId)
end
function turtle.getInventory(slots)
slots = slots or { }
for i = 1, 16 do
@ -784,11 +826,38 @@ function turtle.getInventory(slots)
return slots
end
function turtle.getSummedInventory()
local slots = turtle.getFilledSlots()
local t = { }
for _,slot in pairs(slots) do
local entry = t[slot.iddmg]
if not entry then
entry = {
count = 0,
damage = slot.damage,
name = slot.name,
key = slot.key,
-- deprecate
qty = 0,
dmg = slot.dmg,
id = slot.id,
iddmg = slot.iddmg,
}
t[slot.iddmg] = entry
end
entry.qty = entry.qty + slot.qty
entry.count = entry.qty
end
return t
end
function turtle.emptyInventory(dropAction)
dropAction = dropAction or turtle.drop
for i = 1, 16 do
turtle.emptySlot(i, dropAction)
end
turtle.select(1)
end
function turtle.emptySlot(slot, dropAction)
@ -857,28 +926,243 @@ function turtle.selectSlotWithQuantity(qty, startSlot)
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
function turtle.condense()
local slots = turtle.getInventory()
for i = 16, 1, -1 do
if slots[i].count > 0 then
for j = 1, i - 1 do
if slots[j].count == 0 or slots[i].key == slots[j].key then
turtle.select(i)
turtle.transferTo(j, 64)
local transferred = slots[i].qty - turtle.getItemCount(i)
slots[j].count = slots[j].count + transferred
slots[i].count = slots[i].count - transferred
slots[j].key = slots[i].key
if slots[i].count == 0 then
break
end
end
end
end
end
return true
end
function turtle.getItemCount(idOrName)
if type(idOrName) == 'number' then
return turtle.native.getItemCount(idOrName)
end
local slots = turtle.getFilledSlots()
local count = 0
for _,slot in pairs(slots) do
if slot.iddmg == idOrName or slot.name == idOrName then
count = count + slot.qty
end
end
return count
end
function turtle.equip(side, item)
if item then
if not turtle.select(item) then
return false, 'Unable to equip ' .. item
end
end
if side == 'left' then
return turtle.equipLeft()
end
return turtle.equipRight()
end
function turtle.run(fn, ...)
local args = { ... }
local s, m
if type(fn) == 'string' then
fn = turtle[fn]
end
synchronized(turtle, function()
turtle.abort = false
turtle.status = 'busy'
turtle.resetState()
s, m = pcall(function() fn(unpack(args)) end)
turtle.abort = false
turtle.status = 'idle'
if not s and m then
printError(m)
end
end)
return s, m
end
function turtle.abortAction()
if turtle.status ~= 'idle' then
turtle.abort = true
os.queueEvent('turtle_abort')
end
end
-- [[ Pathing ]] --
function turtle.faceAgainst(pt) -- 4 sided
local pts = { }
for i = 0, 3 do
local hi = turtle.getHeadingInfo(i)
table.insert(pts, {
x = pt.x + hi.xd,
z = pt.z + hi.zd,
y = pt.y + hi.yd,
heading = (hi.heading + 2) % 4,
})
end
return turtle.pathfind(Point.closest(turtle.point, pts), { dest = pts })
end
function turtle.moveAgainst(pt) -- 6 sided
local pts = { }
for i = 0, 5 do
local hi = turtle.getHeadingInfo(i)
local heading, direction
if i < 4 then
heading = (hi.heading + 2) % 4
direction = 'forward'
elseif i == 4 then
direction = 'down'
elseif i == 5 then
direction = 'up'
end
table.insert(pts, {
x = pt.x + hi.xd,
z = pt.z + hi.zd,
y = pt.y + hi.yd,
direction = direction,
heading = heading,
})
end
return turtle.pathfind(Point.closest(turtle.point, pts), { dest = pts })
end
local actionsAt = {
detect = {
up = turtle.detectUp,
down = turtle.detectDown,
forward = turtle.detect,
},
dig = {
up = turtle.digUp,
down = turtle.digDown,
forward = turtle.dig,
},
move = {
up = turtle.moveUp,
down = turtle.moveDown,
forward = turtle.move,
},
attack = {
up = turtle.attackUp,
down = turtle.attackDown,
forward = turtle.attack,
},
place = {
up = turtle.placeUp,
down = turtle.placeDown,
forward = turtle.place,
},
drop = {
up = turtle.dropUp,
down = turtle.dropDown,
forward = turtle.drop,
},
suck = {
up = turtle.suckUp,
down = turtle.suckDown,
forward = turtle.suck,
},
compare = {
up = turtle.compareUp,
down = turtle.compareDown,
forward = turtle.compare,
},
inspect = {
up = turtle.inspectUp,
down = turtle.inspectDown,
forward = turtle.inspect,
},
}
local function _actionAt(action, pt, ...)
local pt = turtle.moveAgainst(pt)
if pt then
return action[pt.direction](...)
end
end
function _actionDownAt(action, pt, ...)
if turtle.pathfind(Point.above(pt)) then
return action.down(...)
end
end
function _actionForwardAt(action, pt, ...)
if turtle.faceAgainst(pt) then
return action.forward(...)
end
end
function _actionUpAt(action, pt, ...)
if turtle.pathfind(Point.below(pt)) then
return action.up(...)
end
end
function turtle.detectAt(pt) return _actionAt(actionsAt.detect, pt) end
function turtle.detectDownAt(pt) return _actionDownAt(actionsAt.detect, pt) end
function turtle.detectForwardAt(pt) return _actionForwardAt(actionsAt.detect, pt) end
function turtle.detectUpAt(pt) return _actionUpAt(actionsAt.detect, pt) end
function turtle.digAt(pt) return _actionAt(actionsAt.dig, pt) end
function turtle.digDownAt(pt) return _actionDownAt(actionsAt.dig, pt) end
function turtle.digForwardAt(pt) return _actionForwardAt(actionsAt.dig, pt) end
function turtle.digUpAt(pt) return _actionUpAt(actionsAt.dig, pt) end
function turtle.attackAt(pt) return _actionAt(actionsAt.attack, pt) end
function turtle.attackDownAt(pt) return _actionDownAt(actionsAt.attack, pt) end
function turtle.attackForwardAt(pt) return _actionForwardAt(actionsAt.attack, pt) end
function turtle.attackUpAt(pt) return _actionUpAt(actionsAt.attack, pt) end
function turtle.placeAt(pt, arg) return _actionAt(actionsAt.place, pt, arg) end
function turtle.placeDownAt(pt, arg) return _actionDownAt(actionsAt.place, pt, arg) end
function turtle.placeForwardAt(pt, arg) return _actionForwardAt(actionsAt.place, pt, arg) end
function turtle.placeUpAt(pt, arg) return _actionUpAt(actionsAt.place, pt, arg) end
function turtle.dropAt(pt, ...) return _actionAt(actionsAt.drop, pt, ...) end
function turtle.dropDownAt(pt, ...) return _actionDownAt(actionsAt.drop, pt, ...) end
function turtle.dropForwardAt(pt, ...) return _actionForwardAt(actionsAt.drop, pt, ...) end
function turtle.dropUpAt(pt, ...) return _actionUpAt(actionsAt.drop, pt, ...) end
function turtle.suckAt(pt, qty) return _actionAt(actionsAt.suck, pt, qty) end
function turtle.suckDownAt(pt, qty) return _actionDownAt(actionsAt.suck, pt, qty) end
function turtle.suckForwardAt(pt, qty) return _actionForwardAt(actionsAt.suck, pt, qty) end
function turtle.suckUpAt(pt, qty) return _actionUpAt(actionsAt.suck, pt, qty) end
function turtle.compareAt(pt) return _actionAt(actionsAt.compare, pt) end
function turtle.compareDownAt(pt) return _actionDownAt(actionsAt.compare, pt) end
function turtle.compareForwardAt(pt) return _actionForwardAt(actionsAt.compare, pt) end
function turtle.compareUpAt(pt) return _actionUpAt(actionsAt.compare, pt) end
function turtle.inspectAt(pt) return _actionAt(actionsAt.inspect, pt) end
function turtle.inspectDownAt(pt) return _actionDownAt(actionsAt.inspect, pt) end
function turtle.inspectForwardAt(pt) return _actionForwardAt(actionsAt.inspect, pt) end
function turtle.inspectUpAt(pt) return _actionUpAt(actionsAt.inspect, pt) end

View File

@ -2,41 +2,15 @@ if device.wireless_modem then
requireInjector(getfenv(1))
local Config = require('config')
local config = {
host = false,
auto = false,
x = 0,
y = 0,
z = 0,
}
local config = { }
Config.load('gps', config)
if config.host then
multishell.setTitle(multishell.getCurrent(), 'GPS Daemon')
if config.auto then
local GPS = require('gps')
local pt
for i = 1, 3 do
pt = GPS.getPoint(10, true)
if pt then
break
end
end
if not pt then
error('Unable to get GPS coordinates')
end
config.x = pt.x
config.y = pt.y
config.z = pt.z
end
os.run(getfenv(1), '/rom/programs/gps', 'host', config.x, config.y, config.z)
os.run(getfenv(1), '/rom/programs/gps', 'host', config.host.x, config.host.y, config.host.z)
print('GPS daemon stopped')
end