EinsDreiDreiSieben/mods/futil/minetest/fake_inventory.lua

271 lines
5.9 KiB
Lua

local FakeInventory = futil.class1()
local function copy_list(list)
local copy = {}
for i = 1, #list do
copy[i] = ItemStack(list[i])
end
return copy
end
function FakeInventory:_init()
self._lists = {}
end
function FakeInventory.create_copy(inv)
local fake_inv = FakeInventory()
for listname, contents in pairs(inv:get_lists()) do
fake_inv:set_size(listname, inv:get_size(listname))
fake_inv:set_width(listname, inv:get_width(listname))
fake_inv:set_list(listname, contents)
end
return fake_inv
end
function FakeInventory.room_for_all(inv, listname, items)
local fake_inv = FakeInventory.create_copy(inv)
for i = 1, #items do
local item = items[i]
local remainder = fake_inv:add_item(listname, item)
if not remainder:is_empty() then
return false
end
end
return true
end
function FakeInventory:is_empty(listname)
local list = self._lists[listname]
if not list then
return true
end
for _, stack in ipairs(list) do
if not stack:is_empty() then
return false
end
end
return true
end
function FakeInventory:get_size(listname)
local list = self._lists[listname]
if not list then
return 0
end
return #list
end
function FakeInventory:set_size(listname, size)
if size == 0 then
self._lists[listname] = nil
return
end
local list = self._lists[listname] or {}
while #list < size do
list[#list + 1] = ItemStack()
end
for i = size + 1, #list do
list[i] = nil
end
self._lists[listname] = list
end
function FakeInventory:get_width(listname)
local list = self._lists[listname] or {}
return list.width or 0
end
function FakeInventory:set_width(listname, width)
local list = self._lists[listname] or {}
list.width = width
self._lists[listname] = list
end
function FakeInventory:get_stack(listname, i)
local list = self._lists[listname]
if not list or i > #list then
return ItemStack()
end
return ItemStack(list[i])
end
function FakeInventory:set_stack(listname, i, stack)
local list = self._lists[listname]
if not list or i > #list then
return
end
list[i] = ItemStack(stack)
end
function FakeInventory:get_list(listname)
local list = self._lists[listname]
if not list then
return
end
return copy_list(list)
end
function FakeInventory:set_list(listname, list)
local ourlist = self._lists[listname]
if not ourlist then
return
end
for i = 1, #ourlist do
ourlist[i] = ItemStack(list[i])
end
end
function FakeInventory:get_lists()
local lists = {}
for listname, list in pairs(self._lists) do
lists[listname] = copy_list(list)
end
return lists
end
function FakeInventory:set_lists(lists)
for listname, list in pairs(lists) do
self:set_list(listname, list)
end
end
-- add item somewhere in list, returns leftover `ItemStack`.
function FakeInventory:add_item(listname, new_item)
local list = self._lists[listname]
new_item = ItemStack(new_item)
if new_item:is_empty() or not list or #list == 0 then
return new_item
end
-- first try to find if it could be added to some existing items
for _, our_stack in ipairs(list) do
if not our_stack:is_empty() then
new_item = our_stack:add_item(new_item)
if new_item:is_empty() then
return new_item
end
end
end
-- then try to add it to empty slots
for _, our_stack in ipairs(list) do
new_item = our_stack:add_item(new_item)
if new_item:is_empty() then
break
end
end
return new_item
end
-- returns `true` if the stack of items can be fully added to the list
function FakeInventory:room_for_item(listname, stack)
local list = self._lists[listname]
if not list then
return false
end
stack = ItemStack(stack)
local copy = copy_list(list)
for _, our_stack in ipairs(copy) do
stack = our_stack:add_item(stack)
if stack:is_empty() then
break
end
end
return stack:is_empty()
end
-- take as many items as specified from the list, returns the items that were actually removed (as an `ItemStack`)
-- note that any item metadata is ignored, so attempting to remove a specific unique item this way will likely remove
-- the wrong one -- to do that use `set_stack` with an empty `ItemStack`.
function FakeInventory:remove_item(listname, stack)
local removed = ItemStack()
stack = ItemStack(stack)
local list = self._lists[listname]
if not list or stack:is_empty() then
return removed
end
local name = stack:get_name()
local count_remaining = stack:get_count()
local taken = 0
for i = #list, 1, -1 do
local our_stack = list[i]
if our_stack:get_name() == name then
local n = our_stack:take_item(count_remaining):get_count()
count_remaining = count_remaining - n
taken = taken + n
end
if count_remaining == 0 then
break
end
end
stack:set_count(taken)
return stack
end
-- returns `true` if the stack of items can be fully taken from the list.
-- If `match_meta` is false, only the items' names are compared (default: `false`).
function FakeInventory:contains_item(listname, stack, match_meta)
local list = self._lists[listname]
if not list then
return false
end
stack = ItemStack(stack)
if match_meta then
local name = stack:get_name()
local wear = stack:get_wear()
local meta = stack:get_meta()
local needed_count = stack:get_count()
for _, our_stack in ipairs(list) do
if our_stack:get_name() == name and our_stack:get_wear() == wear and our_stack:get_meta():equals(meta) then
local n = our_stack:peek_item(needed_count):get_count()
needed_count = needed_count - n
end
if needed_count == 0 then
break
end
end
return needed_count == 0
else
local name = stack:get_name()
local needed_count = stack:get_count()
for _, our_stack in ipairs(list) do
if our_stack:get_name() == name then
local n = our_stack:peek_item(needed_count):get_count()
needed_count = needed_count - n
end
if needed_count == 0 then
break
end
end
return needed_count == 0
end
end
function FakeInventory:get_location()
return {
type = "undefined",
subtype = "FakeInventory",
}
end
futil.FakeInventory = FakeInventory