Implement basic auto-crafting
This commit is contained in:
parent
87ce5758e6
commit
4785e22f30
@ -64,6 +64,10 @@ function itemstack.fromItemDetail(detail, itemLimit)
|
|||||||
return newItemStack(item.fromDetail(detail), detail.count, itemLimit)
|
return newItemStack(item.fromDetail(detail), detail.count, itemLimit)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function itemstack.fromSlot(per, slot)
|
||||||
|
return itemstack.fromItemDetail(per.getItemDetail(slot), per.getItemLimit(slot))
|
||||||
|
end
|
||||||
|
|
||||||
function itemstack.fromString(str)
|
function itemstack.fromString(str)
|
||||||
local strStack = textutils.unserialize(str)
|
local strStack = textutils.unserialize(str)
|
||||||
if type(strStack.item) ~= "string" or
|
if type(strStack.item) ~= "string" or
|
||||||
|
@ -72,7 +72,7 @@ function prototype:moveItemsTo(count, fromSlot, otherChest, toSlot)
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local tx = self.peripheral.pushItems(peripheral.getName(otherChest.peripheral), fromSlot, count, toSlot)
|
local tx = self.peripheral.pushItems(otherChest:getIdentifier(), fromSlot, count, toSlot)
|
||||||
myStack.count = myStack.count - tx
|
myStack.count = myStack.count - tx
|
||||||
if myStack.count == 0 then
|
if myStack.count == 0 then
|
||||||
self.slots[fromSlot] = nil
|
self.slots[fromSlot] = nil
|
||||||
@ -87,6 +87,41 @@ function prototype:moveItemsTo(count, fromSlot, otherChest, toSlot)
|
|||||||
return tx
|
return tx
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function prototype:moveItemsToPeripheral(count, fromSlot, target, toSlot)
|
||||||
|
local myStack = self:getSlot(fromSlot)
|
||||||
|
|
||||||
|
local tx = self.peripheral.pushItems(peripheral.getName(target), fromSlot, count, toSlot)
|
||||||
|
myStack.count = myStack.count - tx
|
||||||
|
if myStack.count == 0 then
|
||||||
|
self.slots[fromSlot] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return tx
|
||||||
|
end
|
||||||
|
|
||||||
|
function prototype:pullItemsFromPeripheral(count, fromSlot, fromTarget, toSlot)
|
||||||
|
local myStack = self:getSlot(toSlot)
|
||||||
|
local perStack = itemstack.fromItemDetail(fromTarget.getItemDetail(fromSlot), fromTarget.getItemLimit(fromSlot))
|
||||||
|
|
||||||
|
if not perStack:canTransferTo(myStack) then
|
||||||
|
error("Cannot pull items from peripheral to chest: "..textutils.serialise({
|
||||||
|
from = { peripheral = peripheral.getName(fromTarget), slot = fromSlot },
|
||||||
|
to = { chest = self:getIdentifier(), slot = toSlot },
|
||||||
|
count = count
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
|
||||||
|
local tx = fromTarget.pushItems(self:getIdentifier(), fromSlot, count, toSlot)
|
||||||
|
if myStack:isBlank() then
|
||||||
|
myStack.item = perStack.item
|
||||||
|
self[toSlot] = myStack
|
||||||
|
end
|
||||||
|
|
||||||
|
myStack.count = myStack.count + tx
|
||||||
|
|
||||||
|
return tx
|
||||||
|
end
|
||||||
|
|
||||||
function prototype:getIdentifier()
|
function prototype:getIdentifier()
|
||||||
return peripheral.getName(self.peripheral)
|
return peripheral.getName(self.peripheral)
|
||||||
end
|
end
|
||||||
|
86
recipe.lua
Normal file
86
recipe.lua
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
local itemstack = require("itemstack")
|
||||||
|
local stackgroup = require("stackgroup")
|
||||||
|
local util = require("util")
|
||||||
|
|
||||||
|
|
||||||
|
local prototype = {}
|
||||||
|
function prototype:getCost()
|
||||||
|
local costs = {}
|
||||||
|
for _,input in pairs(self.inputs) do
|
||||||
|
local itemCost = util.find(costs, function(_, item) return item == input.item end)
|
||||||
|
if itemCost == nil then
|
||||||
|
itemCost = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
costs[input.item] = itemCost + input.count
|
||||||
|
end
|
||||||
|
|
||||||
|
return costs
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns table of item -> missing-count mappings. Empty table if recipe can be afforded
|
||||||
|
function prototype:canAfford(chests)
|
||||||
|
local storage = stackgroup.fromChests(table.unpack(chests))
|
||||||
|
local costs = self:getCost()
|
||||||
|
|
||||||
|
local missing = {}
|
||||||
|
for item,cost in pairs(costs) do
|
||||||
|
local group = storage:findGroup(item)
|
||||||
|
if group == nil or group.total < cost then
|
||||||
|
local totalMissing = cost
|
||||||
|
if group ~= nil then
|
||||||
|
totalMissing = totalMissing - group.total
|
||||||
|
end
|
||||||
|
missing[item] = totalMissing
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return missing
|
||||||
|
end
|
||||||
|
|
||||||
|
function prototype:beginCrafting(chests)
|
||||||
|
local storage = stackgroup.fromChests(table.unpack(chests))
|
||||||
|
|
||||||
|
for _,input in pairs(self.inputs) do
|
||||||
|
local group = storage:findGroup(input.item)
|
||||||
|
group:moveItemsToPeripheral(chests, input.count, input.machine, input.slot)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return poll function to check for readiness
|
||||||
|
return function()
|
||||||
|
for _,output in pairs(self.outputs) do
|
||||||
|
local stackdata = itemstack.fromSlot(output.machine, output.slot)
|
||||||
|
|
||||||
|
if output.item ~= stackdata.item or output.count > stackdata.count then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local recipe = {}
|
||||||
|
|
||||||
|
-- Input or output of a recipe
|
||||||
|
function recipe.component(machine, slot, item, count)
|
||||||
|
return {
|
||||||
|
machine = machine,
|
||||||
|
slot = slot,
|
||||||
|
item = item,
|
||||||
|
count = count
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function recipe.create(inputs, outputs)
|
||||||
|
local recipeDef = {
|
||||||
|
inputs = inputs,
|
||||||
|
outputs = outputs
|
||||||
|
}
|
||||||
|
|
||||||
|
setmetatable(recipeDef, { __index = prototype })
|
||||||
|
|
||||||
|
return recipeDef
|
||||||
|
end
|
||||||
|
|
||||||
|
return recipe
|
@ -2,35 +2,24 @@ local util = require("util")
|
|||||||
|
|
||||||
local stackgroup = {}
|
local stackgroup = {}
|
||||||
local prototype = {}
|
local prototype = {}
|
||||||
local metatable = {}
|
|
||||||
|
|
||||||
function prototype:defragment(chests)
|
function prototype:defragment(chests)
|
||||||
table.sort(self, function(a, b) return a.count < b.count end)
|
table.sort(self, function(a, b) return a.count < b.count end)
|
||||||
|
|
||||||
local resultGroup = {
|
|
||||||
item = self.item,
|
|
||||||
total = self.total
|
|
||||||
}
|
|
||||||
|
|
||||||
setmetatable(resultGroup, metatable)
|
|
||||||
|
|
||||||
local top = #self
|
local top = #self
|
||||||
local bottom = 1
|
|
||||||
|
|
||||||
-- Find largest non-full stack
|
-- Find largest non-full stack
|
||||||
for i=top,bottom,-1 do
|
for i=top,1,-1 do
|
||||||
local entry = self[i]
|
local entry = self[i]
|
||||||
if entry:getStack(chests):availableCount() ~= 0 then
|
if entry:getStack(chests):availableCount() ~= 0 then
|
||||||
top = i
|
top = i
|
||||||
break
|
break
|
||||||
else
|
|
||||||
resultGroup[#resultGroup + 1] = entry
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
while top > bottom do
|
while top > 1 do
|
||||||
local topEntry = self[top]
|
local topEntry = self[top]
|
||||||
local bottomEntry = self[bottom]
|
local bottomEntry = self[1]
|
||||||
|
|
||||||
local txCap = math.min(bottomEntry.count, topEntry:getStack(chests):availableCount())
|
local txCap = math.min(bottomEntry.count, topEntry:getStack(chests):availableCount())
|
||||||
local tx = bottomEntry:getChest(chests):moveItemsTo(txCap, bottomEntry.slot, topEntry:getChest(chests), topEntry.slot)
|
local tx = bottomEntry:getChest(chests):moveItemsTo(txCap, bottomEntry.slot, topEntry:getChest(chests), topEntry.slot)
|
||||||
@ -48,23 +37,46 @@ function prototype:defragment(chests)
|
|||||||
topEntry.count = topEntry.count + tx
|
topEntry.count = topEntry.count + tx
|
||||||
|
|
||||||
if bottomEntry.count == 0 then
|
if bottomEntry.count == 0 then
|
||||||
bottom = bottom + 1
|
top = top - 1
|
||||||
|
table.remove(self, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
if topEntry:getStack(chests):availableCount() == 0 then
|
if topEntry:getStack(chests):availableCount() == 0 then
|
||||||
top = top - 1
|
top = top - 1
|
||||||
resultGroup[#resultGroup + 1] = topEntry
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if top == bottom and self[top].count > 0 then
|
function prototype:moveItemsToPeripheral(chests, count, per, slot)
|
||||||
resultGroup[#resultGroup+1] = self[top]
|
if count > self.total then
|
||||||
|
error("Insufficient items available to transfer to peripheral: "..textutils.serialise({
|
||||||
|
available = self.total,
|
||||||
|
count = count
|
||||||
|
}))
|
||||||
|
end
|
||||||
|
table.sort(self, function(a, b) return a.count < b.count end)
|
||||||
|
|
||||||
|
local totalMoved = 0
|
||||||
|
while totalMoved < count do
|
||||||
|
local currentSlot = self[1]
|
||||||
|
local txCap = math.min(currentSlot.count, count - totalMoved)
|
||||||
|
local tx = currentSlot:getChest(chests):moveItemsToPeripheral(txCap, currentSlot.slot, per, slot)
|
||||||
|
|
||||||
|
if tx == 0 then
|
||||||
|
error("Unexpected error when transferring items to peripheral: "..textutils.serialise({
|
||||||
|
expected = txCap,
|
||||||
|
from = { chest = currentSlot:getChest(chests):getIdentifier(), count = currentSlot.count },
|
||||||
|
to = { peripheral = peripheral.getName(per) }
|
||||||
|
}))
|
||||||
end
|
end
|
||||||
|
|
||||||
return resultGroup
|
self.total = self.total - tx
|
||||||
|
totalMoved = totalMoved + tx
|
||||||
|
if tx == currentSlot.count then
|
||||||
|
table.remove(self, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
metatable.__index = prototype
|
|
||||||
|
|
||||||
local groupEntryPrototype = {}
|
local groupEntryPrototype = {}
|
||||||
|
|
||||||
@ -76,13 +88,15 @@ function groupEntryPrototype:getChest(chests)
|
|||||||
return chests[self.chestIndex]
|
return chests[self.chestIndex]
|
||||||
end
|
end
|
||||||
|
|
||||||
local groupEntryMetatable = {
|
local stackgroupSetPrototype = {}
|
||||||
__index = groupEntryPrototype
|
function stackgroupSetPrototype:findGroup(item)
|
||||||
}
|
return util.find(self, function(group) return group.item == item end)
|
||||||
|
end
|
||||||
|
|
||||||
function stackgroup.fromChests(...)
|
function stackgroup.fromChests(...)
|
||||||
local chests = { ... }
|
local chests = { ... }
|
||||||
local groups = {}
|
local groups = {}
|
||||||
|
setmetatable(groups, { __index = stackgroupSetPrototype })
|
||||||
|
|
||||||
for chestIndex,chest in ipairs(chests) do
|
for chestIndex,chest in ipairs(chests) do
|
||||||
for slotKey,slot in pairs(chest.slots) do
|
for slotKey,slot in pairs(chest.slots) do
|
||||||
@ -93,7 +107,7 @@ function stackgroup.fromChests(...)
|
|||||||
total = 0
|
total = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
setmetatable(group, metatable)
|
setmetatable(group, { __index = prototype })
|
||||||
|
|
||||||
groups[#groups + 1] = group
|
groups[#groups + 1] = group
|
||||||
end
|
end
|
||||||
@ -103,7 +117,7 @@ function stackgroup.fromChests(...)
|
|||||||
slot = slotKey,
|
slot = slotKey,
|
||||||
count = slot.count
|
count = slot.count
|
||||||
}
|
}
|
||||||
setmetatable(entry, groupEntryMetatable)
|
setmetatable(entry, { __index = groupEntryPrototype })
|
||||||
|
|
||||||
group[#group + 1] = entry
|
group[#group + 1] = entry
|
||||||
group.total = group.total + slot.count
|
group.total = group.total + slot.count
|
||||||
|
9
util.lua
9
util.lua
@ -26,12 +26,17 @@ function util.deepEqual(a, b)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function util.find(array, f)
|
function util.find(array, f)
|
||||||
for _,v in pairs(array) do
|
for k,v in pairs(array) do
|
||||||
if f(v) then
|
if f(v, k) then
|
||||||
return v
|
return v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function util.isEmpty(tbl)
|
||||||
|
local next, t = pairs(tbl)
|
||||||
|
return next(t) == nil
|
||||||
|
end
|
||||||
|
|
||||||
return util
|
return util
|
Loading…
x
Reference in New Issue
Block a user