Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- os.loadAPI("drawLib.lua")
- rednet.close("right")
- rednet.open("right")
- local currentState = "Idle"
- local function itemEnchantedOrHighDurability(getItemDetails)
- currentState = "Checking enchant"
- local itemDetails = getItemDetails()
- if itemDetails.enchantments == nil then
- return itemDetails.durability == nil or itemDetails.durability >= 0.9
- end
- return true
- end
- local function destroyOverflow()
- return false
- end
- local itemConditions = {
- ["minecraft:golden_sword"] = {
- keep=itemEnchantedOrHighDurability,
- },
- ["minecraft:cobblestone"] = {
- overflow=destroyOverflow
- }
- }
- local itemCategories = {
- ["Building"] = {
- "minecraft:cobblestone",
- "minecraft:spruce_planks",
- "minecraft:spruce_log"
- },
- ["Functional"] = {
- "minecraft:chest",
- "minecraft:spruce_sign",
- "computercraft:wired_modem",
- "computercraft:cable"
- },
- ["Crafting"] = {
- "minecraft:stick"
- }
- }
- local craftingRecipes = {
- ["minecraft:chest"] = {
- [1]="minecraft:spruce_planks",
- [2]="minecraft:spruce_planks",
- [3]="minecraft:spruce_planks",
- [4]="minecraft:spruce_planks",
- [5]=nil,
- [6]="minecraft:spruce_planks",
- [7]="minecraft:spruce_planks",
- [8]="minecraft:spruce_planks",
- [9]="minecraft:spruce_planks",
- count=1
- },
- ["minecraft:spruce_planks"] = {
- [1]="minecraft:spruce_log",
- count=4
- },
- ["minecraft:spruce_sign"] = {
- [1]="minecraft:spruce_planks",
- [2]="minecraft:spruce_planks",
- [3]="minecraft:spruce_planks",
- [4]="minecraft:spruce_planks",
- [5]="minecraft:spruce_planks",
- [6]="minecraft:spruce_planks",
- [8]="minecraft:stick",
- count=3
- },
- ["minecraft:stick"] = {
- [1]="minecraft:spruce_planks",
- [4]="minecraft:spruce_planks",
- count = 4
- },
- ["computercraft:wired_modem"] = {
- [1]="minecraft:stone",
- [2]="minecraft:stone",
- [3]="minecraft:stone",
- [4]="minecraft:stone",
- [5]="minecraft:redstone",
- [6]="minecraft:stone",
- [7]="minecraft:stone",
- [8]="minecraft:stone",
- [9]="minecraft:stone",
- count = 1
- },
- ["computercraft:cable"] = {
- [2]="minecraft:stone",
- [4]="minecraft:stone",
- [5]="minecraft:redstone",
- [6]="minecraft:stone",
- [8]="minecraft:stone",
- count=6
- }
- }
- local storageConfigs = {
- ["minecraft:chest_9"] = {
- mode="supply"
- },
- ["minecraft:chest_10"] = {
- mode="store",
- storeMode="overflow",
- },
- ["minecraft:chest_11"] = {
- mode="store",
- storeMode="copy",
- },
- ["minecraft:chest_12"] = {
- mode="store",
- storeMode="destroy"
- },
- ["minecraft:chest_13"] = {
- mode="provide",
- provideMode="exact",
- requests = {
- ["minecraft:spruce_sign"]=5
- }
- },
- ["turtle_3"] = {
- mode="crafter",
- craftChest="minecraft:chest_16"
- },
- ["minecraft:chest_14"] = {
- mode="requestOutput"
- },
- ["minecraft:chest_15"] = {
- mode="working"
- },
- ["turtle_1"] = {
- mode="crafter",
- craftChest="minecraft:chest_17"
- },
- ["minecraft:chest_18"] = {
- mode="working"
- }
- }
- local function getItemCondition(name)
- currentState = "Get item condition"
- for itemName,conditions in pairs(itemConditions) do
- if itemName == name then
- return conditions
- end
- end
- return nil
- end
- local function getItemRecipes(itemName)
- local result = {}
- for item,recipe in pairs(craftingRecipes) do
- if item == itemName then
- table.insert(result, recipe)
- end
- end
- return result
- end
- local allStores = {}
- local function calculateStoreContents(store)
- store.counts = {}
- currentState = "Calculate store contents"
- for slot, item in pairs(store.peripheral.list()) do
- if item ~= nil then
- if store.counts[item.name] == nil then
- if store.mode == "copy" then
- store.counts[item.name] = item.count - 1
- else
- store.counts[item.name] = item.count
- end
- else
- store.counts[item.name] = store.counts[item.name] + item.count
- end
- end
- end
- return store
- end
- local function createStore(peripheralName,config)
- currentState = "Create store"
- local displayName = peripheralName
- if config.displayName ~= nil and string.len(config.displayName) > 0 then
- displayName = config.displayName
- end
- local newStore = {
- displayName = displayName,
- peripheral = peripheral.wrap(peripheralName),
- mode = config.storeMode,
- counts = {}
- }
- newStore.canStoreItem = (function(name)
- if config.storeMode == "destroy" then
- return true
- elseif config.storeMode == "overflow" then
- return true
- elseif config.storeMode == "copy" then
- if newStore.counts[name] ~= nil then
- return true
- else
- return false
- end
- end
- end)
- if newStore.peripheral == nil then
- error("Couldn't find peripheral "..peripheralName)
- end
- newStore = calculateStoreContents(newStore)
- allStores[peripheralName] = newStore
- end
- -- Get all possible stores for itemName
- local function getItemStores(itemName)
- local result = {
- stores = {},
- overflows = {},
- destroys = {}
- }
- for storeName, store in pairs(allStores) do
- if store.canStoreItem(itemName) then
- if store.mode == "copy" then
- result.stores[storeName] = store
- elseif store.mode == "overflow" then
- result.overflows[storeName] = store
- elseif store.mode == "destroy" then
- result.destroys[storeName] = store
- end
- end
- end
- return result
- end
- local allSupplies = {}
- local function createSupply(peripheralName,config)
- currentState = "Create Supply"
- local displayName = peripheralName
- if config.displayName ~= nil and string.len(config.displayName) > 0 then
- displayName = config.displayName
- end
- local newSupply = {
- displayName = displayName,
- peripheral = peripheral.wrap(peripheralName)
- }
- if newSupply.peripheral == nil then
- error("Couldn't find peripheral "..peripheralName)
- end
- allSupplies[peripheralName] = newSupply
- end
- local allProvides = {}
- local function createProvide(peripheralName, config)
- currentState = "Create Provide"
- local displayName = peripheralName
- if config.displayName ~= nil and string.len(config.displayName) > 0 then
- displayName = config.displayName
- end
- local newProvide = {
- displayName = displayName,
- peripheral = peripheral.wrap(peripheralName),
- requests = config.requests,
- mode = config.provideMode
- }
- if newProvide.peripheral == nil then
- error("Couldn't find peripheral "..peripheralName)
- end
- allProvides[peripheralName] = newProvide
- end
- local allCrafters = {}
- local function createCrafter(peripheralName, config)
- currentState = "Create crafter"
- local displayName = peripheralName
- if config.displayName ~= nil and string.len(config.displayName) > 0 then
- displayName = config.displayName
- end
- local newCrafter = {
- displayName = displayName,
- peripheral = peripheral.wrap(config.craftChest),
- turtle = peripheral.wrap(peripheralName),
- busy=false,
- crafting=false
- }
- if newCrafter.peripheral == nil then
- error("Couldn't find peripheral "..peripheralName)
- end
- if newCrafter.turtle == nil then
- error("Coudn't find peripheral "..config.turtle)
- end
- -- Power cycle turtle
- -- This ensures it is clear before we start crafting
- newCrafter.turtle.shutdown()
- os.sleep(0.1)
- newCrafter.turtle.turnOn()
- allCrafters[peripheralName] = newCrafter
- end
- local allRequestOutputs = {}
- local function createRequestOutput(peripheralName, config)
- currentState = "Create Request Output"
- local displayName = peripheralName
- if config.displayName ~= nil and string.len(config.displayName) > 0 then
- displayName = config.displayName
- end
- local newRequestOutput = {
- displayName = displayName,
- peripheral = peripheral.wrap(peripheralName)
- }
- allRequestOutputs[peripheralName] = newRequestOutput
- end
- local allWorking = {}
- local function createWorking(peripheralName, config)
- currentState = "Create Working"
- local displayName = peripheralName
- if config.displayName ~= nil and string.len(config.displayName) > 0 then
- displayName = config.displayName
- end
- local newWorking = {
- displayName = displayName,
- peripheral = peripheral.wrap(peripheralName),
- busy=false
- }
- allWorking[peripheralName] = newWorking
- end
- -- Put the requested item into the destination from
- -- the given sources.
- -- Returns the amount retrieved
- local function requestItemsToDestination(destination, itemName, itemCount, targetSlot)
- local sources = getItemStores(itemName).stores
- local remainingCount = itemCount
- for sourceName, source in pairs(sources) do
- for slot, item in pairs(source.peripheral.list()) do
- if item.name == itemName then
- local maxPushCount = remainingCount
- if source.counts ~= nil and source.counts[itemName] < maxPushCount then
- maxPushCount = source.counts[itemName]
- end
- local pushCount = source.peripheral.pushItems(peripheral.getName(destination.peripheral), slot, maxPushCount, targetSlot)
- remainingCount = remainingCount - pushCount
- if source.counts ~= nil then
- source.counts[itemName] = source.counts[itemName] - pushCount
- end
- if remainingCount <= 0 then
- break
- end
- end
- end
- end
- return itemCount - remainingCount
- end
- local function sendSlotToDestinations(source,destinations,slot,itemName,count)
- local remainingCount = count
- for destinationName, destination in pairs(destinations) do
- local pushCount = source.peripheral.pushItems(destinationName, slot)
- if source.counts ~= nil then
- source.counts[itemName] = source.counts[itemName] - pushCount
- end
- if destination.counts ~= nil then
- if destination.counts[itemName] == nil then
- destination.counts[itemName] = pushCount
- else
- destination.counts[itemName] = destination.counts[itemName] + pushCount
- end
- end
- remainingCount = remainingCount - pushCount
- if remainingCount <= 0 then
- break
- end
- end
- return remainingCount
- end
- local function transferItems(source, destination, itemName, itemCount)
- if source == nil or destination == nil or itemName == nil or itemCount == nil then
- error("Transfer items, bad parameters")
- end
- local remainingCount = itemCount
- for slot, item in pairs(source.peripheral.list()) do
- if item.name == itemName then
- local transferCount = source.peripheral.pushItems(peripheral.getName(destination.peripheral), slot, remainingCount)
- if source.counts ~= nil then
- if source.counts[itemName] ~= nil then
- source.counts[itemName] = source.counts[itemName] - transferCount
- end
- end
- if destination.counts ~= nil then
- if source.counts[itemName] ~= nil then
- source.counts[itemName] = source.counts[itemName] + transferCount
- else
- source.counts[itemName] = transferCount
- end
- end
- remainingCount = remainingCount - transferCount
- if remainingCount <= 0 then
- break
- end
- end
- end
- return remainingCount <= 0
- end
- local function transferAll(source, destination)
- for slot, item in pairs(source.peripheral.list()) do
- local transferCount = source.peripheral.pushItems(peripheral.getName(destination.peripheral), slot)
- if source.counts ~= nil then
- if source.counts[item.name] ~= nil then
- source.counts[item.name] = source.counts[item.name] - transferCount
- end
- end
- if destination.counts ~= nil then
- if source.counts[item.name] ~= nil then
- source.counts[item.name] = source.counts[item.name] + transferCount
- else
- source.counts[item.name] = transferCount
- end
- end
- end
- end
- function craftItem(workingChest, itemName, itemCount)
- currentState = "Crafting item "..itemName.." "..itemCount
- -- Find item recipe
- -- Note: Later this should determine if another
- -- recipe can be used
- local recipes = getItemRecipes(itemName)
- if recipes[1] == nil then
- -- No crafting recipe
- return false
- end
- local recipe = recipes[1]
- -- Calculate requirements
- local requirements = {}
- for i=1,9,1 do
- local requirement = recipe[i]
- if requirement ~= nil then
- if requirements[requirement] ~= nil then
- requirements[requirement] = 1 + requirements[requirement]
- else
- requirements[requirement] = 1
- end
- end
- end
- local craftCount = math.ceil(itemCount / recipe.count)
- -- Get all requirements
- for name,count in pairs(requirements) do
- -- Get enough for total requirement
- requirements[name] = math.ceil(count * craftCount)
- if getOrCraftItem(workingChest, name, requirements[name], false, workingChest) == false then
- return false
- end
- end
- -- Find a crafter to craft in
- local crafterName, crafter
- for name,craft in pairs(allCrafters) do
- if craft.busy == false then
- crafterName = name
- crafter = craft
- end
- end
- -- Failure detection
- -- Note: We should change the busy check
- -- If we already have at least 1 crafter,
- -- just wait until one is ready.
- if crafter == nil then
- error("No crafter found! All in use or none")
- end
- -- Mark as busy before send
- -- This ensures it keeps it
- crafter.busy = true
- -- Clear crafting chest
- transferAll(crafter, workingChest)
- -- Transfer requirements
- currentState = "Transfering crafting requirements"
- for requirement, requirementCount in pairs(requirements) do
- print("Transfer",requirement,requirementCount)
- if transferItems(workingChest, crafter, requirement, requirementCount) == false then
- error("Working chest is missing items?!")
- end
- end
- for i=1,craftCount,1 do
- -- Tell crafter to craft
- crafter.crafting = true
- rednet.send(crafter.turtle.getID(), { type="craft", message=recipe })
- -- Wait for craft to finish
- while crafter.crafting == true do
- currentState = "Crafting "..itemName.." #"..i.."/"..craftCount * recipe.count
- os.sleep(1/20)
- end
- end
- -- Return results to working chest
- transferAll(crafter, workingChest)
- crafter.busy = false
- return true
- end
- function getOrCraftItem(destination, itemName, itemCount, clear, workingChest)
- local provideChest;
- if workingChest == nil then
- for _, working in pairs(allWorking) do
- if working.busy == false then
- workingChest = working
- break
- end
- end
- end
- for _, provide in pairs(allProvides) do
- provideChest = provide
- break
- end
- if workingChest == nil or provideChest == nil then
- error("Missing working or provide chest for get or craft operation")
- end
- if clear == true then
- workingChest.busy = true
- transferAll(workingChest, provideChest)
- end
- local count = requestItemsToDestination(workingChest, itemName, itemCount)
- -- If we already have enough, bail
- if count >= itemCount then
- transferItems(workingChest, destination, itemName, itemCount)
- workingChest.busy = false
- return true
- end
- -- Try crafting item instead
- if craftItem(workingChest, itemName, itemCount) == true then
- transferItems(workingChest, destination, itemName, itemCount)
- if clear == true then
- transferAll(workingChest, provideChest)
- workingChest.busy = false
- end
- return true
- end
- print("Failed get or craft",itemName,itemCount,"returning items")
- if clear == true then
- transferAll(workingChest, provideChest)
- workingChest.busy = false
- end
- return false
- end
- local function handleItem(source, slot, itemName, itemCount)
- local itemConditions = getItemCondition(itemName)
- local itemStores = getItemStores(itemName)
- local remainingItems = itemCount
- -- If we don't want to keep this item
- if itemConditions ~= nil and
- itemConditions.keep ~= nil and
- itemConditions.keep(
- (function()
- return source.peripheral.getItemDetail(slot)
- end)) == false then
- -- Find a place to destroy
- remainingItems = sendSlotToDestinations(source, itemStores.destroys, slot, itemName, remainingItems)
- -- If we want to keep this item
- else
- -- Try and put it away
- remainingItems = sendSlotToDestinations(source, itemStores.stores, slot, itemName, remainingItems)
- -- We have overflow
- if remainingItems > 0 then
- -- Should we throw away overflow
- if itemConditions ~= nil and itemConditions.overflow ~= nil and itemConditions.overflow() == false then
- remainingItems = sendSlotToDestinations(source, itemStores.destroys, slot, itemName, remainingItems)
- -- Overflow
- else
- remainingItems = sendSlotToDestinations(source, itemStores.overflows, slot, itemName, remainingItems)
- -- Destroy
- if remainingItems > 0 then
- remainingItems = sendSlotToDestinations(source, itemStores.destroys, slot, itemName, remainingItems)
- end
- end
- end
- end
- return remainingItems
- end
- local function handleSupplies()
- for supplyName, supply in pairs(allSupplies) do
- local itemsToSort = supply.peripheral.list()
- -- Sort each slot of supply inventory
- for slot, item in pairs(itemsToSort) do
- if item ~= nil then
- currentState = "Putting away "..item.name.." "..item.count
- handleItem(supply, slot, item.name, item.count)
- end
- end
- end
- end
- local function handleProvides()
- for providerName, provider in pairs(allProvides) do
- local counts = {}
- for slot, item in pairs(provider.peripheral.list()) do
- if counts[item.name] == nil then
- counts[item.name] = item.count
- else
- counts[item.name] = counts[item.name] + item.count
- end
- if provider.mode == "exact" then
- if provider.requests[item.name] == nil then
- handleItem(provider, slot, item.name, item.count)
- elseif provider.requests[item.name] < counts[item.name] then
- handleItem(provider, slot, item.name, counts[item.name] - provider.requests[item.name])
- end
- end
- end
- for requestName, requestCount in pairs(provider.requests) do
- if counts[requestName] == nil then
- currentState = "Supplying "..requestName.." "..requestCount.." to "..provider.displayName
- requestItemsToDestination(provider, requestName, requestCount)
- elseif counts[requestName] < requestCount then
- currentState = "Supplying "..requestName.." "..requestCount.." to "..provider.displayName
- requestItemsToDestination(provider, requestName, requestCount - counts[requestName])
- end
- end
- end
- end
- local requests = {}
- local function createRequest(itemName, itemCount)
- requests[math.random(1,9999999)] = { name=itemName, count=itemCount }
- end
- local function clearRequests()
- requests = {}
- end
- local function handleRequests()
- for i,request in pairs(requests) do
- requests[i] = nil
- currentState = "Handling request "..request.name.." "..request.count
- local requestOutput
- for name, output in pairs(allRequestOutputs) do
- requestOutput = output
- end
- if requestOutput == nil then
- error("Need request output to handle request")
- end
- local success = getOrCraftItem(requestOutput, request.name, request.count, true)
- print("Request finished", request.name, request.count, "success", success)
- break
- end
- end
- local function sortLoop()
- local crafterCount = 0
- local workingCount = 0
- for _, crafter in pairs(allCrafters) do
- crafterCount = crafterCount + 1
- end
- for _, working in pairs(allWorking) do
- workingCount = workingCount + 1
- end
- local maxParallelCrafts = math.min(crafterCount, workingCount)
- while true do
- currentState = "Idle"
- handleSupplies()
- handleProvides()
- if maxParallelCrafts <= 1 then
- handleRequests()
- elseif maxParallelCrafts <= 2 then
- parallel.waitForAll(handleRequests, handleRequests)
- elseif maxParallelCrafts <= 3 then
- parallel.waitForAll(handleRequests, handleRequests, handleRequests)
- end
- end
- end
- local monitor = peripheral.find("monitor")
- local currentItemCategory = nil
- function uiLoop()
- drawLib.clear()
- local cancelQueue = drawLib.createButton(51,2,27,2,"Cancel Queue",
- (function()
- clearRequests()
- print("Cleared requests")
- end),true)
- drawLib.addButton(cancelQueue)
- local categoryButtons = {}
- local nextCategoryX = 0
- for categoryName,category in pairs(itemCategories) do
- local button = drawLib.createButton(nextCategoryX, 2, string.len(categoryName) + 2, 2, categoryName, nil, true)
- button.action = function()
- currentItemCategory = category
- drawLib.clear()
- drawLib.addButton(cancelQueue)
- for catName,cat in pairs(categoryButtons) do
- drawLib.addButton(cat)
- end
- local itemCount = 1
- for i,item in ipairs(currentItemCategory) do
- drawLib.addButton(drawLib.createButton(35,5+itemCount,0,0,"1", function()
- createRequest(item,1)
- end,true))
- drawLib.addButton(drawLib.createButton(37,5+itemCount,0,0,"8", function()
- createRequest(item,8)
- end,true))
- drawLib.addButton(drawLib.createButton(39,5+itemCount,1,0,"16", function()
- createRequest(item,16)
- end,true))
- drawLib.addButton(drawLib.createButton(42,5+itemCount,1,0,"32", function()
- createRequest(item,32)
- end,true))
- drawLib.addButton(drawLib.createButton(45,5+itemCount,1,0,"64", function()
- createRequest(item,64)
- end,true))
- itemCount = itemCount+1
- end
- end
- nextCategoryX = nextCategoryX + string.len(categoryName) + 4
- drawLib.addButton(button)
- categoryButtons[categoryName] = button
- end
- monitor.setCursorBlink(false)
- monitor.setTextScale(0.5)
- while true do
- monitor.setCursorPos(1,1)
- monitor.setBackgroundColor(colors.black)
- monitor.clear()
- drawLib.render(monitor)
- monitor.setCursorPos(1,1)
- monitor.setBackgroundColor(colors.black)
- monitor.setTextColor(colors.white)
- monitor.write(currentState)
- -- Request queue
- monitor.setCursorPos(55,1)
- monitor.write("Request Queue")
- local requestCounter = 1
- for i,request in pairs(requests) do
- monitor.setCursorPos(51,requestCounter+5)
- requestCounter = requestCounter + 1
- monitor.write("["..i.."] "..request.name.." "..request.count)
- end
- -- Item category display
- if currentItemCategory ~= nil then
- for i,item in ipairs(currentItemCategory) do
- local count = 0
- local stores = getItemStores(item)
- for storeName,store in pairs(stores.stores) do
- if store.counts[item] then
- count = count + store.counts[item]
- end
- end
- monitor.setCursorPos(1,5 + i)
- monitor.write(item.." "..count)
- end
- end
- -- 10hz update should be fine
- os.sleep(0.1)
- end
- end
- local function net(id, msg, protocol)
- -- Handle crafter notifications
- -- Used to identify busy crafters
- if msg.type == "crafter" then
- for _,crafter in pairs(allCrafters) do
- if crafter.turtle.getID() == id then
- if msg.message == "craftFinished" then
- crafter.crafting = false
- end
- end
- end
- end
- end
- local function eventLoop()
- while true do
- local event, n, x, y = os.pullEvent()
- if event == "monitor_touch" then
- drawLib.onClick(x,y,2)
- elseif event == "rednet_message" then
- net(n,x,y)
- end
- end
- end
- local function initialize()
- currentState = "Initialize"
- monitor.clear()
- monitor.setCursorPos(1,1)
- monitor.setTextColor(colors.white)
- monitor.setBackgroundColor(colors.black)
- monitor.write("Initializing... Please wait")
- allStores = {}
- allSupplies = {}
- allProvides = {}
- allCrafters = {}
- for peripheralName,config in pairs(storageConfigs) do
- if config.mode == "store" then
- createStore(peripheralName, config)
- elseif config.mode == "supply" then
- createSupply(peripheralName, config)
- elseif config.mode == "provide" then
- createProvide(peripheralName, config)
- elseif config.mode == "crafter" then
- createCrafter(peripheralName, config)
- elseif config.mode == "requestOutput" then
- createRequestOutput(peripheralName, config)
- elseif config.mode == "working" then
- createWorking(peripheralName, config)
- end
- end
- -- Ensure all systems are initialized and stable!
- -- Primarily for crafters
- os.sleep(6)
- parallel.waitForAny(sortLoop, uiLoop, eventLoop)
- end
- initialize()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement