222 lines
5.2 KiB
Lua
222 lines
5.2 KiB
Lua
local Sentinel = require("storage.sentinel")
|
|
|
|
local ItemGroup = Sentinel:tag({}, Sentinel.ITEMGROUP)
|
|
ItemGroup.__index = ItemGroup
|
|
|
|
--- Create a group of managed itemstacks
|
|
---@param stack table ItemStack to compare to
|
|
---@return table ItemGroup instance representing stack type
|
|
function ItemGroup:from(stack)
|
|
local obj = { stack }
|
|
setmetatable(obj, self)
|
|
obj.__index = obj
|
|
return obj
|
|
end
|
|
|
|
function ItemGroup.collectStacks(stacks)
|
|
local groups = {}
|
|
for _,stack in ipairs(stacks) do
|
|
for _,group in ipairs(groups) do
|
|
if group:addStack(stack) then
|
|
goto continue
|
|
end
|
|
end
|
|
table.insert(groups, ItemGroup:from(stack))
|
|
::continue::
|
|
end
|
|
return groups
|
|
end
|
|
|
|
function ItemGroup:getStackCount()
|
|
return #self
|
|
end
|
|
|
|
function ItemGroup:getDisplayName()
|
|
return self:_getIdentityStack():getDisplayName()
|
|
end
|
|
|
|
function ItemGroup:getSimpleName()
|
|
return self:isEmpty() and "[EMPTY]" or self:_getIdentityStack():getSimpleName()
|
|
end
|
|
|
|
function ItemGroup:isEmpty()
|
|
return self:_getIdentityStack():isEmpty()
|
|
end
|
|
|
|
function ItemGroup:getName()
|
|
return self:_getIdentityStack():getName()
|
|
end
|
|
|
|
function ItemGroup:getEnchantments()
|
|
return self:_getIdentityStack():getEnchantments()
|
|
end
|
|
|
|
function ItemGroup:getNBT()
|
|
return self:_getIdentityStack():getNBT()
|
|
end
|
|
|
|
function ItemGroup:getDamage()
|
|
return self:_getIdentityStack():getDamage()
|
|
end
|
|
|
|
function ItemGroup:getMaxDamage()
|
|
return self:_getIdentityStack():getMaxDamage()
|
|
end
|
|
|
|
function ItemGroup:_getIdentityStack()
|
|
return self[1]
|
|
end
|
|
|
|
function ItemGroup:_iterateStacks()
|
|
return ipairs(self)
|
|
end
|
|
|
|
function ItemGroup:_addStack(stack)
|
|
table.insert(self, stack)
|
|
end
|
|
|
|
function ItemGroup:_removeStack(stackOrIndex)
|
|
if type(stackOrIndex) == "number" then
|
|
return table.remove(self, stackOrIndex) ~= nil
|
|
else
|
|
for index,stack in self:_iterateStacks() do
|
|
if stack == stackOrIndex then
|
|
return self:_removeStack(index)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function ItemGroup:canAddStack(stack)
|
|
if self:_getIdentityStack():canTransfer(stack) then
|
|
for _,chkStack in self:_iterateStacks() do
|
|
if chkStack == stack then
|
|
-- Already in the group
|
|
return true
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
function ItemGroup:addStack(stack)
|
|
if self:canAddStack(stack) then
|
|
self:_addStack(stack)
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
function ItemGroup:transferTo(target, itemCount)
|
|
local targetGroup = nil
|
|
if Sentinel:is(target, Sentinel.CHEST) or Sentinel:is(target, Sentinel.STORAGE) then
|
|
local identity = self:_getIdentityStack()
|
|
local find = target:find(function(stack) return identity:canTransfer(stack) end)
|
|
if #find == 0 then
|
|
return itemCount == nil or itemCount == 0, 0
|
|
end
|
|
|
|
targetGroup = ItemGroup:from(find[1])
|
|
for i=2,#find do
|
|
targetGroup:_addStack(find[i])
|
|
end
|
|
elseif Sentinel:is(target, Sentinel.ITEMSTACK) then
|
|
targetGroup = ItemGroup:from(target)
|
|
elseif Sentinel:is(target, Sentinel.ITEMGROUP) then
|
|
targetGroup = target
|
|
end
|
|
|
|
if targetGroup == nil then
|
|
error("Unexpected transfer target for ItemGroup:transferTo")
|
|
end
|
|
|
|
local targetCap = 0
|
|
for _,stack in targetGroup:_iterateStacks() do
|
|
targetCap = targetCap + (stack:getMaxCount() - stack:getCount())
|
|
end
|
|
|
|
-- TODO: Not efficient
|
|
local transferMax = math.min(itemCount or targetCap, targetCap, self:getItemCount())
|
|
local transfer = 0
|
|
for _,stack in targetGroup:_iterateStacks() do
|
|
for _,from in self:_iterateStacks() do
|
|
if transfer >= transferMax then
|
|
goto complete
|
|
end
|
|
|
|
if stack:getCount() == stack:getMaxCount() then
|
|
goto continue
|
|
end
|
|
local _, xfer = from:transferTo(stack, math.min(stack:getMaxCount() - stack:getCount(), transferMax - transfer))
|
|
transfer = transfer + xfer
|
|
end
|
|
::continue::
|
|
end
|
|
::complete::
|
|
|
|
return itemCount == nil or (itemCount == transfer), transfer
|
|
end
|
|
|
|
function ItemGroup:getItemCount()
|
|
if self:_getIdentityStack():isEmpty() then
|
|
return 0
|
|
end
|
|
local sum = 0
|
|
for _,stack in self:_iterateStacks() do
|
|
sum = sum + stack:getCount()
|
|
end
|
|
return sum
|
|
end
|
|
|
|
function ItemGroup:_copyTrackedStackList()
|
|
local collect = {}
|
|
for index,stack in self:_iterateStacks() do
|
|
table.insert(collect, { stack = stack, index = index })
|
|
end
|
|
return collect
|
|
end
|
|
|
|
function ItemGroup:defragment()
|
|
if self:_getIdentityStack():isEmpty() then
|
|
return 0
|
|
end
|
|
|
|
local entries = self:_copyStackList()
|
|
table.sort(entries, function(a, b)
|
|
return a.stack:getCount() > b.stack:getCount()
|
|
end)
|
|
local endPtr = #entries
|
|
local startPtr = endPtr
|
|
for i=1,endPtr do
|
|
local entry = entries[i].stack
|
|
if entry:getMaxCount() ~= entry:getCount() then
|
|
startPtr = i
|
|
end
|
|
end
|
|
|
|
local toRemove = {}
|
|
while startPtr < endPtr do
|
|
local from = entries[endPtr]
|
|
local to = entries[startPtr]
|
|
|
|
local emptied, _ = from.stack:transferTo(to.stack)
|
|
if emptied then
|
|
table.insert(toRemove, from.index)
|
|
endPtr = endPtr - 1
|
|
end
|
|
|
|
if (not emptied) or (to.stack:getCount() == to.stack:getMaxCount()) then
|
|
startPtr = startPtr + 1
|
|
end
|
|
end
|
|
|
|
table.sort(toRemove, function(a, b) return a > b end)
|
|
for _,index in ipairs(toRemove) do
|
|
self:_removeStack(index)
|
|
end
|
|
|
|
return #toRemove
|
|
end
|
|
|
|
return ItemGroup |