Last active
March 11, 2026 09:17
-
-
Save DataKinds/cc4c04985662595a0b36c8ad55c45642 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| INVENTORY_CHANGE_DEBOUNCE_TIME = 1 -- seconds | |
| CHEST_COUNT = 2 | |
| VERBOSE = true | |
| local pp = (require "cc.pretty").pretty_print | |
| -- example item storage: | |
| -- { | |
| -- { | |
| -- count = 16, | |
| -- name = "minecraft:ink_sac" | |
| -- }, | |
| -- { | |
| -- count = 1, | |
| -- name = "minecraft:crossbow" | |
| -- nbt = "23y53489h5345345349875347" | |
| -- } | |
| -- } | |
| local turtle_items = {} | |
| local cold_items = {} | |
| function tableLength(T) | |
| local count = 0 | |
| for _ in pairs(T) do count = count + 1 end | |
| return count | |
| end | |
| -- items will be reported in separate stacks normally | |
| -- this will also handle sparse item stacks | |
| -- this function outputs an object keyed on `<item name>:<nbt>` | |
| function mergeItemStacks(item_list) | |
| return appendItemStack({}, item_list) | |
| end | |
| function appendItemStack(pre_merged_items, item_list) | |
| for slot, item in pairs(item_list) do | |
| local key = item.name | |
| if item.nbt ~= nil then | |
| key = key .. ":" .. item.nbt | |
| end | |
| if pre_merged_items[key] == nil then | |
| pre_merged_items[key] = item | |
| else | |
| pre_merged_items[key].count = pre_merged_items[key].count + item.count | |
| end | |
| end | |
| return pre_merged_items | |
| end | |
| function getTurtleItems() | |
| out = {} | |
| for i=2,16 do | |
| table.insert(out, turtle.getItemDetail(i)) | |
| end | |
| return mergeItemStacks(out) | |
| end | |
| function indexColdItems() | |
| print("Indexing cold storage...") | |
| cold_items = {} | |
| local items_by_idem_key = {} | |
| for block_offset=1,CHEST_COUNT do | |
| visitChest(block_offset) | |
| local chest = peripheral.find("inventory") | |
| local chest_item_list = chest.list() | |
| for k, _ in pairs(chest_item_list) do | |
| chest_item_list[k].cold_block_offset = block_offset -- TODO: this should be able to have multiple offsets | |
| end | |
| items_by_idem_key = appendItemStack(items_by_idem_key, chest.list()) | |
| returnHome() | |
| end | |
| for _,item in pairs(items_by_idem_key) do | |
| table.insert(cold_items, item) | |
| end | |
| pp(cold_items) | |
| print("Done indexing.") | |
| drawColdItemList() | |
| end | |
| function setNormalColors() | |
| term.setBackgroundColor(colors.lightGray) | |
| term.setTextColor(colors.black) | |
| end | |
| local cold_item_list_scroll_offset = 0 | |
| local cold_item_list_search_term = "SEARCH TERM" | |
| local cold_item_list_highlighted_key | |
| function drawColdItemList() | |
| setNormalColors() | |
| term.clear() | |
| -- draw scrollable item list | |
| for item_idx, item in ipairs(cold_items) do | |
| term.setCursorPos(2,3+cold_item_list_scroll_offset+item_idx-1) | |
| if item_idx == cold_item_list_highlighted_key then | |
| term.setBackgroundColor(colors.green) | |
| else | |
| setNormalColors() | |
| end | |
| term.write(item.name .. " x " .. item.count) | |
| end | |
| -- draw search box | |
| setNormalColors() | |
| term.setCursorPos(1,1) | |
| term.write("> ") | |
| term.write(cold_item_list_search_term) | |
| term.setCursorBlink(true) | |
| end | |
| function clickColdItemList(click_x, click_y) | |
| clicked_key = click_y - cold_item_list_scroll_offset - 2 | |
| if clicked_key == cold_item_list_highlighted_key then | |
| -- if the user double clicked a list item | |
| visitChest(cold_items[clicked_key].cold_block_offset) | |
| sleep(3) | |
| returnHome() | |
| cold_item_list_highlighted_key = nil | |
| else | |
| cold_item_list_highlighted_key = clicked_key | |
| end | |
| end | |
| local visited_block | |
| function visitChest(block_offset) | |
| turtle.turnRight() | |
| for idx=1,block_offset do | |
| turtle.forward() | |
| end | |
| turtle.turnLeft() | |
| visited_block = block_offset | |
| end | |
| function returnHome() | |
| turtle.turnLeft() | |
| for idx=1,visited_block do | |
| turtle.forward() | |
| end | |
| turtle.turnRight() | |
| end | |
| local inv_change_timer | |
| function turtleInventoryEvent() | |
| print("Inventory changed. Waiting...") | |
| if inv_change_timer ~= nil then | |
| os.cancelTimer(inv_change_timer) | |
| end | |
| inv_change_timer = os.startTimer(INVENTORY_CHANGE_DEBOUNCE_TIME) | |
| end | |
| function turtleDebouncedInventoryEvent() | |
| print("Done waiting!") | |
| turtle_items = getTurtleItems() | |
| if VERBOSE then pp(turtle_items) end | |
| print(("Storing %d items..."):format(tableLength(turtle_items))) | |
| indexColdItems() | |
| end | |
| function timerRouter(timer_id) | |
| if timer_id == inv_change_timer then | |
| turtleDebouncedInventoryEvent() | |
| end | |
| end | |
| function mainEventLoop() | |
| local evData = {os.pullEvent()} | |
| local ev = evData[1] | |
| if ev == "turtle_inventory" then | |
| turtleInventoryEvent() | |
| elseif ev == "timer" then | |
| timerRouter(evData[2]) | |
| elseif ev == "mouse_scroll" and evData[2] == -1 then | |
| -- scroll up | |
| cold_item_list_scroll_offset = cold_item_list_scroll_offset + 1 | |
| drawColdItemList() | |
| elseif ev == "mouse_scroll" and evData[2] == 1 then | |
| -- scroll down | |
| cold_item_list_scroll_offset = cold_item_list_scroll_offset - 1 | |
| drawColdItemList() | |
| elseif ev == "mouse_click" then | |
| clickColdItemList(evData[3], evData[4]) | |
| drawColdItemList() | |
| paintutils.drawPixel(evData[3], evData[4], colors.red) | |
| end | |
| end | |
| while true do | |
| mainEventLoop() | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment