diff --git a/apis/chestAdapter.lua b/apis/chestAdapter.lua index 5502967..26241b1 100644 --- a/apis/chestAdapter.lua +++ b/apis/chestAdapter.lua @@ -86,13 +86,12 @@ function ChestAdapter:listItems(throttle) if not entry then self.cache[key] = v - local ikey = { v.name, v.damage, v.nbtHash } - if not itemDB:get(ikey) then + if not itemDB:get(v) then local t = { } for _,k in pairs(keys) do t[k] = v[k] end - itemDB:add(ikey, t) + itemDB:add(t) end else entry.count = entry.count + v.count diff --git a/apis/chestAdapter18.lua b/apis/chestAdapter18.lua index c669ff8..4a7643c 100644 --- a/apis/chestAdapter18.lua +++ b/apis/chestAdapter18.lua @@ -51,17 +51,20 @@ function ChestAdapter:isValid() end function ChestAdapter:getCachedItemDetails(item, k) - local key = { item.name, item.damage, item.nbtHash } - - local detail = itemDB:get(key) + local detail = itemDB:get(item) if not detail then pcall(function() detail = self.getItemMeta(k) end) if not detail then +debug(item) +debug('no details') -- error('Inventory has changed') return end -- NOT SUFFICIENT if detail.name ~= item.name then +debug('name change ?') +debug(item) +debug(detail) -- error('Inventory has changed') return end @@ -72,7 +75,8 @@ function ChestAdapter:getCachedItemDetails(item, k) end end - itemDB:add(key, detail) +debug('adding') + itemDB:add(detail) end if detail then return Util.shallowCopy(detail) @@ -91,25 +95,29 @@ function ChestAdapter:listItems(throttle) throttle = throttle or Util.throttle() for k,v in pairs(self.list()) do - local key = table.concat({ v.name, v.damage, v.nbtHash }, ':') + if v.count > 0 then + local key = table.concat({ v.name, v.damage, v.nbtHash }, ':') - local entry = self.cache[key] - if not entry then - entry = self:getCachedItemDetails(v, k) + local entry = self.cache[key] if not entry then - return -- Inventory has changed + entry = self:getCachedItemDetails(v, k) + if not entry then + debug(key) + debug('inv changed') + return -- Inventory has changed + end + entry.count = 0 + self.cache[key] = entry + table.insert(items, entry) end - entry.count = 0 - self.cache[key] = entry - table.insert(items, entry) - end - if entry then - entry.count = entry.count + v.count + if entry then + entry.count = entry.count + v.count + end + throttle() end - throttle() end - +--read() itemDB:flush() return items diff --git a/apis/itemDB.lua b/apis/itemDB.lua index c921498..4670faf 100644 --- a/apis/itemDB.lua +++ b/apis/itemDB.lua @@ -31,8 +31,8 @@ local function safeString(text) return text end -function itemDB:makeKey(item) - return { item.name, item.damage, item.nbtHash } +local function makeKey(item) + return { item.name, item.damage or '*', item.nbtHash } end function itemDB:splitKey(key, item) @@ -52,26 +52,55 @@ function itemDB:splitKey(key, item) end function itemDB:get(key) + if type(key) == 'string' then - key = self:makeKey(self:splitKey(key)) + key = self:splitKey(key) end - local item = TableDB.get(self, key) - + local item = TableDB.get(self, makeKey(key)) if item then return item end - if not key[2] or key[2] ~= 0 then - item = TableDB.get(self, { key[1], 0, key[3] }) - if item and item.maxDamage > 0 then + -- try finding an item that has damage values + if type(key.damage) == 'number' then + item = TableDB.get(self, makeKey({ name = key.name, nbtHash = key.nbtHash })) + if item and item.maxDamage then item = Util.shallowCopy(item) - item.damage = key[2] - item.displayName = string.format('%s (damage: %s)', item.displayName, (item.damage or '*')) + item.damage = key.damage + if item.maxDamage > 0 and type(item.damage) == 'number' and item.damage > 0 then + item.displayName = string.format('%s (damage: %s)', item.displayName, item.damage) + end return item end end + if key.nbtHash then + item = self:get({ name = key.name, damage = key.damage }) + if item and (item.maxDamage > 0 or item.damage == key.damage) then + item.nbtHash = key.nbtHash + return item + end + + local damage = tonumber(key.damage) + for _,item in pairs(self.data) do + if item.name == key.name and + ((not damage or item.maxDamage > 0) or damage == item.damage) and + item.nbtHash then + item = Util.shallowCopy(item) + item.damage = damage or item.damage + if item.maxDamage > 0 and item.damage and item.damage > 0 then + item.displayName = string.format('%s (damage: %s)', item.displayName, item.damage) + end + item.nbtHash = key.nbtHash + return item + end + end + end + +--debug('miss: ' .. table.concat(makeKey(key), ':')) + +--[[ if not key[3] then for _,item in pairs(self.data) do if item.name == key[1] and @@ -83,11 +112,13 @@ function itemDB:get(key) end end end +--]] end -function itemDB:add(key, item) +function itemDB:add(item) + local key = makeKey(item) if item.maxDamage > 0 then - key = { key[1], 0, key[3] } + key = makeKey({ name = item.name, damage = '*', nbtHash = item.nbtHash }) end item.displayName = safeString(item.displayName) TableDB.add(self, key, item) @@ -99,7 +130,7 @@ function itemDB:getName(item) item = self:splitKey(item) end - local detail = self:get(self:makeKey(item)) + local detail = self:get(item) if detail then return detail.displayName end @@ -113,7 +144,7 @@ function itemDB:getMaxCount(item) item = self:splitKey(item) end - local detail = self:get(self:makeKey(item)) + local detail = self:get(item) if detail then return detail.maxCount end diff --git a/apis/meAdapter.lua b/apis/meAdapter.lua index b5f2c49..ffbfc2f 100644 --- a/apis/meAdapter.lua +++ b/apis/meAdapter.lua @@ -86,13 +86,12 @@ function MEAdapter:refresh() Util.merge(v, v.item) convertItem(v) - local key = { v.name, v.damage, v.nbtHash } - if not itemDB:get(key) then + if not itemDB:get(v) then local t = { } for _,k in pairs(keys) do t[k] = v[k] end - itemDB:add(key, t) + itemDB:add(t) end end itemDB:flush() diff --git a/apis/refinedAdapter.lua b/apis/refinedAdapter.lua index ddeb1a5..d7e762b 100644 --- a/apis/refinedAdapter.lua +++ b/apis/refinedAdapter.lua @@ -38,9 +38,7 @@ function RefinedAdapter:isOnline() end function RefinedAdapter:getCachedItemDetails(item) - local key = { item.name, item.damage, item.nbtHash } - - local detail = itemDB:get(key) + local detail = itemDB:get(item) if not detail then detail = self.findItem(item) if detail then @@ -57,7 +55,7 @@ function RefinedAdapter:getCachedItemDetails(item) end detail = t - itemDB:add(key, detail) + itemDB:add(detail) end end if detail then @@ -92,10 +90,7 @@ function RefinedAdapter:listItems() end function RefinedAdapter:getItemInfo(fingerprint) - - local key = { fingerprint.name, fingerprint.damage, fingerprint.nbtHash } - - local item = itemDB:get(key) + local item = itemDB:get(fingerprint) if not item then return self:getCachedItemDetails(fingerprint) end diff --git a/apis/turtle/craft.lua b/apis/turtle/craft.lua index 084c643..fca16fa 100644 --- a/apis/turtle/craft.lua +++ b/apis/turtle/craft.lua @@ -32,7 +32,7 @@ local function splitKey(key) return item end -local function getItemCount(items, key) +function Craft.getItemCount(items, key) local item = splitKey(key) local count = 0 for _,v in pairs(items) do @@ -108,12 +108,15 @@ function Craft.craftRecipe(recipe, count, inventoryAdapter) end local items = inventoryAdapter:listItems() + if not items then + return 0, 'Inventory changed' + end count = math.ceil(count / recipe.count) local maxCount = recipe.maxCount or math.floor(64 / recipe.count) for key,icount in pairs(Craft.sumIngredients(recipe)) do - local itemCount = getItemCount(items, key) + local itemCount = Craft.getItemCount(items, key) if itemCount < icount * count then local irecipe = Craft.recipes[key] if irecipe then @@ -156,7 +159,7 @@ function Craft.getResourceList(inRecipe, items, inCount) if not summedItem then summedItem = Util.shallowCopy(item) summedItem.recipe = Craft.recipes[key] - summedItem.count = getItemCount(items, key) + summedItem.count = Craft.getItemCount(items, key) summed[key] = summedItem end summedItem.count = summedItem.count - (count * iqty) @@ -173,6 +176,70 @@ function Craft.getResourceList(inRecipe, items, inCount) return summed end +function Craft.getResourceList3(inRecipe, items, inCount) + local summed = { } + local throttle = Util.throttle() + + local function sumItems(recipe, count) + count = math.ceil(count / recipe.count) + local craftable = count + + for key,iqty in pairs(Craft.sumIngredients(recipe)) do + throttle() + local item = splitKey(key) + local summedItem = summed[key] + if not summedItem then + summedItem = Util.shallowCopy(item) + summedItem.recipe = Craft.recipes[key] + summedItem.count = Craft.getItemCount(items, key) + summedItem.ocount = summedItem.count + summedItem.need = 0 + summedItem.used = 0 + summedItem.missing = 0 + summedItem.craftable = 0 + summed[key] = summedItem + end + + local total = count * iqty -- 4 * 2 + local used = math.min(summedItem.count, total) -- 5 + local need = total - used -- 3 + + if recipe.craftingTools and recipe.craftingTools[key] then + if summedItem.count > 0 then + summedItem.used = 1 + need = 0 + else + summedItem.need = 1 + need = 1 + end + else + summedItem.count = summedItem.count - used + summedItem.used = summedItem.used + used + summedItem.need = summedItem.need + need + end + + if need > 0 then + if not summedItem.recipe then +debug(summedItem) +debug({ total, used, need }) + summedItem.missing = summedItem.missing + need + craftable = math.min(craftable, math.floor(used / iqty)) + else + local c = sumItems(summedItem.recipe, need) -- 4 + craftable = math.min(craftable, math.floor((used + c) / iqty)) + summedItem.craftable = summedItem.craftable + c + end + end + end + + return craftable * recipe.count + end + + sumItems(inRecipe, inCount) + + return summed +end + -- given a certain quantity, return how many of those can be crafted function Craft.getCraftableAmount(recipe, count, items, missing) local function sumItems(recipe, summedItems, count) @@ -180,7 +247,7 @@ function Craft.getCraftableAmount(recipe, count, items, missing) for _ = 1, count do for _,item in pairs(recipe.ingredients) do - local summedItem = summedItems[item] or getItemCount(items, item) + local summedItem = summedItems[item] or Craft.getItemCount(items, item) local irecipe = Craft.recipes[item] if irecipe and summedItem <= 0 then diff --git a/apps/chestManager.lua b/apps/chestManager.lua index 700506e..8adf8d9 100644 --- a/apps/chestManager.lua +++ b/apps/chestManager.lua @@ -84,10 +84,6 @@ local function getItem(items, inItem, ignoreDamage, ignoreNbtHash) end end -local function splitKey(key) - return itemDB:splitKey(key) -end - local function getItemQuantity(items, item) local count = 0 for _,v in pairs(items) do @@ -120,7 +116,7 @@ local function mergeResources(t) end for k in pairs(Craft.recipes) do - local v = splitKey(k) + local v = itemDB:splitKey(k) local item = getItem(t, v) if not item then item = Util.shallowCopy(v) @@ -145,7 +141,7 @@ local function filterItems(t, filter, displayMode) filter = filter:lower() end for _,v in pairs(t) do - if not filter or string.find(v.lname, filter) then + if not filter or string.find(v.lname, filter, 1, true) then if not displayMode or displayMode == 0 or displayMode == 1 and v.count > 0 or @@ -169,16 +165,20 @@ local function isGridClear() 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 + local function clear() + for i = 1, 16 do + local count = turtle.getItemCount(i) + if count > 0 then + inventoryAdapter:insert(i, count) + if turtle.getItemCount(i) ~= 0 then + debug('insert failed') + return false + end end end + return true end - return true + return clear() or clear() end local function addCraftingRequest(item, craftList, count) @@ -194,10 +194,16 @@ end local function craftItem(recipe, items, originalItem, craftList, count) - if craftingPaused or not canCraft or not isGridClear() then + if craftingPaused or not canCraft then return 0 end + if not isGridClear() then + if not clearGrid() then + originalItem.status = 'Grid obstructed' + end + end + local missing = { } local toCraft = Craft.getCraftableAmount(recipe, count, items, missing) if missing.name then @@ -211,11 +217,17 @@ local function craftItem(recipe, items, originalItem, craftList, count) local iRecipe = Craft.recipes[key] if iRecipe then local need = count * qty - local has = getItemQuantity(items, splitKey(key)) + local has = getItemQuantity(items, itemDB:splitKey(key)) +--debug({ key, need, has }) if has < need then -debug('crafting ' .. key .. ' - ' .. need - has) +--debug('crafting ' .. key .. ' - ' .. need - has) +--read() craftItem(iRecipe, items, originalItem, { }, math.ceil((need - has) / iRecipe.count)) items = inventoryAdapter:listItems() + if not items then + error('list failed') + --return 0 + end end end end @@ -232,7 +244,6 @@ debug('crafting ' .. key .. ' - ' .. need - has) if count > 0 then local ingredients = Craft.getResourceList(recipe, items, count) -_G._p = ingredients for _,ingredient in pairs(ingredients) do --if not ingredient.recipe and ingredient.count < 0 then if ingredient.count < 0 then @@ -243,6 +254,94 @@ _G._p = ingredients return crafted end +local function forceCraftItem(inRecipe, items, originalItem, craftList, inCount) + local summed = { } + local throttle = Util.throttle() + + local function sumItems(recipe, count) + count = math.ceil(count / recipe.count) + local craftable = count + + for key,iqty in pairs(Craft.sumIngredients(recipe)) do + throttle() + local item = itemDB:splitKey(key) + local summedItem = summed[key] + if not summedItem then + summedItem = Util.shallowCopy(item) + summedItem.recipe = Craft.recipes[key] + +if summedItem.recipe then +summedItem.recipe.key = key +end + summedItem.count = Craft.getItemCount(items, key) + summedItem.ocount = summedItem.count + summedItem.need = 0 + summedItem.used = 0 + summedItem.missing = 0 + summedItem.craftable = 0 + summed[key] = summedItem + end + + local total = count * iqty -- 4 * 2 + local used = math.min(summedItem.count, total) -- 5 + local need = total - used -- 3 + + if recipe.craftingTools and recipe.craftingTools[key] then + if summedItem.count > 0 then + summedItem.used = 1 + need = 0 + else + summedItem.need = 1 + need = 1 + end + else + summedItem.count = summedItem.count - used + summedItem.used = summedItem.used + used + summedItem.need = summedItem.need + need + end + + if need > 0 then + if not summedItem.recipe then + summedItem.missing = summedItem.missing + need + craftable = math.min(craftable, math.floor(used / iqty)) + else + local c = sumItems(summedItem.recipe, need) -- 4 +debug({ 'summed', total, used, need, c }) + craftable = math.min(craftable, math.floor((used + c) / iqty)) + + summedItem.craftable = summedItem.craftable + c + end + end + end + + if craftable > 0 then +debug('crafting ' .. recipe.key) +debug({ count, craftable }) +_G._p = summed + craftable = Craft.craftRecipe(recipe, craftable, inventoryAdapter) + clearGrid() +debug('got ' .. craftable) + + end + + return craftable * recipe.count + end + + local count = sumItems(inRecipe, inCount) +debug({ 'final', inCount, count }) + if count < inCount then + for _,ingredient in pairs(summed) do + --if not ingredient.recipe and ingredient.count < 0 then + if ingredient.missing > 0 then + addCraftingRequest(ingredient, craftList, ingredient.missing) + originalItem.status = string.format('%s missing', itemDB:getName(ingredient)) + originalItem.statusCode = 'missing' + end + end + end + return count +end + local function craftItems(craftList, allItems) for _,key in pairs(Util.keys(craftList)) do @@ -251,8 +350,16 @@ local function craftItems(craftList, allItems) if recipe then item.status = nil item.statusCode = nil - item.crafted = craftItem(recipe, allItems, item, craftList, item.count) + if item.forceCrafting then +recipe.key = key + item.crafted = forceCraftItem(recipe, allItems, item, craftList, item.count) + else + item.crafted = craftItem(recipe, allItems, item, craftList, item.count) + end allItems = inventoryAdapter:listItems() -- refresh counts + if not allItems then + break + end elseif item.rsControl then item.status = 'Activated' end @@ -332,7 +439,7 @@ local function getAutocraftItems() for _,res in pairs(resources) do if res.auto then - res.count = 64 -- this could be higher to increase autocrafting speed + res.count = 256 -- this could be higher to increase autocrafting speed local key = uniqueKey(res) craftList[key] = res end @@ -366,7 +473,7 @@ local function watchResources(items) local outputs = { } for _,res in pairs(resources) do - local item = getItemWithQty(items, res, res.ignoreDamage, res.ignoreDamage) + local item = getItemWithQty(items, res, res.ignoreDamage, res.ignoreNbtHash) if not item then item = { damage = res.damage, @@ -422,8 +529,15 @@ end local function loadResources() resources = Util.readTable(RESOURCE_FILE) or { } - for k,v in pairs(resources) do - Util.merge(v, splitKey(k)) + for _,k in pairs(Util.keys(resources)) do + local v = resources[k] + Util.merge(v, itemDB:splitKey(k)) + if not v.damage then + v.damage = 0 + v.ignoreDamage = true + resources[k] = nil + resources[uniqueKey(v)] = v + end end end @@ -432,10 +546,18 @@ local function saveResources() for k,v in pairs(resources) do v = Util.shallowCopy(v) - v.name = nil - v.damage = nil - v.nbtHash = nil - t[k] = v + local keys = Util.transpose({ 'auto', 'low', 'limit', + 'ignoreDamage', 'ignoreNbtHash', + 'rsControl', 'rsDevice', 'rsSide' }) + + for _,key in pairs(Util.keys(v)) do + if not keys[key] then + v[key] = nil + end + end + if not Util.empty(v) then + t[k] = v + end end Util.writeTable(RESOURCE_FILE, t) @@ -479,33 +601,19 @@ local itemPage = UI.Page { }, [5] = UI.Chooser { width = 7, - formLabel = 'RS Control', formKey = 'rsControl', + formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', nochoice = 'No', choices = { { name = 'Yes', value = true }, { name = 'No', value = false }, }, - help = 'Control via redstone' + help = 'Ignore NBT of item' }, - [6] = UI.Chooser { - width = 25, - formLabel = 'RS Device', formKey = 'rsDevice', - --choices = devices, - help = 'Redstone Device' - }, - [7] = UI.Chooser { - width = 10, - formLabel = 'RS Side', formKey = 'rsSide', - --nochoice = 'No', - choices = { - { name = 'up', value = 'up' }, - { name = 'down', value = 'down' }, - { name = 'east', value = 'east' }, - { name = 'north', value = 'north' }, - { name = 'west', value = 'west' }, - { name = 'south', value = 'south' }, - }, - help = 'Output side' + [6] = UI.Button { + x = 2, y = -2, width = 10, + formLabel = 'Redstone', + event = 'show_rs', + text = 'Configure', }, infoButton = UI.Button { x = 2, y = -2, @@ -513,6 +621,45 @@ local itemPage = UI.Page { text = 'Info', }, }, + rsControl = UI.SlideOut { + backgroundColor = colors.cyan, + titleBar = UI.TitleBar { + title = "Redstone Control", + }, + form = UI.Form { + y = 2, + [1] = UI.Chooser { + width = 7, + formLabel = 'RS Control', formKey = 'rsControl', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Control via redstone' + }, + [2] = UI.Chooser { + width = 25, + formLabel = 'RS Device', formKey = 'rsDevice', + --choices = devices, + help = 'Redstone Device' + }, + [3] = UI.Chooser { + width = 10, + formLabel = 'RS Side', formKey = 'rsSide', + --nochoice = 'No', + choices = { + { name = 'up', value = 'up' }, + { name = 'down', value = 'down' }, + { name = 'east', value = 'east' }, + { name = 'north', value = 'north' }, + { name = 'west', value = 'west' }, + { name = 'south', value = 'south' }, + }, + help = 'Output side' + }, + }, + }, info = UI.SlideOut { titleBar = UI.TitleBar { title = "Information", @@ -525,18 +672,24 @@ local itemPage = UI.Page { ex = -2, y = -2, width = 6, text = 'Okay', event = 'hide_info', - } + }, }, statusBar = UI.StatusBar { } } function itemPage:enable(item) self.item = item +debug(self.item) self.form:setValues(item) self.titleBar.title = item.displayName or item.name - local devices = self.form[6].choices + UI.Page.enable(self) + self:focusFirst() +end + +function itemPage.rsControl:enable() + local devices = self.form[1].choices Util.clear(devices) for _,dev in pairs(device) do if dev.setOutput then @@ -548,25 +701,50 @@ function itemPage:enable(item) table.insert(devices, { name = 'None found', values = '' }) end - UI.Page.enable(self) - self:focusFirst() + UI.SlideOut.enable(self) +end + +function itemPage.rsControl:eventHandler(event) + if event.type == 'form_cancel' then + self:hide() + elseif event.type == 'form_complete' then + self:hide() + 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 == 'show_rs' then + self.rsControl:show() + elseif event.type == 'show_info' then - self.info.textArea.value = - string.format( -[[%sName: %s%s -%sID: %s%s -%sDamage: %s%s -%sNBT: %s%s]], -Ansi.yellow, Ansi.reset, self.item.displayName, -Ansi.yellow, Ansi.reset, self.item.name, -Ansi.yellow, Ansi.reset, self.item.damage, -Ansi.yellow, Ansi.reset, self.item.nbtHash or '(none)') + local value = + string.format('%s%s%s\n%s\n', + Ansi.orange, self.item.displayName, Ansi.reset, + self.item.name) + + if self.item.nbtHash then + value = value .. self.item.nbtHash .. '\n' + end + + value = value .. string.format('\n%sDamage:%s %s', + Ansi.yellow, Ansi.reset, self.item.damage) + + if self.item.maxDamage and self.item.maxDamage > 0 then + value = value .. string.format(' (max: %s)', self.item.maxDamage) + end + + if self.item.maxCount then + value = value .. string.format('\n%sStack Size: %s%s', + Ansi.yellow, Ansi.reset, self.item.maxCount) + end + + self.info.textArea.value = value self.info:show() elseif event.type == 'hide_info' then @@ -578,21 +756,12 @@ Ansi.yellow, Ansi.reset, self.item.nbtHash or '(none)') elseif event.type == 'form_complete' then local values = self.form.values - local keys = { 'name', 'auto', 'low', 'limit', 'damage', - 'nbtHash', - 'rsControl', 'rsDevice', 'rsSide', } + local originalKey = uniqueKey(self.item) - local filtered = { } - for _,key in pairs(keys) do - filtered[key] = values[key] - end + local filtered = Util.shallowCopy(values) filtered.low = tonumber(filtered.low) filtered.limit = tonumber(filtered.limit) - --filtered.ignoreDamage = filtered.ignoreDamage == true - --filtered.auto = filtered.auto == true - --filtered.rsControl = filtered.rsControl == true - if filtered.auto ~= true then filtered.auto = nil end @@ -603,11 +772,19 @@ Ansi.yellow, Ansi.reset, self.item.nbtHash or '(none)') filtered.rsDevice = nil end - if values.ignoreDamage == true then + if filtered.ignoreDamage == true then filtered.damage = 0 - filtered.ignoreDamage = true + else + filtered.ignoreDamage = nil end + if filtered.ignoreNbtHash == true then + filtered.nbtHash = nil + else + filtered.ignoreNbtHash = nil + end +debug(filtered) + resources[originalKey] = nil resources[uniqueKey(filtered)] = filtered saveResources() @@ -735,7 +912,7 @@ function listingPage:eventHandler(event) if resources[key] then resources[key] = nil - Util.writeTable(RESOURCE_FILE, resources) + saveResources() end self.statusBar:timedStatus('Forgot: ' .. item.name, 3) @@ -847,7 +1024,9 @@ local function learnRecipe(page) for _,v in pairs(results) do if not v.craftingTool then recipe = v - recipe.maxCount = maxCount + if maxCount then + recipe.maxCount = maxCount + end break end end @@ -939,54 +1118,84 @@ function learnPage:eventHandler(event) return true end -local craftPage = UI.Dialog { - height = 6, width = UI.term.width - 10, - title = 'Enter amount to craft', - count = UI.TextEntry { - x = 15, - y = 3, - width = 10, - limit = 6, - value = '1', +local craftPage = UI.Page { + titleBar = UI.TitleBar { + title = "Information", }, - accept = UI.Button { - x = -8, y = -2, - backgroundColor = colors.green, - text = '+', event = 'accept', - }, - cancel = UI.Button { - x = -4, y = -2, - backgroundColor = colors.red, - text = '\215', event = 'cancel' + wizard = UI.Wizard { + y = 2, + pages = { + quantity = UI.Window { + index = 1, + text = UI.Text { + x = 6, y = 3, + value = 'Quantity', + }, + count = UI.TextEntry { + x = 15, + y = 3, + width = 10, + limit = 6, + value = 1, + }, + }, + resources = UI.Window { + index = 2, + grid = UI.Grid { + y = 2, ey = -4, + columns = { + { heading = 'Name', key = 'displayName' }, + { heading = 'Qty', key = 'ocount' , width = 5 }, + { heading = 'Used', key = 'used' , width = 4 }, + { heading = 'Need', key = 'need' , width = 4 }, + { heading = 'Miss', key = 'missing' , width = 4 }, + { heading = 'cra', key = 'craftable' , width = 4 }, + }, + sortColumn = 'displayName', + }, + accept = UI.Button { + x = -8, y = -2, + backgroundColor = colors.green, + text = '+', event = 'accept', + }, + cancel = UI.Button { + x = -4, y = -2, + backgroundColor = colors.red, + text = '\215', event = 'cancel' + }, + }, + }, }, } -function craftPage:draw() - UI.Dialog.draw(self) - self:write(6, 3, 'Quantity') -end - function craftPage:enable(item) self.item = item self:focusFirst() - UI.Dialog.enable(self) -end - -function craftPage:disable() - UI.Dialog.disable(self) + UI.Page.enable(self) end function craftPage:eventHandler(event) if event.type == 'cancel' then UI:setPreviousPage() + + elseif event.type == 'enable_view' and event.view == self.wizard.pages.resources then + local items = inventoryAdapter:listItems() + local count = self.wizard.pages.quantity.count.value + local recipe = Craft.recipes[uniqueKey(self.item)] + local ingredients = Craft.getResourceList3(recipe, items, count) + self.wizard.pages.resources.grid:setValues(ingredients) + for _,v in pairs(ingredients) do + v.displayName = itemDB:getName(v) + end + elseif event.type == 'accept' then local key = uniqueKey(self.item) demandCrafting[key] = Util.shallowCopy(self.item) - demandCrafting[key].count = tonumber(self.count.value) + demandCrafting[key].count = tonumber(self.wizard.pages.quantity.count.value) demandCrafting[key].forceCrafting = true UI:setPreviousPage() else - return UI.Dialog.eventHandler(self, event) + return UI.Page.eventHandler(self, event) end return true end @@ -1010,7 +1219,7 @@ Event.onInterval(5, function() if not craftingPaused then local items = inventoryAdapter:listItems() - if Util.size(items) == 0 then + if not items or Util.size(items) == 0 then jobListGrid.parent:clear() jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system') jobListGrid:sync() @@ -1020,10 +1229,13 @@ Event.onInterval(5, function() craftItems(craftList, items) if Util.size(demandCrafting) > 0 then - local list = Util.shallowCopy(demandCrafting) - craftItems(list, inventoryAdapter:listItems()) - for k,v in pairs(list) do - craftList[k] = v + items = inventoryAdapter:listItems() + if items then + local list = Util.shallowCopy(demandCrafting) + craftItems(list, items) + for k,v in pairs(list) do + craftList[k] = v + end end end @@ -1034,9 +1246,11 @@ Event.onInterval(5, function() for _,key in pairs(Util.keys(demandCrafting)) do local item = demandCrafting[key] - item.count = item.count - item.crafted - if item.count <= 0 then -- should check statusCode - demandCrafting[key] = nil + if item.crafted then + item.count = item.count - item.crafted + if item.count <= 0 then -- should check statusCode + demandCrafting[key] = nil + end end end diff --git a/apps/crafter.lua b/apps/crafter.lua index 592d3e1..fa261f0 100644 --- a/apps/crafter.lua +++ b/apps/crafter.lua @@ -11,6 +11,7 @@ local Util = require('util') local colors = _G.colors local multishell = _ENV.multishell +local term = _G.term local turtle = _G.turtle multishell.setTitle(multishell.getCurrent(), 'Crafter') @@ -25,6 +26,7 @@ local inventoryAdapter = ChestAdapter(config.inventory) local RESOURCE_FILE = 'usr/config/resources.db' local RECIPES_FILE = 'usr/config/recipes2.db' +local MACHINES_FILE = 'usr/config/machines.db' local recipes = Util.readTable(RECIPES_FILE) or { } local resources @@ -60,14 +62,21 @@ local function getItemQuantity(items, res) return count end -local function getItemWithQty(items, res) +local function getItemWithQty(items, res, ignoreNbtHash) 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 + ((ignoreNbtHash and v.nbtHash) or res.nbtHash == v.nbtHash) then +debug('found') +debug(v) return v end end +debug('nope:' .. uniqueKey(res)) + local item = Util.shallowCopy(res) + item.count = 0 + item.maxCount = 1 + return item end local function mergeResources(t) @@ -129,69 +138,78 @@ local function clearGrid() end local function gotoMachine(machine) - for _ = 1, machine do + for _ = 1, machine.index do if not turtle.back() then return end end + return true end -local function canCraft(recipe, items, count) - count = math.ceil(count / recipe.count) - - local icount = Util.size(recipe.ingredients) - local maxSlots = math.floor(16 / icount) - - for key,qty in pairs(recipe.ingredients) do - local item = getItem(items, itemDB:splitKey(key)) - if not item then - return 0, itemDB:getName(key) - end - local x = math.min(math.floor(item.count / qty), item.maxCount * maxSlots) - count = math.min(x, count) - end - - return count, '' -end - local function craftItem(recipe, recipeKey, items, cItem, count) repeat until not turtle.forward() local resource = resources[recipeKey] if not resource or not resource.machine then cItem.status = 'machine not selected' - return false + return + end + + local machine = Util.find(machines, 'order', resource.machine) + if not machine then + cItem.status = 'invalid machine' + return end if count == 0 then - cItem.status = 'missing something' - return false + for key in pairs(recipe.ingredients) do + local item = getItemWithQty(items, itemDB:splitKey(key), recipe.ignoreNbtHash) + if item.count == 0 then + cItem.status = 'Missing: ' .. (item.displayName or itemDB:getName(item)) + return false + end + end + return end local slot = 1 for key,qty in pairs(recipe.ingredients) do - local item = itemDB:get(key) - if not item then - cItem.status = 'failed' - return false + local item = getItemWithQty(items, itemDB:splitKey(key), recipe.ignoreNbtHash) + if item.count == 0 then +debug(item) + cItem.status = 'Missing: ' .. (item.displayName or itemDB:getName(item)) + return end local c = count * qty while c > 0 do local maxCount = math.min(c, item.maxCount) inventoryAdapter:provide(item, maxCount, slot) - if turtle.getItemCount(slot) == 0 then -- ~= maxCount then FIXXX !!! - cItem.status = 'failed' - return false + if turtle.getItemCount(slot) ~= maxCount then -- ~= maxCount then FIXXX !!! + cItem.status = 'Extract failed: ' .. (item.displayName or itemDB:getName(item)) + return end c = c - maxCount slot = slot + 1 end end - if not gotoMachine(resource.machine) then + if not gotoMachine(machine) then cItem.status = 'failed to find machine' else - if resource.dir == 'up' then + if machine.empty then + local s, l = pcall(Peripheral.call, + turtle.getAction(machine.dir).side, 'list') + + if not s then + cItem.status = l + return + elseif not Util.empty(l) then + cItem.status = 'machine busy' + return + end + end + + if machine.dir == 'up' then turtle.emptyInventory(turtle.dropUp) else turtle.emptyInventory(turtle.dropDown) @@ -212,15 +230,7 @@ local function expandList(list) for key,qty in pairs(recipe.ingredients) do - local item = getItemWithQty(items, itemDB:splitKey(key)) - if not item then - item = itemDB:get(key) - if not item then - --item = itemDB:splitKey(key) - break - end - item.count = 0 - end + local item = getItemWithQty(items, itemDB:splitKey(key), recipe.ignoreNbtHash) local need = qty * count local irecipe = recipes[key] @@ -271,6 +281,7 @@ end local function craftItems(craftList) expandList(craftList) + lastItems = inventoryAdapter:listItems() -- refresh counts jobListGrid:update() jobListGrid:draw() jobListGrid:sync() @@ -315,10 +326,17 @@ local function loadResources() resources = Util.readTable(RESOURCE_FILE) or { } for k,v in pairs(resources) do Util.merge(v, itemDB:splitKey(k)) - if not v.machine then - local recipe = recipes[k] - v.machine = recipe.machine - v.dir = recipe.dir or 'down' + if v.dir then + for _,m in pairs(machines) do + if m.index == v.machine and m.dir == v.dir then + v.machine = m.order + v.dir = nil + break + end + end + if v.dir then + error('did not find') + end end end end @@ -328,6 +346,7 @@ local function saveResources() for k,v in pairs(resources) do v = Util.shallowCopy(v) + v.name = nil v.damage = nil v.nbtHash = nil @@ -359,6 +378,9 @@ local function findMachines() if not machine then local _ _, machine = turtle.getAction(dir).inspect() + if not machine or type(machine) ~= 'table' then + machine = { name = 'Unknown' } + end end if machine and type(machine) == 'table' then local rawName = getName(side) or machine.name @@ -384,6 +406,16 @@ local function findMachines() getMachine('up') index = index + 1 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 + m.name = m2.name or m.name + m.empty = m2.empty + m.ignore = m2.ignore + end + end end local function jobMonitor() @@ -458,6 +490,10 @@ local itemPage = UI.Page { }, machines = UI.SlideOut { backgroundColor = colors.cyan, + titleBar = UI.TitleBar { + title = 'Select Machine', + previousPage = true, + }, grid = UI.ScrollingGrid { y = 2, ey = -4, values = machines, @@ -476,7 +512,6 @@ local itemPage = UI.Page { x = -9, y = -2, text = 'Cancel', event = 'cancelMachine', }, - statusBar = UI.StatusBar(), }, statusBar = UI.StatusBar { } } @@ -491,11 +526,7 @@ function itemPage:enable(item) self:focusFirst() end -function itemPage.machines:draw() - UI.Window.draw(self) - self:centeredWrite(1, 'Select machine', nil, colors.yellow) -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)) @@ -504,6 +535,7 @@ function itemPage.machines:eventHandler(event) end return true end +]] function itemPage:eventHandler(event) if event.type == 'form_cancel' then @@ -513,17 +545,18 @@ function itemPage:eventHandler(event) UI:setPage('learn', self.item) elseif event.type == 'setMachine' then - self.item.machine = self.machines.grid:getSelected().index - self.item.dir = self.machines.grid:getSelected().dir + self.item.machine = self.machines.grid:getSelected().order self.machines:hide() elseif event.type == 'cancelMachine' then self.machines:hide() elseif event.type == 'selectMachine' then - self.machines.grid:update() + 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(machines, 'index', self.item.machine) + local _, index = Util.find(machineCopy, 'order', self.item.machine) if index then self.machines.grid:setIndex(index) end @@ -544,7 +577,6 @@ function itemPage:eventHandler(event) end filtered.low = tonumber(filtered.low) filtered.machine = self.item.machine - filtered.dir = self.item.dir if values.ignoreDamage == true then filtered.damage = 0 @@ -685,11 +717,113 @@ function learnPage:eventHandler(event) return true end +local machinesPage = UI.Page { + titleBar = UI.TitleBar { + previousPage = true, + title = 'Machines', + }, + grid = UI.Grid { + 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' + }, + }, + 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 + 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 = 'Refresh', event = 'refresh' }, + { text = 'Machines', event = 'machines' }, + { text = 'Refresh', event = 'refresh', x = -9 }, }, }, grid = UI.Grid { @@ -768,6 +902,9 @@ function listingPage:eventHandler(event) 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()) @@ -823,8 +960,8 @@ function listingPage:applyFilter() self.grid:setValues(t) end -loadResources() findMachines() +loadResources() repeat until not turtle.forward() clearGrid() jobMonitor() @@ -832,6 +969,7 @@ lastItems = inventoryAdapter:listItems() UI:setPages({ listing = listingPage, + machines = machinesPage, item = itemPage, learn = learnPage, }) @@ -851,14 +989,16 @@ Event.onInterval(30, function() turtle.refuel() end lastItems = inventoryAdapter:listItems() - local craftList = watchResources(lastItems) + if lastItems then + local craftList = watchResources(lastItems) - jobListGrid:setValues(craftList) - jobListGrid:update() - jobListGrid:draw() - jobListGrid:sync() + jobListGrid:setValues(craftList) + jobListGrid:update() + jobListGrid:draw() + jobListGrid:sync() - craftItems(craftList) + craftItems(craftList) + end end) UI:pullEvents() diff --git a/apps/levelEmitter.lua b/apps/levelEmitter.lua new file mode 100644 index 0000000..3f88ec8 --- /dev/null +++ b/apps/levelEmitter.lua @@ -0,0 +1,371 @@ +_G.requireInjector() + +local ChestAdapter = require('chestAdapter18') +local Config = require('config') +local Event = require('event') +local itemDB = require('itemDB') +local UI = require('ui') +local Util = require('util') + +local colors = _G.colors +local multishell = _ENV.multishell +local rs = _G.rs + +local RESOURCE_FILE = 'usr/config/levelEmitter.db' + +multishell.setTitle(multishell.getCurrent(), 'Level Emitter') + +local config = { + inventoryDirection = { direction = 'north', wrapSide = 'back' }, +} +Config.load('levelEmitter', config) + +local inventoryAdapter = ChestAdapter(config.inventoryDirection) +local resources + +local function getItem(items, inItem, ignoreDamage, ignoreNbtHash) + for _,item in pairs(items) do + if item.name == inItem.name and + (ignoreDamage or item.damage == inItem.damage) and + (ignoreNbtHash or item.nbtHash == inItem.nbtHash) then + return item + end + end +end + +local function uniqueKey(item) + return table.concat({ item.name, item.damage, item.nbtHash }, ':') +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 _,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 getItemWithQty(items, res, ignoreDamage, ignoreNbtHash) + local item = getItem(items, res, ignoreDamage, ignoreNbtHash) + + if item and (ignoreDamage or ignoreNbtHash) then + local count = 0 + + for _,v in pairs(items) do + if item.name == v.name and + (ignoreDamage or item.damage == v.damage) and + (ignoreNbtHash or item.nbtHash == v.nbtHash) then + count = count + v.count + end + end + item.count = count + end + + return item +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) + local keys = Util.transpose({ 'limit', 'low', 'ignoreDamage', 'ignoreNbtHash' }) + + for _,key in pairs(Util.keys(v)) do + if not keys[key] then + v[key] = nil + end + end + if not Util.empty(v) then + t[k] = v + end + end + + Util.writeTable(RESOURCE_FILE, t) +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 = 'Output a signal if below' + }, + [2] = UI.TextEntry { + width = 7, + formLabel = 'Max', formKey = 'limit', help = 'Output a signal if above' + }, + [4] = 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' + }, + [5] = UI.Chooser { + width = 7, + formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash', + nochoice = 'No', + choices = { + { name = 'Yes', value = true }, + { name = 'No', value = false }, + }, + help = 'Ignore NBT of item' + }, + }, + statusBar = UI.StatusBar { } +} + +function itemPage:enable(item) + self.item = item + + self.form:setValues(item) + self.titleBar.title = item.displayName or item.name + + 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 filtered = Util.shallowCopy(self.form.values) + + if filtered.ignoreDamage == true then + filtered.damage = 0 + else + filtered.ignoreDamage = nil + end + + if filtered.ignoreNbtHash == true then + filtered.nbtHash = nil + else + filtered.ignoreNbtHash = nil + end + + local originalKey = uniqueKey(self.item) + resources[originalKey] = nil + + filtered.low = tonumber(filtered.limit) + filtered.limit = tonumber(filtered.limit) + if filtered.limit or filtered.low then + resources[uniqueKey(filtered)] = filtered + else + resources[uniqueKey(filtered)] = nil + end + + saveResources() + + UI:setPreviousPage() + + else + return UI.Page.eventHandler(self, event) + end + return true +end + +local listingPage = UI.Page { + menuBar = UI.MenuBar { + buttons = { + { text = 'Forget', event = 'forget' }, + { text = 'Refresh', event = 'refresh', x = -9 }, + }, + }, + grid = UI.Grid { + y = 2, height = UI.term.height - 2, + columns = { + { heading = 'Name', key = 'displayName' , width = 22 }, + { heading = 'Qty', key = 'count' , width = 5 }, + { heading = 'Max', key = 'limit' , width = 4 }, + }, + sortColumn = 'displayName', + }, + statusBar = UI.StatusBar { + filterText = UI.Text { + x = 2, + value = 'Filter', + }, + filter = UI.TextEntry { + x = 9, ex = -5, + limit = 50, + backgroundColor = colors.gray, + backgroundFocusColor = colors.gray, + }, + display = UI.Button { + x = -3, + event = 'toggle_display', + value = 0, + text = 'A', + }, + }, + accelerators = { + r = 'refresh', + q = 'quit', + }, + displayMode = 0, +} + +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: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 == 'toggle_display' then + local values = { + [0] = 'A', + [1] = 'I', + [2] = 'C', + } + + event.button.value = (event.button.value + 1) % 3 + self.displayMode = event.button.value + event.button.text = values[event.button.value] + event.button:draw() + self:applyFilter() + self.grid:draw() + + elseif event.type == 'forget' then + local item = self.grid:getSelected() + if item then + local key = uniqueKey(item) + + if resources[key] then + resources[key] = nil + saveResources() + 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 = inventoryAdapter:listItems() + mergeResources(self.allItems) + self:applyFilter() +end + +function listingPage:applyFilter() + local filter = self.filter + local displayMode = self.displayMode + local t = self.allItems + + if filter or displayMode > 0 then + t = { } + if filter then + filter = filter:lower() + end + for _,v in pairs(self.allItems) do + if not filter or string.find(v.lname, filter, 1, true) then + if not displayMode or + displayMode == 0 or + displayMode == 1 and v.count > 0 or + displayMode == 2 and v.has_recipe then + table.insert(t, v) + end + end + end + end + + self.grid:setValues(t) +end + +loadResources() + +UI:setPages({ + listing = listingPage, + item = itemPage, +}) + +UI:setPage(listingPage) +listingPage:setFocus(listingPage.statusBar.filter) + +Event.onInterval(5, function() + local items = inventoryAdapter:listItems() + + if items and Util.size(items) > 0 then + for _,res in pairs(resources) do + local item = getItemWithQty(items, res, res.ignoreDamage, res.ignoreNbtHash) + rs.setOutput('bottom', (res.limit and + item and item.count > res.limit) or + (res.low and + (not item or item.count < res.low)) or false) + end + end +end) + +UI:pullEvents() diff --git a/apps/storageActivity.lua b/apps/storageActivity.lua index b169981..f2a020c 100644 --- a/apps/storageActivity.lua +++ b/apps/storageActivity.lua @@ -20,8 +20,9 @@ local changedPage = UI.Page { grid = UI.Grid { ey = -6, columns = { - { heading = 'Qty', key = 'count', width = 5 }, - { heading = 'Change', key = 'change', width = 6 }, + { heading = 'Qty', key = 'count', width = 5 }, + { heading = 'Change', key = 'change', width = 6 }, + { heading = 'Rate', key = 'rate', width = 6 }, { heading = 'Name', key = 'displayName' }, }, sortColumn = 'displayName', @@ -60,6 +61,7 @@ function changedPage.grid:getDisplayValues(row) if row.change < 0 then ind = '' end + row.change = ind .. Util.toBytes(row.change) row.count = Util.toBytes(row.count) @@ -67,7 +69,6 @@ function changedPage.grid:getDisplayValues(row) end function changedPage:eventHandler(event) - if event.type == 'reset' then self.lastItems = nil self.grid:setValues({ }) @@ -98,7 +99,6 @@ function changedPage:refresh() local t = storage:listItems() if not t or Util.empty(t) then -debug('clearing') self.grid:clear() self.grid:centeredWrite(math.ceil(self.height/2), 'Communication failure') return @@ -110,8 +110,10 @@ debug('clearing') if not self.lastItems then self.lastItems = t + self.timestamp = os.clock() self.grid:setValues({ }) else + self.elapsed = os.clock() - self.timestamp local changedItems = { } local found for _,v in pairs(self.lastItems) do @@ -144,6 +146,7 @@ debug('clearing') for _,v in pairs(changedItems) do v.change = v.count - v.lastCount + v.rate = Util.round(60 / self.elapsed * v.change, 1) end self.grid:setValues(changedItems)