This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Tag365's profile picture

Crafting API (Learn Recipes)

Started by Tag365, 29 April 2015 - 02:30 PM
Tag365 #1
Posted 29 April 2015 - 04:30 PM
This API can be used to craft items. It automatically learns recipes when you craft a new item using turtle.craft, and also has functions for crafting items and acting as an automatic crafting table.

download now! Run "pastebin get hyTQn7Rj api".

Here is the script source:

----------------------------
-- Crafting API by Tag365 --
----------------------------

tCraftingTable = {1, 2, 3, 5, 6, 7, 9, 10, 11} -- This is the slots used for crafting.
local tItemSlotsToDrop = {} -- This is used to determine which slots are not part of the crafting table to prevent other items from interfering from the list.
local tCraftingRecipes = {} -- The table holds recipes for items.
nOutputSlot = 8 -- The final location of the crafted item.
local loops = 0 -- This is used to determine when to print that the turtle is missing an item.
local firstSlotEmpty = 0
local currentRecipe = {}
local tCraftCompletion = {}
local turtleCraft = turtle.craft
recipes = tCraftingRecipes
saveLocation = "recipes.list" -- This is the path where the recipe table will be stored.

-- Functions --

-- Downloads the recipes from the server.
function updateRecipesFromServer()
	local url = "http://pastebin.com/raw.php?i=6gCgiaPg"
	if http then
		local hRecipes = http.get(url)
		local str = hRecipes.readAll()
		hRecipes.close()
		ok, tCraftingRecipes = pcall(textutils.unserialize, str)
	end
end

-- Saves the recipe list.
function saveRecipeList()
	local ok, str = pcall(textutils.serialize, tCraftingRecipes)
	if not ok then
		return false, str
	end
	local hFile = fs.open(saveLocation, "w")
	if hFile then
		hFile.write(str)
		hFile.close()
		return true
	end
	return false
end

-- Loads the recipe list.
function loadRecipeList()
	local hFile, ok = fs.open(saveLocation, "r")
	if hFile then
		local str = hFile.readAll()
		hFile.close()
		ok, tCraftingRecipes = pcall(textutils.unserialize, str)
		if type(tCraftingRecipes) ~= "table" then
			tCraftingRecipes = {}
		end
		return ok
	end
	return false
end

-- Adds a new recipe to the table.
function addNewRecipe(sItemName, nItemsGenerated, bIsShapeless, tResources)
	if type(bIsShapeless) == "nil" and type(tResources) == "nil" then
		tResources = nItemsGenerated
		bIsShapeless = false
		nItemsGenerated = 1
	elseif type(tResources) == "nil" then
		tResources = bIsShapeless
		bIsShapeless = false
		nItemsGenerated = 1
	end
	if type(sItemName) ~= "string" then
		error("bad argument #1 for addNewRecipe: string expected, got "..type(sItemName), 2)
	end
	if type(nItemsGenerated) ~= "number" then
		local newItemsGenerated = tonumber(nItemsGenerated)
		if type(nItemsGenerated) ~= "number" then
			error("bad argument #2 for addNewRecipe: number expected, got "..type(nItemsGenerated), 2)
		end
		nItemsGenerated = newItemsGenerated
	end
	if not tCraftingRecipes[sItemName] then
		tCraftingRecipes[sItemName] = {}
	end
	tCraftingRecipes[sItemName][#tCraftingRecipes[sItemName] + 1] = {}
	local tRecipe = tCraftingRecipes[sItemName][#tCraftingRecipes[sItemName]]
	tRecipe["ItemsGenerated"] = nItemsGenerated
	tRecipe["IsShapeless"] = bIsShapeless
	for k, v in pairs(tResources) do
		if not type(v) == "table" then
			error("bad argument #4 for addNewRecipe: multidimensional table expected, got "..type(v).." in item #"..k, 2)
		end
		tRecipe[k] = v
	end
	saveRecipeList()
end

function updateFirstSlotEmpty()
	firstSlotEmpty = false
	for k, v in ipairs(tItemSlotsToDrop) do
		if turtle.getItemCount(v) == 0 then
			firstSlotEmpty = k
			break
		end
	end
	if not firstSlotEmpty then
		if not turtle.detectUp() then
			print("Warning: The turtle's inventory has filled up and the script has dropped items in the air. Items may be lost if left unattended.")
		end
		for k, v in ipairs(tItemSlotsToDrop) do
			selectSlot(v)
			turtle.dropUp(64)
		end
		slotsFilled = 0
	end
end

-- Selects a slot if it is not the selected slot.
function selectSlot(slot)
	if turtle.getSelectedSlot() ~= slot then
		turtle.select(slot)
	end
end

-- Moves an item from the crafting grid to the inventory.
function moveToInventory(slot, amount)
	updateFirstSlotEmpty()
	local remAmount = amount or turtle.getItemCount(slot) - 1
	selectSlot(slot)
	for k, v in ipairs(tItemSlotsToDrop) do
		if turtle.compareTo(v) then
			local added = math.min(turtle.getItemSpace(v) or 4, remAmount)
			turtle.transferTo(v, added)
			remAmount = remAmount - added
			if remAmount <= 0 then
				return
			end
		end
	end
	if tItemSlotsToDrop[firstSlotEmpty] then
		turtle.transferTo(tItemSlotsToDrop[firstSlotEmpty], math.min(turtle.getItemSpace(v), remAmount))
	end
end

-- Moves an item from the inventory to the crafting grid.
function moveFromInventory(slot, item, amount)
	local remAmount = amount or 1
	item = item or turtle.getItemDetail(slot)
	for k, v in ipairs(tItemSlotsToDrop) do
		local tCurrentItem = turtle.getItemDetail(v)
		if tCurrentItem then
			if tCurrentItem["name"] ~= item["name"] and tCurrentItem["name"] ~= item[1] then
			elseif tCurrentItem["damage"] ~= item["damage"] and tCurrentItem["damage"] ~= item[2] then
			else
				selectSlot(v)
				local added = math.min(turtle.getItemSpace(slot) or 1, remAmount)
				turtle.transferTo(slot, added)
				remAmount = remAmount - added
				if remAmount <= 0 then
					return true, 0
				end
			end
		end
	end
	return false, remAmount
end

-- Checks if an item is the correct item.
function checkCraftingSlot(k, v)
	loops = loops + 1
	if loops == 100 then
		print("Waiting for item #"..k.." to be given...")
	end
	if turtle.getItemCount(v) == 0 then
		if currentRecipe[k][1] == "" then
			return true
		else
			selectSlot(v)
			if not moveFromInventory(v, currentRecipe[k], 1) then
				turtle.suck(1)
			end
		end
	elseif turtle.getItemCount(v) > 1 then
		moveToInventory(v)
	end
	if currentRecipe[k][1] == "" then
		moveToInventory(v, 64)
		return true
	end
	local tCurrentItem = turtle.getItemDetail(v)
	if tCurrentItem then
		if currentRecipe[k][1] == "" then
			moveToInventory(v, 64)
			return true
		elseif tCurrentItem["name"] ~= currentRecipe[k][1] then
		elseif currentRecipe[k][2] and tCurrentItem["damage"] ~= currentRecipe[k][2] then
		else
			return true
		end
	end
	moveToInventory(v, 64)
	turtle.suck(64)
	return false
end

-- When all items are available this function will break the while loop in craftItems.
function craftingNotComplete()
	for k, v in ipairs(tCraftCompletion) do
		if v ~= true then
			return true
		end
	end
	return false
end

-- When called this turtle will craft a recipe
function craftItems(sItemName, nRecipeUsed, nItemsToCraft, nTurnTimes)
	nRecipeUsed = nRecipeUsed or 1
	nItemsToCraft = nItemsToCraft or 1
	nTurnTimes = nTurnTimes or 0
	local errLevel = 2
	if nItemsToCraft == -1 then errLevel = 3 end
	if not tCraftingRecipes[sItemName] then
		error("to craft '"..sItemName.."' you must first craft it using turtle.craft, or use crafting.addNewRecipe to add a recipe to the table.", errLevel)
	elseif not tCraftingRecipes[sItemName][nRecipeUsed] then
		nRecipeUsed = 1
	end
	currentRecipe = tCraftingRecipes[sItemName][nRecipeUsed]
	for k=1, 9 do
		tCraftCompletion[k] = false
	end
	if #tItemSlotsToDrop == 0 then
		local addToTable = true
		for k=1, 16 do
			addToTable = true
			for k2, v in pairs(tCraftingTable) do
				if k == v then
					addToTable = false
					break
				end
			end
			if addToTable then
				tItemSlotsToDrop[#tItemSlotsToDrop + 1] = k
			end
		end
	end
	local advance = false
	local loops = 0
	local firstSlotEmpty = 0
	while nItemsToCraft > 0 do
		loops = 0
		while craftingNotComplete() do
			for k, v in ipairs(tCraftingTable) do
				if not currentRecipe[k] then
					currentRecipe[k] = {""}
				end
				tCraftCompletion[k] = checkCraftingSlot(k, v)
				sleep()
			end
		end
		for k, v in ipairs(tItemSlotsToDrop) do
			if turtle.getItemCount(v) > 0 then -- This doesn't belong here according to ComputerCraft, so it needs to go.
				turtle.select(v)
				turtle.drop(64)
			end
		end
		turtle.select(nOutputSlot)
		local itemsGenerated = currentRecipe["ItemsGenerated"] or 1
		if turtle.craft(nItemsToCraft) then
			if nTurnTimes > 0 then
				for k=1, nTurnTimes do
					turtle.turnRight()
				end
			else
				for k=1, nTurnTimes do
					turtle.turnLeft()
				end
			end
			nItemsToCraft = nItemsToCraft - turtle.getItemCount(nOutputSlot)
			turtle.drop(64)
			if nTurnTimes > 0 then
				for k=1, nTurnTimes do
					turtle.turnLeft()
				end
			else
				for k=1, nTurnTimes do
					turtle.turnRight()
				end
			end
		end
		sleep()
		while turtle.suckUp(64) do
			turtle.drop(64)
		end
	end
end

-- When called the turtle turns into an automatic crafting table that crafts the item you want.
function autoCraftingTable(sItemName, nRecipeUsed, nTurnTimes)
	local advance = false
	while true do
		craftItems(sItemName, nRecipeUsed, -1, nTurnTimes)
	end
end

-- Returns the full item name of an already available item.
function getFullItemName(sItemNameSub)
	for k, v in pairs(tCraftingRecipes) do
		if string.find(string.lower(k), string.lower(sItemNameSub)) then
			return k
		end
	end
	return false
end

-- Override the turtle.craft function with one that teaches the turtle a recipe.
function turtle.craft(amount)
	amount = amount or 64
	local tItemsInCraftingGrid = {}
	for k, v in ipairs(tCraftingTable) do
		local item = turtle.getItemDetail(v)
		if item then
			tItemsInCraftingGrid[k] = {[1] = item["name"], [2] = item["damage"]}
		else
			tItemsInCraftingGrid[k] = {""}
		end
	end
	if turtleCraft(math.min(amount, 64)) then
		if turtle.getItemDetail(turtle.getSelectedSlot()) then
			local item = turtle.getItemDetail(turtle.getSelectedSlot())
			if not tCraftingRecipes[item["name"]] then
				addNewRecipe(item["name"], 1, false, tItemsInCraftingGrid)
			end
		end
	end
end

-- Load the local recipes from the hard disk, or download the recipes from the server if possible.
if not loadRecipeList() then
	updateRecipesFromServer()
end
Edited on 01 May 2015 - 10:44 PM
HPWebcamAble #2
Posted 30 April 2015 - 01:10 AM
Why don't you upload your API to pastebin? That way, you can download it in game with this command:


pastebin get <code> <name>
<code> is from the URL on pastebin: www.pastebin.com/<code>
<name> is the file name to save the program to


Also, you should document the API's functions in your post so people have a reference