diff --git a/sys/apis/chestProvider18.lua b/sys/apis/chestProvider18.lua index 043ba9f..5c61ded 100644 --- a/sys/apis/chestProvider18.lua +++ b/sys/apis/chestProvider18.lua @@ -14,7 +14,6 @@ local keys = Util.transpose({ }) function ChestProvider:init(args) - local defaults = { items = { }, name = 'chest', @@ -43,12 +42,10 @@ function ChestProvider:getCachedItemDetails(item, k) if not detail then return end +-- NOT SUFFICIENT if detail.name ~= item.name then return end - if detail.maxDamage and detail.maxDamage > 0 and detail.damage > 0 then - detail.displayName = detail.displayName .. ' (damaged)' - end for _,k in ipairs(Util.keys(detail)) do if not keys[k] then @@ -135,8 +132,8 @@ function ChestProvider:provide(item, qty, slot, direction) end end -function ChestProvider:extract(slot, qty) - self.pushItems(self.direction, slot, qty) +function ChestProvider:extract(slot, qty, toSlot) + self.pushItems(self.direction, slot, qty, toSlot) end function ChestProvider:insert(slot, qty) diff --git a/sys/apis/event.lua b/sys/apis/event.lua index ce85d95..6b9276e 100644 --- a/sys/apis/event.lua +++ b/sys/apis/event.lua @@ -1,25 +1,17 @@ local Util = require('util') -local Process = require('process') local Event = { uid = 1, -- unique id for handlers + routines = { }, + handlers = { namedTimers = { } }, + terminate = false, } -local eventHandlers = { - namedTimers = {} -} - --- debug purposes -function Event.getHandlers() - return eventHandlers -end - function Event.addHandler(type, f) - local event = eventHandlers[type] + local event = Event.handlers[type] if not event then - event = {} - event.handlers = {} - eventHandlers[type] = event + event = { handlers = { } } + Event.handlers[type] = event end local handler = { @@ -35,7 +27,7 @@ end function Event.removeHandler(h) if h and h.event then - eventHandlers[h.event].handlers[h.uid] = nil + Event.handlers[h.event].handlers[h.uid] = nil end end @@ -49,11 +41,11 @@ end function Event.addNamedTimer(name, interval, recurring, f) Event.cancelNamedTimer(name) - eventHandlers.namedTimers[name] = Event.addTimer(interval, recurring, f) + Event.handlers.namedTimers[name] = Event.addTimer(interval, recurring, f) end function Event.getNamedTimer(name) - return eventHandlers.namedTimers[name] + return Event.handlers.namedTimers[name] end function Event.cancelNamedTimer(name) @@ -64,11 +56,6 @@ function Event.cancelNamedTimer(name) end end -function Event.isTimerActive(timer) - return timer.enabled and - os.clock() < timer.start + timer.interval -end - function Event.addTimer(interval, recurring, f) local timer = Event.addHandler('timer', function(t, id) @@ -81,7 +68,6 @@ function Event.addTimer(interval, recurring, f) end if t.recurring then t.fired = false - t.start = os.clock() t.timerId = os.startTimer(t.interval) else Event.removeHandler(t) @@ -91,103 +77,121 @@ function Event.addTimer(interval, recurring, f) timer.cf = f timer.interval = interval timer.recurring = recurring - timer.start = os.clock() timer.enabled = true timer.timerId = os.startTimer(interval) return timer end -function Event.removeTimer(h) - Event.removeHandler(h) +function Event.onInterval(interval, f) + return Event.addTimer(interval, true, f) end -function Event.blockUntilEvent(event, timeout) - return Event.waitForEvent(event, timeout, os.pullEvent) +function Event.onTimeout(timeout, f) + return Event.addTimer(timeout, false, f) end -function Event.waitForEvent(event, timeout, pullEvent) - pullEvent = pullEvent or Event.pullEvent - +function Event.waitForEvent(event, timeout) local timerId = os.startTimer(timeout) repeat - local e, p1, p2, p3, p4 = pullEvent() + local e, p1, p2, p3, p4 = os.pullEvent() if e == event then return e, p1, p2, p3, p4 end until e == 'timer' and p1 == timerId end -local exitPullEvents = false - -local function _pullEvents() - while true do - local e = { os.pullEvent() } - Event.processEvent(e) +function Event.addRoutine(routine) + local r = { co = coroutine.create(routine) } + local s, m = coroutine.resume(r.co) + if not s then + error(m or 'Error processing routine') end -end - -function Event.sleep(t) - local timerId = os.startTimer(t or 0) - repeat - local event, id = Event.pullEvent() - until event == 'timer' and id == timerId -end - -function Event.addThread(fn) - return Process:addThread(fn) + Event.routines[r] = true + r.filter = m + return r end function Event.pullEvents(...) - local routines = { ... } - if #routines > 0 then - Process:addThread(_pullEvents) - for _, routine in ipairs(routines) do - Process:addThread(routine) - end - while true do - local e = Process:pullEvent() - if exitPullEvents or e == 'terminate' then - break - end - end - else - while true do - local e = { os.pullEventRaw() } - Event.processEvent(e) - if exitPullEvents or e[1] == 'terminate' then - break - end - end + + for _, r in ipairs({ ... }) do + Event.addRoutine(r) end + + repeat + local e = Event.pullEvent() + until e[1] == 'terminate' end function Event.exitPullEvents() - exitPullEvents = true + Event.terminate = true os.sleep(0) end function Event.pullEvent(eventType) - local e = { os.pullEventRaw(eventType) } - return Event.processEvent(e) + + while true do + local e = { os.pullEventRaw() } + local routines = Util.keys(Event.routines) + for _, r in ipairs(routines) do + if not r.filter or r.filter == e[1] then + local s, m = coroutine.resume(r.co, table.unpack(e)) + if not s and e[1] ~= 'terminate' then + debug({s, m}) + debug(r) + error(m or 'Error processing event') + end + if coroutine.status(r.co) == 'dead' then + r.co = nil + Event.routines[r] = nil + else + r.filter = m + end + end + end + Event.processEvent(e) + if Event.terminate or e[1] == 'terminate' then + Event.terminate = false + return { 'terminate' } + end + + if not eventType or e[1] == eventType then + return e + end + end end function Event.processEvent(pe) local e, p1, p2, p3, p4, p5 = unpack(pe) - local event = eventHandlers[e] + local event = Event.handlers[e] if event then local keys = Util.keys(event.handlers) for _,key in pairs(keys) do local h = event.handlers[key] - if h then - h.f(h, p1, p2, p3, p4, p5) + if h and not h.co then + local co = coroutine.create(function() + h.f(h, p1, p2, p3, p4, p5) + end) + local s, m = coroutine.resume(co) + if not s then + debug({s, m}) + debug(h) + error(m or 'Error processing ' .. e) + elseif coroutine.status(co) ~= 'dead' then + h.co = co + h.filter = m + Event.routines[h] = true + end end end end - + return e, p1, p2, p3, p4, p5 end +Event.on = Event.addHandler +Event.off = Event.removeHandler + return Event diff --git a/sys/apis/injector.lua b/sys/apis/injector.lua index 5813106..d251243 100644 --- a/sys/apis/injector.lua +++ b/sys/apis/injector.lua @@ -12,9 +12,9 @@ local PASTEBIN_URL = 'http://pastebin.com/raw' local GIT_URL = 'https://raw.githubusercontent.com' local function standardSearcher(modname, env, shell) - if package.loaded[modname] then + if _G.package.loaded[modname] then return function() - return package.loaded[modname] + return _G.package.loaded[modname] end end end @@ -33,7 +33,7 @@ end local function pathSearcher(modname, env, shell) local fname = modname:gsub('%.', '/') .. '.lua' - for dir in string.gmatch(package.path, "[^:]+") do + for dir in string.gmatch(_G.package.path, "[^:]+") do local path = fs.combine(dir, fname) if fs.exists(path) and not fs.isDir(path) then return loadfile(path, env) @@ -109,7 +109,7 @@ local function urlSearcher(modname, env, shell) local fname = modname:gsub('%.', '/') .. '.lua' if fname:sub(1, 1) ~= '/' then - for entry in string.gmatch(package.upath, "[^;]+") do + for entry in string.gmatch(_G.package.upath, "[^;]+") do local url = entry .. '/' .. fname local c = loadUrl(url) if c then @@ -150,7 +150,7 @@ local function requireWrapper(env) return loaded[modname] end - for _,searcher in ipairs(package.loaders) do + for _,searcher in ipairs(_G.package.loaders) do local fn, msg = searcher(modname, env, shell) if fn then local module, msg = fn(modname, env) diff --git a/sys/apis/itemDB.lua b/sys/apis/itemDB.lua index 568d3f3..989d919 100644 --- a/sys/apis/itemDB.lua +++ b/sys/apis/itemDB.lua @@ -1,6 +1,6 @@ local TableDB = require('tableDB') -local itemDB = TableDB({ fileName = 'usr/config/items.db' }) +local itemDB = TableDB({ fileName = 'usr/etc/items.db' }) function itemDB:get(key) @@ -13,6 +13,9 @@ function itemDB:get(key) if key[2] ~= 0 then item = TableDB.get(self, { key[1], 0, key[3] }) if item and item.maxDamage > 0 then + item = Util.shallowCopy(item) + item.damage = key[2] + item.displayName = string.format('%s (damage: %d)', item.displayName, item.damage) return item end end diff --git a/sys/apis/peripheral.lua b/sys/apis/peripheral.lua index fc507f9..b974b4f 100644 --- a/sys/apis/peripheral.lua +++ b/sys/apis/peripheral.lua @@ -50,7 +50,12 @@ function Peripheral.addDevice(deviceList, side) name = uniqueName end - deviceList[name] = peripheral.wrap(side) + local s, m pcall(function() deviceList[name] = peripheral.wrap(side) end) + if not s and m then + printError('wrap failed') + printError(m) + end + if deviceList[name] then Util.merge(deviceList[name], { name = name, diff --git a/sys/apis/point.lua b/sys/apis/point.lua index bc66e9e..b50c1f3 100644 --- a/sys/apis/point.lua +++ b/sys/apis/point.lua @@ -4,13 +4,19 @@ function Point.copy(pt) return { x = pt.x, y = pt.y, z = pt.z } end +function Point.same(pta, ptb) + return pta.x == ptb.x and + pta.y == ptb.y and + pta.z == ptb.z +end + function Point.subtract(a, b) a.x = a.x - b.x a.y = a.y - b.y a.z = a.z - b.z end --- real distance +-- Euclidian distance function Point.pythagoreanDistance(a, b) return math.sqrt( math.pow(a.x - b.x, 2) + @@ -18,7 +24,7 @@ function Point.pythagoreanDistance(a, b) math.pow(a.z - b.z, 2)) end --- turtle distance +-- turtle distance (manhattan) function Point.turtleDistance(a, b) if a.y and b.y then return math.abs(a.x - b.x) + @@ -40,6 +46,35 @@ function Point.calculateTurns(ih, oh) return 1 end +function Point.calculateHeading(pta, ptb) + + local heading + + if (pta.heading % 2) == 0 and pta.z ~= ptb.z then + if ptb.z > pta.z then + heading = 1 + else + heading = 3 + end + elseif (pta.heading % 2) == 1 and pta.x ~= ptb.x then + if ptb.x > pta.x then + heading = 0 + else + heading = 2 + end + elseif pta.heading == 0 and pta.x > ptb.x then + heading = 2 + elseif pta.heading == 2 and pta.x < ptb.x then + heading = 0 + elseif pta.heading == 1 and pta.z > ptb.z then + heading = 3 + elseif pta.heading == 3 and pta.z < ptb.z then + heading = 1 + end + + return heading or pta.heading +end + -- Calculate distance to location including turns -- also returns the resulting heading function Point.calculateMoves(pta, ptb, distance) @@ -88,6 +123,32 @@ 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 + end + end + return lpt +end + function Point.adjacentPoints(pt) local pts = { } diff --git a/sys/apis/refinedProvider.lua b/sys/apis/refinedProvider.lua index 3c9b32e..c0c8b3b 100644 --- a/sys/apis/refinedProvider.lua +++ b/sys/apis/refinedProvider.lua @@ -48,9 +48,6 @@ function RefinedProvider:getCachedItemDetails(item) return end Util.merge(detail, meta) - if detail.maxDamage and detail.maxDamage > 0 and detail.damage > 0 then - detail.displayName = detail.displayName .. ' (damaged)' - end detail.lname = detail.displayName:lower() local t = { } diff --git a/sys/apis/schematic.lua b/sys/apis/schematic.lua index 0015997..c27c64c 100644 --- a/sys/apis/schematic.lua +++ b/sys/apis/schematic.lua @@ -100,15 +100,15 @@ function Schematic:parse(a, h, containsName, spinner) local i4 = h:readbyte(h) local i = bit.blshift(i1, 24) + bit.blshift(i2, 16) + bit.blshift(i3, 8) + i4 - if not self.length or not self.width then - - self:discardBytes(h,i, spinner) - self.twopass = true - - elseif name == "Blocks" then + if name == "Blocks" then for i = 1, i do local id = h:readbyte(h) - self:assignCoord(i, id) + if id > 0 then + table.insert(self.blocks, { + id = id, + index = i, + }) + end if (i % 1000) == 0 then spinner:spin() end @@ -269,15 +269,36 @@ function Schematic:loadpass(fh, spinner) break end self:parse(a, fh, true, spinner) - if self.twopass and self.width and self.length then - break - end spinner:spin() end fh:close() + print('Assigning coords ') + local index = 1 + for _, b in ipairs(self.blocks) do + while index < b.index do + self.x = self.x + 1 + if self.x >= self.width then + self.x = 0 + self.z = self.z + 1 + end + if self.z >= self.length then + self.z = 0 + self.y = self.y + 1 + end + if self.y >= self.height then + self.height = self.y + 1 + end + index = index + 1 + end + b.x = self.x + b.y = self.y + b.z = self.z + spinner:spin() + end + self:assignDamages(spinner) self.damages = nil @@ -313,22 +334,9 @@ function Schematic:load(filename) self:checkFileType(f) - print('Initial pass ') + print('Loading blocks ') self:loadpass(f, spinner) - if self.twopass then - self.twopass = nil - self.blocks = { } - self.damages = { } - self.originalBlocks = { } - self.x, self.y, self.z = 0, 0, 0 - self.height = 0 - self.index = 1 - - print('Second pass ') - self:loadpass(f, spinner) - end - self.rowIndex = { } for k,b in ipairs(self.blocks) do local ri = self.rowIndex[b.y] @@ -342,42 +350,12 @@ function Schematic:load(filename) self.cache = Util.readTable('usr/builder/' .. self.filename .. '.cache') or { } end -function Schematic:assignCoord(i, id) - - if id > 0 then - table.insert(self.blocks, { - id = id, - index = i, - x = self.x, - z = self.z, - y = self.y, - }) - end - - self.x = self.x + 1 - if self.x >= self.width then - self.x = 0 - self.z = self.z + 1 - end - if self.z >= self.length then - self.z = 0 - self.y = self.y + 1 - end - if self.y >= self.height then - self.height = self.y + 1 - end -end - function Schematic:assignDamages(spinner) print('Assigning damages') - local i = 0 for _,b in pairs(self.blocks) do b.dmg = self.damages[b.index] or 0 - i = i + 1 - if (i % 1000) == 0 then - spinner:spin() - end + spinner:spin() end end @@ -849,6 +827,7 @@ function Schematic:getComputedBlock(i) -- has this level been computed ? if not self.rowIndex[b.y].loaded then -- compute each level up til this one (unless saved in cache) + for y = 0, b.y - 1 do if not self.cache[y] then self:determineBlockPlacement(y) @@ -858,7 +837,6 @@ function Schematic:getComputedBlock(i) -- get the block now at the computed location b = self.blocks[i] end - return b end diff --git a/sys/apis/ui.lua b/sys/apis/ui.lua index eb876ad..4a0d9c4 100644 --- a/sys/apis/ui.lua +++ b/sys/apis/ui.lua @@ -67,7 +67,7 @@ function Manager:init(args) local mouseDragged = false local pages = { } - Event.addHandler('term_resize', function(h, side) + Event.on('term_resize', function(h, side) if self.currentPage then -- the parent doesn't have any children set... -- that's why we have to resize both the parent and the current page @@ -81,7 +81,7 @@ function Manager:init(args) end end) - Event.addHandler('mouse_scroll', function(h, direction, x, y) + Event.on('mouse_scroll', function(h, direction, x, y) if self.target then local event = self:pointToChild(self.target, x, y) local directions = { @@ -97,7 +97,7 @@ function Manager:init(args) end) -- this should be moved to the device ! - Event.addHandler('monitor_touch', function(h, side, x, y) + Event.on('monitor_touch', function(h, side, x, y) if self.currentPage then if self.currentPage.parent.device.side == side then self:click(1, x, y) @@ -105,7 +105,7 @@ function Manager:init(args) end end) - Event.addHandler('mouse_click', function(h, button, x, y) + Event.on('mouse_click', function(h, button, x, y) mouseDragged = false if button == 1 and shift and control then -- debug hack @@ -123,7 +123,7 @@ function Manager:init(args) end end) - Event.addHandler('mouse_up', function(h, button, x, y) + Event.on('mouse_up', function(h, button, x, y) if self.currentPage and not mouseDragged then if not self.currentPage.parent.device.side then @@ -132,7 +132,7 @@ function Manager:init(args) end end) - Event.addHandler('mouse_drag', function(h, button, x, y) + Event.on('mouse_drag', function(h, button, x, y) mouseDragged = true if self.target then @@ -146,7 +146,7 @@ function Manager:init(args) end end) - Event.addHandler('paste', function(h, text) + Event.on('paste', function(h, text) if clipboard.isInternal() then text = clipboard.getData() end @@ -156,7 +156,7 @@ function Manager:init(args) end end) - Event.addHandler('char', function(h, ch) + Event.on('char', function(h, ch) control = false if self.currentPage then self:inputEvent(self.currentPage.focused, { type = 'key', key = ch }) @@ -164,7 +164,7 @@ function Manager:init(args) end end) - Event.addHandler('key_up', function(h, code) + Event.on('key_up', function(h, code) if code == keys.leftCtrl or code == keys.rightCtrl then control = false elseif code == keys.leftShift or code == keys.rightShift then @@ -172,7 +172,7 @@ function Manager:init(args) end end) - Event.addHandler('key', function(h, code) + Event.on('key', function(h, code) local ch = keys.getName(code) if not ch then return @@ -1282,6 +1282,8 @@ function UI.Device:runTransitions(transitions, canvas) if Util.empty(transitions) then break end + os.sleep(0) +--[[ local timerId = os.startTimer(0) while true do local e = { os.pullEvent() } @@ -1290,10 +1292,11 @@ function UI.Device:runTransitions(transitions, canvas) end table.insert(queue, e) end +--]] end - for _, e in ipairs(queue) do - Event.processEvent(e) - end +-- for _, e in ipairs(queue) do +-- Event.processEvent(e) +-- end end function UI.Device:sync() diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index fa34c71..fadc339 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -116,23 +116,20 @@ function page.grid:getDisplayValues(row) return row end -Event.addThread(function() - while true do - page.grid:update() - page.grid:draw() - page:sync() - os.sleep(1) - end +Event.onInterval(1, function() + page.grid:update() + page.grid:draw() + page:sync() end) -Event.addHandler('device_attach', function(h, deviceName) +Event.on('device_attach', function(h, deviceName) if deviceName == 'wireless_modem' then page.notification:success('Modem connected') page:sync() end end) -Event.addHandler('device_detach', function(h, deviceName) +Event.on('device_detach', function(h, deviceName) if deviceName == 'wireless_modem' then page.notification:error('Wireless modem not attached') page:sync() diff --git a/sys/apps/Script.lua b/sys/apps/Script.lua index 7b6d989..4d30744 100644 --- a/sys/apps/Script.lua +++ b/sys/apps/Script.lua @@ -532,34 +532,30 @@ if not fs.exists(GROUPS_PATH) then fs.makeDir(GROUPS_PATH) end -Event.addHandler('network_attach', function() +Event.on('network_attach', function() if mainPage.enabled then mainPage:draw() end end) -Event.addHandler('network_detach', function() +Event.on('network_detach', function() if mainPage.enabled then mainPage:draw() end end) -function statusUpdate() - while true do - if mainPage.enabled then - local selected = mainPage.computers:getSelected() - if selected then - local computer = _G.network[selected.id] - mainPage.statusBar.values = { computer } - mainPage.statusBar:draw() - mainPage:sync() - end +Event.onInterval(1, function() + if mainPage.enabled then + local selected = mainPage.computers:getSelected() + if selected then + local computer = _G.network[selected.id] + mainPage.statusBar.values = { computer } + mainPage.statusBar:draw() + mainPage:sync() end - os.sleep(1) end -end +end) UI:setPage(mainPage) - -Event.pullEvents(statusUpdate) +UI:pullEvents() UI.term:reset() diff --git a/sys/apps/Tabs.lua b/sys/apps/Tabs.lua index 7b31d9e..7fabe27 100644 --- a/sys/apps/Tabs.lua +++ b/sys/apps/Tabs.lua @@ -52,7 +52,7 @@ function page.grid:getDisplayValues(row) if elapsed < 60 then row.timestamp = string.format("%ds", math.floor(elapsed)) else - row.timestamp = string.format("%fm", math.floor(elapsed/6)/10) + row.timestamp = string.format("%sm", math.floor(elapsed/6)/10) end if row.isDead then row.status = 'error' diff --git a/sys/apps/Turtles.lua b/sys/apps/Turtles.lua index ce250e5..8af2b22 100644 --- a/sys/apps/Turtles.lua +++ b/sys/apps/Turtles.lua @@ -2,7 +2,7 @@ require = requireInjector(getfenv(1)) local UI = require('ui') local Socket = require('socket') local Terminal = require('terminal') -local TableDB = require('tableDB') +local itemDB = require('itemDB') multishell.setTitle(multishell.getCurrent(), 'Turtles') UI.Button.defaults.focusIndicator = ' ' @@ -31,12 +31,6 @@ local policies = { { label = 'turtleSafe' }, } -local itemInfoDB = TableDB({ - fileName = 'items.db' -}) - -itemInfoDB:load() - local page = UI.Page { --[[ policy = UI.Chooser { @@ -177,7 +171,7 @@ function page.coords:draw() self:clear() self:setCursorPos(1, 1) local ind = 'GPS' - if t.coordSystem ~= 'GPS' then + if not t.point.gps then ind = 'REL' end self:print(string.format('%s : %d,%d,%d\nFuel: %s\n', @@ -204,7 +198,7 @@ function page.tabs.inventory:draw() v.selected = true end if v.id then - local item = itemInfoDB:get({ v.id, v.dmg }) + local item = itemDB:get({ v.id, v.dmg }) if item then v.id = item.displayName else diff --git a/sys/apps/builder.lua b/sys/apps/builder.lua index 52d4c4e..0601096 100644 --- a/sys/apps/builder.lua +++ b/sys/apps/builder.lua @@ -41,6 +41,7 @@ local Builder = { resourceSlots = 14, facing = 'south', confirmFacing = false, + wrenchSucks = false, } local pistonFacings @@ -95,6 +96,7 @@ function subDB:seedDB() [ "minecraft:wall_banner:0" ] = "minecraft:banner:0", [ "minecraft:standing_banner:0" ] = "minecraft:banner:0", [ "minecraft:tripwire:0" ] = "minecraft:string:0", + [ "minecraft:pumpkin_stem:0" ] = "minecraft:pumpkin_seeds:0", } self.dirty = true self:flush() @@ -266,7 +268,7 @@ function Builder:getAirResupplyList(blockIndex) local fuel = subDB:getSubstitutedItem(Builder.fuelItem.id, Builder.fuelItem.dmg) slots[15] = { - id = 'ironchest:BlockIronChest', -- 'minecraft:chest', + id = 'minecraft:chest', --'ironchest:BlockIronChest', -- dmg = 0, qty = 0, need = 1, @@ -821,6 +823,14 @@ function Builder:placePiston(b) return end + if self.wrenchSucks then + turtle.turnRight() + turtle.forward() + turtle.turnLeft() + turtle.forward() + turtle.turnLeft() + end + local success = self:wrenchBlock('forward', 'down', pistonFacings) --wrench piston to point downwards rs.setOutput('front', true) @@ -830,6 +840,11 @@ function Builder:placePiston(b) turtle.select(ps.index) turtle.dig() + if not success and not self.wrenchSucks then + self.wrenchSucks = true + success = self:placePiston(b) + end + return success end @@ -2029,12 +2044,9 @@ UI:setPages({ UI:setPage('start') -turtle.run(function() +local s, m = turtle.run(function() turtle.setPolicy(turtle.policies.digAttack) turtle.setPoint({ x = -1, z = -1, y = 0, heading = 0 }) - turtle.getState().coordSystem = 'relative' turtle.saveLocation('supplies') - Event.pullEvents() + UI:pullEvents() end) - -UI.term:reset() diff --git a/sys/apps/chestManager.lua b/sys/apps/chestManager.lua index 12a6e6f..1ffa026 100644 --- a/sys/apps/chestManager.lua +++ b/sys/apps/chestManager.lua @@ -5,16 +5,18 @@ local ChestProvider = require('chestProvider18') local RefinedProvider = require('refinedProvider') local itemDB = require('itemDB') local Terminal = require('terminal') +local Peripheral = require('peripheral') + +multishell.setTitle(multishell.getCurrent(), 'Resource Manager') -- 3 wide monitor (any side of turtle) --- Config location is /sys/config/chestManager +-- Config location is /sys/config/resourceManager -- adjust directions in that file if needed local config = { - trashDirection = 'up', -- trash /chest in relation to interface - turtleDirection = 'down', -- turtle in relation to interface - noCraftingStorage = 'false' -- no ME crafting (or ability to tell if powered - use with caution) + trashDirection = 'up', -- trash /chest in relation to chest + turtleDirection = 'down', -- turtle in relation to chest } Config.load('resourceManager', config) @@ -26,14 +28,34 @@ if not controller:isValid() then end 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 jobListGrid local craftingPaused = false -local recipes = Util.readTable('recipes') or { } +local recipes = Util.readTable(RECIPES_FILE) or { } +local resources = Util.readTable(RESOURCE_FILE) or { } -multishell.setTitle(multishell.getCurrent(), 'Resource Manager') +for _,r in pairs(resources) do + r.maxDamage = nil + r.displayName = nil + r.count = nil + r.lname = nil + r.has_recipe = nil -function getItem(items, inItem, ignoreDamage) + if not r.ignoreDamage then + r.ignoreDamage = nil + end + + if not r.auto then + r.auto = nil + end +end +Util.writeTable(RESOURCE_FILE, resources) + +local function getItem(items, inItem, ignoreDamage) for _,item in pairs(items) do if item.name == inItem.name then if ignoreDamage then @@ -61,7 +83,7 @@ local function getItemDetails(items, item) cItem = itemDB:get(itemDB:makeKey(item)) if cItem then return { count = 0, maxCount = cItem.maxCount } - enditemDB:makeKey + end return { count = 0, maxCount = 64 } end @@ -69,7 +91,7 @@ local function uniqueKey(item) return table.concat({ item.name, item.damage, item.nbtHash }, ':') end -function getName(item) +local function getName(item) local detail = itemDB:get(itemDB:makeKey(item)) if detail then return detail.displayName @@ -77,60 +99,51 @@ function getName(item) return item.name .. ':' .. item.damage end -function mergeResources(t) - local resources = Util.readTable('resource.limits') or { } - +local function mergeResources(t) 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.limit = v.limit - item.auto = v.auto - item.ignoreDamage = v.ignoreDamage - item.rsControl = v.rsControl - item.rsDevice = v.rsDevice - item.rsSide = v.rsSide + Util.merge(item, v) else - v.count = 0 - table.insert(t, v) + item = Util.shallowCopy(v) + item.count = 0 + table.insert(t, item) end end for _,v in pairs(recipes) do local item = getItem(t, v) - if item then - item.has_recipe = true - else + if not item then item = Util.shallowCopy(v) - item.displayName = getName(item) item.count = 0 - item.has_recipe = true table.insert(t, item) end + item.has_recipe = true end for _,v in pairs(t) do + if not v.displayName then + v.displayName = getName(v) + end v.lname = v.displayName:lower() end end -function filterItems(t, filter) - local r = {} +local 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 + if string.find(v.lname, filter) then table.insert(r, v) end end - else - return t + return r end - return r + return t end -function sumItems3(ingredients, items, summedItems, count) +local function sumItems3(ingredients, items, summedItems, count) local canCraft = 0 for _,item in pairs(ingredients) do @@ -179,7 +192,7 @@ local function sumItems2(ingredients, items, summedItems, count) return canCraft end -function sumItems(items) +local function sumItems(items) local t = {} for _,item in pairs(items) do @@ -197,7 +210,7 @@ function sumItems(items) return t end -function isGridClear() +local function isGridClear() for i = 1, 16 do if turtle.getItemCount(i) ~= 0 then return false @@ -219,22 +232,11 @@ local function clearGrid() return true end -function turtleCraft(recipe, originalItem, qty) +local function turtleCraft(recipe, originalItem, qty) for k,v in pairs(recipe.ingredients) do - -- ugh - local dmg = v.damage - ---FIX - LOOKUP IN ITEMS - if v.max_dmg and v.max_dmg > 0 then - local item = ME.getItemDetail({ id = v.id, nbt_hash = v.nbt_hash }, false) - if item then - dmg = item.dmg - end - end - - chestProvider:provide({ id = v.name, dmg = dmg, nbt_hash = v.nbtHash }, v.count * qty, k) + 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)' @@ -256,7 +258,7 @@ function turtleCraft(recipe, originalItem, qty) return true end -function addCraftingRequest(item, craftList, count) +local function addCraftingRequest(item, craftList, count) local key = uniqueKey(item) local request = craftList[key] if not craftList[key] then @@ -267,9 +269,18 @@ function addCraftingRequest(item, craftList, count) request.count = request.count + count end -function craftRecipe(recipe, items, originalItem, count) +local function craftRecipe(recipe, items, originalItem, count) - local maxCount = 64 + 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 @@ -293,7 +304,7 @@ function craftRecipe(recipe, items, originalItem, count) return true end -function craftItem(recipe, items, originalItem, craftList, count) +local function craftItem(recipe, items, originalItem, craftList, count) if craftingPaused or not device.workbench or not isGridClear() then return @@ -305,12 +316,11 @@ function craftItem(recipe, items, originalItem, craftList, count) if toCraft > 0 then craftRecipe(recipe, items, originalItem, toCraft) + items = chestProvider:listItems() end count = count - toCraft - items = chestProvider:listItems() - local summedItems = { } sumItems3(recipe.ingredients, items, summedItems, count) @@ -321,7 +331,7 @@ function craftItem(recipe, items, originalItem, craftList, count) end end -function craftItems(craftList, allItems) +local function craftItems(craftList, allItems) for _,key in pairs(Util.keys(craftList)) do local item = craftList[key] @@ -336,25 +346,29 @@ function craftItems(craftList, allItems) for key,item in pairs(craftList) do - if controller and not recipes[key] then - if controller:isCrafting(item) then - item.status = '(crafting)' + if not recipes[key] then + if not controller then + item.status = '(no recipe)' else + if controller:isCrafting(item) then + item.status = '(crafting)' + 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(item, count) then - item.status = '(missing ingredients)' - error('failed') + 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(item, count) then + item.status = '(missing ingredients)' + error('failed') + end + item.status = '(crafting)' + end) + if s then + break -- successfully requested crafting end - item.status = '(crafting)' - end) - if s then - break -- successfully requested crafting + count = math.floor(count / 2) end - count = math.floor(count / 2) end end end @@ -363,11 +377,11 @@ end local function jobMonitor(jobList) - local mon + local mon = Peripheral.getByType('monitor') - if device.monitor then + if mon then mon = UI.Device({ - deviceType = 'monitor', + device = mon, textScale = .5, }) else @@ -387,11 +401,10 @@ local function jobMonitor(jobList) }) end -function getAutocraftItems() - local t = Util.readTable('resource.limits') or { } +local function getAutocraftItems() local craftList = { } - for _,res in pairs(t) do + for _,res in pairs(resources) do if res.auto then res.count = 4 -- this could be higher to increase autocrafting speed @@ -426,19 +439,18 @@ local function getItemWithQty(items, res, ignoreDamage) return item end -function watchResources(items) +local function watchResources(items) local craftList = { } - local t = Util.readTable('resource.limits') or { } - for k, res in pairs(t) do + for k, res in pairs(resources) do local item = getItemWithQty(items, res, res.ignoreDamage) if not item then item = { damage = res.damage, nbtHash = res.nbtHash, name = res.name, - displayName = res.displayName, + displayName = getName(res), count = 0 } end @@ -472,7 +484,7 @@ function watchResources(items) return craftList end -itemPage = UI.Page { +local itemPage = UI.Page { backgroundColor = colors.lightGray, titleBar = UI.TitleBar { title = 'Limit Resource', @@ -481,10 +493,10 @@ itemPage = UI.Page { backgroundColor = colors.green }, displayName = UI.Window { - x = 5, y = 2, width = UI.term.width - 10, height = 3, + x = 2, y = 2, width = UI.term.width - 4, height = 3, }, form = UI.Form { - x = 4, y = 4, height = 8, rex = -4, + x = 4, y = 5, height = 8, rex = -4, [1] = UI.TextEntry { width = 7, backgroundColor = colors.gray, @@ -555,7 +567,7 @@ 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) + str = str .. string.format('\n%s', item.nbtHash) end self:setCursorPos(1, 1) self:print(str) @@ -593,9 +605,8 @@ function itemPage:eventHandler(event) elseif event.type == 'form_complete' then local values = self.form.values - local t = Util.readTable('resource.limits') or { } - local keys = { 'name', 'displayName', 'auto', 'low', 'limit', 'damage', - 'maxDamage', 'nbtHash', 'ignoreDamage', + local keys = { 'name', 'auto', 'low', 'limit', 'damage', + 'nbtHash', 'ignoreDamage', 'rsControl', 'rsDevice', 'rsSide', } local filtered = { } @@ -605,16 +616,26 @@ function itemPage:eventHandler(event) filtered.low = tonumber(filtered.low) filtered.limit = tonumber(filtered.limit) - filtered.ignoreDamage = filtered.ignoreDamage == true - filtered.auto = filtered.auto == true - filtered.rsControl = filtered.rsControl == true + --filtered.ignoreDamage = filtered.ignoreDamage == true + --filtered.auto = filtered.auto == true + --filtered.rsControl = filtered.rsControl == true - if filtered.ignoreDamage then + if filtered.auto ~= true then + filtered.auto = nil + end + + if filtered.rsControl ~= true then + filtered.rsControl = nil + filtered.rsSide = nil + filtered.rsDevice = nil + end + + if values.ignoreDamage == true then filtered.damage = 0 end - t[uniqueKey(filtered)] = filtered - Util.writeTable('resource.limits', t) + resources[uniqueKey(filtered)] = filtered + Util.writeTable(RESOURCE_FILE, resources) UI:setPreviousPage() @@ -624,11 +645,12 @@ function itemPage:eventHandler(event) return true end -listingPage = UI.Page { +local listingPage = UI.Page { menuBar = UI.MenuBar { buttons = { { text = 'Learn', event = 'learn' }, { text = 'Forget', event = 'forget' }, + { text = 'Craft', event = 'craft' }, }, }, grid = UI.Grid { @@ -720,6 +742,9 @@ function listingPage:eventHandler(event) self.statusBar.filter:focus() elseif event.type == 'learn' then + UI:setPage('learn') + + elseif event.type == 'craft' then UI:setPage('craft') elseif event.type == 'forget' then @@ -729,16 +754,12 @@ function listingPage:eventHandler(event) if recipes[key] then recipes[key] = nil - Util.writeTable('recipes', recipes) + Util.writeTable(RECIPES_FILE, recipes) end - local resources = Util.readTable('resource.limits') or { } - for k,v in pairs(resources) do - if v.name == item.name and v.damage == item.damage then - resources[k] = nil - Util.writeTable('resource.limits', resources) - break - end + if resources[key] then + resources[key] = nil + Util.writeTable(RESOURCE_FILE, resources) end self.statusBar:timedStatus('Forgot: ' .. item.name, 3) @@ -779,7 +800,7 @@ function listingPage:applyFilter() end -- without duck antenna -local function getTurtleInventory() +local function getTurtleInventoryOld() local inventory = { } for i = 1,16 do if turtle.getItemCount(i) > 0 then @@ -795,6 +816,20 @@ local function getTurtleInventory() return inventory end +local function getTurtleInventory() + local inventory = { } + for i = 1,16 do + local qty = turtle.getItemCount(i) + if qty > 0 then + turtleChestProvider:insert(i, qty) + local items = turtleChestProvider:listItems() + _, inventory[i] = next(items) + turtleChestProvider:extract(1, qty, i) + end + end + return inventory +end + local function filter(t, filter) local keys = Util.keys(t) for _,key in pairs(keys) do @@ -817,10 +852,10 @@ local function learnRecipe(page) clearGrid() - filter(recipe, { 'name', 'damage', 'nbtHash', 'count' }) + filter(recipe, { 'name', 'damage', 'nbtHash', 'count', 'maxCount' }) for _,ingredient in pairs(ingredients) do - filter(ingredient, { 'name', 'damage', 'nbtHash', 'count' }) + filter(ingredient, { 'name', 'damage', 'nbtHash', 'count', 'maxCount' }) --if ingredient.max_dmg > 0 then -- let's try this... -- ingredient.dmg = 0 --end @@ -829,7 +864,7 @@ local function learnRecipe(page) recipes[key] = recipe - Util.writeTable('recipes', recipes) + Util.writeTable(RECIPES_FILE, recipes) local displayName = getName(recipe) @@ -849,13 +884,10 @@ local function learnRecipe(page) end end -craftPage = UI.Dialog { +local learnPage = UI.Dialog { height = 7, width = UI.term.width - 6, backgroundColor = colors.lightGray, - titleBar = UI.TitleBar { - title = 'Learn Recipe', - previousPage = true, - }, + title = 'Learn Recipe', idField = UI.Text { x = 5, y = 3, @@ -875,6 +907,61 @@ craftPage = UI.Dialog { } } +function learnPage:enable() + craftingPaused = true + self:focusFirst() + UI.Dialog.enable(self) +end + +function learnPage:disable() + craftingPaused = false + UI.Dialog.disable(self) +end + +function learnPage:eventHandler(event) + if event.type == 'cancel' then + UI:setPreviousPage() + elseif event.type == 'accept' then + if learnRecipe(self) then + UI:setPreviousPage() + end + else + return UI.Dialog.eventHandler(self, event) + end + return true +end + +local craftPage = UI.Dialog { + height = 6, width = UI.term.width - 10, + backgroundColor = colors.lightGray, + title = 'Enter amount to craft', + idField = UI.TextEntry { + x = 15, + y = 3, + width = 10, + limit = 6, + value = '1', + backgroundColor = colors.black, + backgroundFocusColor = colors.black, + }, + accept = UI.Button { + rx = -7, ry = -1, + backgroundColor = colors.green, + text = '+', event = 'accept', + }, + cancel = UI.Button { + rx = -3, ry = -1, + backgroundColor = colors.red, + backgroundFocusColor = colors.red, + text = '\215', event = 'cancel' + }, +} + +function craftPage:draw() + UI.Dialog.draw(self) + self:write(6, 3, 'Quantity') +end + function craftPage:enable() craftingPaused = true self:focusFirst() @@ -890,9 +977,7 @@ function craftPage:eventHandler(event) if event.type == 'cancel' then UI:setPreviousPage() elseif event.type == 'accept' then - if learnRecipe(self) then - UI:setPreviousPage() - end + else return UI.Dialog.eventHandler(self, event) end @@ -902,6 +987,7 @@ end UI:setPages({ listing = listingPage, item = itemPage, + learn = learnPage, craft = craftPage, }) @@ -913,15 +999,12 @@ jobMonitor() jobListGrid:draw() jobListGrid:sync() -function craftingThread() +local function craftingThread() while true do os.sleep(5) - if not craftingPaused then - local items = chestProvider:listItems() - if Util.size(items) == 0 then jobListGrid.parent:clear() jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system') @@ -942,8 +1025,6 @@ function craftingThread() end end end ---craftingThread() -UI:pullEvents(craftingThread) -UI.term:reset() +UI:pullEvents(craftingThread) jobListGrid.parent:reset() diff --git a/sys/apps/logMonitor.lua b/sys/apps/logMonitor.lua index 304396d..321ade3 100644 --- a/sys/apps/logMonitor.lua +++ b/sys/apps/logMonitor.lua @@ -61,40 +61,37 @@ function getClient(id) return ids[id] end -local function logWriter() - while true do - os.pullEvent('logMessage') - local t = { } - while #messages > 0 do - local msg = messages[1] - table.remove(messages, 1) - local client = getClient(msg.id) - client.scrollingText:appendLine(string.format('%d %s', math.floor(os.clock()), msg.text)) - t[msg.id] = client - end - for _,client in pairs(t) do - client.scrollingText:draw() - end - terminal:sync() +Event.on('logMessage', function() + local t = { } + while #messages > 0 do + local msg = messages[1] + table.remove(messages, 1) + local client = getClient(msg.id) + client.scrollingText:appendLine(string.format('%d %s', math.floor(os.clock()), msg.text)) + t[msg.id] = client end -end + for _,client in pairs(t) do + client.scrollingText:draw() + end + terminal:sync() +end) Message.addHandler('log', function(h, id, msg) table.insert(messages, { id = id, text = msg.contents }) os.queueEvent('logMessage') end) -Event.addHandler('monitor_touch', function() +Event.on('monitor_touch', function() terminal:reset() ids = { } end) -Event.addHandler('mouse_click', function() +Event.on('mouse_click', function() terminal:reset() ids = { } end) -Event.addHandler('char', function() +Event.on('char', function() Event.exitPullEvents() end) diff --git a/sys/apps/mirrorClient.lua b/sys/apps/mirrorClient.lua index a1597b1..fafc6ab 100644 --- a/sys/apps/mirrorClient.lua +++ b/sys/apps/mirrorClient.lua @@ -2,7 +2,7 @@ require = requireInjector(getfenv(1)) local Socket = require('socket') local Terminal = require('terminal') local Logger = require('logger') -local process = require('process') +local Event = require('event') Logger.setScreenLogging() @@ -31,7 +31,12 @@ local function wrapTerm(socket) socket.term[k] = function(...) if not socket.queue then socket.queue = { } - os.queueEvent('mirror_flush') + Event.onTimeout(0, function() + if socket.queue then + socket:write(socket.queue) + socket.queue = nil + end + end) end table.insert(socket.queue, { f = k, @@ -61,17 +66,13 @@ while true do os.queueEvent('term_resize') while true do - local e = process:pullEvent('mirror_flush') + local e = Event.pullEvent() if e == 'terminate' then break end if not socket.connected then break end - if socket.queue then - socket:write(socket.queue) - socket.queue = nil - end end for k,v in pairs(socket.oldTerm) do diff --git a/sys/apps/mirrorHost.lua b/sys/apps/mirrorHost.lua index 050a1c9..4eb2e9f 100644 --- a/sys/apps/mirrorHost.lua +++ b/sys/apps/mirrorHost.lua @@ -1,7 +1,7 @@ require = requireInjector(getfenv(1)) +local Event = require('event') local Socket = require('socket') local Logger = require('logger') -local process = require('process') Logger.setScreenLogging() @@ -20,7 +20,7 @@ while true do print('mirror: connection from ' .. socket.dhost) - local updateThread = process:newThread('updateThread', function() + Event.addRoutine(function() while true do local data = socket:read() if not data then @@ -33,18 +33,15 @@ while true do end) -- ensure socket is connected - process:newThread('pinger', function() - while true do - os.sleep(3) - if not socket:ping() then - break - end + Event.onInterval(3, function(h) + if not socket:ping() then + Event.off(h) end end) while true do - process:pullEvent('modem_message') - if updateThread:isDead() then + Event.pullEvent() + if not socket.connected then break end end diff --git a/sys/apps/shapes.lua b/sys/apps/shapes.lua index 1828c59..da729b2 100644 --- a/sys/apps/shapes.lua +++ b/sys/apps/shapes.lua @@ -243,6 +243,159 @@ turtle.run(function() end) ]] +local levelScript = [[ + +require = requireInjector(getfenv(1)) +local Point = require('point') + +local checkedNodes = { } +local nodes = { } +local box = { } + +local function inBox(pt, box) + return pt.x >= box.x and + pt.y >= box.y and + pt.z >= box.z and + pt.x <= box.ex and + pt.y <= box.ey and + pt.z <= box.ez +end + +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 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 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 +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 Point.closest2(turtle.getPoint(), t) +end + +local function level() + + box.x = math.min(data.startPt.x, data.endPt.x) + box.y = math.min(data.startPt.y, data.endPt.y) + box.z = math.min(data.startPt.z, data.endPt.z) + box.ex = math.max(data.startPt.x, data.endPt.x) + box.ey = math.max(data.startPt.y, data.endPt.y) + box.ez = math.max(data.startPt.z, data.endPt.z) + + turtle.pathfind(data.firstPt) + + turtle.setPolicy("attack", { dig = dig }, "assuredMove") + 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')) + + print(string.format('%d nodes remaining', Util.size(nodes))) + + if Util.size(nodes) == 0 then + break + end + + local node = Point.closest2(turtle.point, nodes) + node = getAdjacentPoint(node) + if not turtle.gotoPoint(node) then + break + end + until turtle.abort + + turtle.resetState() +end + +local s, m = turtle.run(function() + turtle.status = 'Leveling' + + if turtle.enableGPS() then + + local pt = Util.shallowCopy(turtle.point) + local s, m = pcall(level) + turtle.pathfind(pt) + + if not s and m then + error(m) + end + end +end) + +if not s then + error(m) +end +]] + + local data = Util.readTable('/usr/config/shapes') or { } local page = UI.Page { @@ -251,6 +404,7 @@ local page = UI.Page { startCoord = UI.Button { x = 2, y = 6, text = 'Start ', event = 'startCoord' }, endCoord = UI.Button { x = 2, y = 8, text = 'End ', event = 'endCoord' }, supplies = UI.Button { x = 2, y = 10, text = 'Supplies', event = 'supplies' }, + first = UI.Button { x = 2, y = 11, text = 'First', event = 'firstCoord' }, cancel = UI.Button { rx = 2, ry = -2, text = 'Abort', event = 'cancel' }, begin = UI.Button { rx = -7, ry = -2, text = 'Begin', event = 'begin' }, accelerators = { q = 'quit' }, @@ -286,6 +440,7 @@ end function page:runFunction(id, script) +Util.writeFile('script.tmp', script) self.notification:info('Connecting') local fn, msg = loadstring(script, 'script') if not fn then @@ -299,7 +454,6 @@ function page:runFunction(id, script) self.notification:error('Unable to connect') return end - socket:write({ type = 'script', args = script }) socket:close() @@ -321,6 +475,13 @@ function page:eventHandler(event) Util.writeTable('/usr/config/shapes', data) end self:draw() + elseif event.type == 'firstCoord' then + data.firstPt = self:getPoint() + if data.firstPt then + self.statusBar:setStatus('first point set') + Util.writeTable('/usr/config/shapes', data) + end + self:draw() elseif event.type == 'supplies' then data.suppliesPt = self:getPoint() if data.suppliesPt then @@ -329,7 +490,7 @@ function page:eventHandler(event) end elseif event.type == 'begin' then if data.startPt and data.endPt then - local s = 'local data = ' .. textutils.serialize(data) .. script + local s = 'local data = ' .. textutils.serialize(data) .. levelScript self:runFunction(turtleId, s) else self.notification:error('Corners not set') diff --git a/sys/apps/storageManager.lua b/sys/apps/storageManager.lua index d2ea572..fe612e0 100644 --- a/sys/apps/storageManager.lua +++ b/sys/apps/storageManager.lua @@ -863,43 +863,37 @@ jobMonitor() jobListGrid:draw() jobListGrid:sync() -function craftingThread() +Event.onInterval(5, function() - while true do - os.sleep(5) + if not craftingPaused then - if not craftingPaused then + local items = ME.getAvailableItems() + + if Util.size(items) == 0 then + jobListGrid.parent:clear() + jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system') + jobListGrid:sync() + + elseif config.noCraftingStorage ~= 'true' and #ME.getCraftingCPUs() <= 0 then -- only way to determine if AE is online + jobListGrid.parent:clear() + jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'Power failure') + jobListGrid:sync() - local items = ME.getAvailableItems() - - if Util.size(items) == 0 then - jobListGrid.parent:clear() - jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system') - jobListGrid:sync() - - elseif config.noCraftingStorage ~= 'true' and #ME.getCraftingCPUs() <= 0 then -- only way to determine if AE is online - jobListGrid.parent:clear() - jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'Power failure') - jobListGrid:sync() + else + local itemList = watchResources(items) + jobListGrid:setValues(itemList) + jobListGrid:draw() + jobListGrid:sync() + craftItems(itemList) + jobListGrid:update() + jobListGrid:draw() + jobListGrid:sync() - else - local itemList = watchResources(items) - jobListGrid:setValues(itemList) - jobListGrid:draw() - jobListGrid:sync() - craftItems(itemList) - jobListGrid:update() - jobListGrid:draw() - jobListGrid:sync() - - itemList = getAutocraftItems(items) -- autocrafted items don't show on job monitor - craftItems(itemList) - end + itemList = getAutocraftItems(items) -- autocrafted items don't show on job monitor + craftItems(itemList) end end -end +end) -Event.pullEvents(craftingThread) - -UI.term:reset() +UI:pullEvents() jobListGrid.parent:reset() \ No newline at end of file diff --git a/sys/apps/supplier.lua b/sys/apps/supplier.lua index 4b586af..19a81ec 100644 --- a/sys/apps/supplier.lua +++ b/sys/apps/supplier.lua @@ -429,7 +429,6 @@ end turtle.run(function() turtle.setPoint({ x = -1, z = -2, y = -1, heading = 1 }) - turtle.getState().coordSystem = 'relative' turtle.saveLocation('supplies') diff --git a/sys/apps/telnet.lua b/sys/apps/telnet.lua index 3e65a1b..09d2f44 100644 --- a/sys/apps/telnet.lua +++ b/sys/apps/telnet.lua @@ -1,5 +1,5 @@ require = requireInjector(getfenv(1)) -local process = require('process') +local Event = require('event') local Socket = require('socket') local Terminal = require('terminal') @@ -36,7 +36,7 @@ socket:write({ isColor = ct.isColor(), }) -process:newThread('telnet_read', function() +Event.addRoutine(function() while true do local data = socket:read() if not data then @@ -57,7 +57,7 @@ local filter = Util.transpose({ }) while true do - local e = { process:pullEvent() } + local e = Event.pullEvent() local event = e[1] if not socket.connected then diff --git a/sys/apps/vnc.lua b/sys/apps/vnc.lua index b5b0292..f14e5bc 100644 --- a/sys/apps/vnc.lua +++ b/sys/apps/vnc.lua @@ -1,5 +1,5 @@ require = requireInjector(getfenv(1)) -local process = require('process') +local Event = require('event') local Socket = require('socket') local Terminal = require('terminal') @@ -39,7 +39,7 @@ if not ct.isColor() then Terminal.toGrayscale(ct) end -process:newThread('vnc_read', function() +Event.addRoutine(function() while true do local data = socket:read() if not data then @@ -60,7 +60,7 @@ local filter = Util.transpose({ }) while true do - local e = { process:pullEvent() } + local e = Event.pullEvent() local event = e[1] if not socket.connected then diff --git a/sys/etc/scripts/level b/sys/etc/scripts/level new file mode 100644 index 0000000..f8de70c --- /dev/null +++ b/sys/etc/scripts/level @@ -0,0 +1,83 @@ +require = requireInjector(getfenv(1)) +local Point = require('point') + +local checkedNodes = { } +local nodes = { } +local box = { x = 65, ex = 69, y = 65, ey = 70, z = -23, ez = -19 } + +local function inBox(pt, box) + return pt.x >= box.x and + pt.y >= box.y and + pt.z >= box.z and + pt.x <= box.ex and + pt.y <= box.ey and + pt.z <= box.ez +end + +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 inBox(testNode, box) then + local key = toKey(testNode) + if not checkedNodes[key] then + nodes[key] = testNode + end + end + end +end + +local function dig(facing) + local direction = facing + if direction == 'forward' then + direction = turtle.getHeadingInfo(turtle.point.heading).direction + end + 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 inBox(node, box) then + if turtle.getAction(facing).dig() then + addNode(node) + end + end +end + +local function level() + repeat + local node = { x = turtle.point.x, y = turtle.point.y, z = turtle.point.z } + local key = toKey(node) + + checkedNodes[key] = true + nodes[key] = nil + + dig('down') + dig('up') + dig('forward') + + print(string.format('%d nodes remaining', Util.size(nodes))) + + if Util.size(nodes) == 0 then + break + end + + local node = Point.closest(turtle.point, nodes) + if not turtle.gotoPoint(node) then + break + end + until turtle.abort +end + +local pt = Util.shallowCopy(turtle.point) +turtle.setPolicy(turtle.policies.none) +if turtle.pathfind({ x = 65, y = 70, z = -23 }) then +--turtle.reset() + turtle.setPolicy(turtle.policies.digOnly) + level() +end +turtle.pathfind(pt) +--local s, m = turtle.run(level) diff --git a/sys/etc/scripts/summon b/sys/etc/scripts/summon index ff36ce2..0c00e07 100644 --- a/sys/etc/scripts/summon +++ b/sys/etc/scripts/summon @@ -7,7 +7,6 @@ local function summon(id) turtle.status = 'GPSing' turtle.setPoint({ x = 0, y = 0, z = 0, heading = 0 }) - turtle.getState().coordSystem = 'relative' local pts = { [ 1 ] = { x = 0, z = 0, y = 0 }, diff --git a/sys/extensions/tgps.lua b/sys/extensions/tgps.lua index 7679848..9538aa9 100644 --- a/sys/extensions/tgps.lua +++ b/sys/extensions/tgps.lua @@ -6,15 +6,14 @@ require = requireInjector(getfenv(1)) local GPS = require('gps') function turtle.enableGPS(timeout) - if turtle.getState().coordSystem == 'GPS' then + if turtle.point.gps == 'GPS' then return turtle.point end local pt = GPS.getPointAndHeading(timeout) if pt then - turtle.setPoint(pt) - turtle.getState().coordSystem = 'GPS' - return true + turtle.setPoint(pt, true) + return turtle.point end end diff --git a/sys/extensions/tl3.lua b/sys/extensions/tl3.lua index 9bcf7b7..3c1b7b1 100644 --- a/sys/extensions/tl3.lua +++ b/sys/extensions/tl3.lua @@ -7,30 +7,40 @@ local function noop() end turtle.point = { x = 0, y = 0, z = 0, heading = 0 } turtle.status = 'idle' turtle.abort = false +local state = { } -function turtle.getPoint() - return turtle.point +function turtle.getPoint() return turtle.point end +function turtle.getState() return state end + +local function _defaultMove(action) + while not action.move() do + if not state.digPolicy(action) and not state.attackPolicy(action) then + return false + end + end + return true 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) +function turtle.setPoint(pt, isGPS) 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 + turtle.point.gps = isGPS + return true +end + +function turtle.resetState() + --turtle.abort = false -- should be part of state + --turtle.status = 'idle' -- should be part of state + state.attackPolicy = noop + state.digPolicy = noop + state.movePolicy = _defaultMove + state.moveCallback = noop + state.locations = { } + return true end @@ -39,27 +49,16 @@ function turtle.reset() turtle.point.y = 0 turtle.point.z = 0 turtle.point.heading = 0 + turtle.point.gps = false 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' + + turtle.resetState() return true end -function turtle.resetState() - --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 = {} - - return true -end +turtle.reset() local actions = { up = { @@ -116,12 +115,12 @@ end -- [[ Heading data ]] -- local headings = { - [ 0 ] = { xd = 1, zd = 0, yd = 0, heading = 0, direction = 'east' }, + [ 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' }, + [ 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' } + [ 4 ] = { xd = 0, zd = 0, yd = 1, heading = 4, direction = 'up' }, + [ 5 ] = { xd = 0, zd = 0, yd = -1, heading = 5, direction = 'down' } } local namedHeadings = { @@ -133,9 +132,7 @@ local namedHeadings = { down = headings[5] } -function turtle.getHeadings() - return headings -end +function turtle.getHeadings() return headings end function turtle.getHeadingInfo(heading) if heading and type(heading) == 'string' then @@ -181,8 +178,8 @@ local function _place(action, indexOrId) if result[1] then return true end - if not state.moveDig(action) then - state.moveAttack(action) + if not state.digPolicy(action) then + state.attackPolicy(action) end return unpack(result) end) @@ -267,48 +264,66 @@ turtle.digPolicies = { 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 }, +turtle.movePolicies = { + none = noop, + default = _defaultMove, + assured = function(action) + if not _defaultMove(action) then + if action.side == 'back' then + return false + end + local oldStatus = turtle.status + print('stuck') + turtle.status = 'stuck' + repeat + os.sleep(1) + until _defaultMove(action) + turtle.status = oldStatus + end + return true + end, } -function turtle.setPolicy(policy) - if type(policy) == 'string' then - policy = turtle.policies[policy] +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 }, + + attack = { attack = turtle.attackPolicies.attack }, + + defaultMove = { move = turtle.movePolicies.default }, + assuredMove = { move = turtle.movePolicies.assured }, +} + +function turtle.setPolicy(...) + local args = { ... } + for _, policy in pairs(args) do + if type(policy) == 'string' then + policy = turtle.policies[policy] + end + if not policy then + error('Invalid policy') + -- return false, 'Invalid policy' + end + if policy.dig then + state.digPolicy = policy.dig + end + if policy.attack then + state.attackPolicy = policy.attack + end + if policy.move then + state.movePolicy = policy.move + end 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) +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 -- [[ Locations ]] -- function turtle.getLocation(name) @@ -364,6 +379,7 @@ function turtle.turnAround() return turtle.point end +-- combine with setHeading function turtle.setNamedHeading(headingName) local headingInfo = namedHeadings[headingName] if headingInfo then @@ -432,17 +448,8 @@ function turtle.headTowards(pt) 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 + if state.movePolicy(actions.up) then turtle.point.y = turtle.point.y + 1 state.moveCallback('up', turtle.point) return true, turtle.point @@ -450,7 +457,7 @@ function turtle.up() end function turtle.down() - if _move(actions.down) then + if state.movePolicy(actions.down) then turtle.point.y = turtle.point.y - 1 state.moveCallback('down', turtle.point) return true, turtle.point @@ -458,7 +465,7 @@ function turtle.down() end function turtle.forward() - if _move(actions.forward) then + if state.movePolicy(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) @@ -467,7 +474,7 @@ function turtle.forward() end function turtle.back() - if _move(actions.back) then + if state.movePolicy(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) diff --git a/sys/network/snmp.lua b/sys/network/snmp.lua index 74b93a1..d8f8b08 100644 --- a/sys/network/snmp.lua +++ b/sys/network/snmp.lua @@ -129,7 +129,6 @@ local function sendInfo() info.status = turtle.status info.point = turtle.point info.inventory = turtle.getInventory() - info.coordSystem = turtle.getState().coordSystem info.slotIndex = turtle.getSelectedSlot() end device.wireless_modem.transmit(999, os.getComputerID(), info) diff --git a/sys/network/vnc.lua b/sys/network/vnc.lua index fbd580a..8ed813e 100644 --- a/sys/network/vnc.lua +++ b/sys/network/vnc.lua @@ -75,9 +75,9 @@ process:newThread('vnc_server', function() local termInfo = socket:read(5) if termInfo then - -- no new process - only 1 connection allowed - -- due to term size issues - vncHost(socket, termInfo) + -- no new process - only 1 connection allowed + -- due to term size issues + vncHost(socket, termInfo) else socket:close() end diff --git a/sys/services/device.lua b/sys/services/device.lua index b4ce2cf..aeb81e2 100644 --- a/sys/services/device.lua +++ b/sys/services/device.lua @@ -12,7 +12,7 @@ if not term.isColor() then detachColor = colors.lightGray end -Event.addHandler('peripheral', function(event, side) +Event.on('peripheral', function(event, side) if side then local dev = Peripheral.addDevice(device, side) if dev then @@ -23,7 +23,7 @@ Event.addHandler('peripheral', function(event, side) end end) -Event.addHandler('peripheral_detach', function(event, side) +Event.on('peripheral_detach', function(event, side) if side then local dev = Util.find(device, 'side', side) if dev then