Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- filenames = {}
- filenames.configFolder = "config"
- filenames.tankContents = filenames.configFolder.."/tank_contents.json"
- filenames.potionTypes = filenames.configFolder.."/potion_types.json"
- itemNames = {
- redstone = "minecraft:redstone",
- glowstone = "minecraft:glowstone_dust",
- gunpowder = "minecraft:gunpowder",
- dragonBreath = "minecraft:dragon_breath",
- netherwart = "minecraft:nether_wart"
- }
- term.clear()
- term.setCursorPos(1,1)
- term.setTextColor(colors.white)
- if json == nil then
- json = {}
- end
- if not fs.exists("apis/json") then
- print("Downloading json api...")
- shell.run("pastebin","get","4nRg9CHU","apis/json")
- end
- if not fs.exists(filenames.potionTypes) then
- print("Downloading potion types config...")
- shell.run("pastebin","get","YBSC0sGp",filenames.potionTypes)
- end
- UNKNOWN = "unknown"
- potionTypes = {}
- potionDisplayNames = {}
- potionModifiers = {
- normal="Normal",
- extended="Extended",
- levelTwo="Level II",
- getList=function()
- return {
- potionModifiers.normal,
- potionModifiers.extended,
- potionModifiers.levelTwo
- }
- end,
- isValid=function(value)
- if value == potionModifiers.normal then return true end
- if value == potionModifiers.extended then return true end
- if value == potionModifiers.levelTwo then return true end
- end
- }
- potionStyles = {
- normal="Normal",
- splash="Splash",
- lingering="Lingering",
- getList=function()
- return {
- potionStyles.normal,
- potionStyles.splash,
- potionStyles.lingering
- }
- end,
- isValid=function(value)
- if value == potionStyles.normal then return true end
- if value == potionStyles.splash then return true end
- if value == potionStyles.lingering then return true end
- end
- }
- waterTank = nil
- storageTanks = {}
- basinName = nil
- inventories = {}
- inventoryIndex = {}
- monitors = {}
- spout = nil
- depot = nil
- string.startsWith = function(self,str)
- return self:find("^"..str)
- end
- string.split = function(self,sep)
- if sep == nil then
- sep = "%s"
- end
- local t = {}
- for str in self:gmatch("([^"..sep.."]+)") do
- table.insert(t,str)
- end
- return t
- end
- table.contains = function(self,value)
- for _, v in ipairs(self) do
- if v == value then
- return true
- end
- end
- return false
- end
- container={
- contents={
- name=UNKNOWN,
- modifier=potionModifiers.normal,
- style=potionStyles.normal
- },
- contentsMatch=function(self,value)
- if self.contents == nil then
- return value == nil
- elseif value == nil then
- return false
- end
- if self.contents.name ~= value.name then
- return false
- end
- if self.contents.name == UNKNOWN then
- return true
- end
- return self.contents.modifier == value.modifier and self.contents.style == value.style
- end
- }
- function createContainer(t)
- t.contents = {
- name=UNKNOWN,
- modifier=potionModifiers.normal,
- style=potionStyles.normal
- }
- t.contentsMatch = container.contentsMatch
- return t
- end
- function createList(t)
- if t == nil then
- t = {}
- end
- t.insert = table.insert
- t.contains = table.contains
- t.remove = table.remove
- return t
- end
- function clearMonitor(mon)
- if mon ~= nil then
- mon.clear()
- mon.setCursorPos(1,1)
- elseif #monitors > 0 then
- for _,v in ipairs(monitors) do
- v.clear()
- v.setCursorPos(1,1)
- end
- end
- end
- function writeMonitor(text)
- if #monitors > 0 then
- for k,v in ipairs(monitors) do
- clearMonitor(v)
- v.setTextColor(colors.white)
- v.write(text)
- end
- end
- end
- function writeMonitorError(text)
- if #monitors > 0 then
- for k,v in ipairs(monitors) do
- clearMonitor(v)
- v.setTextColor(colors.red)
- v.write(text)
- end
- end
- end
- function clearTerm()
- term.clear()
- term.setCursorPos(1,1)
- end
- function printError(text)
- term.setTextColor(colors.red)
- print(text)
- term.setTextColor(colors.white)
- end
- function printInstruction(text)
- term.setTextColor(colors.lightBlue)
- print(text)
- term.setTextColor(colors.white)
- end
- function printLine()
- print()
- end
- function waitForKey()
- return os.pullEvent("key")
- end
- function wrapPeripherals()
- while true do
- waterTank = nil
- storageTanks = createList()
- basin = nil
- inventories = createList()
- monitors = createList()
- for _,v in ipairs(peripheral.getNames()) do
- if v:startsWith("create:fluid_tank") then
- local t = peripheral.wrap(v)
- local tanks = t.tanks()
- if #tanks > 0 and tanks[1].name == "minecraft:water" then
- waterTank = t
- waterTank.name = v
- else
- t.name = v
- t = createContainer(t)
- if #tanks == 0 then
- t.contents = nil
- end
- storageTanks:insert(t)
- end
- elseif v:startsWith("create:basin") then
- basin = peripheral.wrap(v)
- basin.name = v
- elseif v:startsWith("monitor") then
- monitors:insert(peripheral.wrap(v))
- elseif v:startsWith("create:spout") then
- spout = peripheral.wrap(v)
- spout.name = v
- elseif v:startsWith("create:depot") then
- depot = peripheral.wrap(v)
- depot.name = v
- else
- local sideNames = createList({"top","front","left","right","back","bottom"})
- if not sideNames:contains(v) then
- local p = peripheral.wrap(v)
- if p.pullItems ~= nil then
- p.name = v
- inventories:insert(p)
- end
- end
- end
- end
- local messages = createList()
- if waterTank == nil then
- messages:insert("No water tank found")
- end
- if #storageTanks == 0 then
- messages:insert("No storage tanks found")
- end
- if basin == nil then
- messages:insert("No basin found")
- end
- if spout == nil then
- messages:insert("No spout found")
- end
- if depot == nil then
- messages:insert("No depot found")
- end
- if #inventories == 0 then
- messages:insert("No item inventories found")
- end
- if #messages > 0 then
- writeMonitorError("Could not find necessary peripherals")
- clearTerm()
- printError("Could not find necessary peripherals")
- for _,msg in ipairs(messages) do
- printError("- "..msg)
- end
- printLine()
- print("Press X to quit. Press any other key to retry.")
- local _, key = waitForKey()
- if key == keys.x then
- return false
- end
- else
- return true
- end
- end
- end
- function initializePotionTypes()
- potionTypes = {}
- if not pcall(function()
- potionTypes = json.decodeFromFile(filenames.potionTypes)
- end) then
- --failed to decode file and load potion types
- clearTerm()
- printError("An error occurred while reading potion types config")
- return false
- end
- for typeName,typeData in pairs(potionTypes) do
- for i=1, #typeData.names do
- potionDisplayNames[typeData.names[i]] = typeName
- end
- end
- return true
- end
- function getPotionTypeByName(name)
- if potionTypes[name] ~= nil then
- return name
- end
- if potionDisplayNames[name] == nil then
- return nil
- end
- return potionDisplayNames[name]
- end
- function initializeTankContents()
- if fs.exists(filenames.tankContents) then
- local contents = nil
- if not pcall(function()
- contents = json.decodeFromFile(filenames.tankContents)
- for _,tank in ipairs(storageTanks) do
- if tank.contents ~= nil then
- if contents[tank.name] ~= nil then
- tank.contents = contents[tank.name]
- end
- end
- end
- end) then
- --failed to decode file and set tank contents
- clearTerm()
- printError("An error occurred while reading previous tank contents")
- waitForKey()
- end
- writeTankContents()
- end
- end
- function writeTankContents()
- local contents = {}
- for _,tank in ipairs(storageTanks) do
- if tank.contents ~= nil and tank.contents.name ~= UNKNOWN then
- contents[tank.name] = tank.contents
- end
- end
- if not pcall(function()
- local h = fs.open(filenames.tankContents,"w")
- h.write(json.encode(contents))
- h.close()
- end) then
- printError("Error writing tank contents file")
- waitForKey()
- end
- end
- function setTankContents(tank,potionType,modifier,style)
- local contents = {}
- if #tank.tanks() == 0 then
- contents = nil
- elseif potionType == nil then
- contents = {
- name=UNKNOWN,
- modifier=potionModifiers.normal,
- style=potionStyles.normal
- }
- else
- if modifier == nil then
- modifier = potionModifiers.normal
- end
- if style == nil then
- style = potionStyles.normal
- end
- contents = {
- name=potionType,
- modifier=modifier,
- style=style
- }
- end
- for i,t in ipairs(storageTanks) do
- if t.name == tank.name then
- storageTanks[i].contents = contents
- if contents == nil then
- print("setting contents of "..tank.name.." to nil")
- else
- print("setting contents of "..tank.name.." to...")
- print(json.encode(storageTanks[i].contents))
- end
- end
- end
- writeTankContents()
- end
- function findStorage(potionName,modifier,style)
- local matchingTanks = createList()
- local unknownTanks = createList()
- local emptyTanks = createList()
- local search = {
- name=potionName,
- modifier=modifier,
- style=style
- }
- for i,v in ipairs(storageTanks) do
- print("checking storage tank "..i)
- if #v.tanks() ~= 0 then
- print("tank has fluids")
- if v:contentsMatch(search) then
- print("tank is a match")
- print(json.encode(v.contents))
- matchingTanks:insert(v)
- elseif v.contents.name == UNKNOWN then
- print("tank is unknown")
- unknownTanks:insert(v)
- end
- else
- print("tank is empty")
- emptyTanks:insert(v)
- end
- end
- return matchingTanks, unknownTanks, emptyTanks
- end
- function getItemForModifier(modifier)
- if modifier == potionModifiers.extended then
- return itemNames.redstone
- elseif modifier == potionModifiers.levelTwo then
- return itemNames.glowstone
- end
- return nil
- end
- function getItemForStyle(style)
- if style == potionStyles.splash then
- return itemNames.gunpowder
- elseif style == potionStyles.lingering then
- return itemNames.dragonBreath
- end
- return nil
- end
- function refillWater()
- basin.pullFluid(waterTank.name)
- end
- function indexInventories()
- local totalSlots = 0
- for _,inv in ipairs(inventories) do
- totalSlots = totalSlots + inv.size()
- end
- inventoryIndex = {}
- local currentSlot = 1
- for _,inv in ipairs(inventories) do
- local inventorySize = inv.size()
- clearTerm()
- for slot=1, inventorySize do
- term.setCursorPos(1,1)
- term.write("Indexing Inventories... Slot "..currentSlot.." of "..totalSlots)
- local item = inv.getItemDetail(slot)
- if item ~= nil then
- if inventoryIndex[item.name] == nil then
- inventoryIndex[item.name] = {
- total=0,
- locations=createList()
- }
- end
- inventoryIndex[item.name].total = inventoryIndex[item.name].total + item.count
- inventoryIndex[item.name].locations:insert({
- inv,
- slot,
- item.count
- })
- end
- currentSlot = currentSlot + 1
- end
- end
- clearTerm()
- end
- function checkPotionStorage(potionType,modifier,style,bottleCount)
- if modifier == nil then
- modifier = potionModifiers.normal
- end
- if style == nil then
- style = potionStyles.normal
- end
- if bottleCount == nil then
- bottleCount = 1
- end
- potionType = getPotionTypeByName(potionType)
- if potionType == nil then
- writeMonitorError("canCraftPotion: Unknown potion type provided. "..potionType)
- return false
- end
- if potionModifiers[modifier] == nil then
- writeMonitorError("canCraftPotion: Unknown modifier provided. "..modifier)
- end
- if potionStyles[style] == nil then
- writeMonitorError("canCraftPotion: Unknown style provided. "..style)
- end
- local mbNeeded = bottleCount*250
- local saveContents = false
- local locations = createList()
- for i,tank in ipairs(storageTanks) do
- if tank:contentsMatch({
- name=potionType,
- modifier=modifier,
- style=style
- }) then
- local tanks = tank.tanks()
- if #tanks == 0 then
- --tank contents is out of date
- tank.contents = nil
- saveContents = true
- else
- if tanks[1].amount >= mbNeeded then
- locations:insert({i,mbNeeded})
- break
- elseif tanks[1].amount > 0 then
- locations:insert({i,tanks[1].amount})
- mbNeeded = mbNeeded - tanks[1].amount
- end
- end
- end
- end
- if saveContents then
- writeTankContents()
- end
- if mbNeeded > 0 then
- return false, mbNeeded / 250
- end
- return true, locations
- end
- function canCraftPotion(potionName,modifier,style,recipeCount)
- if modifier == nil then
- modifier = potionModifiers.normal
- end
- if style == nil then
- style = potionStyles.normal
- end
- if recipeCount == nil then
- recipeCount = 1
- end
- potionType = getPotionTypeByName(potionName)
- local missing = createList()
- checkMissingItem(itemNames.netherwart,recipeCount,checkMissingItem)
- if modifier ~= potionModifiers.normal then
- print("checking item for modifier")
- local i = getItemForModifier(modifier)
- print("modifier item:")
- print(i)
- checkMissingItem(i,recipeCount,missing)
- end
- if style ~= potionStyles.normal then
- print("checking item for style")
- local i = getItemForStyle(style)
- print("style item:")
- print(i)
- checkMissingItem(i,recipeCount,missing)
- end
- local recipes = createList()
- local remainingCrafts = recipeCount
- print("checking recipe specific items")
- for r,items in ipairs(potionTypes[potionType].recipes) do
- local crafts = getPossibleCrafts(items,remainingCrafts)
- if crafts > 0 then
- remainingCrafts = remainingCrafts - crafts
- recipes:insert({crafts,r})
- if remainingCrafts == 0 then
- break
- end
- end
- end
- if remainingCrafts > 0 then
- print("remaining crafts > 0")
- for _,item in ipairs(potionTypes[potionType].recipes[1]) do
- print(item)
- checkMissingItem(item,recipeCount,missing)
- end
- end
- if #missing > 0 then
- return false, missing, nil
- end
- return true, nil, recipes
- end
- function checkMissingItem(name,qty,missing)
- print("checking for {"..qty.."} {"..name.."}")
- local count = 0
- if inventoryIndex[name] ~= nil then
- count = inventoryIndex[name].total
- end
- if count < qty then
- print("item not found")
- missing:insert({name,qty - count})
- end
- end
- function getPossibleCrafts(items,remainingCrafts)
- local maxPossible = remainingCrafts
- for _,item in ipairs(items) do
- if inventoryIndex[item] == nil then
- return 0
- else
- if inventoryIndex[item].total < maxPossible then
- maxPossible = inventoryIndex[item].total
- end
- end
- end
- return maxPossible
- end
- function promptPotionType()
- while true do
- printInstruction("Which potion type do you want? (Enter \"cancel\" to quit)")
- local i = tostring(read())
- if i == "cancel" then
- return nil
- end
- local t = getPotionTypeByName(i)
- if t == nil then
- printError("Invalid type provided")
- else
- return t
- end
- end
- end
- function promptModifier()
- local mods = potionModifiers.getList()
- while true do
- printInstruction("Which modifier do you want?")
- for m=1, #mods do
- printInstruction(" "..m..") "..mods[m])
- end
- printInstruction(" "..(#mods+1)..") Cancel")
- local i = tonumber(read())
- if i == nil or i < 1 or i > #mods+1 then
- printError("Invalid value provided")
- elseif i == #mods+1 then
- return nil
- else
- return mods[i]
- end
- end
- end
- function promptStyle()
- local mods = potionStyles.getList()
- while true do
- printInstruction("Which style do you want?")
- for m=1, #mods do
- printInstruction(" "..m..") "..mods[m])
- end
- printInstruction(" "..(#mods+1)..") Cancel")
- local i = tonumber(read())
- if i == nil or i < 1 or i > #mods+1 then
- printError("Invalid value provided")
- elseif i == #mods+1 then
- return nil
- else
- return mods[i]
- end
- end
- end
- function promptQty(qtyType)
- while true do
- printInstruction("How many "..qtyType.." do you want? (Enter \"cancel\" to quit)")
- local i = read()
- if i == "cancel" then
- return nil
- else
- i = tonumber(i)
- if i == nil or i <= 0 then
- printError("Invalid value provided")
- else
- return i
- end
- end
- end
- end
- function getFullPotionName(potionType,modifier,style)
- local fullPotionName = "Potion of "..potionTypes[potionType].names[1]
- if style ~= potionStyles.normal then
- fullPotionName = style.." "..fullPotionName
- end
- if modifier == potionModifiers.extended then
- fullPotionName = potionModifiers.extended.." "..fullPotionName
- elseif modifier == potionModifiers.levelTwo then
- fullPotionName = fullPotionName.." II"
- end
- return fullPotionName
- end
- function isBasinEmpty()
- if #basin.tanks() > 0 then
- return false
- end
- if basin.tanks()[3] ~= nil then
- return false
- end
- return true
- end
- function itemStuckInBasin()
- writeMonitorError("Item stuck in basin!")
- while true do
- sleep(10)
- if basin.getItemDetail(1) == nil then
- return
- end
- end
- end
- function itemMissingInInventory(itemName)
- writeMonitorError("Item could not be found in inventory! "..itemName)
- waitForKey()
- end
- function cannotStorePotion()
- writeMonitorError("Potion in basin could not be stored!")
- waitForKey()
- end
- function craft(com)
- local potionType, modifier, style, qty = nil, nil, nil, nil
- if #com >= 2 then
- potionType = getPotionTypeByName(com[2])
- if potionType == nil then
- printError("Invalid value provided for potion type")
- return
- end
- else
- potionType = promptPotionType()
- if potionType == nil then return end
- end
- if #com >= 3 then
- modifier = com[3]
- if tonumber(modifier) ~= nil then
- modifier = tonumber(modifier)
- if modifier > 0 and modifier <= #potionModifiers.getList() then
- modifier = potionModifiers.getList()[modifier]
- else
- modifier = nil
- end
- else
- if not potionModifiers.isValid(modifier) then
- modifier = nil
- end
- end
- if modifier == nil then
- printError("Invalid value provided for potion modifier")
- return
- end
- else
- modifier = promptModifier()
- if modifier == nil then return end
- end
- if #com >= 4 then
- style = com[4]
- if tonumber(style) ~= nil then
- style = tonumber(style)
- if style > 0 and style <= #potionStyles.getList() then
- style = potionStyles.getList()[style]
- else
- style = nil
- end
- else
- if not potionStyles.isValid(style) then
- style = nil
- end
- end
- if style == nil then
- printError("Invalid value provided for potion style")
- return
- end
- else
- style = promptStyle()
- if style == nil then return end
- end
- if #com == 5 then
- qty = tonumber(com[5])
- if qty == nil then
- printError("Invalid value provided for number of buckets")
- return
- end
- else
- qty = promptQty("buckets")
- if qty == nil then return end
- end
- local result, missing, recipes = canCraftPotion(potionType,modifier,style,qty)
- if not result then
- printError("Cannot craft specified potion. Missing items:")
- for _,m in ipairs(missing) do
- print(" - "..m[1].." x "..m[2])
- return
- end
- end
- clearTerm()
- local remainingQty = qty
- local fullPotionName = getFullPotionName(potionType,modifier,style)
- for _,recipe in ipairs(recipes) do
- local recipeCount = recipe[1]
- local recipeIndex = recipe[2]
- for i=1, recipeCount do
- writeMonitor("Crafting "..(qty-remainingQty+1).." of "..qty..": "..fullPotionName)
- refillWater()
- mix(itemNames.netherwart)
- for _,itemName in ipairs(potionTypes[potionType].recipes[recipeIndex]) do
- mix(itemName)
- end
- if modifier ~= potionModifiers.normal then
- mix(getItemForModifier(modifier))
- end
- if style ~= potionStyles.normal then
- mix(getItemForStyle(style))
- end
- while true do
- if storePotion(potionType,modifier,style) then
- break
- else
- cannotStorePotion()
- end
- end
- remainingQty = remainingQty - 1
- end
- end
- clearMonitor()
- clearTerm()
- end
- function mix(itemName)
- if basin.getItemDetail(1) ~= nil then
- itemStuckInBasin()
- end
- if putItemInBasin(itemName) then
- while true do
- sleep(1)
- if basin.getItemDetail(1) == nil then
- break
- end
- end
- else
- return false
- end
- return true
- end
- function putItemInBasin(itemName)
- while true do
- local itemLocation = inventoryIndex[itemName].locations[1]
- local pullCount = basin.pullItems(itemLocation[1].name,itemLocation[2],1)
- if pullCount == 0 then
- if itemLocation[1].getItemDetail(itemLocation[2]).count == 0 then
- inventoryIndex[itemName].total = inventoryIndex[itemName].total - itemLocation[3]
- inventoryIndex[itemName].locations:remove(1)
- if #inventoryIndex[itemName].locations == 0 then
- if not itemMissingInInventory(itemName) then
- return false
- end
- end
- end
- else
- inventoryIndex[itemName].total = inventoryIndex[itemName].total - 1
- inventoryIndex[itemName].locations[1][3] = inventoryIndex[itemName].locations[1][3]-1
- if inventoryIndex[itemName].locations[1][3] == 0 then
- inventoryIndex[itemName].locations:remove(1)
- end
- return true
- end
- end
- end
- function storePotion(potionType,modifier,style)
- local matchingTanks, unknownTanks, emptyTanks = findStorage(potionType,modifier,style)
- if #matchingTanks > 0 then
- for _,t in ipairs(matchingTanks) do
- if t.pullFluid(basin.name) > 0 then
- if isBasinEmpty() then
- return true
- end
- end
- end
- end
- if #unknownTanks > 0 then
- for _,t in ipairs(unknownTanks) do
- if t.pullFluid(basin.name) > 0 then
- setTankContents(t,potionType,modifier,style)
- if isBasinEmpty() then
- break
- end
- end
- end
- end
- if not isBasinEmpty() and #emptyTanks > 0 then
- for _,t in ipairs(emptyTanks) do
- if t.pullFluid(basin.name) > 0 then
- setTankContents(t,potionType,modifier,style)
- if isBasinEmpty() then
- break
- end
- end
- end
- end
- return isBasinEmpty()
- end
- function run()
- if not wrapPeripherals() then
- return
- end
- clearMonitor()
- clearTerm()
- if not os.loadAPI("apis/json") then
- writeMonitorError("Could not load json api")
- printError("Could not load json api")
- return
- end
- initializeTankContents()
- if not initializePotionTypes() then
- return
- end
- indexInventories()
- while true do
- printInstruction("Please enter a command")
- writeMonitor("Waiting for input!")
- local com = tostring(read()):split(",")
- if #com > 0 then
- if com[1] == "craft" then
- craft(com)
- elseif com[1] == "bottle" then
- elseif com[1] == "index" then
- indexInventories()
- elseif com[1] == "exit" then
- return
- end
- end
- end
- end
- run()
Add Comment
Please, Sign In to add comment