diff --git a/apis/me.lua b/apis/me.lua deleted file mode 100644 index 88d573f..0000000 --- a/apis/me.lua +++ /dev/null @@ -1,167 +0,0 @@ -local Util = require('util') - -local ME = { - jobList = { } -} - -function ME.setDevice(device) - ME.p = device - --Util.merge(ME, ME.p) - - if not device then - error('ME device not attached') - end - - for k,v in pairs(ME.p) do - if not ME[k] then - ME[k] = v - end - end -end - -function ME.isAvailable() - return not Util.empty(ME.getAvailableItems()) -end - --- Strip off color prefix -local function safeString(text) - - local val = text:byte(1) - - if val < 32 or val > 128 then - - local newText = {} - for i = 4, #text do - local val = text:byte(i) - newText[i - 3] = (val > 31 and val < 127) and val or 63 - end - return string.char(unpack(newText)) - end - - return text -end - -function ME.getAvailableItems() - local items - pcall(function() - items = ME.p.getAvailableItems('all') - for k,v in pairs(items) do - v.id = v.item.id - v.name = safeString(v.item.display_name) - v.qty = v.item.qty - v.dmg = v.item.dmg - v.max_dmg = v.item.max_dmg - v.nbt_hash = v.item.nbt_hash - end - end) - - return items or { } -end - -function ME.getItemCount(id, dmg, nbt_hash, ignore_dmg) - - local fingerprint = { - id = id, - nbt_hash = nbt_hash, - } - - if not ignore_dmg or ignore_dmg ~= 'yes' then - fingerprint.dmg = dmg or 0 - end - - local item = ME.getItemDetail(fingerprint, false) - - if item then - return item.qty - end - - return 0 -end - -function ME.extract(id, dmg, nbt_hash, qty, direction, slot) - dmg = dmg or 0 - qty = qty or 1 - direction = direction or 'up' - return pcall(function() - local fingerprint = { - dmg = dmg, - id = id, - nbt_hash = nbt_hash - } - return ME.exportItem(fingerprint, direction, qty, slot) - end) -end - -function ME.insert(slot, qty, direction) - direction = direction or 'up' - return ME.pullItem(direction, slot, qty) -end - -function ME.isCrafting() - local cpus = ME.p.getCraftingCPUs() or { } - for k,v in pairs(cpus) do - if v.busy then - return true - end - end -end - -function ME.isCPUAvailable() - local cpus = ME.p.getCraftingCPUs() or { } - local available = false - - for cpu,v in pairs(cpus) do - if not v.busy then - available = true - elseif not ME.jobList[cpu] then -- something else is crafting something (don't know what) - return false -- return false since we are in an unknown state - end - end - return available -end - -function ME.getJobList() - - local cpus = ME.p.getCraftingCPUs() or { } - for cpu,v in pairs(cpus) do - if not v.busy then - ME.jobList[cpu] = nil - end - end - - return ME.jobList -end - -function ME.craft(id, dmg, nbt_hash, qty) - local cpus = ME.p.getCraftingCPUs() or { } - for cpu,v in pairs(cpus) do - if not v.busy then - ME.p.requestCrafting({ - id = id, - dmg = dmg or 0, - nbt_hash = nbt_hash, - }, - qty or 1, - cpu - ) - - os.sleep(0) -- tell it to craft, yet it doesn't show busy - try waiting a cycle... - cpus = ME.p.getCraftingCPUs() or { } - if not cpus[cpu].busy then - -- print('sleeping again') - os.sleep(.1) -- sigh - cpus = ME.p.getCraftingCPUs() or { } - end - - -- not working :( - if cpus[cpu].busy then - ME.jobList[cpu] = { id = id, dmg = dmg, qty = qty, nbt_hash = nbt_hash } - return true - end - break -- only need to try the first available cpu - end - end - return false -end - -return ME \ No newline at end of file diff --git a/apis/meAdapter.lua b/apis/meAdapter.lua index 01478ba..850c9c3 100644 --- a/apis/meAdapter.lua +++ b/apis/meAdapter.lua @@ -70,11 +70,32 @@ local function convertItem(item) end function MEAdapter:refresh() + local keys = { + 'damage', + 'displayName', + 'maxCount', + 'maxDamage', + 'name', + 'nbtHash', + } self.items = self.getAvailableItems('all') for _,v in pairs(self.items) do Util.merge(v, v.item) convertItem(v) + + local key = { v.name, v.damage, v.nbtHash } + if not itemDB:get(key) then +debug(v) + local t = { } + for _,k in pairs(keys) do + t[k] = v[k] + end +debug(t) + itemDB:add(key, t) + end end + itemDB:flush() + return self.items end diff --git a/apps/Appstore.lua b/apps/Appstore.lua index 0610307..8a74b47 100644 --- a/apps/Appstore.lua +++ b/apps/Appstore.lua @@ -1,5 +1,6 @@ requireInjector(getfenv(1)) +local Ansi = require('ansi') local Config = require('config') local SHA1 = require('sha1') local UI = require('ui') @@ -199,12 +200,16 @@ local appPage = UI.Page({ function appPage.container.viewport:draw() local app = self.parent.parent.app local str = string.format( - 'By: %s\nCategory: %s\nFile name: %s\n\n%s', - app.creator, app.categoryName, app.name, app.description) + '%s \nBy: %s \nCategory: %s\nFile name: %s\n\n%s', + Ansi.yellow .. app.title .. Ansi.reset, + app.creator, + app.categoryName, app.name, + Ansi.yellow .. app.description .. Ansi.reset) self:clear() - local y = self:wrappedWrite(1, 1, app.title, self.width, nil, colors.yellow) - self.height = self:wrappedWrite(1, y, str, self.width) + self:setCursorPos(1, 1) + self:print(str) + self.ymax = self.cursorY if appPage.notification.enabled then appPage.notification:draw() diff --git a/apps/Events.lua b/apps/Events.lua index 178e96d..3276b63 100644 --- a/apps/Events.lua +++ b/apps/Events.lua @@ -7,18 +7,18 @@ local Util = require('util') multishell.setTitle(multishell.getCurrent(), 'Events') UI:configure('Events', ...) -local page = UI.Page({ - menuBar = UI.MenuBar({ +local page = UI.Page { + menuBar = UI.MenuBar { buttons = { { text = 'Filter', event = 'filter' }, { text = 'Reset', event = 'reset' }, { text = 'Pause ', event = 'toggle', name = 'pauseButton' }, }, - }), - grid = UI.Grid({ + }, + grid = UI.Grid { y = 2, columns = { - { heading = 'Event', key = 'event' }, + { key = 'event' }, { key = 'p1' }, { key = 'p2' }, { key = 'p3' }, @@ -26,7 +26,8 @@ local page = UI.Page({ { key = 'p5' }, }, autospace = true, - }), + disableHeader = true, + }, accelerators = { f = 'filter', p = 'toggle', @@ -35,7 +36,7 @@ local page = UI.Page({ q = 'quit', }, filtered = { }, -}) +} function page:eventHandler(event) @@ -53,7 +54,11 @@ function page:eventHandler(event) self.menuBar:draw() elseif event.type == 'grid_select' then - multishell.openTab({ path = 'sys/apps/Lua.lua', args = { event.selected }, focused = true }) + multishell.openTab({ + path = 'sys/apps/Lua.lua', + args = { event.selected }, + focused = true, + }) elseif event.type == 'reset' then self.filtered = { } @@ -68,7 +73,7 @@ function page:eventHandler(event) self.grid:draw() elseif event.type == 'quit' then - Event.exitPullEvents() + UI:exitPullEvents() elseif event.type == 'focus_change' then if event.focused == self.grid then diff --git a/apps/Turtles.lua b/apps/Turtles.lua index 617b07e..769929a 100644 --- a/apps/Turtles.lua +++ b/apps/Turtles.lua @@ -96,34 +96,37 @@ local page = UI.Page { backgroundColor = UI.TabBar.defaults.selectedBackgroundColor, moveUp = UI.Button { x = 5, y = 2, - text = '/\\', + text = 'up', fn = 'turtle.up', }, moveDown = UI.Button { x = 5, y = 4, - text = '\\/', + text = 'dn', fn = 'turtle.down', }, moveForward = UI.Button { x = 9, y = 3, - text = '>', + text = 'f', fn = 'turtle.forward', }, moveBack = UI.Button { x = 2, y = 3, - text = '<', + text = 'b', fn = 'turtle.back', }, turnLeft = UI.Button { x = 2, y = 6, - text = '<-', + text = 'lt', fn = 'turtle.turnLeft', }, turnRight = UI.Button { x = 8, y = 6, - text = '->', + text = 'rt', fn = 'turtle.turnRight', }, + info = UI.TextArea { + x = 2, y = 9 + } }, }, statusBar = UI.StatusBar(), @@ -148,8 +151,12 @@ function page:runFunction(script, nowrap) if not nowrap then script = 'turtle.run(' .. script .. ')' end - if socket:write({ type = 'script', args = script }) then - return true + if socket:write({ type = 'scriptEx', args = script }) then + local t = socket:read(3) + if t then + return table.unpack(t) + end + return false, 'Socket timeout' end end socket = nil @@ -304,12 +311,32 @@ function page.tabs.tabBar:selectTab(tabTitle) end end +function page:showBlocks() + + local script = [[ + local function inspect(direction) + local s,b = turtle['inspect' .. (direction or '')]() + if not s then + return 'minecraft:air:0' + end + return string.format('%s:%d', b.name, b.metadata) + end + + local bu, bf, bd = inspect('Up'), inspect(), inspect('Down') + return string.format('%s\n%s\n%s', bu, bf, bd) + ]] + + local s, m = self:runFunction(script, true) + self.tabs.action.info:setText(s or m) +end + function page:eventHandler(event) if event.type == 'quit' then UI:exitPullEvents() elseif event.type == 'button_press' then if event.button.fn then self:runFunction(event.button.fn, event.button.nowrap) + self:showBlocks() elseif event.button.script then self:runScript(event.button.script) end diff --git a/apps/chestManager.lua b/apps/chestManager.lua index daa5be2..82d730e 100644 --- a/apps/chestManager.lua +++ b/apps/chestManager.lua @@ -16,7 +16,7 @@ multishell.setTitle(multishell.getCurrent(), 'Resource Manager') -- 3 wide monitor (any side of turtle) --- Config location is /sys/config/resourceManager +-- Config location is /sys/config/chestManager -- adjust directions in that file if needed local config = { @@ -55,16 +55,13 @@ if device.workbench then end end -local canCraft = not not duckAntenna or turtleChestAdapter:isValid() - ----------------------------------------------------------------------- FIX ME - local RESOURCE_FILE = 'usr/config/resources.db' -local RECIPES_FILE = 'usr/etc/recipes.db' +local RECIPES_FILE = 'usr/etc/recipes.db' -local jobListGrid local craftingPaused = false +local canCraft = not not duckAntenna or turtleChestAdapter:isValid() local recipes = Util.readTable(RECIPES_FILE) or { } +local jobListGrid local resources Craft.setRecipes(recipes) diff --git a/apps/simpleMiner.lua b/apps/simpleMiner.lua index 24a4566..f953d28 100644 --- a/apps/simpleMiner.lua +++ b/apps/simpleMiner.lua @@ -1,8 +1,9 @@ requireInjector(getfenv(1)) -local Logger = require('logger') -local Point = require('point') -local Util = require('util') +local Logger = require('logger') +local Pathing = require('turtle.pathfind') +local Point = require('point') +local Util = require('util') if device and device.wireless_modem then Logger.setWirelessLogging() @@ -239,6 +240,9 @@ end function safeGoto(x, z, y, h) local oldStatus = turtle.status + + -- only pathfind above or around other turtles (never down) + Pathing.setBox({ x = 0, y = 0, z = 0, ex = x, ey = y + 1, ez = z }) while not turtle.pathfind({ x = x, z = z, y = y or turtle.point.y, heading = h }) do --status('stuck') if turtle.abort then diff --git a/apps/storageManager.lua b/apps/storageManager.lua deleted file mode 100644 index 62ef357..0000000 --- a/apps/storageManager.lua +++ /dev/null @@ -1,901 +0,0 @@ -requireInjector(getfenv(1)) - -local Config = require('config') -local Event = require('event') -local Logger = require('logger') -local ME = require('me') -local UI = require('ui') -local Util = require('util') - --- Must be a crafty turtle with duck antenna ! --- 3 wide monitor (any side of turtle) - --- Config location is /sys/config/storageMonitor --- 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) -} - -Config.load('storageMonitor', config) - -if not device.tileinterface then - error('ME interface not found') -end - -local duckAntenna - -if device.workbench then - - local oppositeSide = { - [ 'left' ] = 'right', - [ 'right' ] = 'left' - } - - local duckAntennaSide = oppositeSide[device.workbench.side] - duckAntenna = peripheral.wrap(duckAntennaSide) -end ---if not device.monitor then --- error('Monitor not found') ---end - -ME.setDevice(device.tileinterface) - -local jobListGrid -local craftingPaused = false - -multishell.setTitle(multishell.getCurrent(), 'Storage Manager') - -Logger.disable() - -function getItem(items, inItem, ignore_dmg) - for _,item in pairs(items) do - if item.id == inItem.id then - if ignore_dmg and ignore_dmg == 'yes' then - return item - elseif item.dmg == inItem.dmg and item.nbt_hash == inItem.nbt_hash then - return item - end - end - end -end - -local function uniqueKey(item) - local key = item.id .. ':' .. item.dmg - if item.nbt_hash then - key = key .. ':' .. item.nbt_hash - end - return key -end - -function mergeResources(t) - local resources = Util.readTable('resource.limits') - resources = resources or { } - - for _,item in pairs(t) do - item.has_recipe = false - end - - for _,v in pairs(resources) do - local item = getItem(t, v) - if item then - item.limit = tonumber(v.limit) - item.low = tonumber(v.low) - item.auto = v.auto - item.ignore_dmg = v.ignore_dmg - else - v.qty = 0 - v.limit = tonumber(v.limit) - v.low = tonumber(v.low) - v.auto = v.auto - v.ignore_dmg = v.ignore_dmg - table.insert(t, v) - end - end - - recipes = Util.readTable('recipes') or { } - - for _,v in pairs(recipes) do - local item = getItem(t, v) - if item then - item.has_recipe = true - else - v.qty = 0 - v.limit = nil - v.low = nil - v.has_recipe = true - v.auto = 'no' - v.ignore_dmg = 'no' - v.has_recipe = 'true' - table.insert(t, v) - end - end -end - -function filterItems(t, filter) - local r = {} - if filter then - filter = filter:lower() - for k,v in pairs(t) do - if string.find(v.lname, filter) then - table.insert(r, v) - end - end - else - return t - end - return r -end - -function sumItems(items) - local t = {} - - for _,item in pairs(items) do - local key = uniqueKey(item) - local summedItem = t[key] - if summedItem then - summedItem.qty = summedItem.qty + item.qty - else - summedItem = Util.shallowCopy(item) - t[key] = summedItem - end - end - - return t -end - -function isGridClear() - for i = 1, 16 do - if turtle.getItemCount(i) ~= 0 then - return false - end - end - return true -end - -local function clearGrid() - for i = 1, 16 do - local count = turtle.getItemCount(i) - if count > 0 then - ME.insert(i, count, config.turtleDirection) - if turtle.getItemCount(i) ~= 0 then - return false - end - end - end - return true -end - -function turtleCraft(recipe, originalItem) - - for k,v in pairs(recipe.ingredients) do - - -- ugh - local dmg = v.dmg - - 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 - - if not ME.extract(v.id, dmg, v.nbt_hash, v.qty, config.turtleDirection, k) then - clearGrid() - originalItem.status = v.name .. ' (extract failed)' - return false - end - end - - if not turtle.craft() then - clearGrid() - return false - end - - clearGrid() - return true -end - -function craftItem(items, recipes, item, originalItem, itemList) - - local key = uniqueKey(item) - local recipe = recipes[key] - - if recipe then - - if not isGridClear() then - return - end - - local summedItems = sumItems(recipe.ingredients) - - for i = 1, math.ceil(item.qty / recipe.qty) do - - local failed = false -- try to craft all components (use all CPUs available) - - for _,ingredient in pairs(summedItems) do - local ignore_dmg = 'no' - if ingredient.max_dmg and ingredient.max_dmg > 0 then - ignore_dmg = 'yes' - end - local qty = ME.getItemCount(ingredient.id, ingredient.dmg, ingredient.nbt_hash, ignore_dmg) - if qty < ingredient.qty then - originalItem.status = ingredient.name .. ' (crafting)' - ingredient.qty = ingredient.qty - qty - if not craftItem(items, recipes, ingredient, originalItem, itemList) then - failed = true - end - end - end - - if failed then - return false - end - - if not failed and not turtleCraft(recipe, originalItem) then - Logger.debug('turtle failed to craft ' .. item.name) - return false - end - end - - return true - - else - - local meItem = getItem(items, item) - if not meItem or not meItem.is_craftable then - - if item.id == originalItem.id and item.dmg == originalItem.dmg then - originalItem.status = '(not craftable)' - else - originalItem.status = item.name .. ' (missing)' - end - - else - - if item.id == originalItem.id and item.dmg == originalItem.dmg then - item.meCraft = true - return false - end - - -- find it in the list of items to be crafted - for _,v in pairs(itemList) do - if v.id == item.id and v.dmg == item.dmg and v.nbt_hash == item.nbt_hash then - v.qty = item.qty + v.qty - return false - end - end - -- add to the item list - table.insert(itemList, { - id = item.id, - dmg = item.dmg, - nbt_hash = item.nbt_hash, - qty = item.qty, - name = item.name, - meCraft = true, - status = '' - }) - end - end - - return false -end - -function craftItems(itemList) - - local recipes = Util.readTable('recipes') or { } - local items = ME.getAvailableItems() - - -- turtle craft anything we can, build up list for ME items - local keys = Util.keys(itemList) - for _,key in pairs(keys) do - local item = itemList[key] - craftItem(items, recipes, item, item, itemList) - end - - -- second pass is to request crafting from ME with aggregated items - for _,item in pairs(itemList) do - if item.meCraft then - - local alreadyCrafting = false - local jobList = ME.getJobList() - - for _,v in pairs(jobList) do - if v.id == item.id and v.dmg == item.dmg and v.nbt_hash == item.nbt_hash then - alreadyCrafting = true - end - end - - if alreadyCrafting then - item.status = '(crafting)' - elseif not ME.isCPUAvailable() then - item.status = '(waiting)' - else - item.status = '(failed)' - - local qty = item.qty - while qty >= 1 do -- try to request smaller quantities until successful - if ME.craft(item.id, item.dmg, item.nbt_hash, qty) then - item.status = '(crafting)' - break -- successfully requested crafting - end - qty = math.floor(qty / 2) - end - end - end - end -end - --- AE 1 (obsolete) -function isCrafting(jobList, id, dmg) - for _, job in pairs(jobList) do - if job.id == id and job.dmg == dmg then - return job - end - end -end - -local nullDevice = { - setCursorPos = function(...) end, - write = function(...) end, - getSize = function() return 13, 20 end, - isColor = function() return false end, - setBackgroundColor = function(...) end, - setTextColor = function(...) end, - clear = function(...) end, -} - -local function jobMonitor(jobList) - - local mon - - if device.monitor then - mon = UI.Device({ - deviceType = 'monitor', - textScale = .5, - }) - else - mon = UI.Device({ - device = nullDevice - }) - end - - jobListGrid = UI.Grid({ - parent = mon, - sortColumn = 'name', - columns = { - { heading = 'Qty', key = 'qty', width = 6 }, - { heading = 'Crafting', key = 'name', width = mon.width / 2 - 10 }, - { heading = 'Status', key = 'status', width = mon.width - 10 }, - }, - }) -end - -function getAutocraftItems(items) - local t = Util.readTable('resource.limits') or { } - local itemList = { } - - for _,res in pairs(t) do - - if res.auto and res.auto == 'yes' then - res.qty = 4 -- this could be higher to increase autocrafting speed - table.insert(itemList, res) - end - end - return itemList -end - -local function getItemWithQty(items, res, ignore_dmg) - - local item = getItem(items, res, ignore_dmg) - - if item then - - if ignore_dmg and ignore_dmg == 'yes' then - local qty = 0 - - for _,v in pairs(items) do - if item.id == v.id and item.nbt_hash == v.nbt_hash then - if item.max_dmg > 0 or item.dmg == v.dmg then - qty = qty + v.qty - end - end - end - - item.qty = qty - end - end - - return item -end - -function watchResources(items) - - local itemList = { } - - local t = Util.readTable('resource.limits') or { } - for k, res in pairs(t) do - local item = getItemWithQty(items, res, res.ignore_dmg) - res.limit = tonumber(res.limit) - res.low = tonumber(res.low) - if not item then - item = { - id = res.id, - dmg = res.dmg, - nbt_hash = res.nbt_hash, - name = res.name, - qty = 0 - } - end - - if res.limit and item.qty > res.limit then - Logger.debug("Purging " .. item.qty-res.limit .. " " .. res.name) - if not ME.extract(item.id, item.dmg, item.nbt_hash, item.qty - res.limit, config.trashDirection) then - Logger.debug('Failed to purge ' .. res.name) - end - - elseif res.low and item.qty < res.low then - if res.ignore_dmg and res.ignore_dmg == 'yes' then - item.dmg = 0 - end - table.insert(itemList, { - id = item.id, - dmg = item.dmg, - nbt_hash = item.nbt_hash, - qty = res.low - item.qty, - name = item.name, - status = '' - }) - 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 - }, - idField = UI.Text { - x = 5, y = 3, width = UI.term.width - 10, - }, - form = UI.Form { - x = 4, y = 4, height = 8, rex = -4, - [1] = UI.TextEntry { - width = 7, - backgroundColor = colors.gray, - backgroundFocusColor = colors.gray, - formLabel = 'Min', formKey = 'low', help = 'Craft if below min' - }, - [2] = UI.TextEntry { - width = 7, - backgroundColor = colors.gray, - backgroundFocusColor = colors.gray, - formLabel = 'Max', formKey = 'limit', help = 'Eject if above max' - }, - [3] = UI.Chooser { - width = 7, - formLabel = 'Autocraft', formKey = 'auto', - nochoice = 'No', - choices = { - { name = 'Yes', value = 'yes' }, - { name = 'No', value = 'no' }, - }, - help = 'Craft until out of ingredients' - }, - [4] = UI.Chooser { - width = 7, - formLabel = 'Ignore Dmg', formKey = 'ignore_dmg', - nochoice = 'No', - choices = { - { name = 'Yes', value = 'yes' }, - { name = 'No', value = 'no' }, - }, - help = 'Ignore damage of item' - }, - }, - statusBar = UI.StatusBar { } -} - -function itemPage:enable() - 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 { } - for k,v in pairs(t) do - if v.id == values.id and v.dmg == values.dmg then - table.remove(t, k) - break - end - end - local keys = { 'name', 'auto', 'id', 'low', 'dmg', 'max_dmg', 'nbt_hash', 'limit', 'ignore_dmg' } - local filtered = { } - for _,key in pairs(keys) do - filtered[key] = values[key] - end - - table.insert(t, 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 = 'Learn', event = 'learn' }, - { text = 'Forget', event = 'forget' }, - }, - }, - grid = UI.Grid { - y = 2, height = UI.term.height - 2, - columns = { - { heading = 'Name', key = 'name' , width = 22 }, - { heading = 'Qty', key = 'qty' , width = 5 }, - { heading = 'Min', key = 'low' , width = 4 }, - { heading = 'Max', key = 'limit', width = 4 }, - }, - sortColumn = 'name', - }, - statusBar = UI.StatusBar { - backgroundColor = colors.gray, - width = UI.term.width, - filterText = UI.Text { - x = 2, width = 6, - value = 'Filter', - }, - filter = UI.TextEntry { - x = 9, width = 19, - limit = 50, - }, - refresh = UI.Button { - x = 31, width = 8, - text = 'Refresh', - event = 'refresh', - }, - }, - accelerators = { - r = 'refresh', - q = 'quit', - } -} - -function listingPage.grid:getRowTextColor(row, selected) - if row.is_craftable then - return colors.yellow - end - if row.has_recipe then - if selected then - return colors.blue - end - return colors.lightBlue - end - return UI.Grid:getRowTextColor(row, selected) -end - -function listingPage.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.qty = Util.toBytes(row.qty) - if row.low then - row.low = Util.toBytes(row.low) - end - if row.limit then - row.limit = Util.toBytes(row.limit) - 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 - Event.exitPullEvents() - - elseif event.type == 'grid_select' then - local selected = event.selected - itemPage.form:setValues(selected) - itemPage.titleBar.title = selected.name - itemPage.idField.value = selected.id - UI:setPage('item') - - elseif event.type == 'refresh' then - self:refresh() - self.grid:draw() - - elseif event.type == 'learn' then - if not duckAntenna then - self.statusBar:timedStatus('Missing peripherals', 3) - else - UI:setPage('craft') - end - - elseif event.type == 'forget' then - local item = self.grid:getSelected() - if item then - local recipes = Util.readTable('recipes') or { } - local key = uniqueKey(item) - local recipe = recipes[key] - - if recipe then - recipes[key] = nil - Util.writeTable('recipes', recipes) - end - - local resources = Util.readTable('resource.limits') or { } - for k,v in pairs(resources) do - if v.id == item.id and v.dmg == item.dmg then - table.remove(resources, k) - Util.writeTable('resource.limits', resources) - break - end - end - - 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 = ME.getAvailableItems('all') - - mergeResources(self.allItems) - - Util.each(self.allItems, function(item) - item.lname = item.name:lower() - end) - - self:applyFilter() -end - -function listingPage:applyFilter() - local t = filterItems(self.allItems, self.filter) - self.grid:setValues(t) -end - --- without duck antenna -local function getTurtleInventory() - local inventory = { } - for i = 1,16 do - if turtle.getItemCount(i) > 0 then - turtle.select(i) - local item = turtle.getItemDetail() - inventory[i] = { - id = item.name, - dmg = item.damage, - qty = item.count, - name = item.name, - } - end - end - return inventory -end - --- Strip off color prefix -local function safeString(text) - - local val = text:byte(1) - - if val < 32 or val > 128 then - - local newText = {} - for i = 4, #text do - local val = text:byte(i) - newText[i - 3] = (val > 31 and val < 127) and val or 63 - end - return string.char(unpack(newText)) - end - - return text -end - -local function filter(t, filter) - local keys = Util.keys(t) - for _,key in pairs(keys) do - if not Util.key(filter, key) then - t[key] = nil - end - end -end - -local function learnRecipe(page) - local t = Util.readTable('recipes') or { } - local recipe = { } - local ingredients = duckAntenna.getAllStacks(false) -- getTurtleInventory() - if ingredients then - turtle.select(1) - if turtle.craft() then - recipe = duckAntenna.getAllStacks(false) -- getTurtleInventory() - if recipe and recipe[1] then - recipe = recipe[1] - local key = uniqueKey(recipe) - - clearGrid() - - recipe.name = safeString(recipe.display_name) - filter(recipe, { 'name', 'id', 'dmg', 'nbt_hash', 'qty', 'max_size' }) - - for _,ingredient in pairs(ingredients) do - ingredient.name = safeString(ingredient.display_name) - filter(ingredient, { 'name', 'id', 'dmg', 'nbt_hash', 'qty', 'max_size', 'max_dmg' }) - - if ingredient.max_dmg > 0 then -- let's try this... - ingredient.dmg = 0 - end - end - recipe.ingredients = ingredients - recipe.ignore_dmg = 'no' - - t[key] = recipe - - Util.writeTable('recipes', t) - listingPage.statusBar.filter:setValue(recipe.name) - listingPage.statusBar:timedStatus('Learned: ' .. recipe.name, 3) - listingPage.filter = recipe.name - listingPage:refresh() - listingPage.grid:draw() - - return true - end - else - page.statusBar:timedStatus('Failed to craft', 3) - end - else - page.statusBar:timedStatus('No recipe defined', 3) - end -end - -craftPage = UI.Dialog { - height = 7, width = UI.term.width - 6, - backgroundColor = colors.lightGray, - titleBar = UI.TitleBar { - title = 'Learn Recipe', - previousPage = true, - }, - idField = UI.Text { - x = 5, - y = 3, - width = UI.term.width - 10, - value = 'Place recipe in turtle' - }, - accept = UI.Button { - rx = -13, ry = -2, - text = 'Ok', event = 'accept', - }, - cancel = UI.Button { - rx = -8, ry = -2, - text = 'Cancel', event = 'cancel' - }, - statusBar = UI.StatusBar { - status = 'Crafting paused' - } -} - -function craftPage:enable() - craftingPaused = true - self:focusFirst() - UI.Dialog.enable(self) -end - -function craftPage:disable() - craftingPaused = false - UI.Dialog.disable(self) -end - -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 - return true -end - -UI:setPages({ - listing = listingPage, - item = itemPage, - craft = craftPage, -}) - -UI:setPage(listingPage) -listingPage:setFocus(listingPage.statusBar.filter) - -clearGrid() -jobMonitor() -jobListGrid:draw() -jobListGrid:sync() - -Event.onInterval(5, function() - - 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() - - 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 - end -end) - -UI:pullEvents() -jobListGrid.parent:reset()