375 lines
11 KiB
Lua
375 lines
11 KiB
Lua
local ui = require("ui")
|
|
local ensure_width = require("cc.strings").ensure_width
|
|
local term_stack = require("term-stack")
|
|
|
|
local LEFT_KEY = {keys.left , {"ctrl", keys.h}}
|
|
local DOWN_KEY = {keys.down , {"ctrl", keys.j}}
|
|
local UP_KEY = {keys.up , {"ctrl", keys.k}}
|
|
local RIGHT_KEY = {keys.right, {"ctrl", keys.l}}
|
|
local REQUEST_KEY = {"ctrl", keys.space}
|
|
local HELP_KEY = keys.f1
|
|
local STATISTICS_KEY = keys.f2
|
|
local REFRESH_KEY = keys.f5
|
|
|
|
local dbg_file = fs.combine(shell.dir(), "logs.txt")
|
|
if dbg_file then
|
|
fs.delete(dbg_file)
|
|
end
|
|
local function dbg(...)
|
|
if dbg_file then
|
|
local out = io.open(dbg_file, "a")
|
|
if not out then return end
|
|
for i = 1, select("#", ...) do
|
|
local value = select(i, ...)
|
|
if type(value) == "table" then
|
|
out:write(textutils.serialise(value))
|
|
else
|
|
out:write(tostring(value))
|
|
out:write(" ")
|
|
end
|
|
end
|
|
out:write("\n")
|
|
out:close()
|
|
else
|
|
local pretty = require("cc.pretty")
|
|
for i = 1, select("#", ...) do
|
|
local value = select(i, ...)
|
|
pretty.pretty_print(value)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function is_inventory(peripheral_name)
|
|
local types = {peripheral.getType(peripheral_name)}
|
|
for _, t in ipairs(types) do
|
|
if t == "inventory" then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function remove_value(array, value)
|
|
for i, v in ipairs(array) do
|
|
if v == value then
|
|
table.remove(array, i)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
local function has_in_array(value, array)
|
|
for _, v in ipairs(array) do
|
|
if v == value then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
local function list_inventories()
|
|
local inventories = {}
|
|
for _, name in ipairs(peripheral.getNames()) do
|
|
if is_inventory(name) and not has_in_array(name, { "top", "left", "right", "back", "bottom", "front" }) then
|
|
table.insert(inventories, name)
|
|
end
|
|
end
|
|
return inventories
|
|
end
|
|
|
|
local function list_items(inventories)
|
|
local items = {}
|
|
for _, name in ipairs(inventories) do
|
|
for slot, item in pairs(peripheral.call(name, "list")) do
|
|
items[item.name] = items[item.name] or {}
|
|
table.insert(items[item.name], {
|
|
count = item.count,
|
|
slot = slot,
|
|
peripheral = name
|
|
})
|
|
end
|
|
end
|
|
|
|
for name, item_collection in pairs(items) do
|
|
local item = item_collection[1]
|
|
local detail = peripheral.call(item.peripheral, "getItemDetail", item.slot)
|
|
items[name].displayName = detail.displayName
|
|
items[name].stack_size = detail.maxCount
|
|
|
|
item_collection.count = 0
|
|
for _, slot in ipairs(item_collection) do
|
|
item_collection.count = item_collection.count + slot.count
|
|
end
|
|
end
|
|
|
|
return items
|
|
end
|
|
|
|
local function get_item_counts(items)
|
|
local item_counts = {}
|
|
for name, item_collection in pairs(items) do
|
|
item_counts[name] = item_collection.count
|
|
end
|
|
return item_counts
|
|
end
|
|
|
|
local function vline(x, y, height)
|
|
for i=1, height do
|
|
term.setCursorPos(x, y+i-1)
|
|
term.write("|")
|
|
end
|
|
end
|
|
|
|
local function clamp(x, min, max)
|
|
return math.min(math.max(x, min), max)
|
|
end
|
|
|
|
local function rect(x, y, w, h)
|
|
return { x = x, y = y, w = w, h = h or w }
|
|
end
|
|
|
|
local function vsplit(area, x)
|
|
local left_area = rect(area.x, area.y, x - area.x, area.h)
|
|
local right_area = rect(area.x + left_area.w+1, area.y, area.w - left_area.w-1, area.h)
|
|
return left_area, right_area
|
|
end
|
|
|
|
local main_view = { event = {} }
|
|
|
|
function main_view:list_shown_names(all_items, name_filter, max_names)
|
|
name_filter = name_filter:lower()
|
|
|
|
local names = {}
|
|
for name, count in pairs(all_items) do
|
|
local displayName = self.items[name].displayName:lower()
|
|
if displayName:find(name_filter) and count > 0 then
|
|
table.insert(names, name)
|
|
end
|
|
end
|
|
|
|
table.sort(names, function(a, b) return all_items[a] > all_items[b] end)
|
|
|
|
if #names > max_names then
|
|
local names_subset = {}
|
|
for i=1, max_names do
|
|
table.insert(names_subset, names[i])
|
|
end
|
|
names = names_subset
|
|
end
|
|
|
|
return names
|
|
end
|
|
|
|
function main_view:display_item_list(x, y, w, all_items, shown_names, highlight_row)
|
|
term_stack.push_cursor(x, y)
|
|
|
|
local max_count = 0
|
|
for _, name in pairs(shown_names) do
|
|
local count = all_items[name]
|
|
max_count = math.max(max_count, count)
|
|
end
|
|
local max_count_width = math.floor(math.log(max_count, 10) + 1)
|
|
|
|
for row, name in pairs(shown_names) do
|
|
local count = all_items[name]
|
|
local item_info = self.items[name]
|
|
local count_width = math.floor(math.log(count, 10) + 1)
|
|
if row == highlight_row then
|
|
term.setTextColor(colors.yellow)
|
|
else
|
|
term.setTextColor(colors.white)
|
|
end
|
|
term.setCursorPos((max_count_width+1 - count_width), row)
|
|
term.write(count)
|
|
term.setCursorPos(max_count_width+2, row)
|
|
term.write(ensure_width(item_info.displayName, w-max_count_width-1))
|
|
end
|
|
|
|
term_stack.pop_cursor()
|
|
end
|
|
|
|
function main_view:move_items(item_name, amount, destination)
|
|
local item_collection = self.items[item_name]
|
|
|
|
local moved_amount = 0
|
|
while moved_amount < amount and item_collection.count > 0 do
|
|
local slot_details = item_collection[#item_collection]
|
|
|
|
local needed_amount = amount - moved_amount
|
|
local transferred_amount = peripheral.call(slot_details.peripheral, "pushItems", destination, slot_details.slot, needed_amount)
|
|
moved_amount = moved_amount + transferred_amount
|
|
slot_details.count = slot_details.count - transferred_amount
|
|
item_collection.count = item_collection.count - transferred_amount
|
|
if slot_details.count == 0 then
|
|
table.remove(item_collection)
|
|
end
|
|
end
|
|
|
|
return moved_amount
|
|
end
|
|
|
|
function main_view:refresh_shown_names(store, area)
|
|
store.shown_names = main_view:list_shown_names(store.all_items, store.search_bar or "", area.h-1)
|
|
store.selected_row = clamp(store.selected_row or 1, 1, #store.shown_names)
|
|
end
|
|
|
|
function main_view:display_items_with_search(store, area, active)
|
|
store.search_bar = store.search_bar or ""
|
|
store.selected_row = store.selected_row or 0
|
|
store.all_items = store.all_items or {}
|
|
store.shown_names = store.shown_names or main_view:list_shown_names(store.all_items, store.search_bar, area.h-1)
|
|
|
|
term_stack.push_cursor(area.x, area.y)
|
|
|
|
local new_search_bar = ui:textbox(area.w, active, "Search", store.search_bar)
|
|
if new_search_bar ~= store.search_bar then
|
|
store.search_bar = new_search_bar
|
|
self:refresh_shown_names(store, area)
|
|
end
|
|
|
|
if active then
|
|
if ui.event[1] == "key" then
|
|
local key = ui.event[2]
|
|
if key == keys.down or (main_view.ctrl_down and key == keys.j) then
|
|
store.selected_row = math.min(store.selected_row + 1, #store.shown_names)
|
|
elseif key == keys.up or (main_view.ctrl_down and key == keys.k) then
|
|
store.selected_row = math.max(store.selected_row - 1, 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
term.setBackgroundColor(colors.black)
|
|
main_view:display_item_list(1, 2, area.w, store.all_items, store.shown_names, store.selected_row)
|
|
|
|
term_stack.pop_cursor()
|
|
end
|
|
|
|
local function main()
|
|
main_view.result_inventory = "minecraft:barrel_4"
|
|
|
|
local inventories = list_inventories()
|
|
remove_value(inventories, main_view.result_inventory)
|
|
main_view.items = list_items(inventories)
|
|
|
|
local update_interval = 0.05
|
|
|
|
local main_area = rect(1, 1, term.getSize())
|
|
local split_x = math.floor(main_area.w*0.5)
|
|
local left_area, right_area = vsplit(main_area, split_x)
|
|
|
|
local is_left_active = true
|
|
local left_store = {
|
|
all_items = get_item_counts(main_view.items)
|
|
}
|
|
local right_store = {
|
|
all_items = {}
|
|
}
|
|
|
|
local update_timer = os.startTimer(update_interval)
|
|
while true do
|
|
---@diagnostic disable-next-line: missing-parameter
|
|
ui.event = {os.pullEvent()}
|
|
|
|
local has_selected_items = false
|
|
for _, count in pairs(right_store.all_items) do
|
|
if count > 0 then
|
|
has_selected_items = true
|
|
break
|
|
end
|
|
end
|
|
if not has_selected_items and not is_left_active then
|
|
is_left_active = true
|
|
end
|
|
|
|
local event = ui.event[1]
|
|
if event == "timer" then
|
|
local timer = ui.event[2]
|
|
if timer == update_timer then
|
|
update_timer = os.startTimer(update_interval)
|
|
end
|
|
elseif event == "key" then
|
|
local key = ui.event[2]
|
|
if key == keys.rightCtrl or key == keys.leftCtrl then
|
|
main_view.ctrl_down = true
|
|
elseif key == keys.tab then
|
|
is_left_active = not is_left_active
|
|
elseif key == keys.f5 then
|
|
main_view.items = list_items(inventories)
|
|
left_store.all_items = get_item_counts(main_view.items)
|
|
right_store.all_items = {}
|
|
main_view:refresh_shown_names(left_store, left_area)
|
|
main_view:refresh_shown_names(right_store, right_area)
|
|
|
|
update_timer = os.startTimer(update_interval)
|
|
end
|
|
|
|
if main_view.ctrl_down then
|
|
if key == keys.space then
|
|
if has_selected_items then
|
|
for name, count in pairs(right_store.all_items) do
|
|
local transferred_count = main_view:move_items(name, count, main_view.result_inventory)
|
|
left_store.all_items[name] = left_store.all_items[name] - transferred_count
|
|
end
|
|
right_store.all_items = {}
|
|
main_view:refresh_shown_names(left_store, left_area)
|
|
main_view:refresh_shown_names(right_store, right_area)
|
|
else
|
|
local target_item = left_store.shown_names[left_store.selected_row]
|
|
if target_item then
|
|
local item_collection = main_view.items[target_item]
|
|
local transferred_count = main_view:move_items(target_item, item_collection.stack_size, main_view.result_inventory)
|
|
left_store.all_items[target_item] = left_store.all_items[target_item] - transferred_count
|
|
main_view:refresh_shown_names(left_store, left_area)
|
|
end
|
|
end
|
|
end
|
|
|
|
if is_left_active then
|
|
if key == keys.l then
|
|
local target_item = left_store.shown_names[left_store.selected_row]
|
|
if target_item then
|
|
local stack_size = main_view.items[target_item].stack_size
|
|
local transferred_count = math.min(left_store.all_items[target_item], stack_size)
|
|
left_store.all_items[target_item] = left_store.all_items[target_item] - transferred_count
|
|
right_store.all_items[target_item] = (right_store.all_items[target_item] or 0) + transferred_count
|
|
main_view:refresh_shown_names(left_store, left_area)
|
|
main_view:refresh_shown_names(right_store, right_area)
|
|
end
|
|
end
|
|
else
|
|
if key == keys.h then
|
|
local target_item = right_store.shown_names[right_store.selected_row]
|
|
if target_item then
|
|
local stack_size = main_view.items[target_item].stack_size
|
|
local transferred_count = math.min(right_store.all_items[target_item], stack_size)
|
|
left_store.all_items[target_item] = left_store.all_items[target_item] + transferred_count
|
|
right_store.all_items[target_item] = right_store.all_items[target_item] - transferred_count
|
|
main_view:refresh_shown_names(left_store, left_area)
|
|
main_view:refresh_shown_names(right_store, right_area)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
elseif event == "key_up" then
|
|
local key = ui.event[2]
|
|
if key == keys.rightCtrl or key == keys.leftCtrl then
|
|
main_view.ctrl_down = false
|
|
end
|
|
end
|
|
|
|
term.setBackgroundColor(colors.black)
|
|
term.clear()
|
|
|
|
if has_selected_items then
|
|
term.setTextColor(colors.lightGray)
|
|
vline(split_x, main_area.y, main_area.h)
|
|
|
|
main_view:display_items_with_search(left_store, left_area, is_left_active)
|
|
main_view:display_items_with_search(right_store, right_area, not is_left_active)
|
|
else
|
|
main_view:display_items_with_search(left_store, main_area, is_left_active)
|
|
end
|
|
end
|
|
end
|
|
|
|
main(...)
|