diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index e091e21..cafb071 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -387,6 +387,10 @@ local function rawExport(source, target, item, qty, slot) local amount = math.min(qty, stack.count) if amount > 0 then amount = transfer(key, amount, slot) + if amount > 0 then + source.lastUpdate = os.clock() + target.lastUpdate = os.clock() + end end qty = qty - amount total = total + amount @@ -460,6 +464,11 @@ local function rawInsert(source, target, slot, qty) _G._debug(m) end + if count > 0 then + source.lastUpdate = os.clock() + target.lastUpdate = os.clock() + end + return count end diff --git a/milo/apps/brewArray.lua b/milo/apps/brewArray.lua new file mode 100644 index 0000000..5b6c0bb --- /dev/null +++ b/milo/apps/brewArray.lua @@ -0,0 +1,159 @@ +--[[ +pastebin run uzghlbnc +package install core +package install milo +reboot + +Use multiple brewing stands at once to brew potions. +SETUP: + Place an introspection module into the turtles inventory. + Connect turtle to milo network with a wired modem. + Connect turtle to a second wired modem that is connected to brewing stands ONLY. + Add as many brewing stands as needed. +CONFIGURATION: + Set turtle as a "Generic Inventory" + export coal to slot 2 + import from slot 3 +Use this turtle for machine crafting. +--]] + +local Event = require('event') +local Peripheral = require('peripheral') +local Util = require('util') + +local device = _G.device +local fs = _G.fs +local os = _G.os +local turtle = _G.turtle + +local STARTUP_FILE = 'usr/autorun/brewArray.lua' + +local function equip(side, item, rawName) + local equipped = Peripheral.lookup('side/' .. side) + + if equipped and equipped.type == item then + return true + end + + if not turtle.equip(side, rawName or item) then + if not turtle.selectSlotWithQuantity(0) then + error('No slots available') + end + turtle.equip(side) + if not turtle.equip(side, item) then + error('Unable to equip ' .. item) + end + end + + turtle.select(1) +end + +equip('left', 'plethora:introspection', 'plethora:module:0') +local intro = device['plethora:introspection'] +local inv = intro.getInventory() + +if not fs.exists(STARTUP_FILE) then + Util.writeFile(STARTUP_FILE, + [[os.sleep(1) +shell.openForegroundTab('packages/milo/apps/brewArray.lua')]]) +end + +local brew +local localName + +print('detecting wired modem connected to brewing stands...') +for _, dev in pairs(device) do + if dev.type == 'wired_modem' then + local list = dev.getNamesRemote() + brew = { } + localName = dev.getNameLocal() + for _, name in pairs(list) do + if device[name].type ~= 'minecraft:brewing_stand' then + brew = nil + break + end + table.insert(brew, device[name]) + end + end + if brew then + print('Using wired modem: ' .. dev.name) + print('Brewing stands: ' .. #brew) + break + end +end + +if not brew then + error('Turtle must be connected to a second wired_modem connected to brewing stands only') +end + +_G.printError([[Program must be restarted if new brewing stands are added.]]) + +-- slots 1-3: bottles +-- slot 4: ingredient +-- slot 5: blaze powder + +local function process(list) + local active = false + + for _, brewing in ipairs(Util.shallowCopy(brew)) do + local s, m = pcall(function()-- block updates can cause errors + local bs = brewing.list() + + local cooking = bs[1] and bs[2] and bs[3] and bs[4] + if cooking then + active = true + end + + -- fuel + local fuel = bs[5] or { count = 0 } + if fuel.count < 1 then + print('fueling ' ..brewing.name) + brewing.pullItems(localName, 5, 1, 5) + end + + if not cooking and (bs[1] or bs[2] or bs[3] or bs[4]) then + print('pulling from : ' .. brewing.name) + for i = 1, 4 do + brewing.pushItems(localName, i, 1, 6 + i) + end + end + + if not cooking and list[1] and list[2] and list[3] and list[4] then + print('brewing : ' .. brewing.name) + for i = 1, 4 do + brewing.pullItems(localName, i, 1, i) + list[i].count = list[i].count - 1 + if list[i].count == 0 then + list[i] = nil + end + end + + -- push brewing stand to end of list + Util.removeByValue(brew, brewing) + table.insert(brew, brewing) + end + end) + if not s and m then + _G.printError(m) + end + end + + return active +end + +Event.on('turtle_inventory', function() + while true do + if not process(inv.list()) then + break + end + os.sleep(3) + end +end) + +Event.onInterval(5, function() + -- for some reason, it keeps stalling ... + os.queueEvent('turtle_inventory') +end) + +os.queueEvent('turtle_inventory') +Event.pullEvents() diff --git a/milo/plugins/statsView.lua b/milo/plugins/statsView.lua new file mode 100644 index 0000000..2212bea --- /dev/null +++ b/milo/plugins/statsView.lua @@ -0,0 +1,268 @@ +local Ansi = require('ansi') +local Event = require('event') +local Milo = require('milo') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors +local context = Milo:getContext() +local device = _G.device +local os = _G.os + +--[[ Configuration Page ]]-- +local template = +[[%sDisplays the amount of items entering or leaving storage.%s +Right-clicking on the activity monitor will reset the totals.]] + +local wizardPage = UI.WizardPage { + title = 'Status Monitor', + index = 2, + backgroundColor = colors.cyan, + [1] = UI.TextArea { + x = 2, ex = -2, y = 2, ey = 6, + marginRight = 0, + value = string.format(template, Ansi.yellow, Ansi.reset), + }, + form = UI.Form { + x = 2, ex = -2, y = 7, ey = -2, + manualControls = true, + [1] = UI.Chooser { + width = 9, + formLabel = 'Font Size', formKey = 'textScale', + nochoice = 'Small', + choices = { + { name = 'Small', value = .5 }, + { name = 'Large', value = 1 }, + }, + help = 'Adjust text scaling', + }, + }, +} + +function wizardPage:setNode(node) + self.form:setValues(node) +end + +function wizardPage:validate() + return self.form:save() +end + +function wizardPage:saveNode(node) + os.queueEvent('monitor_resize', node.name) +end + +function wizardPage:isValidType(node) + local m = device[node.name] + return m and m.type == 'monitor' and { + name = 'Status Monitor', + value = 'status', + category = 'display', + help = 'Display storage status' + } +end + +function wizardPage:isValidFor(node) + return node.mtype == 'activity' +end + +UI:getPage('nodeWizard').wizard:add({ statusMonitor = wizardPage }) + +--[[ Display ]]-- +local function createPage(node) + local monitor = UI.Device { + device = node.adapter, + textScale = node.textScale or .5, + } + + function monitor:resize() + self.textScale = node.textScale or .5 + UI.Device.resize(self) + end + + local page = UI.Page { + parent = monitor, + tabs = UI.Tabs { + [1] = UI.Tab { + tabTitle = 'Overview', + titleBar = UI.TitleBar { + title = 'Overview', + }, + textArea = UI.TextArea { + y = 3, + }, + }, + [2] = UI.Tab { + tabTitle = 'Storage', + titleBar = UI.TitleBar { + title = 'Storage chest usage', + }, + grid = UI.Grid { + y = 2, + columns = { + { heading = 'Name', key = 'name' }, + { heading = 'Size', key = 'size', width = 5 }, + { heading = 'Used', key = 'used', width = 5 }, + { heading = 'Perc', key = 'perc', width = 5 }, + }, + sortColumn = 'name', + }, + }, + [3] = UI.Tab { + tabTitle = 'Offline', + titleBar = UI.TitleBar { + title = 'Offline Nodes', + }, + grid = UI.ScrollingGrid { + y = 2, + columns = { + { heading = 'Name', key = 'name' }, + }, + sortColumn = 'name', + }, + } + }, + timestamp = os.clock(), + } + + local overviewTab = page.tabs[1] + local usageTab = page.tabs[2] + local stateTab = page.tabs[3] + + local function getStorageStats() + local stats = { } + for n in context.storage:filterActive('storage') do + if n.adapter.size and n.adapter.list then + pcall(function() + if not n.adapter.__size then + n.adapter.__size = n.adapter.size() + n.adapter.__used = Util.size(n.adapter.list()) + end + if n.adapter.__lastUpdate ~= n.adapter.lastUpdate then + n.adapter.__used = Util.size(n.adapter.list()) + n.adapter.__lastUpdate = n.adapter.lastUpdate + end + table.insert(stats, { + name = n.displayName or n.name, + size = n.adapter.__size, + used = n.adapter.__used, + perc = math.floor(n.adapter.__used / n.adapter.__size * 100), + }) + end) + end + end + return stats + end + + function stateTab:refresh() + self.grid.values = { } + for _, v in pairs(context.storage.nodes) do + if v.mtype ~= 'hidden' then + if not v.adapter or not v.adapter.online then + table.insert(self.grid.values, { + name = v.displayName or v.name + }) + end + end + end + self.grid:update() + end + + function stateTab:enable() + self:refresh() + self.handle = Event.onInterval(5, function() + self:refresh() + self.grid:draw() + self:sync() + end) + UI.Tab.enable(self) + end + + function stateTab:disable() + Event.off(self.handle) + end + + function usageTab:refresh() + self.grid:setValues(getStorageStats()) + end + + function usageTab:enable() + self:refresh() + self.handle = Event.onInterval(5, function() + self:refresh() + self.grid:draw() + self:sync() + end) + UI.Tab.enable(self) + end + + function usageTab:disable() + Event.off(self.handle) + end + + function usageTab.grid:getRowTextColor(row, selected) + if row.lastCount and row.lastCount ~= row.count then + return row.count > row.lastCount and colors.yellow or colors.lightGray + end + return UI.Grid:getRowTextColor(row, selected) + end + + function overviewTab.textArea:draw() + local stats = getStorageStats() + local usedSlots, totalSlots, totalItems = 0, 0, 0 + local formatString = [[ +Storage Usage : %d%% +Slots : %d of %d used +Unique Items : %d +Total Items : %d +]] + + for _, v in pairs(stats) do + usedSlots = usedSlots + v.used + totalSlots = totalSlots + v.size + end + + for _,v in pairs(context.storage.cache) do + totalItems = totalItems + v.count + end + + self.value = string.format(formatString, + math.floor(usedSlots / totalSlots * 100), + usedSlots, totalSlots, + Util.size(context.storage.cache), + totalItems) + UI.TextArea.draw(self) + end + + function overviewTab:enable() + self.handle = Event.onInterval(5, function() + self.textArea:draw() + self:sync() + end) + UI.Tab.enable(self) + end + + function overviewTab:disable() + Event.off(self.handle) + end + + UI:setPage(page) + return page +end + +local pages = { } + +--[[ Task ]]-- +local task = { + name = 'status', + priority = 99, +} + +function task:cycle() + for node in context.storage:filterActive('status') do + if not pages[node.name] then + pages[node.name] = createPage(node) + end + end +end + +Milo:registerTask(task) diff --git a/neural/elytraFly.lua b/neural/elytraFly.lua index d3da878..99e036f 100644 --- a/neural/elytraFly.lua +++ b/neural/elytraFly.lua @@ -1,25 +1,32 @@ --- credit: osmarks https://pastebin.com/ZP9Q1HCT - -local modules = _G.peripheral.wrap "back" +local modules = _G.peripheral.wrap('back') local os = _G.os +print('Based on code from osmarks') +print('https://pastebin.com/ZP9Q1HCT') + local function get_meta() return modules.getMetaOwner() end while true do local meta = get_meta() - local power = 4 - if meta.isElytraFlying or meta.isFlying then power = 1 end + if not meta.isSneaking then + local power = 4 + if meta.isElytraFlying or meta.isFlying then power = 1 end - while meta.isSneaking or meta.isFlying or meta.isElytraFlying do - meta = get_meta() - modules.launch(meta.yaw, meta.pitch, power) - os.sleep(0.1) - end + while not meta.isSneaking and meta.isFlying or meta.isElytraFlying do + meta = get_meta() + if meta.pitch < 0 then + modules.launch(meta.yaw, meta.pitch, power) + end + os.sleep(0.1) + end - if meta.motionY < -0.8 then - modules.launch(0, 270, power / 2) + if not meta.isSneaking then + if meta.motionY < -0.8 then + modules.launch(0, 270, power / 2) + end + end end os.sleep(0.4)