rewrite autocrafting script
This commit is contained in:
-126
@@ -1,126 +0,0 @@
|
|||||||
function agc(name)
|
|
||||||
return assert(components:getFirst(name), "failed to get component "..name)
|
|
||||||
end
|
|
||||||
|
|
||||||
local computer = agc("computer")
|
|
||||||
local fs = require("filesystem")
|
|
||||||
local kernel = require("kernel")
|
|
||||||
local crafter = agc("digitalCrafter")
|
|
||||||
print("it works")
|
|
||||||
|
|
||||||
|
|
||||||
local function craft(...)
|
|
||||||
while true do
|
|
||||||
local cooldown = crafter.cooldownRemaining
|
|
||||||
if cooldown <= 0 then break end
|
|
||||||
sleep(cooldown)
|
|
||||||
end
|
|
||||||
return crafter:craft(...)
|
|
||||||
end
|
|
||||||
|
|
||||||
print("Which item would you like to craft?")
|
|
||||||
local buf = ""
|
|
||||||
|
|
||||||
local ctrlCReceived = false
|
|
||||||
local keyEvents = {}
|
|
||||||
kernel:registerEventCallback("charTyped", function(...) table.insert(keyEvents, {...}) end)
|
|
||||||
kernel:registerEventCallback("keyPressed", function(...) table.insert(keyEvents, {...}) end)
|
|
||||||
local matchingItems = {}
|
|
||||||
while true do
|
|
||||||
if #keyEvents > 0 then
|
|
||||||
local event = table.remove(keyEvents, 1)
|
|
||||||
if event[1] == "charTyped" then
|
|
||||||
if event[2] == "\n" then
|
|
||||||
if #matchingItems == 1 then
|
|
||||||
print()
|
|
||||||
print("Selected item: "..tostring(matchingItems[1]))
|
|
||||||
break
|
|
||||||
end
|
|
||||||
elseif event[2] == "\b" then
|
|
||||||
if #buf > 0 then
|
|
||||||
buf = buf:sub(1, #buf-1)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
buf = buf .. event[2]
|
|
||||||
end
|
|
||||||
matchingItems = #buf > 0 and crafter:searchItemsByName(buf) or {}
|
|
||||||
printInline("\r["..(#matchingItems).." match"..(#matchingItems ~= 1 and "es" or "").."] "..buf.." ")
|
|
||||||
elseif event[1] == "keyPressed" then
|
|
||||||
local charRepr, code, _, mod = table.unpack(event, 2)
|
|
||||||
if code == 67 and (mod&2>0) then -- ctrl+c
|
|
||||||
ctrlCReceived = true
|
|
||||||
error("ctrl c pressed")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
sleep(0.25)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(#matchingItems == 1, "not exactly 1 matching item")
|
|
||||||
local matchingItem = matchingItems[1]
|
|
||||||
|
|
||||||
local function readNumber(prompt)
|
|
||||||
printInline(prompt)
|
|
||||||
keyEvents = {}
|
|
||||||
local buf = ""
|
|
||||||
while true do
|
|
||||||
if #keyEvents > 0 then
|
|
||||||
local event = table.remove(keyEvents, 1)
|
|
||||||
if event[1] == "charTyped" then
|
|
||||||
if event[2] == "\n" then
|
|
||||||
if #matchingItems == 1 then
|
|
||||||
local num = tonumber(buf)
|
|
||||||
if num ~= nil then print() return num end
|
|
||||||
buf = ""
|
|
||||||
end
|
|
||||||
elseif event[2] == "\b" then
|
|
||||||
if #buf > 0 then
|
|
||||||
buf = buf:sub(1, #buf-1)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
buf = buf .. event[2]
|
|
||||||
end
|
|
||||||
printInline("\r"..prompt.. " "..buf.." ")
|
|
||||||
elseif event[1] == "keyPressed" then
|
|
||||||
local charRepr, code, _, mod = table.unpack(event, 2)
|
|
||||||
if code == 67 and (mod&2>0) then -- ctrl+c
|
|
||||||
ctrlCReceived = true
|
|
||||||
error("ctrl c pressed")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
sleep(0.25)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local quantity = readNumber("How many items do we want to craft?")
|
|
||||||
print("We want to craft "..quantity.."x "..matchingItem)
|
|
||||||
|
|
||||||
local craftingRecipesToExecute = {} -- this is a stack, use table.insert to add new recipes to craft, and remove from end when executing the recipes
|
|
||||||
local itemsLeftToCraft = {[matchingItem] = quantity} -- key=itemname, value=amountNeeded
|
|
||||||
local reservedItems = {} -- itemname:reservedQuantity; needed so we dont queue two recipes that need more ingredients in total than we have in our storage system
|
|
||||||
while next(itemsLeftToCraft) ~= nil do -- if there is anything left that needs to be crafted
|
|
||||||
local toCraft = assert(next(itemsLeftToCraft), "somehow got nil1")
|
|
||||||
itemsLeftToCraft[toCraft] = 0
|
|
||||||
|
|
||||||
-- check if cached recipe exists, if so use that
|
|
||||||
|
|
||||||
-- else
|
|
||||||
-- get possible recipes
|
|
||||||
|
|
||||||
-- compute how often we apply this recipe
|
|
||||||
|
|
||||||
-- add to craftingRecipesToExecute
|
|
||||||
-- reserve items we need for crafting, or add them to itemsLeftToCraft
|
|
||||||
|
|
||||||
-- TODO ac: add extraoutputs to 'recipe' datastructure
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
local fs = require("filesystem")
|
||||||
|
local recipeLib = load(fs:readAllText("./recipeParseLib.lua"), "recipeParseLib.lua")()
|
||||||
|
|
||||||
|
local args = table.pack(...)
|
||||||
|
|
||||||
|
local resultItem = args[1]
|
||||||
|
assert(resultItem, "supply the item you want to add a recipe for as the first item")
|
||||||
|
|
||||||
|
local crafter = components:getFirst("digitalCrafter")
|
||||||
|
|
||||||
|
local recipes = crafter:searchRecipesWithResult(resultItem)
|
||||||
|
|
||||||
|
assert(#recipes > 0, ("no recipe found for %s."):format(resultItem))
|
||||||
|
|
||||||
|
|
||||||
|
local function acToLuaRecipe(acRecipe)
|
||||||
|
return {
|
||||||
|
resultCount = acRecipe.resultCount,
|
||||||
|
slots = acRecipe.ingredients,
|
||||||
|
resultItem = acRecipe.result
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local kernel = require("kernel")
|
||||||
|
local keyEvents = {}
|
||||||
|
kernel:registerEventCallback("charTyped", function(...) table.insert(keyEvents, {...}) end)
|
||||||
|
kernel:registerEventCallback("keyPressed", function(...) table.insert(keyEvents, {...}) end)
|
||||||
|
|
||||||
|
---@param eventFilterOrNil "charTyped"|"keyPressed"|nil
|
||||||
|
local function waitForInputEvent(eventFilterOrNil)
|
||||||
|
sleep(0)
|
||||||
|
while #keyEvents == 0 do sleep(0.5) end
|
||||||
|
local res = table.remove(keyEvents, 1)
|
||||||
|
if eventFilterOrNil == nil or res[1] == eventFilterOrNil then
|
||||||
|
return res
|
||||||
|
else
|
||||||
|
return waitForInputEvent(eventFilterOrNil) or tailCallsAreSus
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local recipe = recipes[1]
|
||||||
|
if #recipes > 1 then
|
||||||
|
print("Looks like there are multiple recipes. Please pick one of the following by typing the number on the left and then enter.")
|
||||||
|
for index, value in ipairs(recipes) do
|
||||||
|
print(("%d: %s"):format(index, recipeLib:serializeSingleRecipe(acToLuaRecipe(value))))
|
||||||
|
end
|
||||||
|
|
||||||
|
local buf = ""
|
||||||
|
while true do
|
||||||
|
printInline(("\rSelection: %s_"):format(buf))
|
||||||
|
local ctyped, repr = table.unpack(waitForInputEvent("charTyped"))
|
||||||
|
if repr == "\b" then
|
||||||
|
buf = buf:sub(1, #buf-1)
|
||||||
|
elseif repr == "\n" then
|
||||||
|
break
|
||||||
|
elseif repr ~= nil then
|
||||||
|
buf = buf .. repr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print()
|
||||||
|
local selection = tonumber(buf)
|
||||||
|
if selection == nil or selection < 1 or selection > #recipes then
|
||||||
|
error(("The inputted number '%s' is not a valid option"):format(buf))
|
||||||
|
end
|
||||||
|
recipe = recipes[selection]
|
||||||
|
end
|
||||||
|
|
||||||
|
recipeLib:addNewRecipe(acToLuaRecipe(recipe))
|
||||||
|
|
||||||
|
print(("Done, added the following recipe:\n%s"):format(recipeLib:serializeSingleRecipe(acToLuaRecipe(recipe))))
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
|
||||||
|
|
||||||
|
--local itemToCraftInternalName, itemToCraftAmount = "nuclearcraft:empty_active_heat_sink", 15
|
||||||
|
--local itemToCraftInternalName, itemToCraftAmount = "nuclearcraft:empty_heat_sink", 1
|
||||||
|
--local itemToCraftInternalName, itemToCraftAmount = "nuclearcraft:helium_collector", 32
|
||||||
|
--local itemToCraftInternalName, itemToCraftAmount = "nuclearcraft:plate_advanced", 32
|
||||||
|
--local itemToCraftInternalName, itemToCraftAmount = "nuclearcraft:pump", 1
|
||||||
|
--local itemToCraftInternalName, itemToCraftAmount = "nuclearcraft:fission_reactor_controller", 1
|
||||||
|
--local itemToCraftInternalName, itemToCraftAmount = "nuclearcraft:fission_reactor_port", 6
|
||||||
|
local itemToCraftInternalName, itemToCraftAmount = "mekanism:quantum_entangloporter", 2
|
||||||
|
|
||||||
|
local function agc(name)
|
||||||
|
return assert(components:getFirst(name), "failed to get component "..name)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function pp(tbl)
|
||||||
|
for k,v in pairs(tbl) do print(k, tostring(v)) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local computer = agc("computer")
|
||||||
|
local fs = require("filesystem")
|
||||||
|
local kernel = require("kernel")
|
||||||
|
local crafter = agc("digitalCrafter")
|
||||||
|
local interface = agc("adapter")
|
||||||
|
|
||||||
|
local loadedRecipes = load(fs:readAllText("./recipeParseLib.lua"),"recipeParseLib.lua")():getRecipes()
|
||||||
|
|
||||||
|
print("Loaded the following recipes:")
|
||||||
|
pp(loadedRecipes)
|
||||||
|
|
||||||
|
local meStockCache = {} -- dict<itemInternalName, amount>; how much we have in storage minus what we have reserved as we go
|
||||||
|
local function me_GetStock(internalItemName)
|
||||||
|
if itemToCraftInternalName == internalItemName then return 0 end -- dont allow using the item that we want to craft
|
||||||
|
local cached = meStockCache[internalItemName]
|
||||||
|
if not cached then
|
||||||
|
cached = interface:getCount(internalItemName)
|
||||||
|
meStockCache[internalItemName] = cached
|
||||||
|
end
|
||||||
|
return cached
|
||||||
|
end
|
||||||
|
|
||||||
|
local itemsToConsume = {}
|
||||||
|
local function me_ReserveItem(internalItemName, amount)
|
||||||
|
assert(meStockCache[internalItemName], "why didnt we look up the stock earlier???")
|
||||||
|
assert(meStockCache[internalItemName] >= amount, "there are not enough items available to reserve them. Tried to reserve "..internalItemName.." x"..tostring(amount))
|
||||||
|
meStockCache[internalItemName] = meStockCache[internalItemName] - amount
|
||||||
|
itemsToConsume[internalItemName] = (itemsToConsume[internalItemName] or 0) + amount
|
||||||
|
end
|
||||||
|
|
||||||
|
local function me_PushIntoCrafter(internalItemName, amount)
|
||||||
|
assert(meStockCache[internalItemName], "why didnt we look up the stock earlier???")
|
||||||
|
assert((itemsToConsume[internalItemName] or 0) >= amount, "we never reserved enough for "..internalItemName.." x"..tostring(amount)..". Why are we trying to extract it?")
|
||||||
|
itemsToConsume[internalItemName] = (itemsToConsume[internalItemName] or 0) - amount
|
||||||
|
interface:extract(internalItemName, 1, amount)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function tblToKeyStr(tbl) return table.concat(tbl,";") end
|
||||||
|
local function keyStrToTbl(keyStr)
|
||||||
|
local rv = {}
|
||||||
|
local buf = ""
|
||||||
|
for i = 1, #keyStr do
|
||||||
|
local chr = keyStr:sub(i,i)
|
||||||
|
if chr == ";" then
|
||||||
|
table.insert(rv, buf)
|
||||||
|
buf = ""
|
||||||
|
else
|
||||||
|
buf = buf .. chr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(rv, buf)
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
local missingItems = {} -- dict<itemInternalName[], amount>
|
||||||
|
local currentItemsNeededToCraft = {} -- dict<keyStrToTbl(itemInternalName[]), amount>
|
||||||
|
local plannedCraftSteps = {} -- array<(tblToKeyStr({*ingredients, result}), amount:int)>
|
||||||
|
local function planCraftForItem(itemNameToCraft, amount)
|
||||||
|
local recipe = loadedRecipes[itemNameToCraft]
|
||||||
|
if not recipe then
|
||||||
|
print(("Could not find a recipe for %s"):format(itemNameToCraft))
|
||||||
|
return amount
|
||||||
|
end -- we dont have a recipe and therefore cannot craft it
|
||||||
|
-- compute concrete recipe applications in the form [concreteIngredientName1-9] --> concreteOutputName
|
||||||
|
|
||||||
|
local meAmount = me_GetStock(itemNameToCraft)
|
||||||
|
local meAmountToTake = math.min(meAmount, amount)
|
||||||
|
local itemsLeftToCraft = amount - meAmountToTake
|
||||||
|
local craftIterations = math.ceil(itemsLeftToCraft / recipe.resultCount)
|
||||||
|
print(("looks like we want to craft %s times a recipe yielding %sx %s as we want %s more items of that."):format(craftIterations, recipe.resultCount, itemNameToCraft, itemsLeftToCraft))
|
||||||
|
while itemsLeftToCraft > 0 do
|
||||||
|
local recipeHasFailed = false
|
||||||
|
local allIngredients = {}
|
||||||
|
for slotIdx=1,9 do
|
||||||
|
local recipeItemOptions = recipe.slots[slotIdx]
|
||||||
|
if recipeItemOptions then
|
||||||
|
--print("option cnt: ", #recipeItemOptions)
|
||||||
|
-- try to find one item we have in stock and reserve this item
|
||||||
|
local itemWasFound = false
|
||||||
|
for _, value in ipairs(recipeItemOptions) do
|
||||||
|
if me_GetStock(value) > 0 then
|
||||||
|
me_ReserveItem(value, 1)
|
||||||
|
itemWasFound = true
|
||||||
|
allIngredients[slotIdx] = value
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not itemWasFound then
|
||||||
|
-- if not, try to find one that we have an recipe for
|
||||||
|
local foundAny = false
|
||||||
|
for _, value in ipairs(recipeItemOptions) do
|
||||||
|
assert(value ~= "", "candidate was empty?")
|
||||||
|
if loadedRecipes[value] then -- if this item is craftable, queue it
|
||||||
|
currentItemsNeededToCraft[value] = (currentItemsNeededToCraft[value] or 0) + 1
|
||||||
|
allIngredients[slotIdx] = value
|
||||||
|
foundAny = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not foundAny then
|
||||||
|
local missingKey = tblToKeyStr(recipeItemOptions)
|
||||||
|
missingItems[missingKey] = (missingItems[missingKey] or 0) + 1
|
||||||
|
recipeHasFailed = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
allIngredients[slotIdx] = "nil"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- store the recipe in the form tblToKeyStr({*ingredients, result}) inside plannedCraftSteps
|
||||||
|
if not recipeHasFailed then
|
||||||
|
print("--- concrete recipe: ---")
|
||||||
|
pp(allIngredients)
|
||||||
|
local key = tblToKeyStr(allIngredients)..";"..itemNameToCraft
|
||||||
|
|
||||||
|
if false and plannedCraftSteps[#plannedCraftSteps][1] == key then
|
||||||
|
-- TODO can do an optimization here where we craft the item one more time at once, assuming all inputs and the output stack to big enough sizes
|
||||||
|
else
|
||||||
|
table.insert(plannedCraftSteps, {key, 1})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
itemsLeftToCraft = itemsLeftToCraft - recipe.resultCount
|
||||||
|
--print(("iteration finished, we need to craft %s more"):format(math.max(0,itemsLeftToCraft)))
|
||||||
|
end
|
||||||
|
print((next(missingItems) == nil) and "yay" or "uh oh looks like we are missing some items for this crafting step")
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function safeCraft(...)
|
||||||
|
while true do
|
||||||
|
local cooldown = crafter.cooldownRemaining
|
||||||
|
if cooldown <= 0 then break end
|
||||||
|
sleep(cooldown)
|
||||||
|
end
|
||||||
|
return crafter:craft(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local keyEvents = {}
|
||||||
|
kernel:registerEventCallback("charTyped", function(...) table.insert(keyEvents, {...}) end)
|
||||||
|
kernel:registerEventCallback("keyPressed", function(...) table.insert(keyEvents, {...}) end)
|
||||||
|
|
||||||
|
---@param eventFilterOrNil "charTyped"|"keyPressed"|nil
|
||||||
|
local function waitForInputEvent(eventFilterOrNil)
|
||||||
|
sleep(0)
|
||||||
|
while #keyEvents == 0 do sleep(0.5) end
|
||||||
|
local res = table.remove(keyEvents, 1)
|
||||||
|
if eventFilterOrNil == nil or res[1] == eventFilterOrNil then
|
||||||
|
return res
|
||||||
|
else
|
||||||
|
return waitForInputEvent(eventFilterOrNil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print(("We want to craft %sx %s"):format(itemToCraftAmount, itemToCraftInternalName))
|
||||||
|
currentItemsNeededToCraft[itemToCraftInternalName] = itemToCraftAmount
|
||||||
|
|
||||||
|
while true do
|
||||||
|
print("new crafting iteration")
|
||||||
|
pp(currentItemsNeededToCraft)
|
||||||
|
local toCraft = next(currentItemsNeededToCraft)
|
||||||
|
if not toCraft then break end
|
||||||
|
-- try to obtain item "toCraft"
|
||||||
|
assert(type(toCraft) == "string")
|
||||||
|
local toCraft_amount = currentItemsNeededToCraft[toCraft]
|
||||||
|
local amountLeft = planCraftForItem(toCraft, toCraft_amount)
|
||||||
|
print("amount left in current step:", amountLeft)
|
||||||
|
assert(amountLeft <= toCraft_amount)
|
||||||
|
if amountLeft == toCraft_amount then -- if we made no progress, we neither have it in stock nor have a recipe for it --> mark it as missing
|
||||||
|
local tblKey = toCraft
|
||||||
|
missingItems[tblKey] = (missingItems[tblKey] or 0) + amountLeft
|
||||||
|
currentItemsNeededToCraft[toCraft] = nil
|
||||||
|
elseif amountLeft == 0 then
|
||||||
|
currentItemsNeededToCraft[toCraft] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local canCraft = (next(missingItems) == nil)
|
||||||
|
if canCraft then
|
||||||
|
print("We have all the needed items. Do we start crafting? [y/n]. Will consume the following ingredients to craft "..itemToCraftAmount.." '"..itemToCraftInternalName.."':")
|
||||||
|
|
||||||
|
local keys = {}
|
||||||
|
for k,_ in pairs(itemsToConsume) do table.insert(keys, k) end
|
||||||
|
table.sort(keys, function (x, y) return itemsToConsume[x] > itemsToConsume[y] end)
|
||||||
|
|
||||||
|
local padsize = #tostring(itemsToConsume[keys[1]])
|
||||||
|
for _, k in ipairs(keys) do
|
||||||
|
local v = itemsToConsume[k]
|
||||||
|
print(string.format("%sx%s: %s", v, string.rep(" ", padsize-#tostring(v)), k))
|
||||||
|
end
|
||||||
|
|
||||||
|
local doCraft = nil
|
||||||
|
while doCraft == nil do
|
||||||
|
local evnt = waitForInputEvent("charTyped")
|
||||||
|
print("event:", evnt)
|
||||||
|
local char = evnt[2]:lower()
|
||||||
|
if char == "y" or char == "\n" then
|
||||||
|
doCraft = true
|
||||||
|
elseif char == "n" then
|
||||||
|
doCraft = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not doCraft then print("Aborted by user.") return end
|
||||||
|
print("Crafting...")
|
||||||
|
for i = #plannedCraftSteps, 1, -1 do
|
||||||
|
local currRecipeToCraft = plannedCraftSteps[i]
|
||||||
|
print(("Current step (%d/%d): %s"):format(#plannedCraftSteps-i+1, #plannedCraftSteps, currRecipeToCraft[1]))
|
||||||
|
local ingredientsAndResult = keyStrToTbl(currRecipeToCraft[1])
|
||||||
|
for i = 1, #ingredientsAndResult do
|
||||||
|
if ingredientsAndResult[i] == "nil" then
|
||||||
|
ingredientsAndResult[i] = nil
|
||||||
|
elseif i < #ingredientsAndResult then -- for all ingredients
|
||||||
|
me_PushIntoCrafter(ingredientsAndResult[i], 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local resultName = ingredientsAndResult[#ingredientsAndResult]
|
||||||
|
local recipeResultCount = loadedRecipes[resultName].resultCount -- how many results we get from one craft
|
||||||
|
local recipeCraftCount = currRecipeToCraft[2]
|
||||||
|
safeCraft(
|
||||||
|
table.pack(table.unpack(ingredientsAndResult, 1, #ingredientsAndResult-1)), -- ingredients
|
||||||
|
resultName, -- result
|
||||||
|
recipeCraftCount, -- how often we craft it
|
||||||
|
false -- outputIntoOwnInventory
|
||||||
|
)
|
||||||
|
print(("noting that we have crafted '%s'."):format(resultName))
|
||||||
|
itemsToConsume[resultName] = (itemsToConsume[resultName] or 0) + recipeCraftCount * recipeResultCount -- account for intermediate crafting recipes
|
||||||
|
end
|
||||||
|
print(("Done, you are now the proud owner of %sx %s!"):format(itemToCraftAmount, itemToCraftInternalName))
|
||||||
|
else
|
||||||
|
print("Cannot craft, we are missing the following items / recipes:")
|
||||||
|
for k,v in pairs(missingItems) do
|
||||||
|
print(string.format("%sx%s: %s", v, string.rep(" ", #tostring(v)), k))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
local rv = {}
|
||||||
|
|
||||||
|
--[[
|
||||||
|
local function tailcall()
|
||||||
|
print("called")
|
||||||
|
if true then
|
||||||
|
print("calling")
|
||||||
|
return tailcall()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tailcall()
|
||||||
|
]]
|
||||||
|
|
||||||
|
local fs = require("filesystem")
|
||||||
|
|
||||||
|
local function trim(str) return string.gsub(str,"^[%s]*", ""):gsub("[%s]*$", "") end
|
||||||
|
|
||||||
|
function rv:getRecipes()
|
||||||
|
if not fs:fileExists("recipes.txt") then return {} end
|
||||||
|
|
||||||
|
local recipeText = fs:readAllText("recipes.txt")
|
||||||
|
local splits = string.split(recipeText, ";")
|
||||||
|
local allRecipes = {} -- outputInternalName : {resultCount:int, slots:{{possible options.....},nil,nil,....}}
|
||||||
|
for _, value in ipairs(splits) do
|
||||||
|
local trimmed = trim(value)
|
||||||
|
if #(trimmed) > 0 then
|
||||||
|
--print("trimmed:", ">>"..trimmed.."<<")
|
||||||
|
local resCnt, resItem, ingredients = trimmed:match("^(%d*) (%S+) = ([^;]*)$")
|
||||||
|
assert(ingredients, ("failed to match ingredients for line: >>%s<< from >>%s<<"):format(trimmed, value))
|
||||||
|
ingredients = trim(ingredients)
|
||||||
|
|
||||||
|
--print(("Can craft %sx %s using ingredients %s"):format(resCnt, resItem, ingredients))
|
||||||
|
assert(allRecipes[resItem] == nil, ("more than one recipe defined for %s"):format(resItem))
|
||||||
|
local slotTable = {}
|
||||||
|
for slotIdx, optionsString in ipairs(ingredients:gsub("%s",""):split(",")) do
|
||||||
|
if optionsString == "nil" then
|
||||||
|
slotTable[slotIdx] = nil
|
||||||
|
else
|
||||||
|
local slotOptions = {}
|
||||||
|
for _, option in ipairs(optionsString:split("|")) do
|
||||||
|
assert((option ~= nil) and (option ~= "nil"), "somehow option was nil or nilstring")
|
||||||
|
assert(option:find(":"), string.format("slotoptions '%s' does not contain :",option))
|
||||||
|
table.insert(slotOptions, option)
|
||||||
|
end
|
||||||
|
slotTable[slotIdx] = slotOptions
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
allRecipes[resItem] = {
|
||||||
|
resultCount = assert(tonumber(resCnt), ("couldnt parse number '%s'"):format(resCnt)),
|
||||||
|
slots = slotTable,
|
||||||
|
resultItem = resItem
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return allRecipes
|
||||||
|
end
|
||||||
|
|
||||||
|
function rv:serializeSingleRecipe(recipe)
|
||||||
|
local slots = {}
|
||||||
|
for i = 1, 9 do
|
||||||
|
if recipe.slots[i] == nil or #(recipe.slots[i]) == 0 then
|
||||||
|
slots[i] = "nil"
|
||||||
|
else
|
||||||
|
slots[i] = table.concat(recipe.slots[i], "|")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return string.format("%d %s = %s", recipe.resultCount, recipe.resultItem, table.concat(slots,","))..";"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function serializeRecipes(recipeTable)
|
||||||
|
local newText = ""
|
||||||
|
for k,v in pairs(recipeTable) do
|
||||||
|
assert(k == v.resultItem, "somehow resultItem doesnt match the key")
|
||||||
|
newText = newText .. rv:serializeSingleRecipe(v) .. "\n"
|
||||||
|
end
|
||||||
|
return newText
|
||||||
|
end
|
||||||
|
|
||||||
|
function rv:addNewRecipe(newRecipeTable)
|
||||||
|
local cnt = assert(newRecipeTable.resultCount, "resultCount not set")
|
||||||
|
local slots = assert(newRecipeTable.slots, "slots not set")
|
||||||
|
local resultItem = assert(newRecipeTable.resultItem, "resultItem not set")
|
||||||
|
|
||||||
|
local currRecipes = rv:getRecipes()
|
||||||
|
assert(currRecipes[resultItem] == nil, ("recipe for %s has already been defined"):format(resultItem))
|
||||||
|
currRecipes[resultItem] = {
|
||||||
|
resultCount = cnt,
|
||||||
|
slots = slots,
|
||||||
|
resultItem = resultItem
|
||||||
|
}
|
||||||
|
fs:writeAllText("recipes.txt", serializeRecipes(currRecipes))
|
||||||
|
end
|
||||||
|
|
||||||
|
return rv
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
4 minecraft:oak_planks = minecraft:oak_log|minecraft:oak_wood|minecraft:stripped_oak_log|minecraft:stripped_oak_wood,nil,nil,nil,nil,nil,nil,nil,nil;
|
||||||
|
4 minecraft:torch = minecraft:charcoal|minecraft:coal,nil,nil,minecraft:stick,nil,nil,nil,nil,nil;
|
||||||
|
4 nuclearcraft:fission_reactor_casing = alltheores:lead_plate|electrodynamics:platelead|railcraft:lead_plate|nuclearcraft:lead_plate|chemlib:lead_plate|thermal:lead_plate,nuclearcraft:plate_advanced,alltheores:lead_plate|electrodynamics:platelead|railcraft:lead_plate|nuclearcraft:lead_plate|chemlib:lead_plate|thermal:lead_plate,nuclearcraft:plate_advanced,nil,nuclearcraft:plate_advanced,alltheores:lead_plate|electrodynamics:platelead|railcraft:lead_plate|nuclearcraft:lead_plate|chemlib:lead_plate|thermal:lead_plate,nuclearcraft:plate_advanced,alltheores:lead_plate|electrodynamics:platelead|railcraft:lead_plate|nuclearcraft:lead_plate|chemlib:lead_plate|thermal:lead_plate;
|
||||||
|
2 nuclearcraft:plate_advanced = nil,nuclearcraft:plate_basic,nil,nuclearcraft:tough_alloy_ingot,minecraft:redstone,nuclearcraft:tough_alloy_ingot,nil,nuclearcraft:plate_basic,nil;
|
||||||
|
2 nuclearcraft:plate_basic = alltheores:lead_ingot|electrodynamics:ingotlead|railcraft:lead_ingot|mekanism:ingot_lead|nuclearcraft:lead_ingot|chemlib:lead_ingot|thermal:lead_ingot,nuclearcraft:graphite_dust|chemlib:graphite_dust,nil,nuclearcraft:graphite_dust|chemlib:graphite_dust,alltheores:lead_ingot|electrodynamics:ingotlead|railcraft:lead_ingot|mekanism:ingot_lead|nuclearcraft:lead_ingot|chemlib:lead_ingot|thermal:lead_ingot,nil,nil,nil,nil;
|
||||||
|
1 nuclearcraft:empty_heat_sink = nuclearcraft:tough_alloy_ingot,nuclearcraft:thermoconducting_plate,nuclearcraft:tough_alloy_ingot,minecraft:iron_bars,minecraft:bucket,minecraft:iron_bars,nuclearcraft:tough_alloy_ingot,nuclearcraft:thermoconducting_plate,nuclearcraft:tough_alloy_ingot;
|
||||||
|
1 minecraft:bucket = minecraft:iron_ingot,nil,minecraft:iron_ingot,nil,minecraft:iron_ingot,nil,nil,nil,nil;
|
||||||
|
16 minecraft:iron_bars = minecraft:iron_ingot,minecraft:iron_ingot,minecraft:iron_ingot,minecraft:iron_ingot,minecraft:iron_ingot,minecraft:iron_ingot,nil,nil,nil;
|
||||||
|
1 nuclearcraft:empty_active_heat_sink = nuclearcraft:thermoconducting_plate,nuclearcraft:motor,nuclearcraft:thermoconducting_plate,nuclearcraft:motor,nuclearcraft:empty_heat_sink,nuclearcraft:motor,nuclearcraft:thermoconducting_plate,nuclearcraft:motor,nuclearcraft:thermoconducting_plate;
|
||||||
|
1 nuclearcraft:motor = alltheores:steel_ingot|electrodynamics:ingotsteel|railcraft:steel_ingot|ad_astra:steel_ingot|mekanism:ingot_steel|nuclearcraft:steel_ingot|thermal:steel_ingot,alltheores:steel_ingot|electrodynamics:ingotsteel|railcraft:steel_ingot|ad_astra:steel_ingot|mekanism:ingot_steel|nuclearcraft:steel_ingot|thermal:steel_ingot,minecraft:gold_nugget,nuclearcraft:coil_copper,nuclearcraft:coil_copper,minecraft:iron_ingot,alltheores:steel_ingot|electrodynamics:ingotsteel|railcraft:steel_ingot|ad_astra:steel_ingot|mekanism:ingot_steel|nuclearcraft:steel_ingot|thermal:steel_ingot,alltheores:steel_ingot|electrodynamics:ingotsteel|railcraft:steel_ingot|ad_astra:steel_ingot|mekanism:ingot_steel|nuclearcraft:steel_ingot|thermal:steel_ingot,minecraft:gold_nugget;
|
||||||
|
1 nuclearcraft:coil_copper = minecraft:copper_ingot,minecraft:copper_ingot,nil,minecraft:iron_ingot,minecraft:iron_ingot,nil,minecraft:copper_ingot,minecraft:copper_ingot,nil;
|
||||||
|
9 minecraft:gold_nugget = minecraft:gold_ingot,nil,nil,nil,nil,nil,nil,nil,nil;
|
||||||
|
1 nuclearcraft:helium_collector = nuclearcraft:plate_advanced,nuclearcraft:thorium_ingot|chemlib:thorium_ingot,nuclearcraft:plate_advanced,nuclearcraft:thorium_plate|chemlib:thorium_plate,nuclearcraft:motor,nuclearcraft:thorium_plate|chemlib:thorium_plate,nuclearcraft:plate_advanced,nuclearcraft:thorium_ingot|chemlib:thorium_ingot,nuclearcraft:plate_advanced;
|
||||||
|
1 nuclearcraft:pump = nuclearcraft:plate_basic,nuclearcraft:motor,nuclearcraft:plate_basic,nuclearcraft:plate_basic,minecraft:cauldron,nuclearcraft:plate_basic,nuclearcraft:plate_basic,nuclearcraft:motor,nuclearcraft:plate_basic;
|
||||||
|
1 minecraft:cauldron = minecraft:iron_ingot,nil,minecraft:iron_ingot,minecraft:iron_ingot,nil,minecraft:iron_ingot,minecraft:iron_ingot,minecraft:iron_ingot,minecraft:iron_ingot;
|
||||||
|
1 nuclearcraft:fission_reactor_controller = nuclearcraft:fission_reactor_casing,nuclearcraft:plate_advanced,nuclearcraft:fission_reactor_casing,nuclearcraft:basic_electric_circuit,nuclearcraft:decay_hastener,nuclearcraft:basic_electric_circuit,nuclearcraft:fission_reactor_casing,nuclearcraft:plate_advanced,nuclearcraft:fission_reactor_casing;
|
||||||
|
1 nuclearcraft:decay_hastener = nuclearcraft:boron_ingot,nuclearcraft:boron_block,nuclearcraft:boron_ingot,nuclearcraft:tough_alloy_ingot,nuclearcraft:tin_silver_ingot,nuclearcraft:tough_alloy_ingot,nuclearcraft:boron_ingot,nuclearcraft:actuator,nuclearcraft:boron_ingot;
|
||||||
|
1 nuclearcraft:actuator = nil,nil,alltheores:steel_ingot|electrodynamics:ingotsteel|railcraft:steel_ingot|ad_astra:steel_ingot|mekanism:ingot_steel|nuclearcraft:steel_ingot|thermal:steel_ingot,nuclearcraft:ferroboron_ingot,minecraft:piston,nil,minecraft:copper_ingot,nuclearcraft:ferroboron_ingot,nil;
|
||||||
|
1 nuclearcraft:boron_block = nuclearcraft:boron_ingot,nuclearcraft:boron_ingot,nuclearcraft:boron_ingot,nuclearcraft:boron_ingot,nuclearcraft:boron_ingot,nuclearcraft:boron_ingot,nuclearcraft:boron_ingot,nuclearcraft:boron_ingot,nuclearcraft:boron_ingot;
|
||||||
|
1 minecraft:piston = minecraft:oak_planks|minecraft:spruce_planks|minecraft:birch_planks|minecraft:jungle_planks|minecraft:acacia_planks|minecraft:dark_oak_planks|minecraft:crimson_planks|minecraft:warped_planks|minecraft:mangrove_planks|minecraft:bamboo_planks|minecraft:cherry_planks|botania:livingwood_planks|botania:mossy_livingwood_planks|botania:framed_livingwood|botania:pattern_framed_livingwood|botania:dreamwood_planks|botania:mossy_dreamwood_planks|botania:framed_dreamwood|botania:pattern_framed_dreamwood|botania:shimmerwood_planks|biomesoplenty:fir_planks|biomesoplenty:pine_planks|biomesoplenty:maple_planks|biomesoplenty:redwood_planks|biomesoplenty:mahogany_planks|biomesoplenty:jacaranda_planks|biomesoplenty:palm_planks|biomesoplenty:willow_planks|biomesoplenty:dead_planks|biomesoplenty:magic_planks|biomesoplenty:umbran_planks|biomesoplenty:hellbark_planks|biomesoplenty:empyreal_planks|integrateddynamics:menril_planks|ad_astra:aeronos_planks|ad_astra:strophar_planks|ad_astra:glacian_planks|voidscape:thunder_planks|undergarden:smogstem_planks|undergarden:wigglewood_planks|undergarden:grongle_planks|thermal:rubberwood_planks,minecraft:oak_planks|minecraft:spruce_planks|minecraft:birch_planks|minecraft:jungle_planks|minecraft:acacia_planks|minecraft:dark_oak_planks|minecraft:crimson_planks|minecraft:warped_planks|minecraft:mangrove_planks|minecraft:bamboo_planks|minecraft:cherry_planks|botania:livingwood_planks|botania:mossy_livingwood_planks|botania:framed_livingwood|botania:pattern_framed_livingwood|botania:dreamwood_planks|botania:mossy_dreamwood_planks|botania:framed_dreamwood|botania:pattern_framed_dreamwood|botania:shimmerwood_planks|biomesoplenty:fir_planks|biomesoplenty:pine_planks|biomesoplenty:maple_planks|biomesoplenty:redwood_planks|biomesoplenty:mahogany_planks|biomesoplenty:jacaranda_planks|biomesoplenty:palm_planks|biomesoplenty:willow_planks|biomesoplenty:dead_planks|biomesoplenty:magic_planks|biomesoplenty:umbran_planks|biomesoplenty:hellbark_planks|biomesoplenty:empyreal_planks|integrateddynamics:menril_planks|ad_astra:aeronos_planks|ad_astra:strophar_planks|ad_astra:glacian_planks|voidscape:thunder_planks|undergarden:smogstem_planks|undergarden:wigglewood_planks|undergarden:grongle_planks|thermal:rubberwood_planks,minecraft:oak_planks|minecraft:spruce_planks|minecraft:birch_planks|minecraft:jungle_planks|minecraft:acacia_planks|minecraft:dark_oak_planks|minecraft:crimson_planks|minecraft:warped_planks|minecraft:mangrove_planks|minecraft:bamboo_planks|minecraft:cherry_planks|botania:livingwood_planks|botania:mossy_livingwood_planks|botania:framed_livingwood|botania:pattern_framed_livingwood|botania:dreamwood_planks|botania:mossy_dreamwood_planks|botania:framed_dreamwood|botania:pattern_framed_dreamwood|botania:shimmerwood_planks|biomesoplenty:fir_planks|biomesoplenty:pine_planks|biomesoplenty:maple_planks|biomesoplenty:redwood_planks|biomesoplenty:mahogany_planks|biomesoplenty:jacaranda_planks|biomesoplenty:palm_planks|biomesoplenty:willow_planks|biomesoplenty:dead_planks|biomesoplenty:magic_planks|biomesoplenty:umbran_planks|biomesoplenty:hellbark_planks|biomesoplenty:empyreal_planks|integrateddynamics:menril_planks|ad_astra:aeronos_planks|ad_astra:strophar_planks|ad_astra:glacian_planks|voidscape:thunder_planks|undergarden:smogstem_planks|undergarden:wigglewood_planks|undergarden:grongle_planks|thermal:rubberwood_planks,minecraft:cobblestone,minecraft:iron_ingot,minecraft:cobblestone,minecraft:cobblestone,minecraft:redstone,minecraft:cobblestone;
|
||||||
|
1 nuclearcraft:fission_reactor_port = nuclearcraft:tough_alloy_plate,nuclearcraft:plate_advanced,nuclearcraft:tough_alloy_plate,nuclearcraft:servo,nuclearcraft:fission_reactor_casing,nuclearcraft:servo,nuclearcraft:tough_alloy_plate,nuclearcraft:plate_advanced,nuclearcraft:tough_alloy_plate;
|
||||||
|
1 nuclearcraft:servo = nuclearcraft:ferroboron_ingot,nil,nuclearcraft:ferroboron_ingot,minecraft:redstone,alltheores:steel_ingot|electrodynamics:ingotsteel|railcraft:steel_ingot|ad_astra:steel_ingot|mekanism:ingot_steel|nuclearcraft:steel_ingot|thermal:steel_ingot,minecraft:redstone,alltheores:steel_ingot|electrodynamics:ingotsteel|railcraft:steel_ingot|ad_astra:steel_ingot|mekanism:ingot_steel|nuclearcraft:steel_ingot|thermal:steel_ingot,minecraft:copper_ingot,alltheores:steel_ingot|electrodynamics:ingotsteel|railcraft:steel_ingot|ad_astra:steel_ingot|mekanism:ingot_steel|nuclearcraft:steel_ingot|thermal:steel_ingot;
|
||||||
|
1 mekanism:quantum_entangloporter = mekanism:ingot_refined_obsidian,electrodynamics:circuitultimate|mekanism:ultimate_control_circuit,mekanism:ingot_refined_obsidian,mekanism:alloy_atomic,mekanism:teleportation_core,mekanism:alloy_atomic,mekanism:ingot_refined_obsidian,electrodynamics:circuitultimate|mekanism:ultimate_control_circuit,mekanism:ingot_refined_obsidian;
|
||||||
|
1 mekanism:teleportation_core = minecraft:lapis_lazuli,mekanism:alloy_atomic,minecraft:lapis_lazuli,minecraft:gold_ingot,minecraft:diamond,minecraft:gold_ingot,minecraft:lapis_lazuli,mekanism:alloy_atomic,minecraft:lapis_lazuli;
|
||||||
|
1 mekanism:ultimate_control_circuit = mekanism:alloy_atomic,electrodynamics:circuitelite|mekanism:elite_control_circuit,mekanism:alloy_atomic,nil,nil,nil,nil,nil,nil;
|
||||||
|
1 mekanism:advanced_control_circuit = mekanism:alloy_infused,electrodynamics:circuitbasic|mekanism:basic_control_circuit,mekanism:alloy_infused,nil,nil,nil,nil,nil,nil;
|
||||||
|
1 mekanism:elite_control_circuit = mekanism:alloy_reinforced,electrodynamics:circuitadvanced|mekanism:advanced_control_circuit,mekanism:alloy_reinforced,nil,nil,nil,nil,nil,nil;
|
||||||
Reference in New Issue
Block a user