Files
opus-apps/milo/apis/init.lua
Kan18 2f67fb2ef8 Try to update Milo for 1.19
Removes Milo trying to access damage on items (`nil` because of The
Flattening). We might also need to reimplement showing durability in the
item's display name - I got a little too carried away with removing
mentions of "damage". Also renames nbtHash to nbt to be consistent with
new CC:T naming. I tried not to touch anything related to MiloRemote for
now, and there are probably still many bugs remaining that need to be
ironed out. Most of the basic functionality works now, though.
2022-12-24 15:39:14 +04:00

412 lines
8.6 KiB
Lua

local Config = require('opus.config')
local Craft = require('milo.craft2')
local itemDB = require('core.itemDB')
local Sound = require('opus.sound')
local Util = require('opus.util')
local os = _G.os
local turtle = _G.turtle
local Milo = {
RESOURCE_FILE = 'usr/config/resources.db',
}
function Milo:init(context)
self.context = context
context.userRecipes = Util.readTable(Craft.USER_RECIPES) or { }
end
function Milo:getContext()
return self.context
end
function Milo:registerPlugin(plugin)
for pluginType, value in pairs(plugin) do
if not self.context.plugins[pluginType] then
self.context.plugins[pluginType] = { value }
else
table.insert(self.context.plugins[pluginType], value)
end
end
end
function Milo:pauseCrafting(reason)
local _, key = Util.find(self.context.state, 'key', reason.key)
if not key then
table.insert(self.context.state, reason)
os.queueEvent('milo_pause', reason)
end
end
function Milo:resumeCrafting(reason)
local _, key = Util.find(self.context.state, 'key', reason.key)
if key then
table.remove(self.context.state, key)
local n = self.context.state[#self.context.state]
if n then
os.queueEvent('milo_pause', n)
else
os.queueEvent('milo_resume')
end
end
end
function Milo:isCraftingPaused()
return self.context.state[#self.context.state]
end
function Milo:getState(key)
if not self.state then
self.state = Config.load('milo.state')
end
return self.state[key]
end
function Milo:setState(key, value)
if not self.state then
self.state = Config.load('milo.state')
end
self.state[key] = value
Config.update('milo.state', self.state)
end
function Milo:resetCraftingStatus()
for _,key in pairs(Util.keys(self.context.craftingQueue)) do
local item = self.context.craftingQueue[key]
if item.crafted >= item.requested or item.aborted then
self.context.craftingQueue[key] = nil
end
end
end
function Milo:registerTask(task)
table.insert(self.context.tasks, task)
end
function Milo:getItem(inItem)
return self:listItems()[inItem.key or itemDB:makeKey(inItem)]
end
-- returns a list of items that matches along with a total count
function Milo:getMatches(item, flags)
local t = { }
local count = 0
local items = self:listItems()
if not flags.ignoreNbt then
local key = item.key or itemDB:makeKey(item)
local v = items[key]
if v then
t[key] = Util.shallowCopy(v)
count = v.count
end
else
for key,v in pairs(items) do
if item.name == v.name and
(flags.ignoreNbt or item.nbt == v.nbt) then
t[key] = Util.shallowCopy(v)
count = count + v.count
end
end
end
return t, count
end
function Milo:clearGrid()
return Craft.clearGrid(self.context.storage)
end
function Milo:getTurtleInventory()
local list = { }
for i, v in pairs(self.context.turtleInventory.adapter.list()) do
list[i] = itemDB:get(v, function()
return self.context.turtleInventory.adapter.getItemDetail(i)
end)
end
itemDB:flush()
return list
end
function Milo:requestCrafting(item)
local key = itemDB:makeKey(item)
if not self.context.craftingQueue[key] then
item.crafted = 0
item.pending = { }
item.key = key
self.context.craftingQueue[key] = item
os.queueEvent('milo_cycle')
end
end
-- queue an action that interacts with storage
function Milo:queueRequest(request, callback)
os.queueEvent('milo_queue')
table.insert(self.context.queue, {
request = request,
callback = callback
})
end
function Milo:craftAndEject(item, count)
local request = self:makeRequest(item, count, function(request)
-- eject rest when finished crafted
return self:eject(item, request.requested)
end)
return request
end
function Milo:makeRequest(item, count, callback)
local current = self:getItem(item) or { count = 0 }
if count <= 0 then
return {
requested = 0,
craft = 0,
count = 0,
current = current.count,
item = item,
key = item.key or itemDB:makeKey(item),
}
end
local toCraft = count - math.min(current.count, count)
if toCraft > 0 then
local recipe = Craft.findRecipe(itemDB:makeKey(item))
if not recipe then
toCraft = 0
else
-- if you ask for 1 stick, getCraftableAmount will return 4 (obviously)
toCraft = math.min(toCraft, Craft.getCraftableAmount(recipe, toCraft, self:listItems(), { }))
end
end
local request = {
requested = count,
craft = toCraft,
count = math.min(count, current.count),
current = current.count,
item = item,
key = item.key or itemDB:makeKey(item),
}
if request.count > 0 then
self:queueRequest(request, callback)
end
if request.craft > 0 then
item = Util.shallowCopy(item)
item.requested = request.craft
item.callback = callback
self:requestCrafting(item)
end
return request
end
function Milo:emptyInventory()
for i = 1, 16 do
if turtle.getItemCount(i) > 0 then
turtle.select(i)
turtle.drop()
end
end
turtle.select(1)
end
function Milo:eject(item, count)
local total = 0
while count > 0 do
local amount = math.min(count, 16*(item.maxCount or 64))
amount = self.context.storage:export(self.context.turtleInventory, nil, amount, item)
if amount == 0 then
break
end
total = total + amount
count = count - amount
--Sound.play('ui.button.click')
Sound.play('entity.illusion_illager.death', .3)
self:emptyInventory()
end
return total
end
function Milo:learnRecipe()
local ingredients = self:getTurtleInventory()
if not ingredients then
return false, 'No recipe defined'
end
for _,v in pairs(ingredients) do
if v.count > 1 then
return false, 'Too many items'
end
end
turtle.select(1)
if not turtle.craft() then
return false, 'Failed to craft'
end
local results = self:getTurtleInventory()
if not results or not results[1] then
return false, 'Failed to craft'
end
local maxCount
local newRecipe = {
ingredients = ingredients,
}
local numResults = 0
for _,v in pairs(results) do
if v.count > 0 then
numResults = numResults + 1
end
end
if numResults > 1 then
for _,v1 in pairs(results) do
for _,v2 in pairs(ingredients) do
if v1.name == v2.name and
v1.nbt == v2.nbt then
if not newRecipe.crafingTools then
newRecipe.craftingTools = { }
end
local tool = Util.shallowCopy(v2)
--[[
Turtles can only craft one item at a time using a tool :(
]] -- Todo: check if this still applies
maxCount = 1
newRecipe.craftingTools[itemDB:makeKey(tool)] = true
v1.craftingTool = true
break
end
end
end
end
local recipe
for _,v in pairs(results) do
if not v.craftingTool then
recipe = v
if maxCount then
recipe.maxCount = maxCount
end
break
end
end
if not recipe then
return false, 'Unknown error'
end
newRecipe.count = recipe.count
local key = itemDB:makeKey(recipe)
if recipe.maxCount ~= 64 then
newRecipe.maxCount = recipe.maxCount
end
for k,ingredient in pairs(Util.shallowCopy(ingredients)) do
ingredients[k] = itemDB:makeKey(ingredient)
end
self:updateRecipe(key, newRecipe)
return recipe
end
function Milo:updateRecipe(result, recipe)
-- save the recipe
if recipe then
recipe = Util.shallowCopy(recipe)
recipe.result = nil
end
self.context.userRecipes[result] = recipe
Util.writeTable(Craft.USER_RECIPES, self.context.userRecipes)
Craft.loadRecipes()
end
function Milo:saveMachineRecipe(recipe, result, machine)
local key = itemDB:makeKey(result)
-- save the recipe
self.context.userRecipes[key] = recipe
Util.writeTable(Craft.USER_RECIPES, self.context.userRecipes)
-- save the machine association
Craft.machineLookup[key] = machine
Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup)
Craft.loadRecipes()
end
function Milo:mergeResources(t)
t = Util.shallowCopy(t)
for k,v in pairs(self.context.resources) do
local item = t[k]
if item then
item = Util.shallowCopy(item)
else
item = itemDB:splitKey(k)
item.count = 0
item.key = k
end
Util.merge(item, v)
t[k] = item
end
for k in pairs(Craft.recipes) do
local item = t[k]
if not item then
item = itemDB:splitKey(k)
item.count = 0
item.key = k
else
item = Util.shallowCopy(item)
end
item.has_recipe = true
t[k] = item
end
for key in pairs(Craft.machineLookup) do
local item = t[key]
if item then
item = Util.shallowCopy(item)
item.is_craftable = true
t[key] = item
end
end
for _,v in pairs(t) do
if not v.displayName then
v.displayName = itemDB:getName(v)
end
v.lname = v.displayName:lower()
end
return t
end
function Milo:saveResources()
Util.writeTable(self.RESOURCE_FILE, self.context.resources)
end
-- Return a list of everything in the system
function Milo:listItems(forceRefresh, throttle)
return forceRefresh and self.context.storage:refresh(throttle) or
self.context.storage:listItems(throttle)
end
return Milo