397 lines
12 KiB
Lua
397 lines
12 KiB
Lua
local term_stack = require("term-stack")
|
|
local ensure_width = require("cc.strings").ensure_width
|
|
local ui = require("ui")
|
|
local main_view = {}
|
|
|
|
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 list_items(inventories)
|
|
local item_registry = {}
|
|
for _, name in ipairs(inventories) do
|
|
for slot, item in pairs(peripheral.call(name, "list")) do
|
|
item_registry[item.name] = item_registry[item.name] or {}
|
|
table.insert(item_registry[item.name], {
|
|
count = item.count,
|
|
slot = slot,
|
|
peripheral = name
|
|
})
|
|
end
|
|
end
|
|
|
|
for _, items in pairs(item_registry) do
|
|
items.count = 0
|
|
for _, slot in ipairs(items) do
|
|
items.count = items.count + slot.count
|
|
end
|
|
end
|
|
|
|
return item_registry
|
|
end
|
|
|
|
local function populate_item_details(item_details, item_registry)
|
|
for name, items in pairs(item_registry) do
|
|
local item = items[1]
|
|
if item and not item_details[name] then
|
|
local detail = peripheral.call(item.peripheral, "getItemDetail", item.slot)
|
|
item_details[name] = {
|
|
display_name = detail.displayName,
|
|
stack_size = detail.maxCount
|
|
}
|
|
end
|
|
end
|
|
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 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 function vline(x, y, height)
|
|
for i=1, height do
|
|
term.setCursorPos(x, y+i-1)
|
|
term.write("|")
|
|
end
|
|
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
|
|
|
|
function main_view:prepare(inventories, result_inventory)
|
|
self.event = {}
|
|
|
|
self.is_left_active = true
|
|
|
|
self.main_area = rect(1, 1, term.getSize())
|
|
self.split_x = math.floor(self.main_area.w*0.5)
|
|
self.left_area, self.right_area = vsplit(self.main_area, self.split_x)
|
|
|
|
self.result_inventory = result_inventory
|
|
self.inventories = inventories
|
|
remove_value(self.inventories, result_inventory)
|
|
|
|
self.item_registry = list_items(self.inventories)
|
|
self.item_details = {}
|
|
populate_item_details(self.item_details, self.item_registry)
|
|
|
|
local all_item_counts = get_item_counts(self.item_registry)
|
|
self.left_store = {
|
|
all_items = all_item_counts,
|
|
filtered_names = self:list_filtered_names(all_item_counts, ""),
|
|
search_bar = "",
|
|
selected_idx = 1,
|
|
scroll = 0
|
|
}
|
|
self.right_store = {
|
|
all_items = {},
|
|
filtered_names = {},
|
|
search_bar = "",
|
|
selected_idx = 1,
|
|
scroll = 0
|
|
}
|
|
end
|
|
|
|
function main_view:list_filtered_names(all_items, name_filter)
|
|
name_filter = name_filter:lower()
|
|
|
|
local names = {}
|
|
for name, count in pairs(all_items) do
|
|
local display_name = self.item_details[name].display_name:lower()
|
|
if display_name:find(name_filter) and count > 0 then
|
|
table.insert(names, name)
|
|
end
|
|
end
|
|
|
|
table.sort(names, function(a, b)
|
|
if all_items[a] == all_items[b] then
|
|
return a < b
|
|
else
|
|
return all_items[a] > all_items[b]
|
|
end
|
|
end)
|
|
|
|
return names
|
|
end
|
|
|
|
function main_view:display_item_list(store, area, active)
|
|
term_stack.push_cursor(area.x, area.y)
|
|
|
|
local filtered_names = store.filtered_names
|
|
local shown_count = #filtered_names
|
|
|
|
local max_count = 0
|
|
for i=1, math.min(area.h, shown_count) do
|
|
local name = filtered_names[i + store.scroll]
|
|
local count = store.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=1, math.min(area.h, shown_count) do
|
|
local i = row + store.scroll
|
|
local name = filtered_names[i]
|
|
local count = store.all_items[name]
|
|
local item_info = self.item_details[name]
|
|
local count_width = math.floor(math.log(count, 10) + 1)
|
|
if i == store.selected_idx and active 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.display_name, area.w-max_count_width-1))
|
|
end
|
|
|
|
if shown_count > area.h then
|
|
local scroll_height = math.max(1, area.h/shown_count*area.h)
|
|
local y = 1+store.scroll/shown_count*area.h
|
|
paintutils.drawLine(area.w, y, area.w, y+scroll_height-1, colors.lightGray)
|
|
end
|
|
|
|
term_stack.pop_cursor()
|
|
end
|
|
|
|
function main_view:move_item(item_name, amount, destination)
|
|
local item_collection = self.item_registry[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)
|
|
if transferred_amount == 0 then break end
|
|
|
|
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:update_selected_item(store, area, idx)
|
|
local filtered_count = #store.filtered_names
|
|
store.selected_idx = clamp(idx, 1, filtered_count)
|
|
|
|
local margin = 2
|
|
local item_list_height = area.h-1
|
|
if store.selected_idx > store.scroll+item_list_height-margin then
|
|
store.scroll = math.min(store.selected_idx-item_list_height+margin, filtered_count-item_list_height)
|
|
elseif store.selected_idx <= store.scroll+margin then
|
|
store.scroll = math.max(store.selected_idx-1-margin, 0)
|
|
end
|
|
end
|
|
|
|
function main_view:select_item(store, area, item_name)
|
|
for i, name in ipairs(store.filtered_names) do
|
|
if name == item_name then
|
|
self:update_selected_item(store, area, i)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
function main_view:refresh_filtered_names(store, area)
|
|
store.filtered_names = main_view:list_filtered_names(store.all_items, store.search_bar or "")
|
|
self:update_selected_item(store, area, store.selected_idx)
|
|
end
|
|
|
|
function main_view:display_items_with_search(store, area, active)
|
|
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_filtered_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_idx = math.min(store.selected_idx + 1, #store.filtered_names)
|
|
elseif key == keys.up or (main_view.ctrl_down and key == keys.k) then
|
|
store.selected_idx = math.max(store.selected_idx - 1, 1)
|
|
end
|
|
|
|
local margin = 2
|
|
local item_list_height = area.h-1
|
|
if store.selected_idx > store.scroll+item_list_height-margin then
|
|
store.scroll = math.min(store.selected_idx-item_list_height+margin, #store.filtered_names - item_list_height)
|
|
elseif store.selected_idx <= store.scroll+margin then
|
|
store.scroll = math.max(store.selected_idx-1-margin, 0)
|
|
end
|
|
end
|
|
end
|
|
|
|
term.setBackgroundColor(colors.black)
|
|
main_view:display_item_list(store, rect(1, 2, area.w, area.h-1), active)
|
|
|
|
term_stack.pop_cursor()
|
|
end
|
|
|
|
function main_view:has_selected_items()
|
|
for _, count in pairs(main_view.right_store.all_items) do
|
|
if count > 0 then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function main_view:refresh_items()
|
|
self.item_registry = list_items(self.inventories)
|
|
populate_item_details(self.item_details, self.item_registry)
|
|
|
|
self.left_store.all_items = get_item_counts(self.item_registry)
|
|
self.right_store.all_items = {}
|
|
self:refresh_filtered_names(self.left_store, self.left_area)
|
|
self:refresh_filtered_names(self.right_store, self.right_area)
|
|
end
|
|
|
|
function main_view:deposit_items()
|
|
for slot, item in pairs(peripheral.call(self.result_inventory, "list")) do
|
|
local total_transferred = 0
|
|
for _, inventory in ipairs(self.inventories) do
|
|
local transferred = peripheral.call(self.result_inventory, "pushItems", inventory, slot)
|
|
total_transferred = total_transferred + transferred
|
|
|
|
if total_transferred == item.count then break end
|
|
end
|
|
end
|
|
self:refresh_items()
|
|
end
|
|
|
|
function main_view:draw()
|
|
term.setBackgroundColor(colors.black)
|
|
term.clear()
|
|
|
|
local has_selected_items = self:has_selected_items()
|
|
if has_selected_items then
|
|
term.setTextColor(colors.lightGray)
|
|
vline(self.split_x, self.main_area.y, self.main_area.h)
|
|
|
|
main_view:display_items_with_search(self.left_store, self.left_area, self.is_left_active)
|
|
main_view:display_items_with_search(self.right_store, self.right_area, not self.is_left_active)
|
|
else
|
|
main_view:display_items_with_search(self.left_store, self.main_area, true)
|
|
end
|
|
end
|
|
|
|
function main_view:on_event(event, ...)
|
|
local has_selected_items = self:has_selected_items()
|
|
if not has_selected_items and not self.is_left_active then
|
|
self.is_left_active = true
|
|
end
|
|
|
|
if event == "key" then
|
|
self:on_key(...)
|
|
elseif event == "key_up" then
|
|
self:on_key_up(...)
|
|
end
|
|
end
|
|
|
|
function main_view:on_key(key)
|
|
local left_selected = self.left_store.filtered_names[self.left_store.selected_idx]
|
|
local right_selected = self.right_store.filtered_names[self.right_store.selected_idx]
|
|
local selected_item = self.is_left_active and left_selected or right_selected
|
|
|
|
if key == keys.rightCtrl or key == keys.leftCtrl then
|
|
self.ctrl_down = true
|
|
elseif key == keys.rightAlt or key == keys.leftAlt then
|
|
self.alt_down = true
|
|
elseif key == keys.tab then
|
|
self.is_left_active = not self.is_left_active
|
|
elseif key == keys.f5 then
|
|
self:refresh_items()
|
|
end
|
|
|
|
if self.ctrl_down then
|
|
local has_selected_items = self:has_selected_items()
|
|
if key == keys.space then
|
|
if has_selected_items then
|
|
for name, count in pairs(self.right_store.all_items) do
|
|
local transferred = self:move_item(name, count, self.result_inventory)
|
|
self.left_store.all_items[name] = self.left_store.all_items[name] - transferred
|
|
end
|
|
self.right_store.all_items = {}
|
|
self:refresh_filtered_names(self.left_store, self.left_area)
|
|
self:refresh_filtered_names(self.right_store, self.right_area)
|
|
else
|
|
if left_selected then
|
|
local item = left_selected
|
|
local stack_size = self.item_details[item].stack_size
|
|
local transferred_count = self:move_item(item, stack_size, self.result_inventory)
|
|
self.left_store.all_items[item] = self.left_store.all_items[item] - transferred_count
|
|
self:refresh_filtered_names(self.left_store, self.left_area)
|
|
self:select_item(self.left_store, self.left_area, item)
|
|
end
|
|
end
|
|
elseif key == keys.d then
|
|
self:deposit_items()
|
|
end
|
|
|
|
if selected_item and (key == keys.l or key == keys.h) then
|
|
local item = selected_item
|
|
local stack_size = self.item_details[item].stack_size
|
|
local right_items = self.right_store.all_items
|
|
local left_items = self.left_store.all_items
|
|
|
|
if key == keys.l then
|
|
local transferred = math.min(left_items[item], stack_size)
|
|
left_items[item] = left_items[item] - transferred
|
|
right_items[item] = (right_items[item] or 0) + transferred
|
|
elseif key == keys.h then
|
|
local transferred = math.min(right_items[item] or 0, stack_size)
|
|
left_items[item] = left_items[item] + transferred
|
|
right_items[item] = (right_items[item] or 0) - transferred
|
|
end
|
|
|
|
self:refresh_filtered_names(self.left_store, self.left_area)
|
|
self:refresh_filtered_names(self.right_store, self.right_area)
|
|
if self.is_left_active then
|
|
self:select_item(self.left_store, self.left_area, item)
|
|
else
|
|
self:select_item(self.right_store, self.right_area, item)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function main_view:on_key_up(key)
|
|
if key == keys.rightCtrl or key == keys.leftCtrl then
|
|
self.ctrl_down = false
|
|
elseif key == keys.rightAlt or key == keys.leftAlt then
|
|
self.alt_down = false
|
|
end
|
|
end
|
|
|
|
return main_view
|