cc-utilities/storage/itemstack.lua
2024-10-08 23:52:54 +02:00

278 lines
6.2 KiB
Lua

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