ComputerCraft Archive

PotionsCore

computer networking Caitlin-Sykes github

Description

-----------------------------------------------------------------------------

Installation

Copy one of these commands into your ComputerCraft terminal:

wget:wget https://raw.githubusercontent.com/Caitlin-Sykes/ComputerCraft-Scripts/main/PotionAutoBrewer/PotionsCore.lua potionscore
Archive:wget https://cc.shobie.xyz/cc/get/gh-Caitlin-Sykes-ComputerCraft-Scripts-potionautobrewer-potionscore potionscore
Quick Install: wget https://cc.shobie.xyz/cc/get/gh-Caitlin-Sykes-ComputerCraft-Scripts-potionautobrewer-potionscore PotionsCore

Usage

Run: PotionsCore

Tags

networking

Source

View Original Source

Code Preview

-- -----------------------------------------------------------------------------
-- Potion Auto brewer Core Functions
-- -----------------------------------------------------------------------------
local PotionsCore = {}

-- Imports Customisation
local Customisation = require("/PotionAutocrafting/Customisation")

-- Import Chests
local Chests = require("/PotionAutocrafting/Chests")

-- Import potions
local Potions = require("/PotionAutocrafting/Potions")

-- Import items
local Items = require("/PotionAutocrafting/Items")

-- Private variables to store peripherals
local ME

-- Cur output
local cur_output = redstone.getBundledOutput(Customisation.BUNDLED_CABLE_SIDE)

-- -----------------------------------------------------------------------------
-- Peripheral Things
-- -----------------------------------------------------------------------------

-- Private method to check for peripheral validity
-- @peripheral -> peripheral to check connection of
-- @name -> name of peripheral
local function ValidatePeripheral(peripheral, name)
    if peripheral == nil then
        error("PERIPHERAL ERROR: Cannot find a connected " .. name .. ".")
    else
        print(name .. " successfully connected.")
    end
    return peripheral
end

-- Initialises and sets up peripherals
function PotionsCore:SetupPeripherals()
    -- ME Bridge
    ME = ValidatePeripheral(peripheral.wrap(Customisation.ME), "ME Bridge")
    cur_output = 0
    redstone.setBundledOutput(Customisation.BUNDLED_CABLE_SIDE, cur_output)

end

-- -----------------------------------------------------------------------------
-- Getters
-- -----------------------------------------------------------------------------
-- Method to return reactor info
function PotionsCore:GetMEInfo()
    return ME
end

-- Function to get a chest by its id
-- @id -> name of the chest, ie "minecraft:chest_0"
local function GetChestById(id)
    if id ~= nil then
        for _, chest in pairs(Chests.chests) do
            if chest.id == id then
                return chest
            end
        end
    end
    return nil
end

-- -----------------------------------------------------------------------------
-- Core Logic
-- -----------------------------------------------------------------------------
-- Handles the potion crafting
-- @potion -> the potion that is being crafted
-- @chest_cluster -> the chest cluster that the job is assigned to
function PotionsCore:PotionCrafting(potion, chest_cluster)

    if (chest_cluster.id ~= nil) then
        print("Needs base potion: " .. potion.base_potion)
        print("Needs the following item: " .. potion.ingredient)
        print("--------------------------")

        -- Gets the attached chest
        chest_modem = peripheral.wrap(chest_cluster.id)
        basin_modem = peripheral.wrap(chest_cluster.basin)

        if (chest_modem ~= nil and basin_modem ~= nil) then
            -- Exporting the base potion to the basin
            ME.exportFluidToPeripheral({
                name = potion.base_potion
            }, chest_cluster.basin, 1000)

            -- Check each slot in the chest for the ingredient
            for slot, chestItem in pairs(chest_modem.list()) do
                if chestItem.name == potion.ingredient then
                    -- Export one of the ingredient to the basin
                    chest_modem.pushItems(chest_cluster.basin, slot, 1)
                    print("Exported " .. potion.ingredient .. " from slot " .. slot .. " to basin")
                    break
                end
            end
        end

    else
        print("Ending the crafting of: " .. potion.result .. " in chest cluster " .. chest_cluster.id)
    end

end

-- Handles the item crafting
-- @item -> the item that is being crafted
-- @chest_cluster -> the chest cluster that the job is assigned to
function PotionsCore:ItemCrafting(item, chest_cluster)
    if (chest_cluster.id ~= nil) then
        print("Starting the crafting of: " .. item.result .. " in chest cluster " .. chest_cluster.id)

        -- Gets the attached chest and basin
        local chest_modem = peripheral.wrap(chest_cluster.id)
        local basin_modem = peripheral.wrap(chest_cluster.basin)

        if (chest_modem ~= nil and basin_modem ~= nil) then

            -- Iterate over each ingredient in item.ingredient and find it in the chest
            for _, ingredientName in ipairs(item.ingredient) do
                local found = false -- Track if we found the ingredient

                -- Check each slot in the chest for the ingredient
                for slot, chestItem in pairs(chest_modem.list()) do
                    if chestItem.name == ingredientName then
                        -- Export one of the ingredient to the basin
                        chest_modem.pushItems(chest_cluster.basin, slot, 1)
                        print("Exported " .. ingredientName .. " from slot " .. slot .. " to basin")
                        found = true
                        break -- Move to the next ingredient once found
                    end
                end

                -- If ingredient wasn't found, print a message (optional)
                if not found then
                    print("Ingredient " .. ingredientName .. " not found in chest " .. chest_cluster.id)
                end
            end
        end
    else
        print("Ending the crafting of: " .. item.result .. " in chest cluster " .. chest_cluster.id)
    end
end

-- Manages the crafting of potions
-- @potion -> the potion to be made
-- @crafted -> "false" if no existing craft, otherwise a chest id
function PotionCraftingManagement(potion, crafted)

    cur_output = redstone.getBundledOutput(Customisation.BUNDLED_CABLE_SIDE)

    -- If it's got a crafting job and isn't in any of the chests
    if ME.isFluidCrafting({
        name = potion.result
    }) and crafted == false then

        -- Looks for the chest that has the corresponding item
        local assignedChestIds = AssignChest(potion, "potion")

        if assignedChestIds then
            print("--------------------------")
            print("Starting Crafting of: " .. potion.result)
            for _, chestId in ipairs(assignedChestIds) do

                -- Gets the chest by its ID
                chest_cluster = GetChestById(chestId)

                -- Turn on Power
                cur_output = colours.combine(cur_output, chest_cluster.cable)
                redstone.setBundledOutput(Customisation.BUNDLED_CABLE_SIDE, cur_output)

                -- Craft the potion
                PotionsCore:PotionCrafting(potion, chest_cluster)

                -- Wait for potion to appear in the basin, so it can export it to ME
                while not IsPotionInBasin(potion.result, chest_cluster.basin) do
                    sleep(1)
                end

                -- Imports fluid from the basin
                ME.importFluidFromPeripheral({
                    name = potion.result
                }, chest_cluster.basin)
            end
        end

        -- If crafted is not false, it must be an id, and if its got a crafting recipe, still crafting
    elseif (ME.isFluidCrafting({
        name = potion.result
    }) and crafted ~= false) then

        -- Looks for the chest that has the corresponding item
        local assignedChestIds = AssignChest(potion, "potion")

        if assignedChestIds then
            for _, chestId in ipairs(assignedChestIds) do

                -- Gets the chest by its ID
                chest_cluster = GetChestById(chestId)

                -- Craft the potion
                PotionsCore:PotionCrafting(potion, chest_cluster)

                -- Wait for potion to appear in the basin, so it can export it to ME
                while not IsPotionInBasin(potion.result, chest_cluster.basin) do
                    sleep(1)
                end

                -- Imports fluid from the basin
                ME.importFluidFromPeripheral({
                    name = potion.result
                }, chest_cluster.basin)
            end
        end
        -- Else if it takes up a chest but does not have an active recipe, reset
    else
        -- If singular chest needs killed, do it manually
        if type(crafted) == "string" then
            print("Shutting down cluster with chest id: " .. crafted)
            local chest_to_reset = GetChestById(crafted)
            chest_to_reset.status = "idle"

            -- Turn off the cluster
            cur_output = colours.subtract(cur_output, chest_to_reset.cable)
            redstone.setBundledOutput(Customisation.BUNDLED_CABLE_SIDE, cur_output)

            -- else iterate through
        else
            for _, chestId in ipairs(crafted) do
                print("Shutting down cluster with chest id: " .. chestId)
                -- Reset each chest status to idle
                local chest_to_reset = GetChestById(chestId)
                chest_to_reset.status = "idle"

                -- Turn off the cluster
                cur_output = colours.subtract(cur_output, chest_to_reset.cable)
                redstone.setBundledOutput(Customisation.BUNDLED_CABLE_SIDE, cur_output)
            end
        end
    end
end

-- Manages the crafting of items
-- @item -> the item to be made
-- @crafted -> "false" if no existing craft, otherwise a chest id
function ItemCraftingManagement(item, crafted)

    cur_output = redstone.getBundledOutput(Customisation.BUNDLED_CABLE_SIDE)

    -- If it's got a crafting job and isn't in any of the chests
    if ME.isItemCrafting({
        name = item.result
    }) and crafted == false then
        -- Looks for the chest that has the corresponding item
        local assignedChestIds = AssignChest(item, "item")

        if assignedChestIds then
            for _, chestId in ipairs(assignedChestIds) do
                -- Gets the chest by its ID
                chest_cluster = GetChestById(chestId)

                -- Turn on Power
                cur_output = colours.combine(cur_output, chest_cluster.cable)
                redstone.setBundledOutput(Customisation.BUNDLED_CABLE_SIDE, cur_output)

                -- Craft the item
                PotionsCore:ItemCrafting(item, chest_cluster)

                -- Wait for item to appear in the basin, so it can export it to ME
                while not IsItemInBasin(item.result, chest_cluster.basin) do
                    sleep(1)
                end

                -- Imports item from the basin
                ME.importItemFromPeripheral({
                    name = item.result
                }, chest_cluster.basin)
            end
        end

        -- If crafted is not false, it must be an id, and if its got a crafting recipe, still crafting
    elseif (ME.isItemCrafting({
        name = item.result
    }) and crafted ~= false) then
        -- Looks for the chest that has the corresponding item
        local assignedChestIds = AssignChest(item, "item")

        if assignedChestIds then
            for _, chestId in ipairs(assignedChestIds) do
                -- Gets the chest by its ID
                chest_cluster = GetChestById(chestId)

                -- Craft the item
                PotionsCore:ItemCrafting(item, chest_cluster)

                -- Wait for item to appear in the basin, so it can export it to ME
                while not IsItemInBasin(item.result, chest_cluster.basin) do
                    sleep(1)
                end

                -- Imports item from the basin
                ME.importItemFromPeripheral({
                    name = item.result
                }, chest_cluster.basin)
            end
        end
        -- Else if it takes up a chest but does not have an active recipe, reset
    else
        for _, chestId in ipairs(crafted) do
            -- Reset each chest status to idle
            local chest_to_reset = GetChestById(chestId)
            chest_to_reset.status = "idle"

            -- Turn off the cluster
            cur_output = colours.subtract(cur_output, chest_to_reset.cable)
            redstone.setBundledOutput(Customisation.BUNDLED_CABLE_SIDE, cur_output)
        end
    end
end

-- -----------------------------------------------------------------------------
-- Handlers
-- -----------------------------------------------------------------------------

-- Checks if the item is already being crafted by going through the chests or ME system
-- @item -> the item that a job may be running for
function CheckIfItemIsBeingCrafted(item)
    local chestIds = {}

    for _, chest in pairs(Chests.chests) do
        -- If the chest status does not equal idle and equals the item
        if chest.status ~= "idle" and chest.status == item then
            -- It is being crafted in the following chest, add to the list
            table.insert(chestIds, chest.id)

            -- Check if the ME system has a job for the item
        elseif ME.isFluidCrafting({
            name = item
        }) or ME.isItemCrafting({
            name = item
        }) then
            return false
        end
    end

    -- If no matching chests or jobs, return nil
    return #chestIds > 0 and chestIds or nil
end

-- Goes through a provided item/PotionList, and checks whether its being crafted
-- @list -> the list of items to check the crafting status of
-- @typeOfCraft -> what type of craft it is, either "potion" or "item"
function PotionsCore:SearchAndStartCrafting(list, typeOfCraft)

    -- For every potion in the given potion list
    for _, item in pairs(list) do

        crafted = CheckIfItemIsBeingCrafted(item.result)

        -- If type is potion then starts the potion crafting logic
        if typeOfCraft == "potion" then
            -- If there is multiple nodes already crafting then go through them all and verify status
            if type(crafted) == "table" then
                -- Iterate over each element in crafted if it's an array
                for _, chestId in ipairs(crafted) do
                    print("Processing chest ID: " .. chestId)
                    print("Starting craft of: " .. item.result)
                    PotionCraftingManagement(item, chestId)
                end

            elseif crafted == false then
                print("--------------------------")
                print("Starting Crafting Job: Potion")
                PotionCraftingManagement(item, false)
            end

            -- else if type is item then do item crafting logic
        elseif typeOfCraft == "item" then
            -- If there is multiple nodes already crafting then go through them all and verify status
            if type(crafted) == "table" then
                -- Iterate over each element in crafted if it's an array
                for _, chestId in ipairs(crafted) do
                    ItemCraftingManagement(item, chestId)
                end
            elseif crafted == false then
                print("--------------------------")
                print("Starting Crafting Job: Item")
                ItemCraftingManagement(item, false)
            end
        else
            error("Invalid type in potions list: " .. list)
        end
    end
    redstone.setBundledOutput(Customisation.BUNDLED_CABLE_SIDE, cur_output)
end

-- Function finds the chest used for crafting by looking for the ingredient used
-- @result -> the item that is being crafted
-- @type -> type of craft, either "potion" or "item"
function AssignChest(result, type)
    local chestIds = {}
    -- For each configured chest cluster
    for _, chest in pairs(Chests.chests) do

        -- Gets the id of the chest attached to this cluster
        local chest_modem = peripheral.wrap(chest.id)

        if chest_modem ~= nil then
            -- If the chest status matches the potion then add id to the list
            if chest.status == result.result then
                table.insert(chestIds, chest.id)
            else
                -- Goes through the chest and looks for the ingredient for the potion
                for slot, item in pairs(chest_modem.list()) do
                    -- If it has the ingredient, set status to the potion and add id to the list
                    if item.name == result.ingredient and type == "potion" then
                        chest.status = result.result
                        table.insert(chestIds, chest.id)
                        break

                    elseif IsItemInIngredients(item, result.ingredient) and type == "item" then
                        chest.status = result.result
                        table.insert(chestIds, chest.id)
                        break
                    end
                end
            end
        else
            print("Cannot find chest by the id: " .. chest.id)
        end
    end
    return #chestIds > 0 and chestIds or false
end

-- Check if the potion is in the basin
-- @potionName -> the name of the potion that is being crafted
-- @basinId -> the id of a basin to check
function IsPotionInBasin(potionName, basinId)
    local basin = peripheral.wrap(basinId)
    if basin then
        for _, fluid in pairs(basin.tanks()) do
            if fluid.name == potionName then
                return true
            end
        end
    end
    return false
end

-- Check if the item is in the basin
-- @itemName -> the name of the item that is being crafted
-- @basinId -> the id of a basin to check
function IsItemInBasin(itemName, basinId)
    local basin = peripheral.wrap(basinId)
    if basin then
        for _, item in pairs(basin.list()) do
            if item.name == itemName then
                return true
            end
        end
    end
    return false
end

-- Function to check if item.name matches any ingredient in the list
-- @item -> the list of items in a container
-- @ingredients -> the list of ingredients for a job, an array
function IsItemInIngredients(item, ingredients)
    for _, ingredient in ipairs(ingredients) do
        if item.name == ingredient then
            return true
        end
    end
    return false
end

-- A function to turn off all the mechanisms
function PotionsCore:TurnOffCreateMechanisms()
    -- For each configured chest cluster
    for _, chest in pairs(Chests.chests) do
        -- Turn off the cluster
        cur_output = colours.subtract(cur_output, chest.cable)
        redstone.setBundledOutput(Customisation.BUNDLED_CABLE_SIDE, cur_output)
    end
end

return PotionsCore