treefarm + turtle improvements + cleanup
This commit is contained in:
107
sys/apps/Pim.lua
107
sys/apps/Pim.lua
@@ -1,107 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Config = require('config')
|
||||
local Event = require('event')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'PIM')
|
||||
|
||||
local inventory = { }
|
||||
local mode = 'sync'
|
||||
|
||||
if not device.pim then
|
||||
error('PIM not attached')
|
||||
end
|
||||
|
||||
local page = UI.Page({
|
||||
menu = UI.Menu({
|
||||
centered = true,
|
||||
y = 2,
|
||||
menuItems = {
|
||||
{ prompt = 'Learn', event = 'learn', help = '' },
|
||||
},
|
||||
}),
|
||||
statusBar = UI.StatusBar({
|
||||
columns = {
|
||||
{ 'Status', 'status', UI.term.width - 7 },
|
||||
{ 'Mode', 'mode', 7 }
|
||||
}
|
||||
}),
|
||||
accelerators = {
|
||||
q = 'quit',
|
||||
},
|
||||
})
|
||||
|
||||
local function learn()
|
||||
if device.pim.getInventorySize() > 0 then
|
||||
local stacks = device.pim.getAllStacks(false)
|
||||
Config.update('pim', stacks)
|
||||
mode = 'sync'
|
||||
page.statusBar:setValue('status', 'Learned inventory')
|
||||
end
|
||||
page.statusBar:setValue('mode', mode)
|
||||
page.statusBar:draw()
|
||||
end
|
||||
|
||||
function page:eventHandler(event)
|
||||
|
||||
if event.type == 'learn' then
|
||||
mode = 'learn'
|
||||
learn()
|
||||
elseif event.type == 'quit' then
|
||||
Event.exitPullEvents()
|
||||
end
|
||||
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
|
||||
local function inInventory(s)
|
||||
for _,i in pairs(inventory) do
|
||||
if i.id == s.id then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function pimWatcher()
|
||||
local playerOn = false
|
||||
|
||||
while true do
|
||||
if device.pim.getInventorySize() > 0 and not playerOn then
|
||||
playerOn = true
|
||||
|
||||
if mode == 'learn' then
|
||||
learn()
|
||||
|
||||
else
|
||||
local stacks = device.pim.getAllStacks(false)
|
||||
for k,stack in pairs(stacks) do
|
||||
if not inInventory(stack) then
|
||||
device.pim.pushItem('down', k, stack.qty)
|
||||
end
|
||||
end
|
||||
page.statusBar:setValue('status', 'Synchronized')
|
||||
page.statusBar:draw()
|
||||
end
|
||||
|
||||
elseif device.pim.getInventorySize() == 0 and playerOn then
|
||||
page.statusBar:setValue('status', 'No player')
|
||||
page.statusBar:draw()
|
||||
playerOn = false
|
||||
end
|
||||
os.sleep(1)
|
||||
end
|
||||
end
|
||||
|
||||
Config.load('pim', inventory)
|
||||
|
||||
if Util.empty(inventory) then
|
||||
mode = 'learn'
|
||||
end
|
||||
page.statusBar:setValue('mode', mode)
|
||||
|
||||
UI:setPage(page)
|
||||
|
||||
Event.pullEvents(pimWatcher)
|
||||
UI.term:reset()
|
||||
@@ -2,6 +2,7 @@ requireInjector(getfenv(1))
|
||||
|
||||
local ChestProvider = require('chestProvider18')
|
||||
local Config = require('config')
|
||||
local Craft = require('turtle.craft')
|
||||
local Event = require('event')
|
||||
local itemDB = require('itemDB')
|
||||
local Peripheral = require('peripheral')
|
||||
@@ -34,13 +35,15 @@ local chestProvider = ChestProvider({ direction = 'west', wrapSide = 'back' })
|
||||
local turtleChestProvider = ChestProvider({ direction = 'up', wrapSide = 'bottom' })
|
||||
|
||||
local RESOURCE_FILE = 'usr/etc/resources.db'
|
||||
local RECIPES_FILE = 'usr/etc/recipes.db'
|
||||
local RECIPES_FILE = 'sys/etc/recipes.db'
|
||||
|
||||
local jobListGrid
|
||||
local craftingPaused = false
|
||||
local recipes = Util.readTable(RECIPES_FILE) or { }
|
||||
local resources = Util.readTable(RESOURCE_FILE) or { }
|
||||
|
||||
Craft.setRecipes(recipes)
|
||||
|
||||
for _,r in pairs(resources) do
|
||||
r.maxDamage = nil
|
||||
r.displayName = nil
|
||||
@@ -70,6 +73,17 @@ local function getItem(items, inItem, ignoreDamage)
|
||||
end
|
||||
end
|
||||
|
||||
local function splitKey(key)
|
||||
local t = Util.split(key, '(.-):')
|
||||
local item = { }
|
||||
if #t[#t] > 2 then
|
||||
item.nbtHash = table.remove(t)
|
||||
end
|
||||
item.damage = tonumber(table.remove(t))
|
||||
item.name = table.concat(t, ':')
|
||||
return item
|
||||
end
|
||||
|
||||
local function getItemQuantity(items, item)
|
||||
item = getItem(items, item)
|
||||
if item then
|
||||
@@ -114,7 +128,8 @@ local function mergeResources(t)
|
||||
end
|
||||
end
|
||||
|
||||
for _,v in pairs(recipes) do
|
||||
for k in pairs(recipes) do
|
||||
local v = splitKey(k)
|
||||
local item = getItem(t, v)
|
||||
if not item then
|
||||
item = Util.shallowCopy(v)
|
||||
@@ -149,8 +164,8 @@ end
|
||||
local function sumItems3(ingredients, items, summedItems, count)
|
||||
|
||||
local canCraft = 0
|
||||
for _,item in pairs(ingredients) do
|
||||
local key = uniqueKey(item)
|
||||
for _,key in pairs(ingredients) do
|
||||
local item = splitKey(key)
|
||||
local summedItem = summedItems[key]
|
||||
if not summedItem then
|
||||
summedItem = Util.shallowCopy(item)
|
||||
@@ -167,52 +182,6 @@ local function sumItems3(ingredients, items, summedItems, count)
|
||||
end
|
||||
end
|
||||
|
||||
local function sumItems2(ingredients, items, summedItems, count)
|
||||
|
||||
local canCraft = 0
|
||||
|
||||
for i = 1, count do
|
||||
for _,item in pairs(ingredients) do
|
||||
local key = uniqueKey(item)
|
||||
local summedItem = summedItems[key]
|
||||
if not summedItem then
|
||||
summedItem = Util.shallowCopy(item)
|
||||
summedItem.recipe = recipes[key]
|
||||
summedItem.count = getItemQuantity(items, summedItem)
|
||||
summedItems[key] = summedItem
|
||||
end
|
||||
if summedItem.recipe and summedItem.count <= 0 then
|
||||
summedItem.count = sumItems2(summedItem.recipe.ingredients, items, summedItems, 1)
|
||||
end
|
||||
if summedItem.count <= 0 then
|
||||
return canCraft
|
||||
end
|
||||
summedItem.count = summedItem.count - item.count
|
||||
end
|
||||
canCraft = canCraft + 1
|
||||
end
|
||||
|
||||
return canCraft
|
||||
end
|
||||
|
||||
local function sumItems(items)
|
||||
local t = {}
|
||||
|
||||
for _,item in pairs(items) do
|
||||
local key = uniqueKey(item)
|
||||
local summedItem = t[key]
|
||||
if summedItem then
|
||||
summedItem.count = summedItem.count + item.count
|
||||
else
|
||||
summedItem = Util.shallowCopy(item)
|
||||
summedItem.recipe = recipes[key]
|
||||
t[key] = summedItem
|
||||
end
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
local function isGridClear()
|
||||
for i = 1, 16 do
|
||||
if turtle.getItemCount(i) ~= 0 then
|
||||
@@ -235,32 +204,6 @@ local function clearGrid()
|
||||
return true
|
||||
end
|
||||
|
||||
local function turtleCraft(recipe, originalItem, qty)
|
||||
|
||||
for k,v in pairs(recipe.ingredients) do
|
||||
|
||||
chestProvider:provide({ id = v.name, dmg = v.damage, nbt_hash = v.nbtHash }, v.count * qty, k)
|
||||
if turtle.getItemCount(k) ~= v.count * qty then
|
||||
clearGrid()
|
||||
originalItem.status = v.name .. ' (extract failed)'
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if not turtle.craft() then
|
||||
clearGrid()
|
||||
return false
|
||||
end
|
||||
|
||||
--for k,ingredient in pairs(recipe.ingredients) do
|
||||
-- local item = getItem(items, ingredient)
|
||||
-- item.count = item.count - ingredient.count
|
||||
--end
|
||||
|
||||
clearGrid()
|
||||
return true
|
||||
end
|
||||
|
||||
local function addCraftingRequest(item, craftList, count)
|
||||
local key = uniqueKey(item)
|
||||
local request = craftList[key]
|
||||
@@ -272,64 +215,30 @@ local function addCraftingRequest(item, craftList, count)
|
||||
request.count = request.count + count
|
||||
end
|
||||
|
||||
local function craftRecipe(recipe, items, originalItem, count)
|
||||
|
||||
local maxCount = recipe.maxCount
|
||||
|
||||
if not maxCount then -- temporary
|
||||
local cItem = itemDB:get(itemDB:makeKey(recipe))
|
||||
if cItem then
|
||||
maxCount = cItem.maxCount
|
||||
else
|
||||
maxCount = 1
|
||||
end
|
||||
end
|
||||
|
||||
local summedItems = sumItems(recipe.ingredients)
|
||||
for key,ingredient in pairs(summedItems) do
|
||||
local details = getItemDetails(items, ingredient)
|
||||
maxCount = math.min(details.maxCount, maxCount)
|
||||
if details.count < ingredient.count * count then
|
||||
if ingredient.recipe then
|
||||
if not craftRecipe(ingredient.recipe, items, originalItem, ingredient.count * count - details.count) then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
repeat
|
||||
if not turtleCraft(recipe, originalItem, math.min(count, maxCount)) then
|
||||
return false
|
||||
end
|
||||
count = count - maxCount
|
||||
until count < 0
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function craftItem(recipe, items, originalItem, craftList, count)
|
||||
|
||||
if craftingPaused or not device.workbench or not isGridClear() then
|
||||
return
|
||||
end
|
||||
|
||||
count = math.ceil(count / recipe.count)
|
||||
|
||||
local toCraft = sumItems2(recipe.ingredients, items, { }, count)
|
||||
local toCraft = Craft.getCraftableAmount(recipe, count, items)
|
||||
|
||||
if toCraft > 0 then
|
||||
craftRecipe(recipe, items, originalItem, toCraft)
|
||||
Craft.craftRecipe(recipe, toCraft, chestProvider)
|
||||
clearGrid()
|
||||
items = chestProvider:listItems()
|
||||
end
|
||||
|
||||
count = count - toCraft
|
||||
|
||||
local summedItems = { }
|
||||
sumItems3(recipe.ingredients, items, summedItems, count)
|
||||
if count > 0 then
|
||||
local summedItems = { }
|
||||
sumItems3(recipe.ingredients, items, summedItems, math.ceil(count / recipe.count))
|
||||
|
||||
for key,ingredient in pairs(summedItems) do
|
||||
if not ingredient.recipe and ingredient.count < 0 then
|
||||
addCraftingRequest(ingredient, craftList, -ingredient.count)
|
||||
for key,ingredient in pairs(summedItems) do
|
||||
if not ingredient.recipe and ingredient.count < 0 then
|
||||
addCraftingRequest(ingredient, craftList, -ingredient.count)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,535 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Peripheral = require('peripheral')
|
||||
local RefinedProvider = require('refinedProvider')
|
||||
local Terminal = require('terminal')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
local controller = RefinedProvider()
|
||||
if not controller:isValid() then
|
||||
error('Refined storage controller not found')
|
||||
end
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Storage Manager')
|
||||
|
||||
function getItem(items, inItem, ignoreDamage)
|
||||
for _,item in pairs(items) do
|
||||
if item.name == inItem.name then
|
||||
if ignoreDamage then
|
||||
return item
|
||||
elseif item.damage == inItem.damage and item.nbtHash == inItem.nbtHash then
|
||||
return item
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function uniqueKey(item)
|
||||
return table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
||||
end
|
||||
|
||||
function mergeResources(t)
|
||||
local resources = Util.readTable('resource.limits') or { }
|
||||
|
||||
for _,v in pairs(resources) do
|
||||
v.low = tonumber(v.low) -- backwards compatibility
|
||||
local item = getItem(t, v)
|
||||
if item then
|
||||
item.low = v.low
|
||||
item.auto = v.auto
|
||||
item.ignoreDamage = v.ignoreDamage
|
||||
item.rsControl = v.rsControl
|
||||
item.rsDevice = v.rsDevice
|
||||
item.rsSide = v.rsSide
|
||||
else
|
||||
v.count = 0
|
||||
table.insert(t, v)
|
||||
end
|
||||
end
|
||||
|
||||
for _,v in pairs(t) do
|
||||
v.lname = v.displayName:lower()
|
||||
end
|
||||
end
|
||||
|
||||
function filterItems(t, filter)
|
||||
if filter then
|
||||
local r = {}
|
||||
filter = filter:lower()
|
||||
for k,v in pairs(t) do
|
||||
if string.find(v.lname, filter) then
|
||||
table.insert(r, v)
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
function craftItems(itemList, allItems)
|
||||
|
||||
for _,item in pairs(itemList) do
|
||||
local cItem = getItem(allItems, item)
|
||||
|
||||
if controller:isCrafting(item) then
|
||||
item.status = '(crafting)'
|
||||
elseif item.rsControl then
|
||||
item.status = 'Activated'
|
||||
elseif not cItem then
|
||||
item.status = '(no recipe)'
|
||||
else
|
||||
|
||||
local count = item.count
|
||||
while count >= 1 do -- try to request smaller quantities until successful
|
||||
local s, m = pcall(function()
|
||||
item.status = '(no recipe)'
|
||||
if not controller:craft(cItem, count) then
|
||||
item.status = '(missing ingredients)'
|
||||
error('failed')
|
||||
end
|
||||
item.status = '(crafting)'
|
||||
end)
|
||||
if s then
|
||||
break -- successfully requested crafting
|
||||
end
|
||||
count = math.floor(count / 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function getAutocraftItems()
|
||||
local t = Util.readTable('resource.limits') or { }
|
||||
local itemList = { }
|
||||
|
||||
for _,res in pairs(t) do
|
||||
|
||||
if res.auto then
|
||||
res.count = 4 -- this could be higher to increase autocrafting speed
|
||||
table.insert(itemList, res)
|
||||
end
|
||||
end
|
||||
return itemList
|
||||
end
|
||||
|
||||
local function getItemWithQty(items, res, ignoreDamage)
|
||||
|
||||
local item = getItem(items, res, ignoreDamage)
|
||||
|
||||
if item then
|
||||
|
||||
if ignoreDamage then
|
||||
local count = 0
|
||||
|
||||
for _,v in pairs(items) do
|
||||
if item.name == v.name and item.nbtHash == v.nbtHash then
|
||||
if item.maxDamage > 0 or item.damage == v.damage then
|
||||
count = count + v.count
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
item.count = count
|
||||
end
|
||||
end
|
||||
|
||||
return item
|
||||
end
|
||||
|
||||
function watchResources(items)
|
||||
|
||||
local itemList = { }
|
||||
|
||||
local t = Util.readTable('resource.limits') or { }
|
||||
for k, res in pairs(t) do
|
||||
res.low = tonumber(res.low) -- backwards compatibility
|
||||
local item = getItemWithQty(items, res, res.ignoreDamage)
|
||||
if not item then
|
||||
item = {
|
||||
damage = res.damage,
|
||||
nbtHash = res.nbtHash,
|
||||
name = res.name,
|
||||
displayName = res.displayName,
|
||||
count = 0
|
||||
}
|
||||
end
|
||||
|
||||
if res.low and item.count < res.low then
|
||||
if res.ignoreDamage then
|
||||
item.damage = 0
|
||||
end
|
||||
table.insert(itemList, {
|
||||
damage = item.damage,
|
||||
nbtHash = item.nbtHash,
|
||||
count = res.low - item.count,
|
||||
name = item.name,
|
||||
displayName = item.displayName,
|
||||
status = '',
|
||||
rsControl = res.rsControl,
|
||||
})
|
||||
end
|
||||
|
||||
if res.rsControl and res.rsDevice and res.rsSide then
|
||||
pcall(function()
|
||||
device[res.rsDevice].setOutput(res.rsSide, item.count < res.low)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
return itemList
|
||||
end
|
||||
|
||||
itemPage = UI.Page {
|
||||
backgroundColor = colors.lightGray,
|
||||
titleBar = UI.TitleBar {
|
||||
title = 'Limit Resource',
|
||||
previousPage = true,
|
||||
event = 'form_cancel',
|
||||
backgroundColor = colors.green
|
||||
},
|
||||
displayName = UI.Window {
|
||||
x = 5, y = 2, width = UI.term.width - 10, height = 3,
|
||||
},
|
||||
form = UI.Form {
|
||||
x = 4, y = 4, height = 10, rex = -4,
|
||||
[1] = UI.TextEntry {
|
||||
width = 7,
|
||||
backgroundColor = colors.gray,
|
||||
backgroundFocusColor = colors.gray,
|
||||
formLabel = 'Min', formKey = 'low', help = 'Craft if below min'
|
||||
},
|
||||
[2] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'Autocraft', formKey = 'auto',
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = true },
|
||||
{ name = 'No', value = false },
|
||||
},
|
||||
help = 'Craft until out of ingredients'
|
||||
},
|
||||
[3] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage',
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = true },
|
||||
{ name = 'No', value = false },
|
||||
},
|
||||
help = 'Ignore damage of item'
|
||||
},
|
||||
[4] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'RS Control', formKey = 'rsControl',
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = true },
|
||||
{ name = 'No', value = false },
|
||||
},
|
||||
help = 'Control via redstone'
|
||||
},
|
||||
[5] = UI.Chooser {
|
||||
width = 25,
|
||||
formLabel = 'RS Device', formKey = 'rsDevice',
|
||||
--choices = devices,
|
||||
help = 'Redstone Device'
|
||||
},
|
||||
[6] = UI.Chooser {
|
||||
width = 10,
|
||||
formLabel = 'RS Side', formKey = 'rsSide',
|
||||
--nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'up', value = 'up' },
|
||||
{ name = 'down', value = 'down' },
|
||||
{ name = 'east', value = 'east' },
|
||||
{ name = 'north', value = 'north' },
|
||||
{ name = 'west', value = 'west' },
|
||||
{ name = 'south', value = 'south' },
|
||||
},
|
||||
help = 'Output side'
|
||||
},
|
||||
},
|
||||
statusBar = UI.StatusBar { }
|
||||
}
|
||||
|
||||
function itemPage.displayName:draw()
|
||||
local item = self.parent.item
|
||||
local str = string.format('Name: %s\nDamage: %d', item.displayName, item.damage)
|
||||
if item.nbtHash then
|
||||
str = str .. string.format('\nNBT: %s\n', item.nbtHash)
|
||||
end
|
||||
self:setCursorPos(1, 1)
|
||||
self:print(str)
|
||||
end
|
||||
|
||||
function itemPage:enable(item)
|
||||
self.item = item
|
||||
|
||||
self.form:setValues(item)
|
||||
self.titleBar.title = item.name
|
||||
|
||||
local devices = self.form[5].choices
|
||||
Util.clear(devices)
|
||||
for _,device in pairs(device) do
|
||||
if device.setOutput then
|
||||
table.insert(devices, { name = device.name, value = device.name })
|
||||
end
|
||||
end
|
||||
|
||||
if Util.size(devices) == 0 then
|
||||
table.insert(devices, { name = 'None found', values = '' })
|
||||
end
|
||||
|
||||
UI.Page.enable(self)
|
||||
self:focusFirst()
|
||||
end
|
||||
|
||||
function itemPage:eventHandler(event)
|
||||
if event.type == 'form_cancel' then
|
||||
UI:setPreviousPage()
|
||||
|
||||
elseif event.type == 'focus_change' then
|
||||
self.statusBar:setStatus(event.focused.help)
|
||||
self.statusBar:draw()
|
||||
|
||||
elseif event.type == 'form_complete' then
|
||||
local values = self.form.values
|
||||
local t = Util.readTable('resource.limits') or { }
|
||||
local keys = { 'name', 'displayName', 'auto', 'low', 'damage',
|
||||
'maxDamage', 'nbtHash', 'ignoreDamage',
|
||||
'rsControl', 'rsDevice', 'rsSide', }
|
||||
|
||||
local filtered = { }
|
||||
for _,key in pairs(keys) do
|
||||
filtered[key] = values[key]
|
||||
end
|
||||
filtered.low = tonumber(filtered.low)
|
||||
|
||||
filtered.ignoreDamage = filtered.ignoreDamage == true
|
||||
filtered.auto = filtered.auto == true
|
||||
filtered.rsControl = filtered.rsControl == true
|
||||
|
||||
if filtered.ignoreDamage then
|
||||
filtered.damage = 0
|
||||
end
|
||||
|
||||
t[uniqueKey(filtered)] = filtered
|
||||
Util.writeTable('resource.limits', t)
|
||||
|
||||
UI:setPreviousPage()
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
listingPage = UI.Page {
|
||||
menuBar = UI.MenuBar {
|
||||
buttons = {
|
||||
{ text = 'Forget', event = 'forget' },
|
||||
},
|
||||
},
|
||||
grid = UI.Grid {
|
||||
y = 2, height = UI.term.height - 2,
|
||||
columns = {
|
||||
{ heading = 'Name', key = 'displayName', width = UI.term.width - 14 },
|
||||
{ heading = 'Qty', key = 'count', width = 5 },
|
||||
{ heading = 'Min', key = 'low', width = 4 },
|
||||
},
|
||||
sortColumn = 'lname',
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
backgroundColor = colors.gray,
|
||||
width = UI.term.width,
|
||||
filterText = UI.Text {
|
||||
x = 2, width = 6,
|
||||
value = 'Filter',
|
||||
},
|
||||
filter = UI.TextEntry {
|
||||
x = 9, rex = -12,
|
||||
limit = 50,
|
||||
},
|
||||
refresh = UI.Button {
|
||||
rx = -9, width = 8,
|
||||
text = 'Refresh',
|
||||
event = 'refresh',
|
||||
},
|
||||
},
|
||||
accelerators = {
|
||||
r = 'refresh',
|
||||
q = 'quit',
|
||||
}
|
||||
}
|
||||
|
||||
function listingPage.grid:getRowTextColor(row, selected)
|
||||
if row.is_craftable then -- not implemented
|
||||
return colors.yellow
|
||||
end
|
||||
return UI.Grid:getRowTextColor(row, selected)
|
||||
end
|
||||
|
||||
function listingPage.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
row.count = Util.toBytes(row.count)
|
||||
if row.low then
|
||||
row.low = Util.toBytes(row.low)
|
||||
end
|
||||
return row
|
||||
end
|
||||
|
||||
function listingPage.statusBar:draw()
|
||||
return UI.Window.draw(self)
|
||||
end
|
||||
|
||||
function listingPage.statusBar.filter:eventHandler(event)
|
||||
if event.type == 'mouse_rightclick' then
|
||||
self.value = ''
|
||||
self:draw()
|
||||
local page = UI:getCurrentPage()
|
||||
page.filter = nil
|
||||
page:applyFilter()
|
||||
page.grid:draw()
|
||||
page:setFocus(self)
|
||||
end
|
||||
return UI.TextEntry.eventHandler(self, event)
|
||||
end
|
||||
|
||||
function listingPage:eventHandler(event)
|
||||
if event.type == 'quit' then
|
||||
UI:exitPullEvents()
|
||||
|
||||
elseif event.type == 'grid_select' then
|
||||
local selected = event.selected
|
||||
UI:setPage('item', selected)
|
||||
|
||||
elseif event.type == 'refresh' then
|
||||
self:refresh()
|
||||
self.grid:draw()
|
||||
self.statusBar.filter:focus()
|
||||
|
||||
elseif event.type == 'forget' then
|
||||
local item = self.grid:getSelected()
|
||||
if item then
|
||||
|
||||
local resources = Util.readTable('resource.limits') or { }
|
||||
resources[uniqueKey(item)] = nil
|
||||
Util.writeTable('resource.limits', resources)
|
||||
|
||||
self.statusBar:timedStatus('Forgot: ' .. item.name, 3)
|
||||
self:refresh()
|
||||
self.grid:draw()
|
||||
end
|
||||
|
||||
elseif event.type == 'text_change' then
|
||||
self.filter = event.text
|
||||
if #self.filter == 0 then
|
||||
self.filter = nil
|
||||
end
|
||||
self:applyFilter()
|
||||
self.grid:draw()
|
||||
self.statusBar.filter:focus()
|
||||
|
||||
else
|
||||
UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function listingPage:enable()
|
||||
self:refresh()
|
||||
self:setFocus(self.statusBar.filter)
|
||||
UI.Page.enable(self)
|
||||
end
|
||||
|
||||
function listingPage:refresh()
|
||||
self.allItems = controller:listItems()
|
||||
mergeResources(self.allItems)
|
||||
self:applyFilter()
|
||||
end
|
||||
|
||||
function listingPage:applyFilter()
|
||||
local t = filterItems(self.allItems, self.filter)
|
||||
self.grid:setValues(t)
|
||||
end
|
||||
|
||||
local function jobMonitor(jobList)
|
||||
|
||||
local mon = Peripheral.getByType('monitor')
|
||||
|
||||
if mon then
|
||||
mon = UI.Device({
|
||||
device = device.monitor,
|
||||
textScale = .5,
|
||||
})
|
||||
else
|
||||
mon = UI.Device({
|
||||
device = Terminal.getNullTerm(term.current())
|
||||
})
|
||||
end
|
||||
|
||||
jobListGrid = UI.Grid {
|
||||
parent = mon,
|
||||
sortColumn = 'displayName',
|
||||
columns = {
|
||||
{ heading = 'Qty', key = 'count', width = 6 },
|
||||
{ heading = 'Crafting', key = 'displayName', width = mon.width / 2 - 10 },
|
||||
{ heading = 'Status', key = 'status', width = mon.width - 10 },
|
||||
},
|
||||
}
|
||||
|
||||
return jobListGrid
|
||||
end
|
||||
|
||||
UI:setPages({
|
||||
listing = listingPage,
|
||||
item = itemPage,
|
||||
})
|
||||
|
||||
UI:setPage(listingPage)
|
||||
listingPage:setFocus(listingPage.statusBar.filter)
|
||||
|
||||
local jobListGrid = jobMonitor()
|
||||
jobListGrid:draw()
|
||||
jobListGrid:sync()
|
||||
|
||||
function craftingThread()
|
||||
|
||||
while true do
|
||||
os.sleep(5)
|
||||
|
||||
--pcall(function()
|
||||
|
||||
local items = controller:listItems()
|
||||
|
||||
if not controller:isOnline() then
|
||||
jobListGrid.parent:clear()
|
||||
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'Power failure')
|
||||
jobListGrid:sync()
|
||||
|
||||
elseif Util.size(items) == 0 then
|
||||
jobListGrid.parent:clear()
|
||||
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system')
|
||||
jobListGrid:sync()
|
||||
|
||||
else
|
||||
local itemList = watchResources(items)
|
||||
jobListGrid:setValues(itemList)
|
||||
jobListGrid:draw()
|
||||
jobListGrid:sync()
|
||||
craftItems(itemList, items)
|
||||
--jobListGrid:update()
|
||||
jobListGrid:draw()
|
||||
jobListGrid:sync()
|
||||
|
||||
itemList = getAutocraftItems() -- autocrafted items don't show on job monitor
|
||||
craftItems(itemList, items)
|
||||
end
|
||||
--end)
|
||||
end
|
||||
end
|
||||
|
||||
UI:pullEvents(craftingThread)
|
||||
|
||||
UI.term:reset()
|
||||
jobListGrid.parent:reset()
|
||||
708
sys/apps/treefarm.lua
Normal file
708
sys/apps/treefarm.lua
Normal file
@@ -0,0 +1,708 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
--[[
|
||||
Requirements:
|
||||
Place turtle against an oak tree or oak sapling
|
||||
Area around turtle must be flat and can only be dirt or grass
|
||||
(9 blocks in each direction from turtle)
|
||||
Turtle must have: crafting table, chest
|
||||
Turtle must have a pick equipped on the left side
|
||||
|
||||
Optional:
|
||||
Add additional sapling types that can grow with a single sapling
|
||||
|
||||
Notes:
|
||||
If the turtle does not get any saplings from the initial tree, place
|
||||
down another sapling in front of the turtle.
|
||||
|
||||
The program will be able to survive server restarts as long as it has
|
||||
created the cobble line. If the program is stopped before that time,
|
||||
place the turtle in the original position before restarting the program.
|
||||
]]--
|
||||
|
||||
local ChestProvider = require('chestProvider18')
|
||||
local Craft = require('turtle.craft')
|
||||
local Level = require('turtle.level')
|
||||
local Point = require('point')
|
||||
local Util = require('util')
|
||||
|
||||
local FUEL_BASE = 0
|
||||
local FUEL_DIRE = FUEL_BASE + 10
|
||||
local FUEL_GOOD = FUEL_BASE + 2000
|
||||
|
||||
local MIN_CHARCOAL = 24
|
||||
local MAX_SAPLINGS = 32
|
||||
|
||||
local GRID_WIDTH = 8
|
||||
local GRID_LENGTH = 10
|
||||
local GRID = {
|
||||
TL = { x = 8, y = 0, z = -8 },
|
||||
TR = { x = 8, y = 0, z = 8 },
|
||||
BL = { x = -10, y = 0, z = -8 },
|
||||
BR = { x = -10, y = 0, z = 8 },
|
||||
}
|
||||
|
||||
local HOME_PT = { x = 0, y = 0, z = 0, heading = 0 }
|
||||
|
||||
local DIG_BLACKLIST = {
|
||||
[ 'minecraft:furnace' ] = true,
|
||||
[ 'minecraft:lit_furnace' ] = true,
|
||||
[ 'minecraft:chest' ] = true,
|
||||
}
|
||||
|
||||
local COBBLESTONE = 'minecraft:cobblestone:0'
|
||||
local CHARCOAL = 'minecraft:coal:1'
|
||||
local OAK_LOG = 'minecraft:log:0'
|
||||
local OAK_PLANK = 'minecraft:planks:0'
|
||||
local CHEST = 'minecraft:chest:0'
|
||||
local FURNACE = 'minecraft:furnace:0'
|
||||
local SAPLING = 'minecraft:sapling:0'
|
||||
local STONE = 'minecraft:stone:0'
|
||||
local TORCH = 'minecraft:torch:0'
|
||||
local DIRT = 'minecraft:dirt:0'
|
||||
local APPLE = 'minecraft:apple:0'
|
||||
local STICK = 'minecraft:stick:0'
|
||||
|
||||
local ALL_SAPLINGS = {
|
||||
SAPLING
|
||||
}
|
||||
|
||||
local state = Util.readTable('usr/config/treefarm') or {
|
||||
trees = {
|
||||
{ x = 1, y = 0, z = 0 }
|
||||
}
|
||||
}
|
||||
|
||||
local clock = os.clock()
|
||||
local recipes = Util.readTable('sys/etc/recipes.db') or { }
|
||||
|
||||
Craft.setRecipes(recipes)
|
||||
|
||||
local function inspect(fn)
|
||||
local s, item = fn()
|
||||
if s and item then
|
||||
return item.name .. ':' .. item.metadata
|
||||
end
|
||||
return 'minecraft:air:0'
|
||||
end
|
||||
|
||||
local function setState(key, value)
|
||||
state[key] = value
|
||||
Util.writeTable('usr/config/treefarm', state)
|
||||
end
|
||||
|
||||
local function refuel()
|
||||
if turtle.getFuelLevel() < FUEL_GOOD then
|
||||
local charcoal = turtle.getItemCount(CHARCOAL)
|
||||
if charcoal > 1 then
|
||||
turtle.refuel(CHARCOAL, math.min(charcoal - 1, MIN_CHARCOAL / 2))
|
||||
print('fuel: ' .. turtle.getFuelLevel())
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function safePlaceBlock(item)
|
||||
|
||||
if turtle.placeUp(item) then
|
||||
return true
|
||||
end
|
||||
|
||||
local s, m = turtle.inspectUp()
|
||||
if s and not DIG_BLACKLIST[m.name] then
|
||||
turtle.digUp()
|
||||
return turtle.placeUp(item)
|
||||
end
|
||||
|
||||
turtle.forward()
|
||||
return turtle.placeUp(item)
|
||||
end
|
||||
|
||||
local function craftItem(item, qty)
|
||||
|
||||
local success
|
||||
|
||||
if safePlaceBlock(CHEST) then
|
||||
|
||||
if turtle.equip('left', 'minecraft:crafting_table') then
|
||||
|
||||
local chestProvider = ChestProvider({
|
||||
wrapSide = 'top',
|
||||
direction = 'down',
|
||||
})
|
||||
if not chestProvider:isValid() then
|
||||
print('invalid chestProvider')
|
||||
read()
|
||||
end
|
||||
-- turtle.emptyInventory(turtle.dropUp)
|
||||
|
||||
Util.print('Crafting %d %s', (qty or 1), item)
|
||||
success = Craft.craftRecipe(recipes[item], qty or 1, chestProvider)
|
||||
|
||||
repeat until not turtle.suckUp()
|
||||
end
|
||||
turtle.equip('left', 'minecraft:diamond_pickaxe')
|
||||
turtle.digUp()
|
||||
end
|
||||
|
||||
return success
|
||||
end
|
||||
|
||||
local function cook(item, count, result, fuel, fuelCount)
|
||||
|
||||
setState('cooking', true)
|
||||
|
||||
fuel = fuel or CHARCOAL
|
||||
fuelCount = fuelCount or math.ceil(count / 8)
|
||||
Util.print('Making %d %s', count, result)
|
||||
|
||||
turtle.dropForwardAt(state.furnace, fuel, fuelCount)
|
||||
turtle.dropDownAt(state.furnace, item, count)
|
||||
|
||||
count = count + turtle.getItemCount(result)
|
||||
turtle.select(1)
|
||||
turtle.pathfind(Point.below(state.furnace))
|
||||
repeat
|
||||
os.sleep(1)
|
||||
turtle.suckUp()
|
||||
until turtle.getItemCount(result) >= count
|
||||
|
||||
setState('cooking')
|
||||
end
|
||||
|
||||
local function makeSingleCharcoal()
|
||||
|
||||
local slots = turtle.getSummedInventory()
|
||||
|
||||
if not state.furnace or
|
||||
slots[CHARCOAL] or
|
||||
not slots[OAK_LOG] or
|
||||
slots[OAK_LOG].count < 2 then
|
||||
return true
|
||||
end
|
||||
|
||||
turtle.faceAgainst(state.furnace)
|
||||
if craftItem(OAK_PLANK) then
|
||||
cook(OAK_LOG, 1, CHARCOAL, OAK_PLANK, 1)
|
||||
turtle.refuel(OAK_PLANK)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function makeCharcoal()
|
||||
|
||||
local slots = turtle.getSummedInventory()
|
||||
|
||||
if not state.furnace or
|
||||
not slots[CHARCOAL] or
|
||||
slots[CHARCOAL].count >= MIN_CHARCOAL then
|
||||
return true
|
||||
end
|
||||
|
||||
local function getLogSlot(slots)
|
||||
local maxslot = { count = 0 }
|
||||
for k,slot in pairs(slots) do
|
||||
if string.match(k, 'minecraft:log') then
|
||||
if slot.count > maxslot.count then
|
||||
maxslot = slot
|
||||
end
|
||||
end
|
||||
end
|
||||
return maxslot
|
||||
end
|
||||
|
||||
repeat
|
||||
local slots = turtle.getSummedInventory()
|
||||
local charcoal = slots[CHARCOAL].count
|
||||
local slot = getLogSlot(slots)
|
||||
|
||||
if slot.count < 8 then
|
||||
break
|
||||
end
|
||||
|
||||
local toCook = math.min(charcoal, math.floor(slot.count / 8))
|
||||
toCook = math.min(toCook, math.floor((MIN_CHARCOAL + 8 - charcoal) / 8))
|
||||
toCook = toCook * 8
|
||||
|
||||
cook(slot.key, toCook, CHARCOAL)
|
||||
|
||||
until charcoal + toCook >= MIN_CHARCOAL
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function emptyFurnace()
|
||||
if state.cooking then
|
||||
|
||||
print('Emptying furnace')
|
||||
|
||||
turtle.suckDownAt(state.furnace)
|
||||
turtle.suckForwardAt(state.furnace)
|
||||
turtle.suckUpAt(state.furnace)
|
||||
setState('cooking')
|
||||
end
|
||||
end
|
||||
|
||||
local function getCobblestone(count)
|
||||
|
||||
local slots = turtle.getSummedInventory()
|
||||
|
||||
if not slots[COBBLESTONE] or slots[COBBLESTONE].count < count then
|
||||
|
||||
print('Collecting cobblestone')
|
||||
|
||||
slots[COBBLESTONE] = true
|
||||
slots[DIRT] = true
|
||||
|
||||
local pt = Point.copy(GRID.BR)
|
||||
pt.x = GRID.BR.x + 2
|
||||
pt.z = GRID.BR.z - 2
|
||||
|
||||
turtle.pathfind(pt)
|
||||
|
||||
repeat
|
||||
turtle.select(1)
|
||||
turtle.digDown()
|
||||
turtle.down()
|
||||
for i = 1, 3 do
|
||||
if inspect(turtle.inspect) == STONE then
|
||||
turtle.dig()
|
||||
end
|
||||
turtle.turnRight()
|
||||
end
|
||||
|
||||
for item in pairs(turtle.getSummedInventory()) do
|
||||
if not slots[item] then
|
||||
turtle.drop(item)
|
||||
end
|
||||
end
|
||||
|
||||
until turtle.getItemCount(COBBLESTONE) >= count
|
||||
|
||||
turtle.gotoPoint(pt)
|
||||
turtle.placeDown(DIRT)
|
||||
|
||||
turtle.drop(DIRT)
|
||||
end
|
||||
end
|
||||
|
||||
local function createFurnace()
|
||||
|
||||
if not state.furnace then
|
||||
if turtle.getFuelLevel() < FUEL_BASE + 100 then
|
||||
return true -- try again later
|
||||
end
|
||||
print('Adding a furnace')
|
||||
getCobblestone(8)
|
||||
|
||||
if craftItem(FURNACE) then
|
||||
turtle.drop(COBBLESTONE)
|
||||
local furnacePt = { x = GRID.BL.x + 2, y = 1, z = GRID.BL.z + 2 }
|
||||
turtle.placeAt(furnacePt, FURNACE)
|
||||
setState('furnace', furnacePt)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function createPerimeter()
|
||||
|
||||
if not state.perimeter then
|
||||
if not state.furnace or
|
||||
turtle.getFuelLevel() < FUEL_BASE + 500 or
|
||||
turtle.getItemCount(OAK_LOG) == 0 or
|
||||
not craftItem(OAK_PLANK, 2) then
|
||||
return true
|
||||
end
|
||||
|
||||
print('Creating a perimeter')
|
||||
|
||||
getCobblestone(GRID_WIDTH * 2 + 1)
|
||||
cook(COBBLESTONE, 2, STONE, OAK_PLANK, 2)
|
||||
turtle.refuel(OAK_PLANK)
|
||||
|
||||
turtle.pathfind(GRID.BL)
|
||||
turtle.digDown()
|
||||
turtle.placeDown(STONE)
|
||||
|
||||
turtle.setMoveCallback(function()
|
||||
local target = COBBLESTONE
|
||||
if math.abs(turtle.point.x) == GRID_LENGTH and
|
||||
math.abs(turtle.point.z) == GRID_WIDTH then
|
||||
target = STONE
|
||||
end
|
||||
|
||||
if inspect(turtle.inspectDown) ~= target then
|
||||
turtle.digDown()
|
||||
turtle.placeDown(target)
|
||||
end
|
||||
end)
|
||||
|
||||
turtle.pathfind(GRID.BR)
|
||||
|
||||
turtle.clearMoveCallback()
|
||||
turtle.drop(COBBLESTONE)
|
||||
turtle.drop(DIRT)
|
||||
|
||||
setState('perimeter', true)
|
||||
end
|
||||
end
|
||||
|
||||
local function createChests()
|
||||
if state.chest_1 then
|
||||
return false
|
||||
end
|
||||
if state.perimeter and
|
||||
turtle.getFuelLevel() > FUEL_BASE + 100 and
|
||||
Craft.canCraft(CHEST, 4, turtle.getSummedInventory()) then
|
||||
|
||||
print('Adding storage')
|
||||
if craftItem(CHEST, 4) then
|
||||
|
||||
local pt = Point.copy(GRID.BL)
|
||||
pt.x = pt.x + 1
|
||||
pt.y = pt.y - 1
|
||||
|
||||
for i = 1, 2 do
|
||||
pt.z = pt.z + 1
|
||||
|
||||
turtle.digDownAt(pt)
|
||||
turtle.placeDown(CHEST)
|
||||
|
||||
pt.z = pt.z + 1
|
||||
|
||||
turtle.digDownAt(pt)
|
||||
turtle.placeDown(CHEST)
|
||||
|
||||
setState('chest_' .. i, Util.shallowCopy(pt))
|
||||
|
||||
pt.z = pt.z + 1
|
||||
end
|
||||
turtle.drop(DIRT)
|
||||
turtle.refuel(OAK_PLANK)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function dropOffItems()
|
||||
|
||||
if state.chest_1 then
|
||||
local slots = turtle.getSummedInventory()
|
||||
|
||||
if state.chest_1 and
|
||||
slots[CHARCOAL] and
|
||||
slots[CHARCOAL].count >= MIN_CHARCOAL and
|
||||
(turtle.getItemCount('minecraft:log') > 0 or
|
||||
turtle.getItemCount('minecraft:log2') > 0) then
|
||||
|
||||
print('Storing logs')
|
||||
turtle.pathfind(state.chest_1)
|
||||
turtle.dropDown('minecraft:log')
|
||||
turtle.dropDown('minecraft:log2')
|
||||
end
|
||||
|
||||
if slots[APPLE] then
|
||||
print('Storing apples')
|
||||
turtle.dropDownAt(state.chest_2, APPLE)
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function eatSaplings()
|
||||
|
||||
local slots = turtle.getSummedInventory()
|
||||
|
||||
for _, sapling in pairs(ALL_SAPLINGS) do
|
||||
if slots[sapling] and slots[sapling].count > MAX_SAPLINGS then
|
||||
turtle.refuel(sapling, slots[sapling].count - MAX_SAPLINGS)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function placeTorches()
|
||||
if state.torches then
|
||||
return
|
||||
end
|
||||
|
||||
if Craft.canCraft(TORCH, 4, turtle.getSummedInventory()) and
|
||||
turtle.getFuelLevel() > 100 then
|
||||
|
||||
print('Placing torches')
|
||||
|
||||
if craftItem(TORCH, 4) then
|
||||
local pts = { }
|
||||
for x = -4, 4, 8 do
|
||||
for z = -4, 4, 8 do
|
||||
table.insert(pts, { x = x, y = 0, z = z })
|
||||
end
|
||||
end
|
||||
Point.eachClosest(turtle.point, pts, function(pt)
|
||||
turtle.placeAt(pt, TORCH)
|
||||
end)
|
||||
turtle.refuel(STICK)
|
||||
turtle.refuel(OAK_PLANK)
|
||||
setState('torches', true)
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function randomSapling()
|
||||
|
||||
local sapling = SAPLING
|
||||
|
||||
if #state.trees > 1 then
|
||||
ALL_SAPLINGS = { }
|
||||
|
||||
local slots = turtle.getFilledSlots()
|
||||
for _, slot in pairs(slots) do
|
||||
if slot.name == 'minecraft:sapling' then
|
||||
table.insert(ALL_SAPLINGS, slot.key)
|
||||
end
|
||||
end
|
||||
sapling = ALL_SAPLINGS[math.random(1, #ALL_SAPLINGS)]
|
||||
end
|
||||
|
||||
return sapling
|
||||
end
|
||||
|
||||
local function fellTree(pt)
|
||||
|
||||
local function desparateRefuel(min)
|
||||
if turtle.getFuelLevel() < min then
|
||||
local logs = turtle.getItemCount(OAK_LOG)
|
||||
if logs > 0 then
|
||||
if craftItem(OAK_PLANK, math.min(8, logs * 4)) then
|
||||
turtle.refuel(OAK_PLANK)
|
||||
print('fuel: ' .. turtle.getFuelLevel())
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
turtle.setMoveCallback(function() desparateRefuel(FUEL_DIRE) end)
|
||||
|
||||
desparateRefuel(FUEL_DIRE)
|
||||
|
||||
if turtle.digUpAt(Point.above(pt)) then
|
||||
Level(
|
||||
{ x = GRID_WIDTH-1, y = 1, z = GRID_WIDTH-1 },
|
||||
{ x = -(GRID_WIDTH-1), y = 50, z = -(GRID_WIDTH-1) },
|
||||
Point.above(pt))
|
||||
end
|
||||
|
||||
desparateRefuel(FUEL_BASE + 100)
|
||||
turtle.clearMoveCallback()
|
||||
turtle.setPolicy("attack")
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function fell()
|
||||
|
||||
local pts = Util.shallowCopy(state.trees)
|
||||
|
||||
local pt = table.remove(pts, math.random(1, #pts))
|
||||
if not turtle.faceAgainst(pt) or
|
||||
not string.match(inspect(turtle.inspect), 'minecraft:log') then
|
||||
return true
|
||||
end
|
||||
|
||||
print('Chopping')
|
||||
|
||||
local fuel = turtle.getFuelLevel()
|
||||
table.insert(pts, 1, pt)
|
||||
|
||||
Point.eachClosest(turtle.point, pts, function(pt)
|
||||
if turtle.faceAgainst(pt) and
|
||||
string.match(inspect(turtle.inspect), 'minecraft:log') then
|
||||
turtle.dig()
|
||||
fellTree(pt)
|
||||
end
|
||||
turtle.placeAt(pt, randomSapling())
|
||||
end)
|
||||
|
||||
print('Used ' .. (fuel - turtle.getFuelLevel()) .. ' fuel')
|
||||
return true
|
||||
end
|
||||
|
||||
local function moreTrees()
|
||||
|
||||
if #state.trees > 1 then
|
||||
return
|
||||
end
|
||||
|
||||
if not state.chest_1 or turtle.getItemCount(SAPLING) < 9 then
|
||||
return true
|
||||
end
|
||||
|
||||
print('Adding more trees')
|
||||
|
||||
local singleTree = state.trees[1]
|
||||
|
||||
state.trees = { }
|
||||
for x = -2, 2, 2 do
|
||||
for z = -2, 2, 2 do
|
||||
table.insert(state.trees, { x = x, y = 0, z = z })
|
||||
end
|
||||
end
|
||||
|
||||
turtle.digAt(singleTree)
|
||||
fellTree(singleTree)
|
||||
|
||||
setState('trees', state.trees)
|
||||
|
||||
Point.eachClosest(turtle.point, state.trees, function(pt)
|
||||
turtle.placeDownAt(pt, randomSapling())
|
||||
end)
|
||||
end
|
||||
|
||||
function getTurtleFacing(block)
|
||||
local directions = {
|
||||
[5] = 2,
|
||||
[3] = 3,
|
||||
[4] = 0,
|
||||
[2] = 1,
|
||||
}
|
||||
|
||||
if not safePlaceBlock(block) then
|
||||
error('unable to place chest above')
|
||||
end
|
||||
local _, bi = turtle.inspectUp()
|
||||
turtle.digUp()
|
||||
return directions[bi.metadata]
|
||||
end
|
||||
|
||||
function saveTurtleFacing()
|
||||
if not state.facing then
|
||||
setState('facing', getTurtleFacing(CHEST))
|
||||
end
|
||||
end
|
||||
|
||||
local function findGround()
|
||||
print('Locating ground level')
|
||||
turtle.setPoint(HOME_PT)
|
||||
|
||||
while true do
|
||||
local s, block = turtle.inspectDown()
|
||||
|
||||
if not s then block = { name = 'minecraft:air', metadata = 0 } end
|
||||
b = block.name .. ':' .. block.metadata
|
||||
|
||||
if b == 'minecraft:dirt:0' or
|
||||
b == 'minecraft:grass:0' or
|
||||
block.name == 'minecraft:chest' then
|
||||
break
|
||||
end
|
||||
|
||||
if b == COBBLESTONE or b == STONE then
|
||||
error('lost')
|
||||
end
|
||||
|
||||
if b == TORCH or b == FURNACE then
|
||||
turtle.forward()
|
||||
else
|
||||
turtle.digDown()
|
||||
turtle.down()
|
||||
end
|
||||
|
||||
if turtle.point.y < -20 then
|
||||
error('lost')
|
||||
end
|
||||
end
|
||||
turtle.setPoint(HOME_PT)
|
||||
end
|
||||
|
||||
local function findHome()
|
||||
|
||||
if not state.perimeter then
|
||||
return
|
||||
end
|
||||
|
||||
print('Determining location')
|
||||
|
||||
turtle.point.heading = getTurtleFacing(CHEST)
|
||||
turtle.setHeading(state.facing)
|
||||
turtle.point.heading = 0
|
||||
|
||||
local pt = Point.copy(turtle.point)
|
||||
|
||||
while inspect(turtle.inspectDown) ~= COBBLESTONE do
|
||||
pt.x = pt.x - 1
|
||||
turtle.pathfind(pt)
|
||||
if pt.x < -16 then
|
||||
error('lost')
|
||||
end
|
||||
end
|
||||
while inspect(turtle.inspectDown) == COBBLESTONE do
|
||||
pt.z = pt.z - 1
|
||||
turtle.pathfind(pt)
|
||||
if pt.z < -16 then
|
||||
error('lost')
|
||||
end
|
||||
end
|
||||
|
||||
turtle.setPoint({
|
||||
x = -(GRID_LENGTH),
|
||||
y = 0,
|
||||
z = -GRID_WIDTH,
|
||||
heading = turtle.point.heading
|
||||
})
|
||||
end
|
||||
|
||||
local function updateClock()
|
||||
|
||||
local ONE_HOUR = 50
|
||||
|
||||
if os.clock() - clock > ONE_HOUR then
|
||||
clock = os.clock()
|
||||
else
|
||||
print('sleeping for ' .. math.floor(ONE_HOUR - (os.clock() - clock)))
|
||||
os.sleep(ONE_HOUR - (os.clock() - clock))
|
||||
clock = os.clock()
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local tasks = {
|
||||
{ desc = 'Finding ground', fn = findGround },
|
||||
{ desc = 'Determine facing', fn = saveTurtleFacing },
|
||||
{ desc = 'Finding home', fn = findHome },
|
||||
{ desc = 'Adding trees', fn = moreTrees },
|
||||
{ desc = 'Chopping', fn = fell },
|
||||
{ desc = 'Snacking', fn = eatSaplings },
|
||||
{ desc = 'Creating chest', fn = createChests },
|
||||
{ desc = 'Creating furnace', fn = createFurnace },
|
||||
{ desc = 'Emptying furnace', fn = emptyFurnace },
|
||||
{ desc = 'Making charcoal', fn = makeSingleCharcoal },
|
||||
{ desc = 'Making charcoal', fn = makeCharcoal },
|
||||
{ desc = 'Creating perimeter', fn = createPerimeter },
|
||||
{ desc = 'Placing torches', fn = placeTorches },
|
||||
{ desc = 'Refueling', fn = refuel },
|
||||
{ desc = 'Dropping off items', fn = dropOffItems },
|
||||
{ desc = 'Condensing', fn = turtle.condense },
|
||||
{ desc = 'Sleeping', fn = updateClock },
|
||||
}
|
||||
|
||||
turtle.run(function()
|
||||
|
||||
turtle.setPolicy("attack")
|
||||
|
||||
while not turtle.abort do
|
||||
print('fuel: ' .. turtle.getFuelLevel())
|
||||
for _,task in ipairs(Util.shallowCopy(tasks)) do
|
||||
--print(task.desc)
|
||||
turtle.status = task.desc
|
||||
turtle.select(1)
|
||||
if not task.fn() then
|
||||
Util.filterInplace(tasks, function(v) return v.fn ~= task.fn end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
Reference in New Issue
Block a user