From 72bd16502b46d288be3898e20fb4abdee02a5356 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 1 Apr 2017 19:21:49 -0400 Subject: [PATCH] builder upgrade --- apps/Lua.lua | 10 +- apps/System.lua | 2 +- apps/builder.lua | 287 ++++++++++------- apps/refinedManager.lua | 603 +++++++++++++++++++++++++++++++++++ apps/simpleMiner.lua | 11 +- apps/supplier.lua | 19 +- sys/apis/blocks.lua | 18 +- sys/apis/chestProvider.lua | 12 +- sys/apis/chestProvider18.lua | 103 ++++++ sys/apis/peripheral.lua | 31 +- sys/apis/ui.lua | 27 +- sys/extensions/device.lua | 2 +- sys/extensions/os.lua | 10 + sys/services/device.lua | 2 +- 14 files changed, 977 insertions(+), 160 deletions(-) create mode 100644 apps/refinedManager.lua create mode 100644 sys/apis/chestProvider18.lua diff --git a/apps/Lua.lua b/apps/Lua.lua index fa5ca6d..255164f 100644 --- a/apps/Lua.lua +++ b/apps/Lua.lua @@ -68,9 +68,6 @@ end function page:enable() self:setFocus(self.prompt) UI.Page.enable(self) - if not device then - self.menuBar.Device:disable() - end end local function autocomplete(env, oLine, x) @@ -131,6 +128,13 @@ function page:eventHandler(event) self.prompt:updateCursor() elseif event.type == 'device' then + if not _G.device then + sandboxEnv.device = { } + for _,side in pairs(peripheral.getNames()) do + local key = string.format('%s:%s', peripheral.getType(side), side) + sandboxEnv.device[ key ] = peripheral.wrap(side) + end + end self:setPrompt('device', true) self:executeStatement('device') diff --git a/apps/System.lua b/apps/System.lua index 94b4717..2ae96e9 100644 --- a/apps/System.lua +++ b/apps/System.lua @@ -86,7 +86,7 @@ local systemPage = UI.Page { grid = UI.ScrollingGrid { y = 4, values = { - { name = 'CC version', value = os.version() }, + { name = 'CC version', value = os.getVersion() }, { name = 'Lua version', value = _VERSION }, { name = 'MC version', value = _MC_VERSION or 'unknown' }, { name = 'Disk free', value = Util.toBytes(fs.getFreeSpace('/')) }, diff --git a/apps/builder.lua b/apps/builder.lua index c477d1c..793b9ef 100644 --- a/apps/builder.lua +++ b/apps/builder.lua @@ -11,11 +11,15 @@ local UI = require('ui') local Schematic = require('schematic') local Profile = require('profile') local TableDB = require('tableDB') -local ChestProvider = require('chestProvider') local MEProvider = require('meProvider') local Blocks = require('blocks') local Point = require('point') +local ChestProvider = require('chestProvider') +if os.getVersion() == 1.8 then + ChestProvider = require('chestProvider18') +end + Logger.filter('modem_send', 'event', 'ui') if device.wireless_modem then @@ -38,24 +42,10 @@ local Builder = { fuelItem = { id = 'minecraft:coal', dmg = 0 }, resourceSlots = 15, facing = 'south', + confirmFacing = false, } --- these wrenches work relative to the turtle -local GoodWrenches = { - [ 'appliedEnergistics2:item.ToolCertusQuartzWrench' ] = true, - [ 'appliedEnergistics2:item.ToolNetherQuartzWrench' ] = true, - [ 'EnderIO:itemYetaWrench' ] = true, -} - ---[[ --- these wrenches work but take more hits to turn a piston - -local BadButUsableWrenches = { - [ 'ThermalExpansion:wrench' ] = true, - [ 'MineFactoryReloaded:hammer' ] = true, - [ 'ImmersiveEngineering:tool' ] = true, -} ---]] +local pistonFacings --[[-- SubDB --]]-- subDB = TableDB({ @@ -359,7 +349,7 @@ function Builder:getBlockCounts() local blocks = { } -- add a couple essential items to the supply list to allow replacements - local wrench = subDB:getSubstitutedItem('ThermalExpansion:wrench', 0) + local wrench = subDB:getSubstitutedItem('SubstituteAWrench', 0) wrench.qty = 0 wrench.need = 1 blocks[wrench.id .. ':' .. wrench.dmg] = wrench @@ -461,7 +451,7 @@ function Builder:getSupplyList(blockIndex) index = 15, } - local wrench = subDB:getSubstitutedItem('ThermalExpansion:wrench', 0) + local wrench = subDB:getSubstitutedItem('SubstituteAWrench', 0) slots[16] = { id = wrench.id, dmg = wrench.dmg, @@ -612,9 +602,7 @@ function Builder:getSupplies() if s.need > 0 then local item = self.itemProvider:getItemInfo(s.id, s.dmg) if item then - if item.name then - s.name = item.name - end + s.name = item.name local qty = math.min(s.need - s.qty, item.qty) @@ -629,6 +617,8 @@ function Builder:getSupplies() self.itemProvider:provide(item, qty, s.index) s.qty = turtle.getItemCount(s.index) end + else + s.name = blocks.blockDB:getName(s.id, s.dmg) end end if s.qty < s.need then @@ -855,26 +845,83 @@ end function Builder:getWrenchSlot() - local wrench = subDB:getSubstitutedItem('ThermalExpansion:wrench', 0) + local wrench = subDB:getSubstitutedItem('SubstituteAWrench', 0) return Builder:selectItem(wrench.id, wrench.dmg) end -function Builder:wrenchBlock(side, count) +function Builder:getTurtleFacing() + + if os.getVersion() == 1.8 then + + local directions = { -- reversed directions + [5] = 'west', + [3] = 'north', + [4] = 'east', + [2] = 'south', + } + + if self:selectItem('minecraft:piston', 0) then + turtle.placeUp() + local _, bi = turtle.inspectUp() + turtle.digUp() + return directions[bi.metadata] + end + return + end + return Builder.facing +end + +function Builder:wrenchBlock(side, facing) local s = Builder:getWrenchSlot() if not s then b.needResupply = true - return + return false end - turtle.select(s.index) - for i = 1,count do + local key = turtle.point.heading .. '-' .. facing + local count = pistonFacings[side][key] + + if count then + turtle.select(s.index) + for i = 1,count do + turtle.getAction(side).place() + end + return true + end + + local directions = { + [5] = 'east', + [3] = 'south', + [4] = 'west', + [2] = 'north', + [0] = 'down', + [1] = 'up', + } + + if turtle.getHeadingInfo(facing).heading < 4 then + local offsetDirection = (turtle.getHeadingInfo(Builder.facing).heading + + turtle.getHeadingInfo(facing).heading) % 4 + facing = turtle.getHeadingInfo(offsetDirection).direction + end + + count = 0 + print('determining wrench count') + for i = 1, 6 do + local _, bi = turtle.getAction(side).inspect() + local pistonFacing = directions[bi.metadata] + + if facing == pistonFacing then + pistonFacings[side][key] = count + return true + end + count = count + 1 turtle.getAction(side).place() end - return true + return false end -- place piston, wrench piston to face downward, extend, remove piston @@ -890,15 +937,10 @@ function Builder:placePiston(b) end if not turtle.place(ps.index) then - return false + return end - local wrenchCount = 5 - if GoodWrenches[ws.id] then - wrenchCount = 2 - end - - local success = self:wrenchBlock('forward', wrenchCount) --wrench piston to point downwards + local success = self:wrenchBlock('forward', 'down') --wrench piston to point downwards rs.setOutput('front', true) os.sleep(.25) @@ -907,7 +949,7 @@ function Builder:placePiston(b) turtle.select(ps.index) turtle.dig() - return true + return success end function Builder:goto(x, z, y, heading) @@ -1008,6 +1050,10 @@ function Builder:placeDirectionalBlock(b, slot, travelPlane) local isSouth = (turtle.getHeadingInfo(Builder.facing).heading + turtle.getHeadingInfo(stairUpDirections[d]).heading) % 4 == 1 + if os.getVersion() == 1.8 then + isSouth = false -- no stair bug in this version + end + if isSouth then -- for some reason, the south facing stair doesn't place correctly @@ -1058,6 +1104,7 @@ function Builder:placeDirectionalBlock(b, slot, travelPlane) [ 'piston-west' ] = 'west', [ 'piston-east' ] = 'east', [ 'piston-down' ] = 'down', + [ 'piston-up' ] = 'up', } if pistonDirections[d] then @@ -1071,55 +1118,23 @@ function Builder:placeDirectionalBlock(b, slot, travelPlane) return false end - if GoodWrenches[ws.id] then - -- piston turns relative to turtle position :) - local rotatedPistonDirections = { - [ 'piston-east' ] = 'south', - [ 'piston-south' ] = 'west', - [ 'piston-west' ] = 'north', - [ 'piston-north' ] = 'east', - [ 'piston-down' ] = 'down', - } + -- piston turns relative to turtle position :) + local rotatedPistonDirections = { + [ 'piston-east' ] = 0, + [ 'piston-south' ] = 1, + [ 'piston-west' ] = 2, + [ 'piston-north' ] = 3, + } - local wrenchCount + self:gotoEx(b.x, b.z, b.y, nil, travelPlane) - if d == 'piston-down' then - self:gotoEx(b.x -1, b.z, b.y, 0, travelPlane) - wrenchCount = 2 - else - local hi = turtle.getHeadingInfo(rotatedPistonDirections[d]) - self:gotoEx(b.x + hi.xd, b.z + hi.zd, b.y, (hi.heading + 2) % 4, travelPlane) - wrenchCount = 1 - end + local heading = rotatedPistonDirections[d] + if heading and turtle.point.heading % 2 ~= heading % 2 then + turtle.setHeading(heading) + end - if self:place(slot) then - self:wrenchBlock('forward', wrenchCount) - turtle.up() - b.placed = self:placePiston(b) - end - - else -- cresent wrench - -- piston turns relative to the world :( - local wrenchCounts = { - [ 1 ] = 4, -- east - [ 2 ] = 2, -- south - [ 3 ] = 3, -- west - [ 4 ] = 1, -- north - } - - self:goto(b.x, b.z, b.y, nil, travelPlane) - - local wrenchCount = 5 - - if d ~= 'piston-down' then - local offsetDirection = (turtle.getHeadingInfo(Builder.facing).heading + - turtle.getHeadingInfo(pistonDirections[d]).heading) % 4 - wrenchCount = wrenchCounts[offsetDirection + 1] - end - - if self:placeDown(slot) then - b.placed = self:wrenchBlock('down', wrenchCount) - end + if self:placeDown(slot) then + b.placed = self:wrenchBlock('down', pistonDirections[d]) end end @@ -1254,6 +1269,13 @@ function Builder:build() else travelPlane = self:findTravelPlane(self.index) turtle.status = 'building' + if not self.confirmFacing then + local facing = self:getTurtleFacing() + if facing then + self.confirmFacing = true + self.facing = facing + end + end end UI:setPage('blank') @@ -1366,6 +1388,11 @@ function blankPage:draw() self:setCursorPos(1, 1) end +function blankPage:enable() + self:sync() + UI.Page.enable(self) +end + --[[-- selectSubstitutionPage --]]-- selectSubstitutionPage = UI.Page({ titleBar = UI.TitleBar({ @@ -1524,6 +1551,7 @@ function substitutionPage:eventHandler(event) elseif event.type == 'accept' or event.type == 'air' or event.type == 'revert' then self.statusBar:setStatus('Saving changes...') self.statusBar:draw() + self:sync() if event.type == 'air' then self:applySubstitute('minecraft:air', 0) @@ -1674,8 +1702,8 @@ listingPage = UI.Page({ grid = UI.ScrollingGrid({ columns = { { heading = 'Name', key = 'name', width = UI.term.width - 14 }, - { heading = 'Need', key = 'fNeed', width = 5 }, - { heading = 'Have', key = 'fQty', width = 5 }, + { heading = 'Need', key = 'need', width = 5 }, + { heading = 'Have', key = 'qty', width = 5 }, }, sortColumn = 'name', y = 3, @@ -1743,6 +1771,13 @@ function listingPage:eventHandler(event) return UI.Page.eventHandler(self, event) end +function listingPage.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.need = Util.toBytes(row.need) + row.qty = Util.toBytes(row.qty) + return row +end + function listingPage.grid:getRowTextColor(row, selected) if row.is_craftable then return colors.yellow @@ -1759,13 +1794,15 @@ function listingPage:refresh() for _,b in pairs(supplyList) do if b.need > 0 then local item = Builder.itemProvider:getItemInfo(b.id, b.dmg) + if item then local block = blocks.blockDB:lookup(b.id, b.dmg) if not block then - blocks.blockDB:add(b.id, b.dmg, item.name) + blocks.blockDB:add(b.id, b.dmg, item.name, b.id) elseif not blocks.name and item.name then blocks.blockDB:add(b.id, b.dmg, item.name) end + b.name = item.name b.qty = item.qty b.is_craftable = item.is_craftable elseif not b.name then @@ -1775,19 +1812,17 @@ function listingPage:refresh() end blocks.blockDB:flush() - local t = {} - for _,b in pairs(supplyList) do - local block = blocks.blockDB:lookup(b.id, b.dmg) - if block then - b.name = block.name + if self.fullList then + self.grid:setValues(supplyList) + else + local t = {} + for _,b in pairs(supplyList) do + if self.fullList or b.qty < b.need then + table.insert(t, b) + end end - if self.fullList or b.qty < b.need then - table.insert(t, b) - end - b.fNeed = Util.toBytes(b.need) - b.fQty = Util.toBytes(b.qty) + self.grid:setValues(t) end - self.grid:setValues(t) self.grid:setIndex(1) end @@ -1882,13 +1917,19 @@ function startPage:eventHandler(event) if event.type == 'startLevel' then local dialog = UI.Dialog({ - text = UI.Text({ x = 5, y = 3, value = '0 - ' .. schematic.height }), - textEntry = UI.TextEntry({ x = 15, y = 3, '0 - 11' }) + title = 'Enter Starting Level', + height = 7, + form = UI.Form { + y = 3, x = 2, height = 4, + text = UI.Text({ x = 5, y = 1, textColor = colors.gray, value = '0 - ' .. schematic.height }), + textEntry = UI.TextEntry({ x = 15, y = 1, '0 - 11', width = 7 }), + }, + statusBar = UI.StatusBar(), }) dialog.eventHandler = function(self, event) - if event.type == 'accept' then - local l = tonumber(self.textEntry.value) + if event.type == 'form_complete' then + local l = tonumber(self.form.textEntry.value) if l and l < schematic.height and l >= 0 then for k,v in pairs(schematic.blocks) do if v.y >= l then @@ -1901,25 +1942,32 @@ function startPage:eventHandler(event) else self.statusBar:timedStatus('Invalid Level', 3) end - return true + elseif event.type == 'form_cancel' or event.type == 'cancel' then + UI:setPreviousPage() + else + return UI.Dialog.eventHandler(self, event) end - - return UI.Dialog.eventHandler(self, event) + return true end - dialog.titleBar.title = 'Enter Starting Level' - dialog:setFocus(dialog.textEntry) + dialog:setFocus(dialog.form.textEntry) UI:setPage(dialog) elseif event.type == 'startBlock' then - local dialog = UI.Dialog({ - text = UI.Text({ x = 5, y = 3, value = '1 - ' .. #schematic.blocks }), - textEntry = UI.TextEntry({ x = 15, y = 3, value = tostring(Builder.index) }) - }) + local dialog = UI.Dialog { + title = 'Enter Block Number', + height = 7, + form = UI.Form { + y = 3, x = 2, height = 4, + text = UI.Text { x = 5, y = 1, value = '1 - ' .. #schematic.blocks, textColor = colors.gray }, + textEntry = UI.TextEntry { x = 15, y = 1, value = tostring(Builder.index), width = 7 } + }, + statusBar = UI.StatusBar(), + } dialog.eventHandler = function(self, event) - if event.type == 'accept' then - local bn = tonumber(self.textEntry.value) + if event.type == 'form_complete' then + local bn = tonumber(self.form.textEntry.value) if bn and bn < #schematic.blocks and bn >= 0 then Builder.index = bn Builder:saveProgress(Builder.index) @@ -1927,14 +1975,15 @@ function startPage:eventHandler(event) else self.statusBar:timedStatus('Invalid Block', 3) end - return true + elseif event.type == 'form_cancel' or event.type == 'cancel' then + UI:setPreviousPage() + else + return UI.Dialog.eventHandler(self, event) end - - return UI.Dialog.eventHandler(self, event) + return true end - dialog.titleBar.title = 'Enter Block Number' - dialog:setFocus(dialog.textEntry) + dialog:setFocus(dialog.form.textEntry) UI:setPage(dialog) elseif event.type == 'assignBlocks' then @@ -1991,6 +2040,12 @@ function startPage:eventHandler(event) else print('Starting build') end + + -- reset piston cache in case wrench was substituted + pistonFacings = { + down = { }, + forward = { }, + } Builder:build() Profile.display() diff --git a/apps/refinedManager.lua b/apps/refinedManager.lua new file mode 100644 index 0000000..713a8f6 --- /dev/null +++ b/apps/refinedManager.lua @@ -0,0 +1,603 @@ +local injector = requireInjector or load(http.get('http://pastebin.com/raw/c0TWsScv').readAll())() +require = injector(getfenv(1)) +local Event = require('event') +local UI = require('ui') +local Peripheral = require('peripheral') + +local controller = Peripheral.getByType('refinedstorage:controller') +if not controller then + error('Refined storage controller not found') +end + +multishell.setTitle(multishell.getCurrent(), 'Storage Manager') + +-- 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 listItems() + local items = { } + local list + + pcall(function() + list = controller.listAvailableItems() + end) + + if list then + for k,v in pairs(list) do + local item = controller.findItem(v) + + if item then + Util.merge(item, item.getMetadata()) + item.displayName = safeString(item.displayName) + if item.maxDamage and item.maxDamage > 0 and item.damage > 0 then + item.displayName = item.displayName .. ' (damaged)' + end + item.lname = item.displayName:lower() + + table.insert(items, item) + end + end + end + + return items +end + +function getItem(items, inItem, ignoreDamage) + for _,item in pairs(items) do + if item.name == inItem.name then + if ignoreDamage and ignoreDamage == 'yes' then + return item + elseif item.damage == inItem.damage and item.nbtHash == inItem.nbtHash then + return item + end + end + end +end + +local function uniqueKey(item) + local key = item.name .. ':' .. item.damage + if item.nbtHash then + key = key .. ':' .. item.nbtHash + end + return key +end + +function mergeResources(t) + local resources = Util.readTable('resource.limits') + resources = resources or { } + + 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.ignoreDamage = v.ignoreDamage + else + v.count = 0 + v.limit = tonumber(v.limit) + v.low = tonumber(v.low) + v.auto = v.auto + v.ignoreDamage = v.ignoreDamage + 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 getJobList() + local list = { } + + for _,task in pairs(controller.getCraftingTasks()) do + table.insert(list, task.getPattern().outputs[1]) + end + + return list +end + +function craftItems(itemList, allItems) + + for _,item in pairs(itemList) do + + local alreadyCrafting = false + local jobList = getJobList() + + for _,v in pairs(jobList) do + if v.name == item.name and v.damage == item.damage and v.nbtHash == item.nbtHash then + alreadyCrafting = true + end + end + + local cItem = getItem(allItems, item) + + if alreadyCrafting then + item.status = '(crafting)' + elseif not cItem then + item.status = '(no recipe)' + else + + local count = item.count + while count >= 1 do -- try to request smaller quantities until successful + local s, m = pcall(function() + item.status = '(no recipe)' + if not cItem.craft(count) then + item.status = '(missing ingredients)' + error('failed') + end + item.status = '(crafting)' + end) + if s then + break -- successfully requested crafting + end + count = math.floor(count / 2) + end + end + end +end + +function getAutocraftItems(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.count = 4 -- this could be higher to increase autocrafting speed + table.insert(itemList, res) + end + end + return itemList +end + +local function getItemWithQty(items, res, ignoreDamage) + + local item = getItem(items, res, ignoreDamage) + + if item then + + if ignoreDamage and ignoreDamage == 'yes' then + local count = 0 + + for _,v in pairs(items) do + if item.name == v.name and item.nbtHash == v.nbtHash then + if item.maxDamage > 0 or item.damage == v.damage then + count = count + v.count + end + end + end + + item.count = count + end + end + + return item +end + +function watchResources(items) + + local itemList = { } + + local t = Util.readTable('resource.limits') or { } + for k, res in pairs(t) do + local item = getItemWithQty(items, res, res.ignoreDamage) + res.limit = tonumber(res.limit) + res.low = tonumber(res.low) + if not item then + item = { + damage = res.damage, + nbtHash = res.nbtHash, + name = res.name, + displayName = res.displayName, + count = 0 + } + end + + if res.low and item.count < res.low then + if res.ignoreDamage and res.ignoreDamage == 'yes' then + item.damage = 0 + end + table.insert(itemList, { + damage = item.damage, + nbtHash = item.nbtHash, + count = res.low - item.count, + name = item.name, + displayName = item.displayName, + status = '' + }) + end + end + + return itemList +end + +itemPage = UI.Page { + backgroundColor = colors.lightGray, + titleBar = UI.TitleBar { + title = 'Limit Resource', + previousPage = true, + event = 'form_cancel', + backgroundColor = colors.green + }, + displayName = UI.Window { + x = 5, y = 3, width = UI.term.width - 10, height = 3, + }, + form = UI.Form { + x = 4, y = 6, 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.Chooser { + width = 7, + formLabel = 'Autocraft', formKey = 'auto', + nochoice = 'No', + choices = { + { name = 'Yes', value = 'yes' }, + { name = 'No', value = 'no' }, + }, + help = 'Craft until out of ingredients' + }, + [3] = UI.Chooser { + width = 7, + formLabel = 'Ignore Dmg', formKey = 'ignoreDamage', + nochoice = 'No', + choices = { + { name = 'Yes', value = 'yes' }, + { name = 'No', value = 'no' }, + }, + help = 'Ignore damage of item' + }, + }, + statusBar = UI.StatusBar { } +} + +function itemPage.displayName:draw() + local item = self.parent.item + local str = string.format('Name: %s\nDamage: %d', item.displayName, item.damage) + if item.nbtHash then + str = str .. string.format('\nNBT: %s\n', item.nbtHash) + end + debug(str) + self:setCursorPos(1, 1) + self:print(str) +end + +function itemPage:enable(item) + self.item = item + + self.form:setValues(item) + self.titleBar.title = item.name + self.displayName.value = item.displayName + + 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.name == values.name and v.damage == values.damage then + t[k] = nil + break + end + end + local keys = { 'name', 'displayName', 'auto', 'low', 'damage', 'maxDamage', 'nbtHash', 'limit', 'ignoreDamage' } + local filtered = { } + for _,key in pairs(keys) do + filtered[key] = values[key] + end + + if filtered.ignoreDamage and filtered.ignoreDamage == 'yes' then + filtered.damage = 0 + end + + t[uniqueKey(filtered)] = filtered + --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 = 'Forget', event = 'forget' }, + }, + }, + grid = UI.Grid { + y = 2, height = UI.term.height - 2, + columns = { + { heading = 'Name', key = 'displayName', width = UI.term.width - 14 }, + { heading = 'Qty', key = 'count', width = 5 }, + { heading = 'Min', key = 'low', width = 4 }, + }, + sortColumn = 'lname', + }, + statusBar = UI.StatusBar { + backgroundColor = colors.gray, + width = UI.term.width, + filterText = UI.Text { + x = 2, width = 6, + value = 'Filter', + }, + filter = UI.TextEntry { + x = 9, rex = -12, + limit = 50, + }, + refresh = UI.Button { + rx = -9, width = 8, + text = 'Refresh', + event = 'refresh', + }, + }, + accelerators = { + r = 'refresh', + q = 'quit', + } +} + +function listingPage.grid:getRowTextColor(row, selected) + if row.is_craftable then -- not implemented + return colors.yellow + end + return UI.Grid:getRowTextColor(row, selected) +end + +function listingPage.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.count = Util.toBytes(row.count) + if row.low then + row.low = Util.toBytes(row.low) + end + 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 + UI:setPage('item', selected) + + elseif event.type == 'refresh' then + self:refresh() + self.grid:draw() + + elseif event.type == 'forget' then + local item = self.grid:getSelected() + if item then + + local resources = Util.readTable('resource.limits') or { } + resources[uniqueKey(item)] = nil + Util.writeTable('resource.limits', resources) + + self.statusBar:timedStatus('Forgot: ' .. item.name, 3) + self:refresh() + self.grid:draw() + end + + elseif event.type == 'text_change' then + self.filter = event.text + if #self.filter == 0 then + self.filter = nil + end + self:applyFilter() + self.grid:draw() + self.statusBar.filter:focus() + + else + UI.Page.eventHandler(self, event) + end + return true +end + +function listingPage:enable() + self:refresh() + self:setFocus(self.statusBar.filter) + UI.Page.enable(self) +end + +function listingPage:refresh() + self.allItems = listItems() + mergeResources(self.allItems) + self:applyFilter() +end + +function listingPage:applyFilter() + local t = filterItems(self.allItems, self.filter) + self.grid:setValues(t) +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, + sync = function(...) end, +} + +local function jobMonitor(jobList) + + local mon = Peripheral.getByType('monitor') + + if mon then + mon = UI.Device({ + device = mon, + textScale = .5, + }) + else + mon = UI.Device({ + device = nullDevice + }) + end + + jobListGrid = UI.Grid { + parent = mon, + sortColumn = 'displayName', + columns = { + { heading = 'Qty', key = 'count', width = 6 }, + { heading = 'Crafting', key = 'displayName', width = mon.width / 2 - 10 }, + { heading = 'Status', key = 'status', width = mon.width - 10 }, + }, + } +end + +local function jobMonitor(jobList) + + local mon = Peripheral.getByType('monitor') + 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, + sync = function(...) end, + blit = function(...) end, + } + + if mon then + mon = UI.Device({ + device = mon, + textScale = .5, + }) + else + mon = UI.Device({ + device = nullDevice + }) + end + + jobListGrid = UI.Grid { + parent = mon, + sortColumn = 'displayName', + columns = { + { heading = 'Qty', key = 'count', width = 6 }, + { heading = 'Crafting', key = 'displayName', width = mon.width / 2 - 10 }, + { heading = 'Status', key = 'status', width = mon.width - 10 }, + }, + } + + return jobListGrid +end + +UI:setPages({ + listing = listingPage, + item = itemPage, +}) + +UI:setPage(listingPage) +listingPage:setFocus(listingPage.statusBar.filter) + +local jobListGrid = jobMonitor() +jobListGrid:draw() +jobListGrid:sync() + +function craftingThread() + + while true do + os.sleep(5) + + pcall(function() + + local items = listItems() + + if controller.getNetworkEnergyStored() == 0 then + jobListGrid.parent:clear() + jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'Power failure') + jobListGrid:sync() + + elseif Util.size(items) == 0 then + jobListGrid.parent:clear() + jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system') + jobListGrid:sync() + + else + local itemList = watchResources(items) + jobListGrid:setValues(itemList) + jobListGrid:draw() + jobListGrid:sync() + craftItems(itemList, items) + jobListGrid:update() + jobListGrid:draw() + jobListGrid:sync() + + itemList = getAutocraftItems(items) -- autocrafted items don't show on job monitor + craftItems(itemList, items) + end + end) + end +end + +Event.pullEvents(craftingThread) + +UI.term:reset() +jobListGrid.parent:reset() diff --git a/apps/simpleMiner.lua b/apps/simpleMiner.lua index 4882fc1..99c7545 100644 --- a/apps/simpleMiner.lua +++ b/apps/simpleMiner.lua @@ -24,6 +24,11 @@ local options = { local MIN_FUEL = 7500 local LOW_FUEL = 1500 +local MAX_FUEL = 100000 + +if not term.isColor() then + MAX_FUEL = 20000 +end local mining = { diameter = 1, @@ -175,6 +180,7 @@ end function refuel() if turtle.getFuelLevel() < MIN_FUEL then + local oldStatus = turtle.status status('refueling') if turtle.selectSlot('minecraft:coal:0') then @@ -193,7 +199,7 @@ function refuel() end) end log('Fuel: ' .. turtle.getFuelLevel()) - status('boring') + status(oldStatus) end turtle.select(1) @@ -315,7 +321,7 @@ function mineable(action) collectDrops(action.suck) end - if turtle.getFuelLevel() < 99000 then + if turtle.getFuelLevel() < (MAX_FUEL - 1000) then if block.name == 'minecraft:lava' or block.name == 'minecraft:flowing_lava' then if turtle.selectSlot('minecraft:bucket:0') then if action.place() then @@ -546,6 +552,7 @@ end local function main() repeat while #mining.locations > 0 do + status('searching') if not boreCommand() then return end diff --git a/apps/supplier.lua b/apps/supplier.lua index a61b9f3..be807d8 100644 --- a/apps/supplier.lua +++ b/apps/supplier.lua @@ -6,6 +6,11 @@ local Point = require('point') local TableDB = require('tableDB') local MEProvider = require('meProvider') +local ChestProvider = require('chestProvider') +if os.getVersion() == 1.8 then + ChestProvider = require('chestProvider18') +end + if not device.wireless_modem then error('No wireless modem detected') end @@ -324,9 +329,9 @@ Message.addHandler('needSupplies', turtle.select(15) turtle.placeDown() os.sleep(.1) -- random computer not connected error - local p = peripheral.wrap('bottom') + local p = ChestProvider({ direction = 'up', wrapSide = 'bottom' }) for i = 1, 16 do - p.pullItem('up', i, 64) + p:insert(i, 64) end Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true, point = pt }) @@ -334,9 +339,9 @@ Message.addHandler('needSupplies', Message.waitForMessage('thanks', 5, __BUILDER_ID) --os.sleep(0) - p.condenseItems() + --p.condenseItems() for i = 1, 16 do - p.pushItem('up', i, 64) + p:extract(i, 64) end turtle.digDown() turtle.status = 'waiting' @@ -386,6 +391,12 @@ turtle.setPoint({ x = -1, z = -2, y = 0, heading = 0 }) turtle.saveLocation('supplies') Builder.itemProvider = MEProvider() +if not Builder.itemProvider:isValid() then + Builder.itemProvider = ChestProvider() + if not Builder.itemProvider:isValid() then + error('A chest or ME interface must be below turtle') + end +end turtle.run(function() Event.pullEvents(onTheWay) diff --git a/sys/apis/blocks.lua b/sys/apis/blocks.lua index 850a075..dbf3683 100644 --- a/sys/apis/blocks.lua +++ b/sys/apis/blocks.lua @@ -96,15 +96,13 @@ function blockDB:seedDB(dir) end lastID = strId - local sep = string.find(line[2], ':') - if not sep then - nid = tonumber(line[2]) - dmg = 0 - else - nid = tonumber(string.sub(line[2], 1, sep - 1)) - dmg = tonumber(string.sub(line[2], sep + 1, #line[2])) + local t = { } + string.gsub(line[2], '(%d+)', function(d) table.insert(t, d) end) + nid = tonumber(t[1]) + dmg = 0 + if t[2] then + dmg = tonumber(t[2]) end - self:add(nid, dmg, name, strId) end @@ -653,13 +651,13 @@ function blockTypeDB:seedDB() }) blockTypeDB:addTemp('piston', { -- piston placement is broken in 1.7 -- need to add work around { 0, nil, 0, 'piston-down' }, - { 1, nil, 0 }, + { 1, nil, 0, 'piston-up' }, { 2, nil, 0, 'piston-north' }, { 3, nil, 0, 'piston-south' }, { 4, nil, 0, 'piston-west' }, { 5, nil, 0, 'piston-east' }, { 8, nil, 0, 'piston-down' }, - { 9, nil, 0 }, + { 9, nil, 0, 'piston-up' }, { 10, nil, 0, 'piston-north' }, { 11, nil, 0, 'piston-south' }, { 12, nil, 0, 'piston-west' }, diff --git a/sys/apis/chestProvider.lua b/sys/apis/chestProvider.lua index 5418333..3688bd1 100644 --- a/sys/apis/chestProvider.lua +++ b/sys/apis/chestProvider.lua @@ -54,7 +54,9 @@ function ChestProvider:getItemInfo(id, dmg) item.max_size = stack.max_size end end - return item + if item.name then + return item + end end function ChestProvider:craft(id, dmg, qty) @@ -79,7 +81,13 @@ function ChestProvider:provide(item, qty, slot) end end end - + +function ChestProvider:extract(slot, qty) + if self.p then + self.p.pushItem(self.direction, slot, qty) + end +end + function ChestProvider:insert(slot, qty) if self.p then local s, m = pcall(function() self.p.pullItem(self.direction, slot, qty) end) diff --git a/sys/apis/chestProvider18.lua b/sys/apis/chestProvider18.lua new file mode 100644 index 0000000..631e74a --- /dev/null +++ b/sys/apis/chestProvider18.lua @@ -0,0 +1,103 @@ +local class = require('class') +local Logger = require('logger') + +local ChestProvider = class() + +function ChestProvider:init(args) + + args = args or { } + + self.stacks = {} + self.name = 'chest' + self.direction = args.direction or 'up' + self.wrapSide = args.wrapSide or 'bottom' + self.p = peripheral.wrap(self.wrapSide) +end + +function ChestProvider:isValid() + return self.p and self.p.list +end + +function ChestProvider:refresh() + if self.p then + --self.p.condenseItems() + self.stacks = self.p.list() + local t = { } + for _,s in pairs(self.stacks) do + s.id = s.name + s.dmg = s.damage + s.qty = s.count + local key = s.id .. ':' .. s.dmg + if t[key] and t[key].qty < 64 then + t[key].max_size = t[key].qty + else + t[key] = { + qty = s.qty + } + end + end + for _,s in ipairs(self.stacks) do + local key = s.id .. ':' .. s.dmg + if t[key].max_size then + s.max_size = t[key].qty + else + s.max_size = 64 + end + end + end + return self.stacks +end + +function ChestProvider:getItemInfo(id, dmg) + local item = { id = id, dmg = dmg, qty = 0, max_size = 64 } + for k,stack in pairs(self.stacks) do + if stack.id == id and stack.dmg == dmg then + local meta = self.p.getItemMeta(k) + if meta then + item.name = meta.displayName + item.qty = item.qty + meta.count + item.max_size = meta.maxCount + end + end + end + if item.name then + return item + end +end + +function ChestProvider:craft(id, dmg, qty) + return false +end + +function ChestProvider:craftItems(items) +end + +function ChestProvider:provide(item, qty, slot) + if self.p then + self:refresh() + for key,stack in pairs(self.stacks) do + if stack.id == item.id and stack.dmg == item.dmg then + local amount = math.min(qty, stack.qty) + self.p.pushItems(self.direction, key, amount, slot) + qty = qty - amount + if qty <= 0 then + break + end + end + end + end +end + +function ChestProvider:extract(slot, qty) + if self.p then + self.p.pushItems(self.direction, slot, qty) + end +end + +function ChestProvider:insert(slot, qty) + if self.p then + self.p.pullItems(self.direction, slot, qty) + end +end + +return ChestProvider diff --git a/sys/apis/peripheral.lua b/sys/apis/peripheral.lua index bf93fac..19380e6 100644 --- a/sys/apis/peripheral.lua +++ b/sys/apis/peripheral.lua @@ -1,6 +1,21 @@ local Peripheral = { } -function Peripheral.addDevice(side) +local function getDeviceList() + + if _G.device then + return _G.device + end + + local deviceList = { } + + for _,side in pairs(peripheral.getNames()) do + Peripheral.addDevice(deviceList, side) + end + + return deviceList +end + +function Peripheral.addDevice(deviceList, side) local name = side local ptype = peripheral.getType(side) @@ -28,33 +43,33 @@ function Peripheral.addDevice(side) if sides[name] then local i = 1 local uniqueName = ptype - while device[uniqueName] do + while deviceList[uniqueName] do uniqueName = ptype .. '_' .. i i = i + 1 end name = uniqueName end - device[name] = peripheral.wrap(side) - Util.merge(device[name], { + deviceList[name] = peripheral.wrap(side) + Util.merge(deviceList[name], { name = name, type = ptype, side = side, }) - return device[name] + return deviceList[name] end function Peripheral.getBySide(side) - return Util.find(device, 'side', side) + return Util.find(getDeviceList(), 'side', side) end function Peripheral.getByType(typeName) - return Util.find(device, 'type', typeName) + return Util.find(getDeviceList(), 'type', typeName) end function Peripheral.getByMethod(method) - for _,p in pairs(device) do + for _,p in pairs(getDeviceList()) do if p[method] then return p end diff --git a/sys/apis/ui.lua b/sys/apis/ui.lua index c2dac11..5a0cf54 100644 --- a/sys/apis/ui.lua +++ b/sys/apis/ui.lua @@ -438,6 +438,7 @@ end function Manager:pullEvents(...) Event.pullEvents(...) + self.term:reset() end function Manager:exitPullEvents() @@ -1075,7 +1076,7 @@ function UI.TransitionSlideLeft:update(device) self.canvas:blit(device, { x = self.x, y = self.y, - ex = self.ex - x + self.x + 1, + ex = self.ex - x + self.x, ey = self.ey }, { x = x, y = self.y }) end @@ -1108,13 +1109,13 @@ function UI.TransitionSlideRight:update(device) self.lastScreen:blit(device, { x = self.x, y = self.y, - ex = self.ex - x + self.x + 1, + ex = self.ex - x + self.x, ey = self.ey }, { x = x, y = self.y }) self.canvas:blit(device, { x = self.ex - x + self.x, y = self.y, - ex = self.ex + 1, + ex = self.ex, ey = self.ey }, { x = self.x, y = self.y }) end @@ -1431,8 +1432,6 @@ end UI.Grid = class(UI.Window) UI.Grid.defaults = { UIElement = 'Grid', - x = 1, - y = 1, index = 1, inverseSort = false, disableHeader = false, @@ -2571,7 +2570,7 @@ UI.StatusBar.defaults = { function UI.StatusBar:init(args) local defaults = UI:getDefaults(UI.StatusBar, args) UI.GridLayout.init(self, defaults) - self:setStatus(self.status) + self:setStatus(self.status, true) end function UI.StatusBar:setParent() @@ -2583,12 +2582,15 @@ function UI.StatusBar:setParent() end end -function UI.StatusBar:setStatus(status) +function UI.StatusBar:setStatus(status, noDraw) if type(status) == 'string' then self.values[1] = { status = status } else self.values[1] = status end + if not noDraw then + self:draw() + end end function UI.StatusBar:setValue(name, value) @@ -2796,7 +2798,8 @@ function UI.TextEntry:updateScroll() elseif self.pos < self.scroll then self.scroll = self.pos end --- debug('p:%d s:%d w:%d l:%d', self.pos, self.scroll, self.width, self.limit) + + --debug('p:%d s:%d w:%d l:%d', self.pos, self.scroll, self.width, self.limit) end function UI.TextEntry:draw() @@ -2910,7 +2913,7 @@ function UI.TextEntry:eventHandler(event) return true elseif event.type == 'mouse_click' then - if self.focused then + if self.focused and event.x > 1 then self.pos = event.x + self.scroll - 2 self:updateCursor() return true @@ -3166,7 +3169,7 @@ function UI.Dialog:init(args) titleBar = UI.TitleBar({ previousPage = true, title = defaults.title }), }) - UI.setProperties(defaults, args) + --UI.setProperties(defaults, args) UI.Page.init(self, defaults) end @@ -3266,8 +3269,8 @@ function UI.NftImage:setImage(image) end UI:loadTheme('config/ui.theme') -if _HOST and string.find(_HOST, 'CCEmuRedux') then - UI:loadTheme('config/ccemuredux.theme') +if os.getVersion() >= 1.79 then + UI:loadTheme('config/ext.theme') end UI:setDefaultDevice(UI.Device({ device = term.current() })) diff --git a/sys/extensions/device.lua b/sys/extensions/device.lua index e83e56a..1f56390 100644 --- a/sys/extensions/device.lua +++ b/sys/extensions/device.lua @@ -4,5 +4,5 @@ require = requireInjector(getfenv(1)) local Peripheral = require('peripheral') for _,side in pairs(peripheral.getNames()) do - Peripheral.addDevice(side) + Peripheral.addDevice(device, side) end diff --git a/sys/extensions/os.lua b/sys/extensions/os.lua index 2af8c54..58b2722 100644 --- a/sys/extensions/os.lua +++ b/sys/extensions/os.lua @@ -90,6 +90,16 @@ function os.isPocket() return not not pocket end +function os.getVersion() + if _CC_VERSION then + return tonumber(_CC_VERSION) + end + if _HOST then + return tonumber(_HOST:gmatch('[%d]+%.?[%d][%d]', '%1')()) + end + return 1.7 +end + function os.registerApp(entry) local apps = { } Config.load('apps', apps) diff --git a/sys/services/device.lua b/sys/services/device.lua index 55f0ee8..b4ce2cf 100644 --- a/sys/services/device.lua +++ b/sys/services/device.lua @@ -14,7 +14,7 @@ end Event.addHandler('peripheral', function(event, side) if side then - local dev = Peripheral.addDevice(side) + local dev = Peripheral.addDevice(device, side) if dev then term.setTextColor(attachColor) Util.print('[%s] %s attached', dev.side, dev.name)