From 8468134919fd3c54bd826c5594ca7a191ebb92e5 Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Wed, 25 Mar 2026 17:47:13 -0400 Subject: [PATCH] feat: implement auto-discard feature for excess stock management --- manager/operations.lua | 86 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/manager/operations.lua b/manager/operations.lua index 0b2f8c1..ad2949a 100644 --- a/manager/operations.lua +++ b/manager/operations.lua @@ -765,6 +765,92 @@ function O.autoCompost() return didWork end +------------------------------------------------- +-- Auto-discard excess stock +------------------------------------------------- + +function O.discardExcess() + if #cfg.TRASH_DROPPERS == 0 then return false end + + local catalogue = cache.catalogue + local didWork = false + + -- Build a flat list of dropper handles + free capacity + local droppers = {} + for _, dName in ipairs(cfg.TRASH_DROPPERS) do + local d = O.wrapCached(dName) + if d then + local used = 0 + local contents = d.list() + if contents then + for _, item in pairs(contents) do used = used + item.count end + end + local free = (d.size() * 64) - used + if free > 0 then + table.insert(droppers, { name = dName, handle = d, free = free }) + end + end + end + if #droppers == 0 then return false end + + -- Round-robin index across droppers + local dIdx = 1 + + for itemName, maxCount in pairs(cfg.STOCK_LIMITS) do + if catalogue[itemName] then + local totalInStorage = 0 + for _, src in ipairs(catalogue[itemName]) do + totalInStorage = totalInStorage + src.total + end + + local excess = totalInStorage - maxCount + if excess > 0 then + log.info("DISCARD", "%s: %d in stock, limit %d, discarding %d", + itemName, totalInStorage, maxCount, excess) + local discarded = 0 + local srcSnapshot = { table.unpack(catalogue[itemName]) } + for _, source in ipairs(srcSnapshot) do + if discarded >= excess then break end + if source.total > 0 then + local chest = O.wrapCached(source.chest) + if chest then + for slot, slotItem in pairs(chest.list()) do + if slotItem.name == itemName then + -- Find a dropper with free space (round-robin) + local startIdx = dIdx + local dropper = nil + repeat + if droppers[dIdx].free > 0 then + dropper = droppers[dIdx] + end + dIdx = (dIdx % #droppers) + 1 + until dropper or dIdx == startIdx + if not dropper then break end -- all droppers full + + local batch = math.min(slotItem.count, excess - discarded, dropper.free) + local n = chest.pushItems(dropper.name, slot, batch) + if n and n > 0 then + state.adjustCache(itemName, source.chest, -n) + dropper.free = dropper.free - n + discarded = discarded + n + didWork = true + end + if discarded >= excess then break end + end + end + end + end + end + if discarded > 0 then + log.info("DISCARD", "Discarded %s x%d", itemName, discarded) + end + end + end + end + + return didWork +end + ------------------------------------------------- -- Low-stock alert checker -------------------------------------------------