SX c035e3b484 Natural sorting for chests (#135)
* Natural sorting for chests

* Fix sorting of separated tools/items containing infotext, reduce overhead of original stack collector

Co-authored-by: SX <50966843+S-S-X@users.noreply.github.com>
2020-12-03 15:48:07 +02:00

176 lines
4.5 KiB
Lua

-- Table to define order of type sorting
local itemtypes = {
node = 1,
tool = 2,
craft = 3,
none = 4
}
function technic.chests.sort_inv(inv, mode)
local list = inv:get_list("main")
if not list then return end
local unique_items = {}
if mode ~= 4 then
local items = {}
for _,stack in pairs(list) do
if not stack:is_empty() then
local name = stack:get_name()
local wear = stack:get_wear()
local meta = stack:get_metadata()
local count = stack:get_count()
local def = minetest.registered_items[name]
local itemtype = (def and itemtypes[def.type]) and def.type or "none"
local key = string.format("%s %05d %s", name, wear, meta)
if not items[key] then
items[key] = {
stacks = {stack},
wear = wear,
count = count,
itemtype = itemtype,
key = key,
}
else
items[key].count = items[key].count + count
table.insert(items[key].stacks, stack)
end
end
end
for k,v in pairs(items) do
table.insert(unique_items, v)
end
end
if mode == 1 then
-- Quantity
table.sort(unique_items, function(a, b)
if a.count ~= b.count then
return a.count > b.count
end
return a.key < b.key
end)
elseif mode == 2 then
-- Type
table.sort(unique_items, function(a, b)
if itemtypes[a.itemtype] ~= itemtypes[b.itemtype] then
return itemtypes[a.itemtype] < itemtypes[b.itemtype]
end
return a.key < b.key
end)
elseif mode == 3 then
-- Wear
table.sort(unique_items, function(a, b)
if a.itemtype == "tool" and b.itemtype == "tool" then
if a.wear ~= b.wear then
return a.wear < b.wear
end
return a.key < b.key
elseif a.itemtype == "tool" or b.itemtype == "tool" then
return a.itemtype == "tool"
end
return a.key < b.key
end)
elseif mode == 4 then
-- Natural
do -- Collect item stacks for sorting
local function name(stack)
return stack:get_meta():get("infotext")
or stack:get_description()
or stack:get_name()
end
local lookup = {}
for _,stack in pairs(list) do
local key = minetest.get_translated_string('', name(stack))
if not lookup[key] then
table.insert(unique_items, {
stacks = {stack},
key = key,
})
lookup[key] = #unique_items
else
table.insert(unique_items[lookup[key]].stacks, stack)
end
end
end
local function padnum(value)
local dec, n = string.match(value, "(%.?)0*(.+)")
return #dec > 0 and ("%.12f"):format(value) or ("%s%03d%s"):format(dec, #n, n)
end
table.sort(unique_items, function(a, b)
local sort_a = ("%s%3d"):format(tostring(a.key):gsub("%.?%d+", padnum), #b.key)
local sort_b = ("%s%3d"):format(tostring(b.key):gsub("%.?%d+", padnum), #a.key)
return sort_a < sort_b
end)
else
-- Item
table.sort(unique_items, function(a, b)
return a.key < b.key
end)
end
inv:set_list("main", {})
for _,item in ipairs(unique_items) do
for _,stack in ipairs(item.stacks) do
inv:add_item("main", stack)
end
end
end
function technic.chests.get_inv_items(inv)
local list = inv:get_list("main")
if not list then return {} end
local keys = {}
for _,stack in pairs(list) do
if not stack:is_empty() then
keys[stack:get_name()] = true
end
end
local items = {}
for k,_ in pairs(keys) do
items[#items + 1] = k
end
return items
end
function technic.chests.move_inv(from_inv, to_inv, filter)
local list = from_inv:get_list("main")
if not list then return {} end
local moved_items = {}
for i,stack in ipairs(list) do
if not stack:is_empty() then
local move_stack = false
local name = stack:get_name()
if name == filter or not filter then
move_stack = true
elseif type(filter) == "table" then
for _,k in pairs(filter) do
if name == k then
move_stack = true
break
end
end
end
if move_stack then
local leftover = to_inv:add_item("main", stack)
if not leftover:is_empty() then
from_inv:set_stack("main", i, leftover)
stack:set_count(stack:get_count() - leftover:get_count())
else
from_inv:set_stack("main", i, "")
end
table.insert(moved_items, stack:to_table())
end
end
end
return moved_items
end
function technic.chests.log_inv_change(pos, name, change, items)
local spos = minetest.pos_to_string(pos)
if change == "move" then
minetest.log("action", name.." moves "..items.." in chest at "..spos)
elseif change == "put" then
minetest.log("action", name.." puts "..items.." into chest at "..spos)
elseif change == "take" then
minetest.log("action", name.." takes "..items.." from chest at "..spos)
end
end