Files
opus-apps/apps/Crafter.lua
2018-02-24 15:02:56 -05:00

1130 lines
27 KiB
Lua

--[[
Turtle/machine crafting.
Requirements:
Turtle must be restricted forward and back by some obstacle.
The turtle must have access to the main inventory at the most forward location.
Machines must be placed above or below the line along the turtle's backwards travel.
Optional:
Monitors can be placed touching the turtle at the most forward position to
display crafting status.
Sample setups:
M = machine, I = inventory, O = obstacle, T = turtle
Turtle facing <---
MMMM
IT O
MMM
IMMMM
O O
MMMMMM
]]--
_G.requireInjector()
local InventoryAdapter = require('inventoryAdapter')
local Config = require('config')
local Event = require('event')
local itemDB = require('itemDB')
local Peripheral = require('peripheral')
local UI = require('ui')
local Terminal = require('terminal')
local Util = require('util')
local colors = _G.colors
local os = _G.os
local term = _G.term
local turtle = _G.turtle
local config = {
computerFacing = 'north',
inventorySide = 'front',
monitor = 'type/monitor',
}
Config.load('crafter', config)
repeat until not turtle.forward()
local inventoryAdapter = InventoryAdapter.wrap({
side = config.inventorySide,
facing = config.computerFacing
})
if not inventoryAdapter then
error('Invalid inventory configuration')
end
local RESOURCE_FILE = 'usr/config/resources.db'
local RECIPES_FILE = 'usr/config/recipes2.db'
local MACHINES_FILE = 'usr/config/machines.db'
local STATUS_ERROR = 'error'
local STATUS_INFO = 'info'
local STATUS_SUCCESS = 'success'
local STATUS_WARNING = 'warning'
local recipes = Util.readTable(RECIPES_FILE) or { }
local resources
local machines = { }
local jobListGrid
local listing, docked = false, false
local function getItem(items, inItem, ignoreDamage)
for _,item in pairs(items) do
if item.name == inItem.name then
if ignoreDamage then
return item
elseif item.damage == inItem.damage and item.nbtHash == inItem.nbtHash then
return item
end
end
end
end
local function uniqueKey(item)
return table.concat({ item.name, item.damage, item.nbtHash }, ':')
end
local function getItemQuantity(items, res)
local count = 0
for _,v in pairs(items) do
if res.name == v.name and
((not res.damage and v.maxDamage > 0) or res.damage == v.damage) and
((not res.nbtHash and v.nbtHash) or res.nbtHash == v.nbtHash) then
count = count + v.count
end
end
return count
end
local function mergeResources(t)
for _,v in pairs(resources) do
local item = getItem(t, v)
if item then
Util.merge(item, v)
else
item = Util.shallowCopy(v)
item.count = 0
table.insert(t, item)
end
end
for k in pairs(recipes) do
local v = itemDB:splitKey(k)
local item = getItem(t, v)
if not item then
item = Util.shallowCopy(v)
item.count = 0
table.insert(t, item)
end
item.has_recipe = true
end
for _,v in pairs(t) do
if not v.displayName then
v.displayName = itemDB:getName(v)
end
v.lname = v.displayName:lower()
end
end
local function filterItems(t, filter)
if filter then
local r = {}
filter = filter:lower()
for _,v in pairs(t) do
if string.find(v.lname, filter) then
table.insert(r, v)
end
end
return r
end
return t
end
local function clearGrid()
for i = 1, 16 do
local count = turtle.getItemCount(i)
if count > 0 then
inventoryAdapter:insert(i, count)
if turtle.getItemCount(i) ~= 0 then
return false
end
end
end
return true
end
local function undock()
while listing do
os.sleep(.5)
end
docked = false
end
local function gotoMachine(machine)
undock()
for _ = 1, machine.index do
if not turtle.back() then
return
end
end
return true
end
local function dock()
if not docked then
repeat until not turtle.forward()
end
docked = true
end
local function getItems()
while not docked do
os.sleep(.5)
end
listing = true
local items
for _ = 1, 5 do
items = inventoryAdapter:listItems()
if items then
break
end
end
if not items then
error('could not check inventory')
end
listing = false
return items
end
local function isMachineEmpty(machine, item)
local side = turtle.getAction(machine.dir).side
local methods = Util.transpose(Peripheral.getMethods(side))
local list = { true }
if methods.getAllStacks then -- 1.7x
list = Peripheral.call(side, 'getAllStacks', false)
elseif methods.list then
list = Peripheral.call(side, 'list')
elseif methods.getProgress then
if Peripheral.call(side, 'getProgress') == 0 then
return true
end
else
item.statusCode = STATUS_ERROR
item.status = 'Unable to check empty status'
return
end
if tonumber(machine.ignoreSlot) then
list[tonumber(machine.ignoreSlot)] = nil
end
if Util.empty(list) then
return true
end
item.statusCode = STATUS_INFO
item.status = 'machine busy'
end
local function craftItem(ikey, item, items, machineStatus)
dock()
local resource = resources[ikey]
if not resource or not resource.machine then
item.statusCode = STATUS_ERROR
item.status = 'machine not defined'
return
end
local machine = Util.find(machines, 'order', resource.machine)
if not machine then
item.statusCode = STATUS_ERROR
item.status = 'invalid machine'
return
end
local ms = machineStatus[machine.order]
if not ms then
ms = { count = 0 }
machineStatus[machine.order] = ms
end
local slot = 1
local maxCount = math.ceil(item.need / item.recipe.count)
maxCount = math.min(machine.maxCount or 64, maxCount)
if maxCount > 0 and maxCount - ms.count <= 0 then
item.statusCode = STATUS_INFO
item.status = 'machine busy'
return
end
maxCount = maxCount - ms.count
for key,qty in pairs(item.recipe.ingredients) do
local ingredient = itemDB:get(key)
local c = math.min(maxCount * qty, getItemQuantity(items, ingredient))
c = math.min(c, ingredient.maxCount)
c = math.floor(c / qty)
if c < maxCount then
maxCount = c
end
if maxCount <= 0 then
item.status = 'Missing ' .. ingredient.displayName
item.statusCode = STATUS_WARNING
return
end
end
if machine.maxCount then
ms.count = ms.count + maxCount
end
turtle.setStatus('Craft: ' .. itemDB:getName(ikey))
for key,qty in pairs(item.recipe.ingredients) do
local ingredient = itemDB:get(key)
-- local c = item.craftable * qty
-- while c > 0 do
--debug(key)
inventoryAdapter:provide(ingredient, maxCount * qty, slot)
if turtle.getItemCount(slot) ~= maxCount * qty then
item.status = 'Extract failed: ' .. (ingredient.displayName or itemDB:getName(ingredient))
item.statusCode = STATUS_ERROR
return
end
-- c = c - maxCount
slot = slot + 1
--end
end
if not gotoMachine(machine) then
item.status = 'failed to find machine'
item.statusCode = STATUS_ERROR
else
if machine.empty and not isMachineEmpty(machine, item) then
return
end
if machine.dir == 'up' then
turtle.emptyInventory(turtle.dropUp)
else
turtle.emptyInventory(turtle.dropDown)
end
if #turtle.getFilledSlots() ~= 0 then
item.statusCode = STATUS_INFO
item.status = 'machine busy'
else
item.statusCode = STATUS_SUCCESS
item.status = 'crafting'
end
end
end
local function expandList(list, items)
local summed = { }
local function sumItems(key, count)
local item = itemDB:splitKey(key)
local summedItem = summed[key]
if not summedItem then
summedItem = Util.shallowCopy(item)
summedItem.recipe = recipes[key]
summedItem.count = getItemQuantity(items, item)
summedItem.displayName = itemDB:getName(item)
summedItem.total = 0
summedItem.need = 0
summedItem.used = 0
summedItem.craftable = 0
summed[key] = summedItem
end
local total = count
local used = math.min(summedItem.count, total)
local need = total - used
summedItem.total = summedItem.total + total
summedItem.count = summedItem.count - used
summedItem.used = summedItem.used + used
summedItem.need = summedItem.need + need
if need > 0 and summedItem.recipe then
need = math.ceil(need / summedItem.recipe.count)
for ikey,iqty in pairs(summedItem.recipe.ingredients) do
sumItems(ikey, math.ceil(need * iqty))
end
end
end
for key, item in pairs(list) do
sumItems(key, item.count)
end
return Util.filter(summed, function(a) return a.need > 0 end)
end
local function watchResources(items)
local craftList = { }
for _,res in pairs(resources) do
if res.low then
local item = Util.shallowCopy(res)
item.nbtHash = res.nbtHash
item.damage = res.damage
if res.ignoreDamage then
item.damage = nil
end
item.count = getItemQuantity(items, item)
if item.count < res.low then
item.displayName = itemDB:getName(res)
item.count = res.low -- - item.count
craftList[uniqueKey(res)] = item
end
end
end
return craftList
end
local function craftItems()
local machineStatus = { }
local items = getItems()
local craftList = watchResources(items)
local list = expandList(craftList, items)
jobListGrid:setValues(list)
jobListGrid:update()
jobListGrid:draw()
jobListGrid:sync()
for key, item in pairs(list) do
if item.need > 0 and item.recipe then
craftItem(key, item, items, machineStatus)
dock()
items = getItems()
clearGrid()
elseif item.need > 0 then
item.status = 'no recipe'
item.statusCode = STATUS_ERROR
end
jobListGrid:update()
jobListGrid:draw()
jobListGrid:sync()
end
turtle.setStatus('idle')
end
local function loadResources()
resources = Util.readTable(RESOURCE_FILE) or { }
for k,v in pairs(resources) do
Util.merge(v, itemDB:splitKey(k))
end
end
local function saveResources()
local t = { }
for k,v in pairs(resources) do
v = Util.shallowCopy(v)
v.name = nil
v.damage = nil
v.nbtHash = nil
t[k] = v
end
Util.writeTable(RESOURCE_FILE, t)
end
local function findMachines()
turtle.setStatus('Inspecting machines')
dock()
local function getName(dir)
local side = turtle.getAction(dir).side
if Peripheral.isPresent(side) then
local methods = Util.transpose(Peripheral.getMethods(side))
if methods.getMetadata then
local name = Peripheral.call(side, 'getMetadata').displayName
if name and not string.find(name, '.', 1, true) then
return name
end
elseif methods.getInventoryName then -- 1.7x
return Peripheral.call(side, 'getInventoryName')
end
return Peripheral.getType(side)
end
local _, machine = turtle.getAction(dir).inspect()
if not machine or type(machine) ~= 'table' then
return 'Unknown'
end
return machine.name or 'Unknown'
end
local index = 0
local function getMachine(dir)
local name = getName(dir)
table.insert(machines, {
name = name,
rawName = name,
index = index,
dir = dir,
order = #machines + 1
})
end
repeat
getMachine('down')
getMachine('up')
index = index + 1
undock()
until not turtle.back()
local mf = Util.readTable(MACHINES_FILE) or { }
for _,m in pairs(machines) do
local m2 = Util.find(mf, 'order', m.order)
if m2 then
if not m2.rawName then
m2.rawName = m.rawName
end
if m.rawName == m2.rawName then
m.name = m2.name or m.name
end
m.empty = m2.empty
m.ignore = m2.ignore
m.ignoreSlot = m2.ignoreSlot
m.maxCount = m2.maxCount
end
end
end
local function jobMonitor()
local mon = Peripheral.lookup(config.monitor)
if mon then
mon = UI.Device({
device = mon,
textScale = .5,
})
else
mon = UI.Device({
device = Terminal.getNullTerm(term.current())
})
end
jobListGrid = UI.Grid({
parent = mon,
sortColumn = 'displayName',
columns = {
{ heading = 'Qty', key = 'need', width = 6 },
{ heading = 'Crafting', key = 'displayName', width = (mon.width - 18) / 2 },
{ heading = 'Status', key = 'status', },
},
})
function jobListGrid:getRowTextColor(row, selected)
if row.statusCode == STATUS_ERROR then
return colors.red
elseif row.statusCode == STATUS_WARNING then
return colors.yellow
elseif row.statusCode == STATUS_SUCCESS then
return colors.lime
end
return UI.Grid:getRowTextColor(row, selected)
end
jobListGrid:draw()
jobListGrid:sync()
end
local itemPage = UI.Page {
titleBar = UI.TitleBar {
title = 'Limit Resource',
previousPage = true,
event = 'form_cancel',
},
form = UI.Form {
x = 1, y = 2, height = 10, ex = -1,
[1] = UI.TextEntry {
width = 7,
formLabel = 'Min', formKey = 'low', help = 'Craft if below min'
},
[2] = UI.Chooser {
width = 7,
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Ignore damage of item'
},
[3] = UI.Button {
text = 'Select', event= 'selectMachine',
formLabel = 'Machine'
},
info = UI.TextArea {
x = 2, ex = -2, y = 6, height = 3,
textColor = colors.gray,
},
button = UI.Button {
x = 2, y = 9,
text = 'Recipe', event = 'learn',
},
},
machines = UI.SlideOut {
backgroundColor = colors.cyan,
titleBar = UI.TitleBar {
title = 'Select Machine',
previousPage = true,
},
grid = UI.ScrollingGrid {
y = 2, ey = -4,
values = machines,
disableHeader = true,
columns = {
{ heading = '', key = 'index', width = 2 },
{ heading = 'Name', key = 'name'},
},
sortColumn = 'order',
},
button1 = UI.Button {
x = -14, y = -2,
text = 'Ok', event = 'setMachine',
},
button2 = UI.Button {
x = -9, y = -2,
text = 'Cancel', event = 'cancelMachine',
},
},
statusBar = UI.StatusBar { }
}
function itemPage:enable(item)
if item then
self.item = Util.shallowCopy(item)
self.form:setValues(item)
self.titleBar.title = item.displayName or item.name
end
UI.Page.enable(self)
self:focusFirst()
end
function itemPage.form.info:draw()
local recipe = recipes[uniqueKey(itemPage.item)]
self.value = ''
if recipe and itemPage.item.machine then
self.value = string.format('Crafts %d using the %s machine',
recipe.count,
machines[itemPage.item.machine].name)
end
UI.TextArea.draw(self)
end
function itemPage.machines.grid:getRowTextColor(row, selected)
if itemPage.item.machine == row.order then
return colors.yellow
end
return UI.Grid:getRowTextColor(row, selected)
end
--[[
function itemPage.machines:eventHandler(event)
if event.type == 'grid_focus_row' then
self.statusBar:setStatus(string.format('%d %s', event.selected.index, event.selected.dir))
else
return UI.SlideOut.eventHandler(self, event)
end
return true
end
]]
function itemPage:eventHandler(event)
if event.type == 'form_cancel' then
UI:setPreviousPage()
elseif event.type == 'learn' then
UI:setPage('learn', self.item)
elseif event.type == 'setMachine' then
self.item.machine = self.machines.grid:getSelected().order
self.machines:hide()
elseif event.type == 'cancelMachine' then
self.machines:hide()
elseif event.type == 'selectMachine' then
local machineCopy = Util.shallowCopy(machines)
Util.filterInplace(machineCopy, function(m) return not m.ignore end)
self.machines.grid:setValues(machineCopy)
if self.item.machine then
local _, index = Util.find(machineCopy, 'order', self.item.machine)
if index then
self.machines.grid:setIndex(index)
end
else
self.machines.grid:setIndex(1)
end
self.machines:show()
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 keys = { 'name', 'low', 'damage', 'nbtHash', 'machine' }
local filtered = { }
for _,key in pairs(keys) do
filtered[key] = values[key]
end
filtered.low = tonumber(filtered.low)
filtered.machine = self.item.machine
if values.ignoreDamage == true then
filtered.damage = 0
filtered.ignoreDamage = true
end
local key = uniqueKey(filtered)
resources[key] = filtered
saveResources()
UI:setPreviousPage()
else
return UI.Page.eventHandler(self, event)
end
return true
end
local learnPage = UI.Page {
ingredients = UI.ScrollingGrid {
y = 2, height = 3,
disableHeader = true,
columns = {
{ heading = 'Name', key = 'displayName', width = 31 },
{ heading = 'Qty', key = 'count' , width = 5 },
},
sortColumn = 'displayName',
},
grid = UI.ScrollingGrid {
y = 6, height = 5,
disableHeader = true,
columns = {
{ heading = 'Name', key = 'displayName', width = 31 },
{ heading = 'Qty', key = 'count' , width = 5 },
},
sortColumn = 'displayName',
},
filter = UI.TextEntry {
x = 20, ex = -2, y = 5,
limit = 50,
shadowText = 'filter',
backgroundColor = colors.lightGray,
backgroundFocusColor = colors.lightGray,
},
count = UI.TextEntry {
x = 11, y = -1, width = 5,
limit = 50,
},
button1 = UI.Button {
x = -14, y = -1,
text = 'Ok', event = 'accept',
},
button2 = UI.Button {
x = -9, y = -1,
text = 'Cancel', event = 'cancel',
},
}
function learnPage:enable(target)
self.target = target
self.allItems = getItems()
mergeResources(self.allItems)
self.filter.value = ''
self.grid.values = self.allItems
self.grid:update()
self.ingredients.values = { }
self.count.value = 1
if target.has_recipe then
local recipe = recipes[uniqueKey(target)]
self.count.value = recipe.count
for k,v in pairs(recipe.ingredients) do
self.ingredients.values[k] =
{ name = k, count = v, displayName = itemDB:getName(k) }
end
end
self.ingredients:update()
self:setFocus(self.filter)
UI.Page.enable(self)
end
function learnPage:draw()
UI.Window.draw(self)
self:write(2, 1, 'Ingredients', nil, colors.yellow)
self:write(2, 5, 'Inventory', nil, colors.yellow)
self:write(2, 12, 'Produces')
end
function learnPage:eventHandler(event)
if event.type == 'text_change' and event.element == self.filter then
local t = filterItems(learnPage.allItems, event.text)
self.grid:setValues(t)
self.grid:draw()
elseif event.type == 'cancel' then
UI:setPreviousPage()
elseif event.type == 'accept' then
local recipe = {
count = tonumber(self.count.value) or 1,
ingredients = { },
}
for key, item in pairs(self.ingredients.values) do
recipe.ingredients[key] = item.count
end
recipes[uniqueKey(self.target)] = recipe
Util.writeTable(RECIPES_FILE, recipes)
UI:setPreviousPage()
elseif event.type == 'grid_select' then
if event.element == self.grid then
local key = uniqueKey(event.selected)
if not self.ingredients.values[key] then
self.ingredients.values[key] = Util.shallowCopy(event.selected)
self.ingredients.values[key].count = 0
end
self.ingredients.values[key].count = self.ingredients.values[key].count + 1
self.ingredients:update()
self.ingredients:draw()
elseif event.element == self.ingredients then
event.selected.count = event.selected.count - 1
if event.selected.count == 0 then
self.ingredients.values[uniqueKey(event.selected)] = nil
self.ingredients:update()
end
self.ingredients:draw()
end
else
return UI.Page.eventHandler(self, event)
end
return true
end
local machinesPage = UI.Page {
titleBar = UI.TitleBar {
previousPage = true,
title = 'Machines',
},
grid = UI.ScrollingGrid {
y = 2, ey = -2,
values = machines,
columns = {
{ heading = 'Name', key = 'name' },
{ heading = 'Side', key = 'dir', width = 5 },
{ heading = 'Index', key = 'index', width = 5 },
},
sortColumn = 'order',
},
detail = UI.SlideOut {
backgroundColor = colors.cyan,
form = UI.Form {
x = 1, y = 2, ex = -1, ey = -2,
[1] = UI.TextEntry {
formLabel = 'Name', formKey = 'name', help = '...',
limit = 64,
},
[2] = UI.Chooser {
width = 7,
formLabel = 'Hidden', formKey = 'ignore',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Do not show this machine'
},
[3] = UI.Chooser {
width = 7,
formLabel = 'Empty', formKey = 'empty',
nochoice = 'No',
choices = {
{ name = 'Yes', value = true },
{ name = 'No', value = false },
},
help = 'Check if machine is empty before crafting'
},
[4] = UI.TextEntry {
formLabel = 'Ignore Slot', formKey = 'ignoreSlot', help = '...',
limit = 4,
},
[5] = UI.TextEntry {
formLabel = 'Max Craft', formKey = 'maxCount', help = '...',
limit = 4,
},
},
statusBar = UI.StatusBar(),
},
statusBar = UI.StatusBar {
values = 'Select Machine',
},
accelerators = {
h = 'toggle_hidden',
}
}
function machinesPage:enable()
self.grid:update()
UI.Page.enable(self)
end
function machinesPage.detail:eventHandler(event)
if event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help)
end
return UI.SlideOut.eventHandler(self, event)
end
function machinesPage.grid:getRowTextColor(row, selected)
if row.ignore then
return colors.yellow
end
return UI.Grid:getRowTextColor(row, selected)
end
function machinesPage:eventHandler(event)
if event.type == 'grid_select' then
self.detail.form:setValues(event.selected)
self.detail:show()
elseif event.type == 'toggle_hidden' then
local selected = self.grid:getSelected()
if selected then
selected.ignore = not selected.ignore
Util.writeTable(MACHINES_FILE, machines)
self:draw()
end
elseif event.type == 'form_complete' then
self.detail.form.values.empty = self.detail.form.values.empty == true
self.detail.form.values.ignore = self.detail.form.values.ignore == true
self.detail.form.values.ignoreSlot = tonumber(self.detail.form.values.ignoreSlot)
self.detail.form.values.maxCount = tonumber(self.detail.form.values.maxCount)
Util.writeTable(MACHINES_FILE, machines)
self.detail:hide()
elseif event.type == 'form_cancel' then
self.detail:hide()
else
UI.Page.eventHandler(self, event)
end
return true
end
local listingPage = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = 'Forget', event = 'forget' },
{ text = 'Machines', event = 'machines' },
{ text = 'Refresh', event = 'refresh', x = -9 },
},
},
grid = UI.Grid {
y = 2, height = UI.term.height - 2,
columns = {
{ heading = 'Name', key = 'displayName' },
{ heading = 'Qty', key = 'count' , width = 5 },
{ heading = 'Min', key = 'low' , width = 4 },
},
sortColumn = 'displayName',
},
statusBar = UI.Window {
y = -1,
filter = UI.TextEntry {
limit = 50,
shadowText = 'filter',
shadowTextColor = colors.lightGray,
backgroundColor = colors.gray,
backgroundFocusColor = colors.gray,
},
},
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
return colors.cyan
end
return UI.Grid:getRowTextColor(row, selected)
end
function listingPage.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
row.count = Util.toBytes(row.count)
if row.low then
row.low = Util.toBytes(row.low)
end
return row
end
function listingPage.statusBar.filter:eventHandler(event)
if event.type == 'mouse_rightclick' then
self.value = ''
self:draw()
local page = UI:getCurrentPage()
page.filter = nil
page:applyFilter()
page.grid:draw()
page:setFocus(self)
end
return UI.TextEntry.eventHandler(self, event)
end
function listingPage:eventHandler(event)
if event.type == 'quit' then
UI:exitPullEvents()
elseif event.type == 'grid_select' then
local selected = event.selected
UI:setPage('item', selected)
elseif event.type == 'refresh' then
self:refresh()
self.grid:draw()
self.statusBar.filter:focus()
elseif event.type == 'machines' then
UI:setPage('machines')
elseif event.type == 'craft' then
UI:setPage('craft', self.grid:getSelected())
elseif event.type == 'forget' then
local item = self.grid:getSelected()
if item then
local key = uniqueKey(item)
if recipes[key] then
recipes[key] = nil
Util.writeTable(RECIPES_FILE, recipes)
end
if resources[key] then
resources[key] = nil
Util.writeTable(RESOURCE_FILE, resources)
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 = getItems()
mergeResources(self.allItems)
self:applyFilter()
end
function listingPage:applyFilter()
local t = filterItems(self.allItems, self.filter)
self.grid:setValues(t)
end
-- randomly errors in 1.7x with "you are not attached to this computer"
print('Inspecting machines')
local retryCount = 0
while true do
Util.clear(machines)
local s, m = pcall(findMachines)
if not s and m then
_G.printError(m)
else
break
end
retryCount = retryCount + 1
if retryCount > 3 then
error(m)
end
print('retrying...')
end
loadResources()
dock()
clearGrid()
jobMonitor()
UI:setPages({
listing = listingPage,
machines = machinesPage,
item = itemPage,
learn = learnPage,
})
UI:setPage(listingPage)
listingPage:setFocus(listingPage.statusBar.filter)
Event.on('turtle_abort', function()
UI:exitPullEvents()
end)
Event.onInterval(30, function()
dock()
if turtle.getFuelLevel() < 100 then
turtle.select(1)
inventoryAdapter:provide({ name = 'minecraft:coal', damage = 1 }, 16, 1)
turtle.refuel()
end
craftItems()
end)
turtle.setStatus('idle')
UI:pullEvents()
jobListGrid.parent:reset()