local Inventory = require("storage.inventory") local ItemStack = {} ItemStack.__index = ItemStack function ItemStack:fromDetail(inv, detail, slot) local obj = { slot = slot, inv = inv } if detail == nil then obj.maxCount = inv.getItemLimit(slot) else obj.name = detail.name obj.damage = detail.damage obj.maxDamage = detail.maxDamage obj.count = detail.count obj.maxCount = detail.maxCount obj.enchantments = detail.enchantments obj.displayName = detail.displayName end setmetatable(obj, self) obj.__index = obj return obj end function ItemStack:clone(withSlot) local obj = { slot = withSlot or self.slot, inv = self.inv, name = self.name, damage = self.damage, maxDamage = self.maxDamage, count = self.count, maxCount = self.maxCount, enchantments = self.enchantments, displayName = self.displayName } setmetatable(obj, self) obj.__index = obj return obj end function ItemStack:toSerializable() local ser = { slot = self.slot, maxCount = self.maxCount } if self.count ~= nil then -- Not empty ser.name = self.name ser.damage = self.damage ser.maxDamage = self.maxDamage ser.count = self.count ser.enchantments = self.enchantments ser.displayName = self.displayName end return ser end function ItemStack:fromSerializable(ser, inv) ser.inv = inv setmetatable(ser, self) ser.__index = ser return ser end function ItemStack:getCount() return self.count or 0 end function ItemStack:getName() return self.name end function ItemStack:getDamage() return self.damage end function ItemStack:getMaxDamage() return self.maxDamage end function ItemStack:getMaxCount() return self.maxCount end function ItemStack:getEnchantments() return self.enchantments end function ItemStack:getSlot() return self.slot end function ItemStack:getInventory() return self.inv end function ItemStack:isEmpty() return self.count == nil or self.count == 0 end function ItemStack:getDisplayName() return self.displayName end function ItemStack:getSimpleName() local displayName = self:getDisplayName() if displayName ~= nil then return displayName end local name = self:getName() if name ~= nil then local _, e = name:find(":") if e == nil then return name end local simpleName = name:sub(e + 1) if #simpleName == 0 then return name end return simpleName end return Inventory.getSimpleName(self:getInventory()).."["..(self:getSlot() or "NO_SLOT").."]" end function ItemStack:hasChanged(listObj, thorough) local listItem = listObj[self.slot] if listItem == nil or listItem.name ~= self.name or listItem.count ~= self.count then return true end return thorough and (self ~= self:fromDetail(self.inv, self.inv.getItemDetail(self.slot), self.slot)) end function ItemStack:_modify(countDelta, stack) local newCount = self:getCount() + countDelta if newCount < 0 then error("ERROR: New stack count is negative: "..newCount) end if newCount == 0 then -- Clear data self.maxCount = self.inv.getItemLimit(self.slot) self.name = nil self.damage = nil self.maxDamage = nil self.count = nil self.maxCount = nil self.enchantments = nil self.displayName = nil else -- If stack is empty, copy stack data from source if self:isEmpty() then self.maxCount = stack.maxCount self.name = stack.name self.damage = stack.damage self.maxDamage = stack.maxDamage self.maxCount = stack.maxCount self.enchantments = stack.enchantments self.displayName = stack.displayName end self.count = newCount end end function ItemStack:transferTo(target, count) local cap = math.min(count or self:getCount(), target:getMaxCount() - target:getCount(), self:getCount()) -- If we can't transfer any data, then if cap == 0 then return count == 0, 0 end local result, xfer = pcall(self:getInventory().pushItems, peripheral.getName(target:getInventory()), self:getSlot(), cap, target:getSlot()) if not result then return false, xfer end self:_modify(-xfer, self) target:_modify(xfer, self) return xfer == count, xfer end local function objEquals(o1, o2) local objType = type(o1) if objType ~= type(o2) then return false end if objType == "table" then if #o1 ~= #o2 then return false end for i,v in ipairs(o1) do if not objEquals(v, o2[i]) then return false end end for k,v in pairs(o1) do if not objEquals(v, o2[k]) then return false end end return true else return o1 == o2 end end function ItemStack:__eq(other) return type(other) == "table" and self.count == other.count and self.maxCount == other.maxCount and self:canTransfer(other) end -- Determines if two stacks can be transferred to eachother -- Empty stacks are always valid transfer nodes function ItemStack:canTransfer(stack) return self:isEmpty() or stack:isEmpty() or (self.name == stack.name and self.damage == stack.damage and self.maxDamage == stack.maxDamage and self.displayName == stack.displayName and objEquals(self.enchantments, stack.enchantments)) end local function queryField(query, field) local queryType = type(query) if queryType == "nil" then return true elseif queryType == "function" then return query(field) end return objEquals(query, field) end --- Matches a query object/function(s) --- @param query table | function function ItemStack:matches(query) if query == nil then return true elseif type(query) == "function" then return query(self) end return queryField(query.name, self.name) and queryField(query.damage, self.damage) and queryField(query.count, self.count) and queryField(query.maxDamage, self.maxDamage) and queryField(query.enchantments, self.enchantments) and queryField(query.maxCount, self.maxCount) and queryField(query.displayName, self.displayName) end return ItemStack