diff --git a/inventoryManager.lua b/inventoryManager.lua index 1097592..669e126 100644 --- a/inventoryManager.lua +++ b/inventoryManager.lua @@ -195,6 +195,35 @@ local SMELTABLE = { ["farmersdelight:minced_beef"] = { result = "farmersdelight:beef_patty", furnaces = {"minecraft:furnace", "minecraft:smoker"} }, } +-- Pre-build furnace compatibility sets for O(1) lookup +for _, recipe in pairs(SMELTABLE) do + recipe.furnaceSet = {} + for _, ft in ipairs(recipe.furnaces) do + recipe.furnaceSet[ft] = true + end +end + +-- Pre-built smelt candidate lists per furnace type (sorted: food first, then alpha) +-- Avoids rebuilding & re-sorting in autoSmelt on every cycle. +local smeltCandidatesByType = {} +do + for _, ftype in ipairs(FURNACE_TYPES) do + smeltCandidatesByType[ftype] = {} + end + for itemName, recipe in pairs(SMELTABLE) do + local isFood = recipe.furnaceSet["minecraft:smoker"] or false + for ft, _ in pairs(recipe.furnaceSet) do + table.insert(smeltCandidatesByType[ft], { name = itemName, recipe = recipe, food = isFood }) + end + end + for _, list in pairs(smeltCandidatesByType) do + table.sort(list, function(a, b) + if a.food ~= b.food then return a.food end + return a.name < b.name + end) + end +end + -- Fuel items, ordered by preference (best first) -- burn_time = how many items one fuel smelts local FUEL_LIST = { @@ -782,7 +811,28 @@ end local function refreshCache(onProgress) activity.scanning = true - local chests = getChests() + -- Single pass over peripherals: classify into chests and furnaces + local chests = {} + local furnaces = {} + local furnaceTypeSet = {} + for _, ft in ipairs(FURNACE_TYPES) do furnaceTypeSet[ft] = true end + + for _, name in ipairs(peripheral.getNames()) do + local ptype = peripheral.getType(name) + if ptype == "minecraft:chest" then + table.insert(chests, name) + elseif furnaceTypeSet[ptype] then + table.insert(furnaces, name) + end + end + + -- Update peripheral caches so getChests()/getFurnaces() stay fresh + local now = os.clock() + cachedChests = chests + cachedChestsTime = now + cachedFurnaces = furnaces + cachedFurnacesTime = now + local catalogue = {} local totalSlots = 0 local usedSlots = 0 @@ -837,16 +887,8 @@ local function refreshCache(onProgress) cache.dropperOk = peripheral.wrap(DROPPER_NAME) ~= nil cache.barrelOk = peripheral.wrap(BARREL_NAME) ~= nil - -- Count furnaces - local furnaceCount = 0 - for _, ftype in ipairs(FURNACE_TYPES) do - for _, name in ipairs(peripheral.getNames()) do - if peripheral.getType(name) == ftype then - furnaceCount = furnaceCount + 1 - end - end - end - cache.furnaceCount = furnaceCount + -- Furnace count already computed from single-pass above + cache.furnaceCount = #furnaces -- Scan furnace contents for smelter dashboard refreshFurnaceStatus() @@ -1722,6 +1764,9 @@ local function drawSmelterDashboard() bx1, by1, bx2, by2 = drawButton(bx2 + 2, 4, "Missing", colors.white, tabMissingBg) addSmelterZone(bx1, by1, bx2, by2, "tab", "missing") + -- Hoisted counter: reused by bottom accent bar to avoid re-scanning recipes + local craftAvailCount = nil + if smelterView == "status" then -- ===== Furnace Status View ===== -- Column headers @@ -1839,12 +1884,9 @@ local function drawSmelterDashboard() local resultShort = recipe.result:gsub("^minecraft:", ""):gsub("_", " ") resultShort = resultShort:sub(1,1):upper() .. resultShort:sub(2) local types = "" - for _, ft in ipairs(recipe.furnaces) do - if ft == "minecraft:furnace" then types = types .. "F" - elseif ft == "minecraft:smoker" then types = types .. "S" - elseif ft == "minecraft:blast_furnace" then types = types .. "B" - end - end + if recipe.furnaceSet["minecraft:furnace"] then types = types .. "F" end + if recipe.furnaceSet["minecraft:smoker"] then types = types .. "S" end + if recipe.furnaceSet["minecraft:blast_furnace"] then types = types .. "B" end local enabled = not disabledRecipes[inputName] local inStorage = 0 if cache.catalogue[inputName] then @@ -1965,6 +2007,7 @@ local function drawSmelterDashboard() }) end end + craftAvailCount = #availList -- Column headers monFill(5, colors.gray) @@ -2050,6 +2093,7 @@ local function drawSmelterDashboard() }) end end + craftAvailCount = #CRAFTABLE - #missList -- Column headers monFill(5, colors.gray) @@ -2136,12 +2180,8 @@ local function drawSmelterDashboard() bottomMsg = " Tap MAKE to craft " if activity.crafting then bottomMsg = " CRAFTING... " end elseif smelterView == "missing" then - local totalC = #CRAFTABLE - local availC = 0 - for _, r in ipairs(CRAFTABLE) do - if canCraftRecipe(r) then availC = availC + 1 end - end - bottomMsg = string.format(" Available: %d/%d recipes ", availC, totalC) + local availC = craftAvailCount or 0 + bottomMsg = string.format(" Available: %d/%d recipes ", availC, #CRAFTABLE) end monCenter(h, bottomMsg, colors.pink, colors.purple) @@ -2172,9 +2212,11 @@ local function sortBarrel(barrelOverride) for slot, item in pairs(contents) do local moved = 0 + local triedChests = {} -- dedup: skip chests already tried via catalogue if catalogue[item.name] then for _, entry in ipairs(catalogue[item.name]) do + triedChests[entry.chest] = true local n = barrel.pushItems(entry.chest, slot) if n and n > 0 then moved = moved + n @@ -2189,15 +2231,17 @@ local function sortBarrel(barrelOverride) if moved < item.count then for _, chest in ipairs(chests) do - local n = barrel.pushItems(chest, slot) - if n and n > 0 then - moved = moved + n - adjustCache(item.name, chest, n) - needsRedraw = true - smelterNeedsRedraw = true - print(string.format("[SORT] %s x%d -> %s", item.name, n, chest)) + if not triedChests[chest] then + local n = barrel.pushItems(chest, slot) + if n and n > 0 then + moved = moved + n + adjustCache(item.name, chest, n) + needsRedraw = true + smelterNeedsRedraw = true + print(string.format("[SORT] %s x%d -> %s", item.name, n, chest)) + end + if moved >= item.count then break end end - if moved >= item.count then break end end end @@ -2275,15 +2319,7 @@ local function autoSmelt() local inputItem = contents[SLOT_INPUT] if inputItem then local recipe = SMELTABLE[inputItem.name] - local validHere = false - if recipe then - for _, ft in ipairs(recipe.furnaces) do - if ft == furnaceType then - validHere = true - break - end - end - end + local validHere = recipe and recipe.furnaceSet[furnaceType] or false if not validHere then -- This item doesn't belong in this furnace type — pull it out for _, chest in ipairs(chests) do @@ -2336,37 +2372,13 @@ local function autoSmelt() -- 4) Load smeltable items into empty input slot inputItem = contents[SLOT_INPUT] if not inputItem then - -- Build sorted candidate list: food first, then everything else - local candidates = {} - for itemName, recipe in pairs(SMELTABLE) do - -- Check if this furnace type is compatible - local compatible = false - for _, ft in ipairs(recipe.furnaces) do - if ft == furnaceType then - compatible = true - break - end - end - if compatible and not disabledRecipes[itemName] and catalogue[itemName] then - local isFood = false - for _, ft in ipairs(recipe.furnaces) do - if ft == "minecraft:smoker" then - isFood = true - break - end - end - table.insert(candidates, { name = itemName, recipe = recipe, food = isFood }) - end - end - -- Sort: food first - table.sort(candidates, function(a, b) - if a.food ~= b.food then return a.food end - return a.name < b.name - end) + -- Use pre-built candidate list (already sorted: food first, then alpha) + local candidates = smeltCandidatesByType[furnaceType] or {} -- Try each candidate for _, cand in ipairs(candidates) do local itemName = cand.name + if not disabledRecipes[itemName] and catalogue[itemName] then -- Count total of this item across all chests local totalInStorage = 0 for _, src in ipairs(catalogue[itemName]) do @@ -2403,6 +2415,7 @@ local function autoSmelt() end if loaded or remaining < math.min(available, 64) then break end end + end end end end