mirror of
https://github.com/kepler155c/opus
synced 2025-10-23 11:47:39 +00:00
reorganization
This commit is contained in:
@@ -3,14 +3,26 @@ requireInjector(getfenv(1))
|
||||
local class = require('class')
|
||||
local Config = require('config')
|
||||
local Event = require('event')
|
||||
local FileUI = require('fileui')
|
||||
local FileUI = require('ui.fileui')
|
||||
local NFT = require('nft')
|
||||
local SHA1 = require('sha1')
|
||||
local Tween = require('tween')
|
||||
local Tween = require('ui.tween')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
local REGISTRY_DIR = 'usr/.registry'
|
||||
local TEMPLATE = [[
|
||||
local env = { }
|
||||
for k,v in pairs(getfenv(1)) do
|
||||
env[k] = v
|
||||
end
|
||||
setmetatable(env, { __index = _G })
|
||||
|
||||
local s, m = os.run(env, 'sys/apps/appRun.lua', %s, ...)
|
||||
if not s then
|
||||
error(m)
|
||||
end
|
||||
]]
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Overview')
|
||||
UI:configure('Overview', ...)
|
||||
@@ -39,6 +51,11 @@ local function loadApplications()
|
||||
|
||||
Util.each(applications, function(v, k) v.key = k end)
|
||||
applications = Util.filter(applications, function(_, a) return not a.disabled end)
|
||||
|
||||
applications = Util.filter(applications, function(_, a)
|
||||
return Util.startsWidth(a.run, 'http') or shell.resolveProgram(a.run)
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
loadApplications()
|
||||
@@ -285,6 +302,7 @@ function page:eventHandler(event)
|
||||
end
|
||||
Config.update('Overview', config)
|
||||
multishell.openTab({
|
||||
title = event.button.app.title,
|
||||
path = 'sys/apps/shell',
|
||||
args = { event.button.app.run },
|
||||
focused = true,
|
||||
|
@@ -7,7 +7,7 @@ local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
local GROUPS_PATH = 'usr/groups'
|
||||
local SCRIPTS_PATH = 'sys/etc/scripts'
|
||||
local SCRIPTS_PATH = 'usr/etc/scripts'
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Script')
|
||||
UI:configure('script', ...)
|
||||
|
@@ -20,8 +20,7 @@ local options = {
|
||||
desc = 'Displays the options' },
|
||||
}
|
||||
|
||||
local USR_SCRIPTS_PATH = 'usr/scripts'
|
||||
local SYS_SCRIPTS_PATH = 'sys/etc/scripts'
|
||||
local SCRIPTS_PATH = 'usr/etc/scripts'
|
||||
|
||||
local nullTerm = Terminal.getNullTerm(term.current())
|
||||
local turtles = { }
|
||||
@@ -228,21 +227,8 @@ end
|
||||
|
||||
function page.tabs.scripts:draw()
|
||||
|
||||
local function combineDirs(...)
|
||||
local list = { }
|
||||
for _,path in pairs({...}) do
|
||||
if fs.exists(path) then
|
||||
local files = fs.list(path)
|
||||
for _,f in pairs(files) do
|
||||
list[f] = fs.combine(path, f)
|
||||
end
|
||||
end
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
Util.clear(self.values)
|
||||
local files = combineDirs(SYS_SCRIPTS_PATH, USR_SCRIPTS_PATH)
|
||||
local files = fs.list(SCRIPTS_PATH)
|
||||
for f,path in pairs(files) do
|
||||
table.insert(self.values, { label = f, path = path })
|
||||
end
|
||||
|
@@ -1,36 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
Base64 = require('base64')
|
||||
|
||||
local args = { ... }
|
||||
|
||||
if not args[2] then
|
||||
error('Syntax: base64dl <file name> <url>')
|
||||
end
|
||||
|
||||
local c = http.get(args[2])
|
||||
|
||||
if not c then
|
||||
error('unable to open url')
|
||||
end
|
||||
|
||||
local data = c.readAll()
|
||||
c.close()
|
||||
|
||||
print('size: ' .. #data)
|
||||
local decoded = Base64.decode(data)
|
||||
print('decoded: ' .. #decoded)
|
||||
|
||||
local file = io.open(shell.resolve(args[1]), "wb")
|
||||
if not file then
|
||||
error('Unable to open ' .. args[1], 2)
|
||||
end
|
||||
for k,b in ipairs(decoded) do
|
||||
if (k % 1000) == 0 then
|
||||
os.sleep(0)
|
||||
end
|
||||
file:write(b)
|
||||
end
|
||||
|
||||
file:close()
|
||||
print('done')
|
2169
sys/apps/builder.lua
2169
sys/apps/builder.lua
File diff suppressed because it is too large
Load Diff
@@ -1,939 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local ChestAdapter = require('chestAdapter18')
|
||||
local Config = require('config')
|
||||
local Craft = require('turtle.craft')
|
||||
local Event = require('event')
|
||||
local itemDB = require('itemDB')
|
||||
local Peripheral = require('peripheral')
|
||||
local RefinedAdapter = require('refinedAdapter')
|
||||
local Terminal = require('terminal')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Resource Manager')
|
||||
|
||||
-- 3 wide monitor (any side of turtle)
|
||||
|
||||
-- Config location is /sys/config/resourceManager
|
||||
-- adjust directions in that file if needed
|
||||
|
||||
local config = {
|
||||
trashDirection = 'up', -- trash /chest in relation to chest
|
||||
turtleDirection = 'down', -- turtle in relation to chest
|
||||
}
|
||||
|
||||
Config.load('resourceManager', config)
|
||||
|
||||
local controller = RefinedAdapter()
|
||||
if not controller:isValid() then
|
||||
-- error('Refined storage controller not found')
|
||||
controller = nil
|
||||
end
|
||||
|
||||
local chestAdapter = ChestAdapter({ direction = 'west', wrapSide = 'back' })
|
||||
local turtleChestAdapter = ChestAdapter({ direction = 'up', wrapSide = 'bottom' })
|
||||
|
||||
local RESOURCE_FILE = 'usr/etc/resources.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
|
||||
r.count = nil
|
||||
r.lname = nil
|
||||
r.has_recipe = nil
|
||||
|
||||
if not r.ignoreDamage then
|
||||
r.ignoreDamage = nil
|
||||
end
|
||||
|
||||
if not r.auto then
|
||||
r.auto = nil
|
||||
end
|
||||
end
|
||||
Util.writeTable(RESOURCE_FILE, resources)
|
||||
|
||||
local 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 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
|
||||
return item.count
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function getItemDetails(items, item)
|
||||
local cItem = getItem(items, item)
|
||||
if cItem then
|
||||
return cItem
|
||||
end
|
||||
cItem = itemDB:get(itemDB:makeKey(item))
|
||||
if cItem then
|
||||
return { count = 0, maxCount = cItem.maxCount }
|
||||
end
|
||||
return { count = 0, maxCount = 64 }
|
||||
end
|
||||
|
||||
local function uniqueKey(item)
|
||||
return table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
||||
end
|
||||
|
||||
local function getName(item)
|
||||
local detail = itemDB:get(itemDB:makeKey(item))
|
||||
if detail then
|
||||
return detail.displayName
|
||||
end
|
||||
return item.name .. ':' .. item.damage
|
||||
end
|
||||
|
||||
local function mergeResources(t)
|
||||
for _,v in pairs(resources) do
|
||||
local item = getItem(t, v)
|
||||
if item then
|
||||
Util.merge(item, v)
|
||||
else
|
||||
item = Util.shallowCopy(v)
|
||||
item.count = 0
|
||||
table.insert(t, item)
|
||||
end
|
||||
end
|
||||
|
||||
for k in pairs(recipes) do
|
||||
local v = splitKey(k)
|
||||
local item = getItem(t, v)
|
||||
if not item then
|
||||
item = Util.shallowCopy(v)
|
||||
item.count = 0
|
||||
table.insert(t, item)
|
||||
end
|
||||
item.has_recipe = true
|
||||
end
|
||||
|
||||
for _,v in pairs(t) do
|
||||
if not v.displayName then
|
||||
v.displayName = getName(v)
|
||||
end
|
||||
v.lname = v.displayName:lower()
|
||||
end
|
||||
end
|
||||
|
||||
local 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
|
||||
|
||||
local function sumItems3(ingredients, items, summedItems, count)
|
||||
|
||||
local canCraft = 0
|
||||
for _,key in pairs(ingredients) do
|
||||
local item = splitKey(key)
|
||||
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
|
||||
summedItem.count = summedItem.count - count
|
||||
if summedItem.recipe and summedItem.count < 0 then
|
||||
local need = math.ceil(-summedItem.count / summedItem.recipe.count)
|
||||
summedItem.count = 0
|
||||
sumItems3(summedItem.recipe.ingredients, items, summedItems, need)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function isGridClear()
|
||||
for i = 1, 16 do
|
||||
if turtle.getItemCount(i) ~= 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function clearGrid()
|
||||
for i = 1, 16 do
|
||||
local count = turtle.getItemCount(i)
|
||||
if count > 0 then
|
||||
chestAdapter:insert(i, count)
|
||||
if turtle.getItemCount(i) ~= 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function addCraftingRequest(item, craftList, count)
|
||||
local key = uniqueKey(item)
|
||||
local request = craftList[key]
|
||||
if not craftList[key] then
|
||||
request = { name = item.name, damage = item.damage, nbtHash = nbtHash, count = 0 }
|
||||
request.displayName = getName(request)
|
||||
craftList[key] = request
|
||||
end
|
||||
request.count = request.count + count
|
||||
end
|
||||
|
||||
local function craftItem(recipe, items, originalItem, craftList, count)
|
||||
|
||||
if craftingPaused or not device.workbench or not isGridClear() then
|
||||
return
|
||||
end
|
||||
|
||||
local toCraft = Craft.getCraftableAmount(recipe, count, items)
|
||||
|
||||
if toCraft > 0 then
|
||||
Craft.craftRecipe(recipe, toCraft, chestAdapter)
|
||||
clearGrid()
|
||||
items = chestAdapter:listItems()
|
||||
end
|
||||
|
||||
count = count - toCraft
|
||||
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function craftItems(craftList, allItems)
|
||||
|
||||
for _,key in pairs(Util.keys(craftList)) do
|
||||
local item = craftList[key]
|
||||
local recipe = recipes[key]
|
||||
if recipe then
|
||||
craftItem(recipe, allItems, item, craftList, item.count)
|
||||
allItems = chestAdapter:listItems() -- refresh counts
|
||||
elseif item.rsControl then
|
||||
item.status = 'Activated'
|
||||
end
|
||||
end
|
||||
|
||||
for key,item in pairs(craftList) do
|
||||
|
||||
if not recipes[key] then
|
||||
if not controller then
|
||||
item.status = '(no recipe)'
|
||||
else
|
||||
if controller:isCrafting(item) then
|
||||
item.status = '(crafting)'
|
||||
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(item, 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
|
||||
end
|
||||
end
|
||||
|
||||
local function jobMonitor(jobList)
|
||||
|
||||
local mon = Peripheral.getByType('monitor')
|
||||
|
||||
if mon then
|
||||
mon = UI.Device({
|
||||
device = mon,
|
||||
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 },
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
local function getAutocraftItems()
|
||||
local craftList = { }
|
||||
|
||||
for _,res in pairs(resources) do
|
||||
|
||||
if res.auto then
|
||||
res.count = 4 -- this could be higher to increase autocrafting speed
|
||||
local key = uniqueKey(res)
|
||||
craftList[key] = res
|
||||
end
|
||||
end
|
||||
return craftList
|
||||
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
|
||||
|
||||
local function watchResources(items)
|
||||
|
||||
local craftList = { }
|
||||
|
||||
for k, res in pairs(resources) do
|
||||
local item = getItemWithQty(items, res, res.ignoreDamage)
|
||||
if not item then
|
||||
item = {
|
||||
damage = res.damage,
|
||||
nbtHash = res.nbtHash,
|
||||
name = res.name,
|
||||
displayName = getName(res),
|
||||
count = 0
|
||||
}
|
||||
end
|
||||
|
||||
if res.limit and item.count > res.limit then
|
||||
chestAdapter:provide(res, item.count - res.limit, nil, config.trashDirection)
|
||||
|
||||
elseif res.low and item.count < res.low then
|
||||
if res.ignoreDamage then
|
||||
item.damage = 0
|
||||
end
|
||||
local key = uniqueKey(res)
|
||||
craftList[key] = {
|
||||
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 craftList
|
||||
end
|
||||
|
||||
local itemPage = UI.Page {
|
||||
backgroundColor = colors.lightGray,
|
||||
titleBar = UI.TitleBar {
|
||||
title = 'Limit Resource',
|
||||
previousPage = true,
|
||||
event = 'form_cancel',
|
||||
backgroundColor = colors.green
|
||||
},
|
||||
displayName = UI.Window {
|
||||
x = 2, y = 2, width = UI.term.width - 4, height = 3,
|
||||
},
|
||||
form = UI.Form {
|
||||
x = 4, y = 5, height = 8, rex = -4,
|
||||
[1] = UI.TextEntry {
|
||||
width = 7,
|
||||
backgroundColor = colors.gray,
|
||||
backgroundFocusColor = colors.gray,
|
||||
formLabel = 'Min', formKey = 'low', help = 'Craft if below min'
|
||||
},
|
||||
[2] = UI.TextEntry {
|
||||
width = 7,
|
||||
backgroundColor = colors.gray,
|
||||
backgroundFocusColor = colors.gray,
|
||||
formLabel = 'Max', formKey = 'limit', help = 'Eject if above max'
|
||||
},
|
||||
[3] = 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'
|
||||
},
|
||||
[4] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'Ignore Dmg', formKey = 'ignore_dmg',
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = true },
|
||||
{ name = 'No', value = false },
|
||||
},
|
||||
help = 'Ignore damage of item'
|
||||
},
|
||||
[5] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'RS Control', formKey = 'rsControl',
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = true },
|
||||
{ name = 'No', value = false },
|
||||
},
|
||||
help = 'Control via redstone'
|
||||
},
|
||||
[6] = UI.Chooser {
|
||||
width = 25,
|
||||
formLabel = 'RS Device', formKey = 'rsDevice',
|
||||
--choices = devices,
|
||||
help = 'Redstone Device'
|
||||
},
|
||||
[7] = 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('\n%s', 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[6].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 keys = { 'name', 'auto', 'low', 'limit', 'damage',
|
||||
'nbtHash', 'ignoreDamage',
|
||||
'rsControl', 'rsDevice', 'rsSide', }
|
||||
|
||||
local filtered = { }
|
||||
for _,key in pairs(keys) do
|
||||
filtered[key] = values[key]
|
||||
end
|
||||
filtered.low = tonumber(filtered.low)
|
||||
filtered.limit = tonumber(filtered.limit)
|
||||
|
||||
--filtered.ignoreDamage = filtered.ignoreDamage == true
|
||||
--filtered.auto = filtered.auto == true
|
||||
--filtered.rsControl = filtered.rsControl == true
|
||||
|
||||
if filtered.auto ~= true then
|
||||
filtered.auto = nil
|
||||
end
|
||||
|
||||
if filtered.rsControl ~= true then
|
||||
filtered.rsControl = nil
|
||||
filtered.rsSide = nil
|
||||
filtered.rsDevice = nil
|
||||
end
|
||||
|
||||
if values.ignoreDamage == true then
|
||||
filtered.damage = 0
|
||||
end
|
||||
|
||||
resources[uniqueKey(filtered)] = filtered
|
||||
Util.writeTable(RESOURCE_FILE, resources)
|
||||
|
||||
UI:setPreviousPage()
|
||||
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local listingPage = UI.Page {
|
||||
menuBar = UI.MenuBar {
|
||||
buttons = {
|
||||
{ text = 'Learn', event = 'learn' },
|
||||
{ text = 'Forget', event = 'forget' },
|
||||
{ text = 'Craft', event = 'craft' },
|
||||
},
|
||||
},
|
||||
grid = UI.Grid {
|
||||
y = 2, height = UI.term.height - 2,
|
||||
columns = {
|
||||
{ heading = 'Name', key = 'displayName' , width = 22 },
|
||||
{ heading = 'Qty', key = 'count' , width = 5 },
|
||||
{ heading = 'Min', key = 'low' , width = 4 },
|
||||
{ heading = 'Max', key = 'limit' , width = 4 },
|
||||
},
|
||||
sortColumn = 'displayName',
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
backgroundColor = colors.gray,
|
||||
width = UI.term.width,
|
||||
filterText = UI.Text {
|
||||
x = 2, width = 6,
|
||||
value = 'Filter',
|
||||
},
|
||||
filter = UI.TextEntry {
|
||||
x = 9, width = 19,
|
||||
limit = 50,
|
||||
},
|
||||
refresh = UI.Button {
|
||||
x = 31, width = 8,
|
||||
text = 'Refresh',
|
||||
event = 'refresh',
|
||||
},
|
||||
},
|
||||
accelerators = {
|
||||
r = 'refresh',
|
||||
q = 'quit',
|
||||
}
|
||||
}
|
||||
|
||||
function listingPage.grid:getRowTextColor(row, selected)
|
||||
if row.is_craftable then
|
||||
return colors.yellow
|
||||
end
|
||||
if row.has_recipe then
|
||||
if selected then
|
||||
return colors.blue
|
||||
end
|
||||
return colors.lightBlue
|
||||
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
|
||||
if row.limit then
|
||||
row.limit = Util.toBytes(row.limit)
|
||||
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 == 'learn' then
|
||||
UI:setPage('learn')
|
||||
|
||||
elseif event.type == 'craft' then
|
||||
UI:setPage('craft')
|
||||
|
||||
elseif event.type == 'forget' then
|
||||
local item = self.grid:getSelected()
|
||||
if item then
|
||||
local key = uniqueKey(item)
|
||||
|
||||
if recipes[key] then
|
||||
recipes[key] = nil
|
||||
Util.writeTable(RECIPES_FILE, recipes)
|
||||
end
|
||||
|
||||
if resources[key] then
|
||||
resources[key] = nil
|
||||
Util.writeTable(RESOURCE_FILE, resources)
|
||||
end
|
||||
|
||||
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 = chestAdapter:listItems()
|
||||
mergeResources(self.allItems)
|
||||
self:applyFilter()
|
||||
end
|
||||
|
||||
function listingPage:applyFilter()
|
||||
local t = filterItems(self.allItems, self.filter)
|
||||
self.grid:setValues(t)
|
||||
end
|
||||
|
||||
-- without duck antenna
|
||||
local function getTurtleInventoryOld()
|
||||
local inventory = { }
|
||||
for i = 1,16 do
|
||||
if turtle.getItemCount(i) > 0 then
|
||||
turtle.select(i)
|
||||
local item = turtle.getItemDetail()
|
||||
inventory[i] = {
|
||||
name = item.name,
|
||||
damage = item.damage,
|
||||
count = item.count,
|
||||
}
|
||||
end
|
||||
end
|
||||
return inventory
|
||||
end
|
||||
|
||||
local function getTurtleInventory()
|
||||
local inventory = { }
|
||||
for i = 1,16 do
|
||||
local qty = turtle.getItemCount(i)
|
||||
if qty > 0 then
|
||||
turtleChestAdapter:insert(i, qty)
|
||||
local items = turtleChestAdapter:listItems()
|
||||
_, inventory[i] = next(items)
|
||||
turtleChestAdapter:extract(1, qty, i)
|
||||
end
|
||||
end
|
||||
return inventory
|
||||
end
|
||||
|
||||
local function filter(t, filter)
|
||||
local keys = Util.keys(t)
|
||||
for _,key in pairs(keys) do
|
||||
if not Util.key(filter, key) then
|
||||
t[key] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function learnRecipe(page)
|
||||
local recipe = { }
|
||||
local ingredients = getTurtleInventory()
|
||||
if ingredients then
|
||||
turtle.select(1)
|
||||
if device.workbench and turtle.craft() then
|
||||
recipe = getTurtleInventory()
|
||||
if recipe and recipe[1] then
|
||||
clearGrid()
|
||||
|
||||
local key = uniqueKey(recipe[1])
|
||||
local newRecipe = {
|
||||
count = recipe[1].count,
|
||||
ingredients = ingredients,
|
||||
}
|
||||
if recipe[1].maxCount ~= 64 then
|
||||
newRecipe.maxCount = recipe[1].maxCount
|
||||
end
|
||||
|
||||
for k,ingredient in pairs(ingredients) do
|
||||
ingredients[k] = uniqueKey(ingredient)
|
||||
end
|
||||
|
||||
recipes[key] = newRecipe
|
||||
|
||||
Util.writeTable(RECIPES_FILE, recipes)
|
||||
|
||||
local displayName = getName(recipe[1])
|
||||
|
||||
listingPage.statusBar.filter:setValue(displayName)
|
||||
listingPage.statusBar:timedStatus('Learned: ' .. displayName, 3)
|
||||
listingPage.filter = displayName
|
||||
listingPage:refresh()
|
||||
listingPage.grid:draw()
|
||||
|
||||
return true
|
||||
end
|
||||
else
|
||||
page.statusBar:timedStatus('Failed to craft', 3)
|
||||
end
|
||||
else
|
||||
page.statusBar:timedStatus('No recipe defined', 3)
|
||||
end
|
||||
end
|
||||
|
||||
local learnPage = UI.Dialog {
|
||||
height = 7, width = UI.term.width - 6,
|
||||
backgroundColor = colors.lightGray,
|
||||
title = 'Learn Recipe',
|
||||
idField = UI.Text {
|
||||
x = 5,
|
||||
y = 3,
|
||||
width = UI.term.width - 10,
|
||||
value = 'Place recipe in turtle'
|
||||
},
|
||||
accept = UI.Button {
|
||||
rx = -13, ry = -2,
|
||||
text = 'Ok', event = 'accept',
|
||||
},
|
||||
cancel = UI.Button {
|
||||
rx = -8, ry = -2,
|
||||
text = 'Cancel', event = 'cancel'
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
status = 'Crafting paused'
|
||||
}
|
||||
}
|
||||
|
||||
function learnPage:enable()
|
||||
craftingPaused = true
|
||||
self:focusFirst()
|
||||
UI.Dialog.enable(self)
|
||||
end
|
||||
|
||||
function learnPage:disable()
|
||||
craftingPaused = false
|
||||
UI.Dialog.disable(self)
|
||||
end
|
||||
|
||||
function learnPage:eventHandler(event)
|
||||
if event.type == 'cancel' then
|
||||
UI:setPreviousPage()
|
||||
elseif event.type == 'accept' then
|
||||
if learnRecipe(self) then
|
||||
UI:setPreviousPage()
|
||||
end
|
||||
else
|
||||
return UI.Dialog.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local craftPage = UI.Dialog {
|
||||
height = 6, width = UI.term.width - 10,
|
||||
backgroundColor = colors.lightGray,
|
||||
title = 'Enter amount to craft',
|
||||
idField = UI.TextEntry {
|
||||
x = 15,
|
||||
y = 3,
|
||||
width = 10,
|
||||
limit = 6,
|
||||
value = '1',
|
||||
backgroundColor = colors.black,
|
||||
backgroundFocusColor = colors.black,
|
||||
},
|
||||
accept = UI.Button {
|
||||
rx = -7, ry = -1,
|
||||
backgroundColor = colors.green,
|
||||
text = '+', event = 'accept',
|
||||
},
|
||||
cancel = UI.Button {
|
||||
rx = -3, ry = -1,
|
||||
backgroundColor = colors.red,
|
||||
backgroundFocusColor = colors.red,
|
||||
text = '\215', event = 'cancel'
|
||||
},
|
||||
}
|
||||
|
||||
function craftPage:draw()
|
||||
UI.Dialog.draw(self)
|
||||
self:write(6, 3, 'Quantity')
|
||||
end
|
||||
|
||||
function craftPage:enable()
|
||||
craftingPaused = true
|
||||
self:focusFirst()
|
||||
UI.Dialog.enable(self)
|
||||
end
|
||||
|
||||
function craftPage:disable()
|
||||
craftingPaused = false
|
||||
UI.Dialog.disable(self)
|
||||
end
|
||||
|
||||
function craftPage:eventHandler(event)
|
||||
if event.type == 'cancel' then
|
||||
UI:setPreviousPage()
|
||||
elseif event.type == 'accept' then
|
||||
|
||||
else
|
||||
return UI.Dialog.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
UI:setPages({
|
||||
listing = listingPage,
|
||||
item = itemPage,
|
||||
learn = learnPage,
|
||||
craft = craftPage,
|
||||
})
|
||||
|
||||
UI:setPage(listingPage)
|
||||
listingPage:setFocus(listingPage.statusBar.filter)
|
||||
|
||||
clearGrid()
|
||||
jobMonitor()
|
||||
jobListGrid:draw()
|
||||
jobListGrid:sync()
|
||||
|
||||
Event.onInterval(5, function()
|
||||
|
||||
if not craftingPaused then
|
||||
local items = chestAdapter:listItems()
|
||||
if 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 craftList = watchResources(items)
|
||||
jobListGrid:setValues(craftList)
|
||||
--jobListGrid:draw()
|
||||
--jobListGrid:sync()
|
||||
craftItems(craftList, items)
|
||||
jobListGrid:update()
|
||||
jobListGrid:draw()
|
||||
jobListGrid:sync()
|
||||
craftList = getAutocraftItems(items) -- autocrafted items don't show on job monitor
|
||||
craftItems(craftList, items)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
UI:pullEvents()
|
||||
jobListGrid.parent:reset()
|
@@ -1,101 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Event = require('event')
|
||||
local Message = require('message')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Log Monitor')
|
||||
|
||||
if not device.wireless_modem then
|
||||
error('Wireless modem is required')
|
||||
end
|
||||
device.wireless_modem.open(59998)
|
||||
|
||||
local ids = { }
|
||||
local messages = { }
|
||||
local terminal = UI.term
|
||||
|
||||
if device.openperipheral_bridge then
|
||||
|
||||
UI.Glasses = require('glasses')
|
||||
|
||||
terminal = UI.Glasses({
|
||||
x = 4,
|
||||
y = 175,
|
||||
height = 40,
|
||||
width = 64,
|
||||
textScale = .5,
|
||||
backgroundOpacity = .65,
|
||||
|
||||
})
|
||||
elseif device.monitor then
|
||||
terminal = UI.Device({
|
||||
deviceType = 'monitor',
|
||||
textScale = .5
|
||||
})
|
||||
end
|
||||
|
||||
terminal:clear()
|
||||
|
||||
function getClient(id)
|
||||
if not ids[id] then
|
||||
ids[id] = {
|
||||
titleBar = UI.TitleBar({ title = 'ID: ' .. id, parent = terminal }),
|
||||
scrollingText = UI.ScrollingText({ parent = terminal })
|
||||
}
|
||||
local clientCount = Util.size(ids)
|
||||
local clientHeight = math.floor((terminal.height - clientCount) / clientCount)
|
||||
terminal:clear()
|
||||
local y = 1
|
||||
for k,v in pairs(ids) do
|
||||
v.titleBar.y = y
|
||||
y = y + 1
|
||||
v.scrollingText.height = clientHeight
|
||||
v.scrollingText.y = y
|
||||
y = y + clientHeight
|
||||
v.scrollingText:clear()
|
||||
|
||||
v.titleBar:draw()
|
||||
v.scrollingText:draw()
|
||||
end
|
||||
end
|
||||
return ids[id]
|
||||
end
|
||||
|
||||
Event.on('logMessage', function()
|
||||
local t = { }
|
||||
while #messages > 0 do
|
||||
local msg = messages[1]
|
||||
table.remove(messages, 1)
|
||||
local client = getClient(msg.id)
|
||||
client.scrollingText:appendLine(string.format('%d %s', math.floor(os.clock()), msg.text))
|
||||
t[msg.id] = client
|
||||
end
|
||||
for _,client in pairs(t) do
|
||||
client.scrollingText:draw()
|
||||
end
|
||||
terminal:sync()
|
||||
end)
|
||||
|
||||
Message.addHandler('log', function(h, id, msg)
|
||||
table.insert(messages, { id = id, text = msg.contents })
|
||||
os.queueEvent('logMessage')
|
||||
end)
|
||||
|
||||
Event.on('monitor_touch', function()
|
||||
terminal:reset()
|
||||
ids = { }
|
||||
end)
|
||||
|
||||
Event.on('mouse_click', function()
|
||||
terminal:reset()
|
||||
ids = { }
|
||||
end)
|
||||
|
||||
Event.on('char', function()
|
||||
Event.exitPullEvents()
|
||||
end)
|
||||
|
||||
Event.pullEvents(logWriter)
|
||||
terminal:reset()
|
@@ -1,25 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Terminal = require('terminal')
|
||||
|
||||
local args = { ... }
|
||||
local mon = device[table.remove(args, 1) or 'monitor']
|
||||
if not mon then
|
||||
error('mirror: Invalid device')
|
||||
end
|
||||
|
||||
mon.clear()
|
||||
mon.setTextScale(.5)
|
||||
mon.setCursorPos(1, 1)
|
||||
|
||||
local oterm = Terminal.copy(term.current())
|
||||
Terminal.mirror(term.current(), mon)
|
||||
|
||||
term.current().getSize = mon.getSize
|
||||
|
||||
if #args > 0 then
|
||||
shell.run(unpack(args))
|
||||
Terminal.copy(oterm, term.current())
|
||||
|
||||
mon.setCursorBlink(false)
|
||||
end
|
@@ -1,86 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Event = require('event')
|
||||
local Logger = require('logger')
|
||||
local Socket = require('socket')
|
||||
local Terminal = require('terminal')
|
||||
local Util = require('util')
|
||||
|
||||
Logger.setScreenLogging()
|
||||
|
||||
local remoteId
|
||||
local args = { ... }
|
||||
if #args == 1 then
|
||||
remoteId = tonumber(args[1])
|
||||
else
|
||||
print('Enter host ID')
|
||||
remoteId = tonumber(read())
|
||||
end
|
||||
|
||||
if not remoteId then
|
||||
error('Syntax: mirrorClient <host ID>')
|
||||
end
|
||||
|
||||
local function wrapTerm(socket)
|
||||
local methods = { 'blit', 'clear', 'clearLine', 'setCursorPos', 'write',
|
||||
'setTextColor', 'setTextColour', 'setBackgroundColor',
|
||||
'setBackgroundColour', 'scroll', 'setCursorBlink', }
|
||||
|
||||
socket.term = multishell.term
|
||||
socket.oldTerm = Util.shallowCopy(socket.term)
|
||||
|
||||
for _,k in pairs(methods) do
|
||||
socket.term[k] = function(...)
|
||||
if not socket.queue then
|
||||
socket.queue = { }
|
||||
Event.onTimeout(0, function()
|
||||
if socket.queue then
|
||||
socket:write(socket.queue)
|
||||
socket.queue = nil
|
||||
end
|
||||
end)
|
||||
end
|
||||
table.insert(socket.queue, {
|
||||
f = k,
|
||||
args = { ... },
|
||||
})
|
||||
socket.oldTerm[k](...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
while true do
|
||||
print('connecting...')
|
||||
local socket
|
||||
|
||||
while true do
|
||||
socket = Socket.connect(remoteId, 5901)
|
||||
if socket then
|
||||
break
|
||||
end
|
||||
os.sleep(3)
|
||||
end
|
||||
|
||||
print('connected')
|
||||
|
||||
wrapTerm(socket)
|
||||
|
||||
os.queueEvent('term_resize')
|
||||
|
||||
while true do
|
||||
local e = Event.pullEvent()
|
||||
if e[1] == 'terminate' then
|
||||
break
|
||||
end
|
||||
if not socket.connected then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
for k,v in pairs(socket.oldTerm) do
|
||||
socket.term[k] = v
|
||||
end
|
||||
|
||||
socket:close()
|
||||
|
||||
end
|
@@ -1,53 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Event = require('event')
|
||||
local Logger = require('logger')
|
||||
local Socket = require('socket')
|
||||
|
||||
Logger.setScreenLogging()
|
||||
|
||||
local args = { ... }
|
||||
local mon = device[args[1] or 'monitor']
|
||||
|
||||
if not mon then
|
||||
error('Monitor not attached')
|
||||
end
|
||||
|
||||
mon.setBackgroundColor(colors.black)
|
||||
mon.clear()
|
||||
|
||||
while true do
|
||||
local socket = Socket.server(5901)
|
||||
|
||||
print('mirror: connection from ' .. socket.dhost)
|
||||
|
||||
Event.addRoutine(function()
|
||||
while true do
|
||||
local data = socket:read()
|
||||
if not data then
|
||||
break
|
||||
end
|
||||
for _,v in ipairs(data) do
|
||||
mon[v.f](unpack(v.args))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- ensure socket is connected
|
||||
Event.onInterval(3, function(h)
|
||||
if not socket:ping() then
|
||||
Event.off(h)
|
||||
end
|
||||
end)
|
||||
|
||||
while true do
|
||||
Event.pullEvent()
|
||||
if not socket.connected then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
print('connection lost')
|
||||
|
||||
socket:close()
|
||||
end
|
@@ -1,335 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Event = require('event')
|
||||
local GPS = require('gps')
|
||||
local Logger = require('logger')
|
||||
local MEProvider = require('meProvider')
|
||||
local Point = require('point')
|
||||
local Socket = require('socket')
|
||||
local Util = require('util')
|
||||
|
||||
if not device.wireless_modem then
|
||||
error('Modem is required')
|
||||
end
|
||||
|
||||
Logger.setWirelessLogging()
|
||||
|
||||
if not turtle then
|
||||
error('Can only be run on a turtle')
|
||||
end
|
||||
|
||||
local blocks = { }
|
||||
local meProvider = MEProvider()
|
||||
local items = { }
|
||||
|
||||
local pickups = Util.readTable('pickup.tbl') or { }
|
||||
local cells = Util.readTable('cells.tbl') or { }
|
||||
local refills = Util.readTable('refills.tbl') or { }
|
||||
local fluids = Util.readTable('fluids.tbl') or { }
|
||||
local chestPt = turtle.loadLocation('chest')
|
||||
local chargePt = turtle.loadLocation('charge')
|
||||
|
||||
local fuel = {
|
||||
item = {
|
||||
id = 'minecraft:coal',
|
||||
dmg = 0,
|
||||
},
|
||||
qty = 64
|
||||
}
|
||||
|
||||
local slots
|
||||
|
||||
turtle.setMoveCallback(function(action, pt)
|
||||
if slots then
|
||||
for _,slot in pairs(slots) do
|
||||
if turtle.getItemCount(slot.index) ~= slot.qty then
|
||||
printError('Slots changed')
|
||||
Event.exitPullEvents()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function refuel()
|
||||
if turtle.getFuelLevel() < 5000 then
|
||||
print('refueling')
|
||||
turtle.status = 'refueling'
|
||||
gotoPoint(chestPt, true)
|
||||
dropOff(chestPt)
|
||||
while turtle.getFuelLevel() < 5000 do
|
||||
turtle.select(1)
|
||||
meProvider:provide(fuel.item, fuel.qty, 1)
|
||||
turtle.refuel(64)
|
||||
print(turtle.getFuelLevel())
|
||||
os.sleep(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function pickUp(pt)
|
||||
turtle.status = 'picking up'
|
||||
gotoPoint(pt, true)
|
||||
while true do
|
||||
if not turtle.selectOpenSlot() then
|
||||
dropOff(chestPt)
|
||||
gotoPoint(pt, true)
|
||||
end
|
||||
turtle.select(1)
|
||||
if not turtle.suckDown(64) then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function dropOff(pt)
|
||||
if turtle.selectSlotWithItems() then
|
||||
gotoPoint(pt, true)
|
||||
turtle.emptyInventory(turtle.dropDown)
|
||||
if pt == chestPt then
|
||||
print('refreshing items')
|
||||
items = meProvider:refresh()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function gotoPoint(pt, doDetect)
|
||||
slots = turtle.getInventory()
|
||||
while not turtle.pathfind(pt, blocks) do
|
||||
if turtle.abort then
|
||||
error('aborted')
|
||||
end
|
||||
turtle.status = 'blocked'
|
||||
os.sleep(5)
|
||||
end
|
||||
|
||||
if doDetect and not turtle.detectDown() then
|
||||
printError('Missing target')
|
||||
Event.exitPullEvents()
|
||||
end
|
||||
end
|
||||
|
||||
function checkCell(pt)
|
||||
if not turtle.selectOpenSlot() then
|
||||
dropOff(chestPt)
|
||||
end
|
||||
|
||||
print('checking cell')
|
||||
turtle.status = 'recharging'
|
||||
gotoPoint(pt, true)
|
||||
local c = peripheral.wrap('bottom')
|
||||
local energy = c.getMaxEnergyStored() -
|
||||
c.getEnergyStored()
|
||||
if energy > 20000 then
|
||||
print('charging cell')
|
||||
turtle.selectOpenSlot()
|
||||
turtle.digDown()
|
||||
gotoPoint(chargePt, true)
|
||||
turtle.dropDown()
|
||||
os.sleep(energy / 20000)
|
||||
turtle.suckDown()
|
||||
print('replacing cell')
|
||||
gotoPoint(pt)
|
||||
if not turtle.placeDown() then
|
||||
error('could not place down cell')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function fluid(points)
|
||||
print('checking fluid')
|
||||
turtle.status = 'fluiding'
|
||||
gotoPoint(points.source, true)
|
||||
turtle.select(1)
|
||||
turtle.digDown()
|
||||
gotoPoint(points.target)
|
||||
if not turtle.placeDown() then
|
||||
error('could not place fluid container')
|
||||
end
|
||||
os.sleep(5)
|
||||
turtle.digDown()
|
||||
gotoPoint(points.source)
|
||||
turtle.placeDown()
|
||||
end
|
||||
|
||||
function refill(entry)
|
||||
dropOff(chestPt)
|
||||
|
||||
turtle.status = 'refilling'
|
||||
gotoPoint(chestPt)
|
||||
for _,item in pairs(entry.items) do
|
||||
meProvider:provide(item, tonumber(item.qty), turtle.selectOpenSlot())
|
||||
end
|
||||
|
||||
if turtle.selectSlotWithItems() then
|
||||
if entry.point then
|
||||
dropOff(entry.point)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function oldRefill(points)
|
||||
gotoPoint(points.source)
|
||||
repeat until not turtle.suckDown(64)
|
||||
if points.target then
|
||||
dropOff(points.target)
|
||||
end
|
||||
if points.targets then
|
||||
for k,target in pairs(points.targets) do
|
||||
dropOff(target)
|
||||
end
|
||||
end
|
||||
dropOff(points.source)
|
||||
dropOff(chestPt)
|
||||
end
|
||||
|
||||
local function makeKey(pt)
|
||||
return string.format('%d:%d:%d', pt.x, pt.y, pt.z)
|
||||
end
|
||||
|
||||
local function pickupHost(socket)
|
||||
|
||||
while true do
|
||||
local data = socket:read()
|
||||
if not data then
|
||||
print('pickup: closing connection to ' .. socket.dhost)
|
||||
return
|
||||
end
|
||||
|
||||
print('command: ' .. data.type)
|
||||
|
||||
if data.type == 'pickup' then
|
||||
local key = makeKey(data.point)
|
||||
pickups[key] = data.point
|
||||
Util.writeTable('pickup.tbl', pickups)
|
||||
socket:write( { type = "response", response = 'added' })
|
||||
|
||||
elseif data.type == 'items' then
|
||||
socket:write( { type = "response", response = items })
|
||||
|
||||
elseif data.type == 'refill' then
|
||||
local key = makeKey(data.entry.point)
|
||||
refills[key] = data.entry
|
||||
Util.writeTable('refills.tbl', refills)
|
||||
socket:write( { type = "response", response = 'added' })
|
||||
|
||||
elseif data.type == 'setPickup' then
|
||||
chestPt = data.point
|
||||
turtle.storeLocation('chest', chestPt)
|
||||
socket:write( { type = "response", response = 'Location set' })
|
||||
|
||||
elseif data.type == 'setRecharge' then
|
||||
chargePt = data.point
|
||||
turtle.storeLocation('charge', chargePt)
|
||||
socket:write( { type = "response", response = 'Location set' })
|
||||
|
||||
elseif data.type == 'charge' then
|
||||
local key = makeKey(data.point)
|
||||
cells[key] = data.point
|
||||
Util.writeTable('cells.tbl', cells)
|
||||
socket:write( { type = "response", response = 'added' })
|
||||
|
||||
elseif data.type == 'fluid' then
|
||||
|
||||
elseif data.type == 'clear' then
|
||||
local key = makeKey(data.point)
|
||||
refills[key] = nil
|
||||
cells[key] = nil
|
||||
fluids[key] = nil
|
||||
pickups[key] = nil
|
||||
|
||||
Util.writeTable('refills.tbl', refills)
|
||||
Util.writeTable('cells.tbl', cells)
|
||||
Util.writeTable('fluids.tbl', fluids)
|
||||
Util.writeTable('pickup.tbl', pickups)
|
||||
|
||||
socket:write( { type = "response", response = 'cleared' })
|
||||
else
|
||||
print('unknown command')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.addRoutine(function()
|
||||
while true do
|
||||
print('waiting for connection on port 5222')
|
||||
local socket = Socket.server(5222)
|
||||
|
||||
print('pickup: connection from ' .. socket.dhost)
|
||||
|
||||
Event.addRoutine(function() pickupHost(socket) end)
|
||||
end
|
||||
end)
|
||||
|
||||
local function eachEntry(t, fn)
|
||||
|
||||
local keys = Util.keys(t)
|
||||
for _,key in pairs(keys) do
|
||||
if t[key] then
|
||||
if turtle.abort then
|
||||
return
|
||||
end
|
||||
fn(t[key])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function eachClosestEntry(t, fn)
|
||||
|
||||
local points = { }
|
||||
for k,v in pairs(t) do
|
||||
v = Util.shallowCopy(v)
|
||||
v.key = k
|
||||
table.insert(points, v)
|
||||
end
|
||||
|
||||
while not Util.empty(points) do
|
||||
local closest = Point.closest(turtle.point, points)
|
||||
if turtle.abort then
|
||||
return
|
||||
end
|
||||
if t[closest.key] then
|
||||
fn(closest)
|
||||
end
|
||||
for k,v in pairs(points) do
|
||||
if v.key == closest.key then
|
||||
table.remove(points, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.addRoutine(function()
|
||||
|
||||
while true do
|
||||
if chestPt then
|
||||
eachClosestEntry(pickups, pickUp)
|
||||
eachEntry(refills, refill)
|
||||
refuel()
|
||||
end
|
||||
eachEntry(fluids, fluid)
|
||||
if chargePt then
|
||||
eachEntry(cells, checkCell)
|
||||
end
|
||||
print('sleeping')
|
||||
turtle.status = 'sleeping'
|
||||
if turtle.abort then
|
||||
printError('aborted')
|
||||
break
|
||||
end
|
||||
os.sleep(60)
|
||||
end
|
||||
|
||||
Event.exitPullEvents()
|
||||
end)
|
||||
|
||||
turtle.run(function()
|
||||
|
||||
if not turtle.enableGPS() then
|
||||
error('turtle: No GPS found')
|
||||
end
|
||||
|
||||
refuel()
|
||||
Event.pullEvents()
|
||||
|
||||
end)
|
@@ -1,231 +0,0 @@
|
||||
if not device.wireless_modem then
|
||||
error('Wireless modem is required')
|
||||
end
|
||||
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Event = require('event')
|
||||
local GPS = require('gps')
|
||||
local Socket = require('socket')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Pickup Remote')
|
||||
|
||||
local id
|
||||
|
||||
local mainPage = UI.Page({
|
||||
menu = UI.Menu({
|
||||
centered = true,
|
||||
y = 2,
|
||||
menuItems = {
|
||||
{ prompt = 'Pickup', event = 'pickup', help = 'Pickup items from this location' },
|
||||
{ prompt = 'Charge cell', event = 'charge', help = 'Recharge this cell' },
|
||||
{ prompt = 'Refill', event = 'refill', help = 'Recharge this cell' },
|
||||
{ prompt = 'Set pickup location', event = 'setPickup', help = 'Recharge this cell' },
|
||||
{ prompt = 'Set recharge location', event = 'setRecharge', help = 'Recharge this cell' },
|
||||
{ prompt = 'Clear', event = 'clear', help = 'Remove this location' },
|
||||
},
|
||||
}),
|
||||
statusBar = UI.StatusBar(),
|
||||
accelerators = {
|
||||
q = 'quit',
|
||||
},
|
||||
})
|
||||
|
||||
local refillPage = UI.Page({
|
||||
menuBar = UI.MenuBar({
|
||||
y = 1,
|
||||
buttons = {
|
||||
{ text = 'Done', event = 'done', help = 'Pickup items from this location' },
|
||||
{ text = 'Back', event = 'back', help = 'Recharge this cell' },
|
||||
},
|
||||
}),
|
||||
grid1 = UI.ScrollingGrid({
|
||||
columns = {
|
||||
{ heading = 'Name', key = 'name', width = UI.term.width-9 },
|
||||
{ heading = 'Qty', key = 'fQty', width = 5 },
|
||||
},
|
||||
sortColumn = 'name',
|
||||
height = 8,
|
||||
y = 3,
|
||||
}),
|
||||
grid2 = UI.ScrollingGrid({
|
||||
columns = {
|
||||
{ heading = 'Name', key = 'name', width = UI.term.width-9 },
|
||||
{ heading = 'Qty', key = 'qty', width = 5 },
|
||||
},
|
||||
sortColumn = 'name',
|
||||
height = 4,
|
||||
y = 12,
|
||||
}),
|
||||
statusBar = UI.StatusBar(),
|
||||
accelerators = {
|
||||
q = 'quit',
|
||||
},
|
||||
})
|
||||
|
||||
refillPage.menuBar:add({
|
||||
filter = UI.TextEntry({
|
||||
x = UI.term.width-10,
|
||||
width = 10,
|
||||
})
|
||||
})
|
||||
|
||||
local function sendCommand(cmd)
|
||||
local socket = Socket.connect(id, 5222)
|
||||
if not socket then
|
||||
mainPage.statusBar:timedStatus('Unable to connect', 3)
|
||||
return
|
||||
end
|
||||
|
||||
socket:write(cmd)
|
||||
local m = socket:read(3)
|
||||
socket:close()
|
||||
if m then
|
||||
return m.response
|
||||
end
|
||||
mainPage.statusBar:timedStatus('No response', 3)
|
||||
end
|
||||
|
||||
local function getPoint()
|
||||
local gpt = GPS.getPoint()
|
||||
if not gpt then
|
||||
mainPage.statusBar:timedStatus('Unable to get location', 3)
|
||||
end
|
||||
return gpt
|
||||
end
|
||||
|
||||
function refillPage:eventHandler(event)
|
||||
|
||||
if event.type == 'grid_select' then
|
||||
|
||||
local item = {
|
||||
name = event.selected.name,
|
||||
id = event.selected.id,
|
||||
dmg = event.selected.dmg,
|
||||
qty = 0,
|
||||
}
|
||||
|
||||
local dialog = UI.Dialog({
|
||||
x = 1,
|
||||
width = UI.term.width,
|
||||
text = UI.Text({ x = 3, y = 3, value = 'Quantity' }),
|
||||
textEntry = UI.TextEntry({ x = 14, y = 3 })
|
||||
})
|
||||
|
||||
dialog.eventHandler = function(self, event)
|
||||
if event.type == 'accept' then
|
||||
local l = tonumber(self.textEntry.value)
|
||||
if l and l <= 1024 and l > 0 then
|
||||
item.qty = self.textEntry.value
|
||||
table.insert(refillPage.grid2.values, item)
|
||||
refillPage.grid2:update()
|
||||
UI:setPreviousPage()
|
||||
else
|
||||
self.statusBar:timedStatus('Invalid Quantity', 3)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return UI.Dialog.eventHandler(self, event)
|
||||
end
|
||||
|
||||
dialog.titleBar.title = item.name
|
||||
dialog:setFocus(dialog.textEntry)
|
||||
UI:setPage(dialog)
|
||||
|
||||
elseif event.type == 'text_change' then
|
||||
local text = event.text
|
||||
if #text == 0 then
|
||||
self.grid1.values = self.allItems
|
||||
else
|
||||
self.grid1.values = { }
|
||||
for _,item in pairs(self.allItems) do
|
||||
if string.find(item.lname, text) then
|
||||
table.insert(self.grid1.values, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
--self.grid:adjustWidth()
|
||||
self.grid1:update()
|
||||
self.grid1:setIndex(1)
|
||||
self.grid1:draw()
|
||||
|
||||
elseif event.type == 'back' then
|
||||
UI:setPreviousPage()
|
||||
|
||||
elseif event.type == 'done' then
|
||||
UI:setPage(mainPage)
|
||||
local pt = getPoint()
|
||||
if pt then
|
||||
local response = sendCommand({ type = 'refill', entry = { point = pt, items = self.grid2.values } })
|
||||
if response then
|
||||
mainPage.statusBar:timedStatus(response, 3)
|
||||
end
|
||||
end
|
||||
|
||||
elseif event.type == 'grid_focus_row' then
|
||||
self.statusBar:setStatus(event.selected.id .. ':' .. event.selected.dmg)
|
||||
self.statusBar:draw()
|
||||
end
|
||||
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
|
||||
function refillPage:enable()
|
||||
for _,item in pairs(self.allItems) do
|
||||
item.lname = string.lower(item.name)
|
||||
item.fQty = Util.toBytes(item.qty)
|
||||
end
|
||||
|
||||
self.grid1:setValues(self.allItems)
|
||||
|
||||
self.menuBar.filter.value = ''
|
||||
self.menuBar.filter.pos = 1
|
||||
self:setFocus(self.menuBar.filter)
|
||||
UI.Page.enable(self)
|
||||
end
|
||||
|
||||
function mainPage:eventHandler(event)
|
||||
|
||||
if event.type == 'quit' then
|
||||
Event.exitPullEvents()
|
||||
|
||||
elseif event.type == 'refill' then
|
||||
local response = sendCommand({ type = 'items' })
|
||||
if response then
|
||||
refillPage.allItems = response
|
||||
refillPage.grid2:setValues({ })
|
||||
UI:setPage(refillPage)
|
||||
end
|
||||
|
||||
elseif event.type == 'pickup' or event.type == 'setPickup' or
|
||||
event.type == 'setRecharge' or event.type == 'charge' or
|
||||
event.type == 'clear' then
|
||||
local pt = getPoint()
|
||||
if pt then
|
||||
local response = sendCommand({ type = event.type, point = pt })
|
||||
if response then
|
||||
self.statusBar:timedStatus(response, 3)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
|
||||
local args = { ... }
|
||||
if #args == 1 then
|
||||
id = tonumber(args[1])
|
||||
end
|
||||
|
||||
if not id then
|
||||
error('Syntax: pickupRemote <turtle ID>')
|
||||
end
|
||||
|
||||
UI:setPage(mainPage)
|
||||
|
||||
Event.pullEvents()
|
||||
UI.term:reset()
|
@@ -1,538 +0,0 @@
|
||||
-- +---------------------+------------+---------------------+
|
||||
-- | | | |
|
||||
-- | | RecGif | |
|
||||
-- | | | |
|
||||
-- +---------------------+------------+---------------------+
|
||||
|
||||
local version = "Version 1.1.6"
|
||||
|
||||
-- Records your terminal and saves the result as an animating GIF.
|
||||
-- http://www.computercraft.info/forums2/index.php?/topic/24840-recgif/
|
||||
|
||||
-- ----------------------------------------------------------
|
||||
|
||||
-- Original code by Bomb Bloke
|
||||
-- Modified to integrate with opus os
|
||||
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Util = require('util')
|
||||
|
||||
local recTerm, oldTerm, arg, showInput, skipLast, lastDelay, curInput = {}, Util.shallowCopy(multishell.term), {...}, false, false, 2, ""
|
||||
local curBlink, oldBlink, tTerm, buffer, colourNum, xPos, yPos, oldXPos, oldYPos, tCol, bCol, xSize, ySize = false, false, {}, {}, {}, 1, 1, 1, 1, colours.white, colours.black, oldTerm.getSize()
|
||||
local greys, buttons = {["0"] = true, ["7"] = true, ["8"] = true, ["f"] = true}, {"l", "r", "m"}
|
||||
local charW, charH, chars, resp
|
||||
local filename
|
||||
|
||||
local calls = { }
|
||||
local curCalls = { delay = 0 }
|
||||
local callListCount = 0
|
||||
local callCount = 0
|
||||
|
||||
local function showSyntax()
|
||||
print('Gif Recorder by Bomb Bloke\n')
|
||||
print('Syntax: recGif [-i] [-s] [-ld:<delay>] filename')
|
||||
print(' -i : show input')
|
||||
print(' -s : skip last')
|
||||
print(' -ld : last delay')
|
||||
end
|
||||
|
||||
for i = #arg, 1, -1 do
|
||||
local curArg = arg[i]:lower()
|
||||
|
||||
if curArg == "-i" then
|
||||
showInput, ySize = true, ySize + 1
|
||||
table.remove(arg, i)
|
||||
elseif curArg == "-s" then
|
||||
skipLast = true
|
||||
table.remove(arg, i)
|
||||
elseif curArg:sub(1, 4) == "-ld:" then
|
||||
curArg = tonumber(curArg:sub(5))
|
||||
if curArg then lastDelay = curArg end
|
||||
table.remove(arg, i)
|
||||
elseif curArg == '-?' then
|
||||
showSyntax()
|
||||
return
|
||||
elseif i ~= #arg then
|
||||
showSyntax()
|
||||
printError('\nInvalid argument')
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
print('Press control-p to stop recording')
|
||||
|
||||
local filename = arg[#arg]
|
||||
if not filename then
|
||||
print('Enter file name:')
|
||||
filename = read()
|
||||
end
|
||||
|
||||
if #filename == 0 then
|
||||
showSyntax()
|
||||
print()
|
||||
error('Invalid file name')
|
||||
end
|
||||
|
||||
print('Initializing...')
|
||||
|
||||
-- don't pollute global env
|
||||
-- convert these to require style apis
|
||||
local function loadAPI(url, env)
|
||||
local apiEnv = Util.shallowCopy(env)
|
||||
apiEnv.shell = nil
|
||||
apiEnv.multishell = nil
|
||||
setmetatable(apiEnv, { __index = _G })
|
||||
local fn = Util.loadUrl(url, apiEnv)
|
||||
fn()
|
||||
return apiEnv
|
||||
end
|
||||
|
||||
bbpack = loadAPI('http://pastebin.com/raw/PdrJjb5S', getfenv(1))
|
||||
GIF = loadAPI('http://pastebin.com/raw/5uk9uRjC', getfenv(1))
|
||||
|
||||
Util.runUrl(getfenv(1), 'http://pastebin.com/raw/cUYTGbpb', 'get', 'Y0eLUPtr')
|
||||
|
||||
local function snooze()
|
||||
local myEvent = tostring({})
|
||||
os.queueEvent(myEvent)
|
||||
os.pullEvent(myEvent)
|
||||
end
|
||||
|
||||
local function safeString(text)
|
||||
local newText = {}
|
||||
|
||||
for i = 1, #text do
|
||||
local val = text:byte(i)
|
||||
newText[i] = (val > 31 and val < 127) and val or 63
|
||||
end
|
||||
|
||||
return string.char(unpack(newText))
|
||||
end
|
||||
|
||||
local function safeCol(text, subst)
|
||||
local newText = {}
|
||||
|
||||
for i = 1, #text do
|
||||
local val = text:sub(i, i)
|
||||
newText[i] = greys[val] and val or subst
|
||||
end
|
||||
|
||||
return table.concat(newText)
|
||||
end
|
||||
|
||||
-- Build a terminal that records stuff:
|
||||
|
||||
recTerm = multishell.term
|
||||
|
||||
for key, func in pairs(oldTerm) do
|
||||
recTerm[key] = function(...)
|
||||
local result = { func(...) }
|
||||
|
||||
if callCount == 0 then
|
||||
os.queueEvent('capture_frame')
|
||||
end
|
||||
callCount = callCount + 1
|
||||
curCalls[callCount] = { key, ... }
|
||||
return unpack(result)
|
||||
end
|
||||
end
|
||||
|
||||
local tabId = multishell.getCurrent()
|
||||
|
||||
multishell.addHotkey(25, function()
|
||||
os.queueEvent('recorder_stop')
|
||||
end)
|
||||
|
||||
local tabs = multishell.getTabs()
|
||||
for _,tab in pairs(tabs) do
|
||||
if tab.isOverview then
|
||||
multishell.hideTab(tabId)
|
||||
multishell.setFocus(tab.tabId)
|
||||
os.queueEvent('term_resize')
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local curTime = os.clock() - 1
|
||||
|
||||
while true do
|
||||
local event = { os.pullEventRaw() }
|
||||
|
||||
if event[1] == 'recorder_stop' or event[1] == 'terminate' then
|
||||
break
|
||||
end
|
||||
|
||||
if event[1] == 'capture_frame' then
|
||||
local newTime = os.clock()
|
||||
|
||||
if callListCount > 0 then
|
||||
calls[callListCount].delay = (newTime - curTime)
|
||||
end
|
||||
|
||||
curTime = newTime
|
||||
callListCount = callListCount + 1
|
||||
calls[callListCount] = curCalls
|
||||
|
||||
curCalls, callCount = { delay = 0 }, 0
|
||||
end
|
||||
end
|
||||
|
||||
multishell.removeHotkey(25)
|
||||
|
||||
for k,fn in pairs(oldTerm) do
|
||||
multishell.term[k] = fn
|
||||
end
|
||||
|
||||
multishell.unhideTab(tabId)
|
||||
multishell.setFocus(tabId)
|
||||
|
||||
if #calls[#calls] == 0 then calls[#calls] = nil end
|
||||
if skipLast and #calls > 1 then calls[#calls] = nil end
|
||||
|
||||
calls[#calls].delay = lastDelay
|
||||
|
||||
print(string.format("Encoding %d frames...", #calls))
|
||||
--Util.writeTable('tmp/raw.txt', calls)
|
||||
|
||||
-- Perform a quick re-parse of the recorded data (adding frames for when the cursor blinks):
|
||||
|
||||
do
|
||||
local callListCount, tempCalls, blink, oldBlink, curBlink, blinkDelay = 1, {}, false, false, true, 0
|
||||
|
||||
for i = 1, #calls - 1 do
|
||||
curCalls = calls[i]
|
||||
tempCalls[callListCount] = curCalls
|
||||
for j = 1, #curCalls do if curCalls[j][1] == "setCursorBlink" then blink = curCalls[j][2] end end
|
||||
|
||||
if blink then
|
||||
if blinkDelay == 0 then
|
||||
curCalls[#curCalls + 1] = {"toggleCur", curBlink}
|
||||
blinkDelay, curBlink = 0.4, not curBlink
|
||||
end
|
||||
|
||||
while tempCalls[callListCount].delay > blinkDelay do
|
||||
local remainder = tempCalls[callListCount].delay - blinkDelay
|
||||
tempCalls[callListCount].delay = blinkDelay
|
||||
callListCount = callListCount + 1
|
||||
tempCalls[callListCount] = {{"toggleCur", curBlink}, ["delay"] = remainder}
|
||||
blinkDelay, curBlink = 0.4, not curBlink
|
||||
end
|
||||
|
||||
blinkDelay = blinkDelay - tempCalls[callListCount].delay
|
||||
else
|
||||
if oldBlink then curCalls[#curCalls + 1] = {"toggleCur", false} end
|
||||
blinkDelay = (curCalls.delay - blinkDelay) % 0.4
|
||||
end
|
||||
|
||||
callListCount, oldBlink = callListCount + 1, blink
|
||||
end
|
||||
|
||||
tempCalls[callListCount] = calls[#calls]
|
||||
tempCalls[callListCount][#tempCalls[callListCount] + 1] = {"toggleCur", false}
|
||||
|
||||
calls, curCalls = tempCalls, nil
|
||||
end
|
||||
|
||||
snooze()
|
||||
|
||||
-- Load font data:
|
||||
do
|
||||
local ascii, counter = GIF.toPaintutils(GIF.flattenGIF(GIF.loadGIF("ascii.gif"))), 0
|
||||
local newFont, ybump, xbump = #ascii ~= #ascii[1], 0, 0
|
||||
charW, charH, chars = newFont and #ascii[1] / 16 or #ascii[1] * 3 / 64, #ascii / 16, {}
|
||||
|
||||
for yy = 0, newFont and 15 or 7 do
|
||||
for xx = 0, 15 do
|
||||
local newChar, length = {}, 0
|
||||
|
||||
-- Place in 2d grid of bools:
|
||||
for y = 1, charH do
|
||||
local newRow = {}
|
||||
|
||||
for x = 1, charW do
|
||||
local set = ascii[y + ybump][x + xbump] == 1
|
||||
if set and x > length then length = x end
|
||||
newRow[x] = set
|
||||
end
|
||||
|
||||
newChar[y] = newRow
|
||||
end
|
||||
|
||||
-- Center:
|
||||
if not newFont then for y = 1, charH do for x = 1, math.floor((charW - length) / 2) do table.insert(newChar[y], 1, false) end end end
|
||||
|
||||
chars[counter] = newChar
|
||||
counter, xbump = counter + 1, xbump + (newFont and charW or charH)
|
||||
end
|
||||
xbump, ybump = 0, ybump + charH
|
||||
end
|
||||
end
|
||||
|
||||
snooze()
|
||||
|
||||
-- Terminal data translation:
|
||||
|
||||
do
|
||||
local hex, counter = "0123456789abcdef", 1
|
||||
|
||||
for i = 1, 16 do
|
||||
colourNum[counter] = hex:sub(i, i)
|
||||
counter = counter * 2
|
||||
end
|
||||
end
|
||||
|
||||
for y = 1, ySize do
|
||||
buffer[y] = {}
|
||||
for x = 1, xSize do buffer[y][x] = {" ", colourNum[tCol], colourNum[bCol]} end
|
||||
end
|
||||
|
||||
if showInput then for x = 1, xSize do buffer[ySize][x][3] = colourNum[colours.lightGrey] end end
|
||||
|
||||
tTerm.blit = function(text, fgCol, bgCol)
|
||||
if xPos > xSize or xPos + #text - 1 < 1 or yPos < 1 or yPos > ySize then return end
|
||||
|
||||
if not _HOST then text = safeString(text) end
|
||||
|
||||
if not term.isColour() then
|
||||
fgCol = safeCol(fgCol, "0")
|
||||
bgCol = safeCol(bgCol, "f")
|
||||
end
|
||||
|
||||
if xPos < 1 then
|
||||
text = text:sub(2 - xPos)
|
||||
fgCol = fgCol:sub(2 - xPos)
|
||||
bgCol = bgCol:sub(2 - xPos)
|
||||
xPos = 1
|
||||
end
|
||||
|
||||
if xPos + #text - 1 > xSize then
|
||||
text = text:sub(1, xSize - xPos + 1)
|
||||
fgCol = fgCol:sub(1, xSize - xPos + 1)
|
||||
bgCol = bgCol:sub(1, xSize - xPos + 1)
|
||||
end
|
||||
|
||||
for x = 1, #text do
|
||||
buffer[yPos][xPos + x - 1][1] = text:sub(x, x)
|
||||
buffer[yPos][xPos + x - 1][2] = fgCol:sub(x, x)
|
||||
buffer[yPos][xPos + x - 1][3] = bgCol:sub(x, x)
|
||||
end
|
||||
|
||||
xPos = xPos + #text
|
||||
end
|
||||
|
||||
tTerm.write = function(text)
|
||||
text = tostring(text)
|
||||
tTerm.blit(text, string.rep(colourNum[tCol], #text), string.rep(colourNum[bCol], #text))
|
||||
end
|
||||
|
||||
tTerm.clearLine = function()
|
||||
local oldXPos = xPos
|
||||
|
||||
xPos = 1
|
||||
tTerm.write(string.rep(" ", xSize))
|
||||
|
||||
xPos = oldXPos
|
||||
end
|
||||
|
||||
tTerm.clear = function()
|
||||
local oldXPos, oldYPos = xPos, yPos
|
||||
|
||||
for y = 1, ySize do
|
||||
xPos, yPos = 1, y
|
||||
tTerm.write(string.rep(" ", xSize))
|
||||
end
|
||||
|
||||
xPos, yPos = oldXPos, oldYPos
|
||||
end
|
||||
|
||||
tTerm.setCursorPos = function(x, y)
|
||||
xPos, yPos = math.floor(x), math.floor(y)
|
||||
end
|
||||
|
||||
tTerm.setTextColour = function(col)
|
||||
tCol = col
|
||||
end
|
||||
|
||||
tTerm.setTextColor = function(col)
|
||||
tCol = col
|
||||
end
|
||||
|
||||
tTerm.setBackgroundColour = function(col)
|
||||
bCol = col
|
||||
end
|
||||
|
||||
tTerm.setBackgroundColor = function(col)
|
||||
bCol = col
|
||||
end
|
||||
|
||||
tTerm.scroll = function(lines)
|
||||
if math.abs(lines) < ySize then
|
||||
local oldXPos, oldYPos = xPos, yPos
|
||||
|
||||
for y = 1, ySize do
|
||||
if y + lines > 0 and y + lines <= ySize then
|
||||
for x = 1, xSize do
|
||||
xPos, yPos = x, y
|
||||
tTerm.blit(buffer[y + lines][x][1], buffer[y + lines][x][2], buffer[y + lines][x][3])
|
||||
end
|
||||
else
|
||||
yPos = y
|
||||
tTerm.clearLine()
|
||||
end
|
||||
end
|
||||
|
||||
xPos, yPos = oldXPos, oldYPos
|
||||
else tTerm.clear() end
|
||||
end
|
||||
|
||||
tTerm.toggleCur = function(newBlink)
|
||||
curBlink = newBlink
|
||||
end
|
||||
|
||||
tTerm.newInput = function(input)
|
||||
local oldTC, oldBC, oldX, oldY = tCol, bCol, xPos, yPos
|
||||
tCol, bCol, xPos, yPos, ySize, input = colours.grey, colours.lightGrey, 1, ySize + 1, ySize + 1, input .. " "
|
||||
|
||||
while #curInput + #input + 1 > xSize do curInput = curInput:sub(curInput:find(" ") + 1) end
|
||||
curInput = curInput .. input .. " "
|
||||
tTerm.clearLine()
|
||||
tTerm.write(curInput)
|
||||
|
||||
tCol, bCol, xPos, yPos, ySize = oldTC, oldBC, oldX, oldY, ySize - 1
|
||||
end
|
||||
|
||||
tTerm.key = function(key)
|
||||
tTerm.newInput((not keys.getName(key)) and "unknownKey" or keys.getName(key))
|
||||
end
|
||||
|
||||
tTerm.mouse_click = function(button, x, y)
|
||||
tTerm.newInput(buttons[button] .. "C@" .. tostring(x) .. "x" .. tostring(y))
|
||||
end
|
||||
|
||||
local image = {["width"] = xSize * charW, ["height"] = ySize * charH}
|
||||
|
||||
for i = 1, #calls do
|
||||
local xMin, yMin, xMax, yMax, oldBuffer, curCalls, changed = xSize + 1, ySize + 1, 0, 0, {}, calls[i], false
|
||||
calls[i] = nil
|
||||
|
||||
for y = 1, ySize do
|
||||
oldBuffer[y] = {}
|
||||
for x = 1, xSize do oldBuffer[y][x] = {buffer[y][x][1], buffer[y][x][2], buffer[y][x][3], buffer[y][x][4]} end
|
||||
end
|
||||
|
||||
snooze()
|
||||
|
||||
if showInput then ySize = ySize - 1 end
|
||||
for j = 1, #curCalls do if tTerm[curCalls[j][1]] then tTerm[curCalls[j][1]](unpack(curCalls[j], 2)) end end
|
||||
if showInput then ySize = ySize + 1 end
|
||||
|
||||
if i > 1 then
|
||||
for yy = 1, ySize do for xx = 1, xSize do if buffer[yy][xx][1] ~= oldBuffer[yy][xx][1] or (buffer[yy][xx][2] ~= oldBuffer[yy][xx][2] and buffer[yy][xx][1] ~= " ") or buffer[yy][xx][3] ~= oldBuffer[yy][xx][3] then
|
||||
changed = true
|
||||
if xx < xMin then xMin = xx end
|
||||
if xx > xMax then xMax = xx end
|
||||
if yy < yMin then yMin = yy end
|
||||
if yy > yMax then yMax = yy end
|
||||
end end end
|
||||
else xMin, yMin, xMax, yMax, changed = 1, 1, xSize, ySize, true end
|
||||
|
||||
if oldBlink and (xPos ~= oldXPos or yPos ~= oldYPos or not curBlink) and oldXPos > 0 and oldYPos > 0 and oldXPos <= xSize and oldYPos <= ySize then
|
||||
changed = true
|
||||
if oldXPos < xMin then xMin = oldXPos end
|
||||
if oldXPos > xMax then xMax = oldXPos end
|
||||
if oldYPos < yMin then yMin = oldYPos end
|
||||
if oldYPos > yMax then yMax = oldYPos end
|
||||
buffer[oldYPos][oldXPos][4] = false
|
||||
end
|
||||
|
||||
if curBlink and (xPos ~= oldXPos or yPos ~= oldYPos or not oldBlink) and xPos > 0 and yPos > 0 and xPos <= xSize and yPos <= ySize then
|
||||
changed = true
|
||||
if xPos < xMin then xMin = xPos end
|
||||
if xPos > xMax then xMax = xPos end
|
||||
if yPos < yMin then yMin = yPos end
|
||||
if yPos > yMax then yMax = yPos end
|
||||
buffer[yPos][xPos][4] = true
|
||||
end
|
||||
|
||||
oldBlink, oldXPos, oldYPos = curBlink, xPos, yPos
|
||||
|
||||
local thisFrame = {
|
||||
["xstart"] = (xMin - 1) * charW,
|
||||
["ystart"] = (yMin - 1) * charH,
|
||||
["xend"] = (xMax - xMin + 1) * charW,
|
||||
["yend"] = (yMax - yMin + 1) * charH,
|
||||
["delay"] = curCalls.delay,
|
||||
["disposal"] = 1
|
||||
}
|
||||
|
||||
for y = 1, (yMax - yMin + 1) * charH do
|
||||
local row = {}
|
||||
for x = 1, (xMax - xMin + 1) * charW do row[x] = " " end
|
||||
thisFrame[y] = row
|
||||
end
|
||||
|
||||
snooze()
|
||||
|
||||
for yy = yMin, yMax do
|
||||
local yBump = (yy - yMin) * charH
|
||||
|
||||
for xx = xMin, xMax do if buffer[yy][xx][1] ~= oldBuffer[yy][xx][1] or (buffer[yy][xx][2] ~= oldBuffer[yy][xx][2] and buffer[yy][xx][1] ~= " ") or buffer[yy][xx][3] ~= oldBuffer[yy][xx][3] or buffer[yy][xx][4] ~= oldBuffer[yy][xx][4] or i == 1 then
|
||||
local thisChar, thisT, thisB, xBump = chars[buffer[yy][xx][1]:byte()], buffer[yy][xx][2], buffer[yy][xx][3], (xx - xMin) * charW
|
||||
if thisChar then
|
||||
for y = 1, charH do
|
||||
for x = 1, charW do
|
||||
local ch = thisChar[y][x] and thisT or thisB
|
||||
thisFrame[y + yBump][x + xBump] = ch
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if buffer[yy][xx][4] then
|
||||
thisT, thisChar = colourNum[tCol], chars[95]
|
||||
for y = 1, charH do for x = 1, charW do if thisChar[y][x] then thisFrame[y + yBump][x + xBump] = thisT end end end
|
||||
end
|
||||
end end
|
||||
|
||||
for y = yBump + 1, yBump + charH do
|
||||
local skip, chars, row = 0, {}, {}
|
||||
|
||||
for x = 1, #thisFrame[y] do
|
||||
if thisFrame[y][x] == " " then
|
||||
if #chars > 0 then
|
||||
row[#row + 1] = table.concat(chars)
|
||||
chars = {}
|
||||
end
|
||||
|
||||
skip = skip + 1
|
||||
else
|
||||
if skip > 0 then
|
||||
row[#row + 1] = skip
|
||||
skip = 0
|
||||
end
|
||||
|
||||
chars[#chars + 1] = thisFrame[y][x]
|
||||
end
|
||||
end
|
||||
|
||||
if #chars > 0 then row[#row + 1] = table.concat(chars) end
|
||||
thisFrame[y] = row
|
||||
end
|
||||
|
||||
snooze()
|
||||
end
|
||||
|
||||
if changed then
|
||||
image[#image + 1] = thisFrame
|
||||
else
|
||||
image[#image].delay = image[#image].delay + curCalls.delay
|
||||
end
|
||||
end
|
||||
|
||||
buffer = nil
|
||||
|
||||
GIF.saveGIF(image, filename)
|
||||
|
||||
fs.delete('ascii.gif')
|
||||
|
||||
print("Encode complete")
|
@@ -1,517 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local GPS = require('gps')
|
||||
local Socket = require('socket')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Shapes')
|
||||
|
||||
local args = { ... }
|
||||
local turtleId = args[1] or error('Supply turtle ID')
|
||||
turtleId = tonumber(turtleId)
|
||||
|
||||
local script = [[
|
||||
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local GPS = require('gps')
|
||||
local ChestAdapter = require('chestAdapter18')
|
||||
local Point = require('point')
|
||||
local Util = require('util')
|
||||
|
||||
local itemAdapter
|
||||
|
||||
function dumpInventory()
|
||||
|
||||
for i = 1, 16 do
|
||||
local qty = turtle.getItemCount(i)
|
||||
if qty > 0 then
|
||||
itemAdapter:insert(i, qty)
|
||||
end
|
||||
if turtle.getItemCount(i) ~= 0 then
|
||||
print('Adapter is full or missing - make space or replace')
|
||||
print('Press enter to continue')
|
||||
read()
|
||||
end
|
||||
end
|
||||
turtle.select(1)
|
||||
end
|
||||
|
||||
local function refuel()
|
||||
while turtle.getFuelLevel() < 4000 do
|
||||
print('Refueling')
|
||||
turtle.select(1)
|
||||
|
||||
itemAdapter:provide({ name = 'minecraft:coal', damage = 0 }, 64, 1)
|
||||
if turtle.getItemCount(1) == 0 then
|
||||
print('Out of fuel, add fuel to chest/ME system')
|
||||
turtle.status = 'waiting'
|
||||
os.sleep(5)
|
||||
else
|
||||
turtle.refuel(64)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function goto(pt)
|
||||
while not turtle.gotoPoint(pt) do
|
||||
print('stuck')
|
||||
os.sleep(5)
|
||||
end
|
||||
end
|
||||
|
||||
local function pathTo(pt)
|
||||
while not turtle.pathfind(pt) do
|
||||
print('stuck')
|
||||
os.sleep(5)
|
||||
end
|
||||
end
|
||||
|
||||
local function resupply()
|
||||
|
||||
if data.suppliesPt then
|
||||
pathTo(data.suppliesPt)
|
||||
|
||||
itemAdapter = ChestAdapter({ direction = 'up', wrapSide = 'bottom' })
|
||||
dumpInventory()
|
||||
refuel()
|
||||
end
|
||||
end
|
||||
|
||||
local function makePlane(y)
|
||||
local pt = { x = math.min(data.startPt.x, data.endPt.x),
|
||||
ex = math.max(data.startPt.x, data.endPt.x),
|
||||
z = math.min(data.startPt.z, data.endPt.z),
|
||||
ez = math.max(data.startPt.z, data.endPt.z) }
|
||||
|
||||
local blocks = { }
|
||||
for z = pt.z, pt.ez do
|
||||
for x = pt.x, pt.ex do
|
||||
table.insert(blocks, { x = x, y = y, z = z })
|
||||
end
|
||||
end
|
||||
|
||||
return blocks
|
||||
end
|
||||
|
||||
local function optimizeRoute(plane, ptb)
|
||||
|
||||
local maxDistance = 99999999
|
||||
|
||||
local function getNearestNeighbor(p, pt, threshold)
|
||||
local key, block, heading
|
||||
local moves = maxDistance
|
||||
|
||||
local function getMoves(b, k)
|
||||
local distance = math.abs(pt.x - b.x) + math.abs(pt.z - b.z)
|
||||
|
||||
if distance < moves then
|
||||
-- this operation is expensive - only run if distance is close
|
||||
local c, h = Point.calculateMoves(pt, b, distance)
|
||||
if c < moves then
|
||||
block = b
|
||||
key = k
|
||||
moves = c
|
||||
heading = h
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function blockReady(b)
|
||||
return not b.u
|
||||
end
|
||||
|
||||
local mid = pt.index
|
||||
local forward = mid + 1
|
||||
local backward = mid - 1
|
||||
while forward <= #p or backward > 0 do
|
||||
if forward <= #p then
|
||||
local b = p[forward]
|
||||
if blockReady(b) then
|
||||
getMoves(b, forward)
|
||||
if moves <= threshold then
|
||||
break
|
||||
end
|
||||
if moves < maxDistance and math.abs(b.z - pt.z) > moves and pt.index > 0 then
|
||||
forward = #p
|
||||
end
|
||||
end
|
||||
forward = forward + 1
|
||||
end
|
||||
if backward > 0 then
|
||||
local b = p[backward]
|
||||
if blockReady(b) then
|
||||
getMoves(b, backward)
|
||||
if moves <= threshold then
|
||||
break
|
||||
end
|
||||
if moves < maxDistance and math.abs(pt.z - b.z) > moves then
|
||||
backward = 0
|
||||
end
|
||||
end
|
||||
backward = backward - 1
|
||||
end
|
||||
end
|
||||
pt.x = block.x
|
||||
pt.z = block.z
|
||||
pt.y = block.y
|
||||
pt.heading = heading
|
||||
pt.index = key
|
||||
block.u = true
|
||||
return block
|
||||
end
|
||||
|
||||
local throttle = Util.throttle()
|
||||
local t = { }
|
||||
ptb.index = 0
|
||||
local threshold = 0
|
||||
for i = 1, #plane do
|
||||
local b = getNearestNeighbor(plane, ptb, threshold)
|
||||
table.insert(t, b)
|
||||
throttle()
|
||||
threshold = 1
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
local function clear()
|
||||
|
||||
local pt = Util.shallowCopy(data.startPt)
|
||||
pt.y = math.min(data.startPt.y, data.endPt.y)
|
||||
pt.heading = 0
|
||||
|
||||
local osy = pt.y
|
||||
local sy = osy + 1
|
||||
local ey = math.max(data.startPt.y, data.endPt.y)
|
||||
local firstPlane = true
|
||||
|
||||
resupply()
|
||||
|
||||
while true do
|
||||
|
||||
if sy > ey then
|
||||
sy = ey
|
||||
end
|
||||
|
||||
local plane = makePlane(sy)
|
||||
plane = optimizeRoute(plane, pt)
|
||||
|
||||
if firstPlane then
|
||||
turtle.pathfind(plane[1])
|
||||
turtle.setPolicy(turtle.policies.digAttack)
|
||||
firstPlane = false
|
||||
end
|
||||
|
||||
for _,b in ipairs(plane) do
|
||||
turtle.gotoPoint(b)
|
||||
if sy < ey then
|
||||
turtle.digUp()
|
||||
end
|
||||
if sy > osy then
|
||||
turtle.digDown()
|
||||
end
|
||||
if turtle.abort then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if turtle.abort then
|
||||
break
|
||||
end
|
||||
if sy + 1 >= ey then
|
||||
break
|
||||
end
|
||||
|
||||
sy = sy + 3
|
||||
end
|
||||
turtle.setPolicy(turtle.policies.none)
|
||||
resupply()
|
||||
end
|
||||
|
||||
turtle.run(function()
|
||||
turtle.status = 'Clearing'
|
||||
|
||||
if turtle.enableGPS() then
|
||||
|
||||
local pt = Util.shallowCopy(turtle.point)
|
||||
local s, m = pcall(clear)
|
||||
pathTo(pt)
|
||||
|
||||
if not s and m then
|
||||
error(m)
|
||||
read()
|
||||
end
|
||||
end
|
||||
end)
|
||||
]]
|
||||
|
||||
local levelScript = [[
|
||||
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Point = require('point')
|
||||
local Util = require('util')
|
||||
|
||||
local checkedNodes = { }
|
||||
local nodes = { }
|
||||
local box = { }
|
||||
|
||||
local function inBox(pt, box)
|
||||
return pt.x >= box.x and
|
||||
pt.y >= box.y and
|
||||
pt.z >= box.z and
|
||||
pt.x <= box.ex and
|
||||
pt.y <= box.ey and
|
||||
pt.z <= box.ez
|
||||
end
|
||||
|
||||
local function toKey(pt)
|
||||
return table.concat({ pt.x, pt.y, pt.z }, ':')
|
||||
end
|
||||
|
||||
local function addNode(node)
|
||||
|
||||
for i = 0, 5 do
|
||||
local hi = turtle.getHeadingInfo(i)
|
||||
local testNode = { x = node.x + hi.xd, y = node.y + hi.yd, z = node.z + hi.zd }
|
||||
|
||||
if inBox(testNode, box) then
|
||||
local key = toKey(testNode)
|
||||
if not checkedNodes[key] then
|
||||
nodes[key] = testNode
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function dig(action)
|
||||
|
||||
local directions = {
|
||||
top = 'up',
|
||||
bottom = 'down',
|
||||
}
|
||||
|
||||
-- convert to up, down, north, south, east, west
|
||||
local direction = directions[action.side] or
|
||||
turtle.getHeadingInfo(turtle.point.heading).direction
|
||||
|
||||
local hi = turtle.getHeadingInfo(direction)
|
||||
local node = { x = turtle.point.x + hi.xd, y = turtle.point.y + hi.yd, z = turtle.point.z + hi.zd }
|
||||
if inBox(node, box) then
|
||||
|
||||
local key = toKey(node)
|
||||
checkedNodes[key] = true
|
||||
nodes[key] = nil
|
||||
|
||||
if action.dig() then
|
||||
addNode(node)
|
||||
repeat until not action.dig() -- sand, etc
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function move(action)
|
||||
if action == 'turn' then
|
||||
dig(turtle.getAction('forward'))
|
||||
elseif action == 'up' then
|
||||
dig(turtle.getAction('up'))
|
||||
elseif action == 'down' then
|
||||
dig(turtle.getAction('down'))
|
||||
elseif action == 'back' then
|
||||
dig(turtle.getAction('up'))
|
||||
dig(turtle.getAction('down'))
|
||||
end
|
||||
end
|
||||
|
||||
local function getAdjacentPoint(pt)
|
||||
local t = { }
|
||||
table.insert(t, pt)
|
||||
for i = 0, 5 do
|
||||
local hi = turtle.getHeadingInfo(i)
|
||||
local heading
|
||||
if i < 4 then
|
||||
heading = (hi.heading + 2) % 4
|
||||
end
|
||||
table.insert(t, { x = pt.x + hi.xd, z = pt.z + hi.zd, y = pt.y + hi.yd, heading = heading })
|
||||
end
|
||||
|
||||
return Point.closest2(turtle.getPoint(), t)
|
||||
end
|
||||
|
||||
local function level()
|
||||
|
||||
box.x = math.min(data.startPt.x, data.endPt.x)
|
||||
box.y = math.min(data.startPt.y, data.endPt.y)
|
||||
box.z = math.min(data.startPt.z, data.endPt.z)
|
||||
box.ex = math.max(data.startPt.x, data.endPt.x)
|
||||
box.ey = math.max(data.startPt.y, data.endPt.y)
|
||||
box.ez = math.max(data.startPt.z, data.endPt.z)
|
||||
|
||||
turtle.pathfind(data.firstPt)
|
||||
|
||||
turtle.setPolicy("attack", { dig = dig }, "assuredMove")
|
||||
turtle.setMoveCallback(move)
|
||||
|
||||
repeat
|
||||
local key = toKey(turtle.point)
|
||||
|
||||
checkedNodes[key] = true
|
||||
nodes[key] = nil
|
||||
|
||||
dig(turtle.getAction('down'))
|
||||
dig(turtle.getAction('up'))
|
||||
dig(turtle.getAction('forward'))
|
||||
|
||||
print(string.format('%d nodes remaining', Util.size(nodes)))
|
||||
|
||||
if Util.size(nodes) == 0 then
|
||||
break
|
||||
end
|
||||
|
||||
local node = Point.closest2(turtle.point, nodes)
|
||||
node = getAdjacentPoint(node)
|
||||
if not turtle.gotoPoint(node) then
|
||||
break
|
||||
end
|
||||
until turtle.abort
|
||||
|
||||
turtle.resetState()
|
||||
end
|
||||
|
||||
local s, m = turtle.run(function()
|
||||
turtle.status = 'Leveling'
|
||||
|
||||
if turtle.enableGPS() then
|
||||
|
||||
local pt = Util.shallowCopy(turtle.point)
|
||||
local s, m = pcall(level)
|
||||
turtle.pathfind(pt)
|
||||
|
||||
if not s and m then
|
||||
error(m)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if not s then
|
||||
error(m)
|
||||
end
|
||||
]]
|
||||
|
||||
|
||||
local data = Util.readTable('/usr/config/shapes') or { }
|
||||
|
||||
local page = UI.Page {
|
||||
titleBar = UI.TitleBar { title = 'Shapes' },
|
||||
info = UI.Window { x = 5, y = 3, height = 1 },
|
||||
startCoord = UI.Button { x = 2, y = 6, text = 'Start ', event = 'startCoord' },
|
||||
endCoord = UI.Button { x = 2, y = 8, text = 'End ', event = 'endCoord' },
|
||||
supplies = UI.Button { x = 2, y = 10, text = 'Supplies', event = 'supplies' },
|
||||
first = UI.Button { x = 2, y = 11, text = 'First', event = 'firstCoord' },
|
||||
cancel = UI.Button { rx = 2, ry = -2, text = 'Abort', event = 'cancel' },
|
||||
begin = UI.Button { rx = -7, ry = -2, text = 'Begin', event = 'begin' },
|
||||
accelerators = { q = 'quit' },
|
||||
notification = UI.Notification(),
|
||||
statusBar = UI.StatusBar(),
|
||||
}
|
||||
|
||||
function page.info:draw()
|
||||
|
||||
local function size(a, b)
|
||||
return (math.abs(a.x - b.x) + 1) *
|
||||
(math.abs(a.y - b.y) + 1) *
|
||||
(math.abs(a.z - b.z) + 1)
|
||||
end
|
||||
|
||||
self:clear()
|
||||
if not data.startPt then
|
||||
self:write(1, 1, 'Set starting corner')
|
||||
elseif not data.endPt then
|
||||
self:write(1, 1, 'Set ending corner')
|
||||
else
|
||||
self:write(1, 1, 'Blocks: ' .. size(data.startPt, data.endPt))
|
||||
end
|
||||
end
|
||||
|
||||
function page:getPoint()
|
||||
local pt = GPS.getPoint()
|
||||
if not pt then
|
||||
self.notification:error('GPS not available')
|
||||
end
|
||||
return pt
|
||||
end
|
||||
|
||||
function page:runFunction(id, script)
|
||||
|
||||
Util.writeFile('script.tmp', script)
|
||||
self.notification:info('Connecting')
|
||||
local fn, msg = loadstring(script, 'script')
|
||||
if not fn then
|
||||
self.notification:error('Error in script')
|
||||
debug(msg)
|
||||
return
|
||||
end
|
||||
|
||||
local socket = Socket.connect(id, 161)
|
||||
if not socket then
|
||||
self.notification:error('Unable to connect')
|
||||
return
|
||||
end
|
||||
socket:write({ type = 'script', args = script })
|
||||
socket:close()
|
||||
|
||||
self.notification:success('Sent')
|
||||
end
|
||||
|
||||
function page:eventHandler(event)
|
||||
if event.type == 'startCoord' then
|
||||
data.startPt = self:getPoint()
|
||||
if data.startPt then
|
||||
self.statusBar:setStatus('starting corner set')
|
||||
Util.writeTable('/usr/config/shapes', data)
|
||||
end
|
||||
self:draw()
|
||||
elseif event.type == 'endCoord' then
|
||||
data.endPt = self:getPoint()
|
||||
if data.endPt then
|
||||
self.statusBar:setStatus('ending corner set')
|
||||
Util.writeTable('/usr/config/shapes', data)
|
||||
end
|
||||
self:draw()
|
||||
elseif event.type == 'firstCoord' then
|
||||
data.firstPt = self:getPoint()
|
||||
if data.firstPt then
|
||||
self.statusBar:setStatus('first point set')
|
||||
Util.writeTable('/usr/config/shapes', data)
|
||||
end
|
||||
self:draw()
|
||||
elseif event.type == 'supplies' then
|
||||
data.suppliesPt = self:getPoint()
|
||||
if data.suppliesPt then
|
||||
self.statusBar:setStatus('supplies location set')
|
||||
Util.writeTable('/usr/config/shapes', data)
|
||||
end
|
||||
elseif event.type == 'begin' then
|
||||
if data.startPt and data.endPt then
|
||||
local s = 'local data = ' .. textutils.serialize(data) .. levelScript
|
||||
self:runFunction(turtleId, s)
|
||||
else
|
||||
self.notification:error('Corners not set')
|
||||
end
|
||||
self.statusBar:setStatus('')
|
||||
elseif event.type == 'cancel' then
|
||||
self:runFunction(turtleId, 'turtle.abortAction()')
|
||||
self.statusBar:setStatus('')
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
UI:setPage(page)
|
||||
|
||||
UI:pullEvents()
|
||||
UI.term:reset()
|
@@ -41,7 +41,12 @@ end
|
||||
function shell.run(...)
|
||||
|
||||
local path, args = parseCommandLine(...)
|
||||
path = shell.resolveProgram(path)
|
||||
local isUrl = not not path:match("^(https?:)//(([^/:]+):?([0-9]*))(/?.*)$")
|
||||
|
||||
if not isUrl then
|
||||
path = shell.resolveProgram(path)
|
||||
end
|
||||
|
||||
if path then
|
||||
tProgramStack[#tProgramStack + 1] = path
|
||||
local oldTitle
|
||||
@@ -51,7 +56,15 @@ function shell.run(...)
|
||||
multishell.setTitle(multishell.getCurrent(), fs.getName(path))
|
||||
end
|
||||
|
||||
local result, err = os.run(Util.shallowCopy(sandboxEnv), path, unpack(args))
|
||||
local result, err
|
||||
|
||||
if isUrl then
|
||||
local env = Util.shallowCopy(sandboxEnv)
|
||||
setmetatable(env, { __index = _G })
|
||||
result, err = Util.runUrl(env, path, unpack(args))
|
||||
else
|
||||
result, err = os.run(Util.shallowCopy(sandboxEnv), path, unpack(args))
|
||||
end
|
||||
|
||||
if multishell then
|
||||
local title = 'shell'
|
||||
|
@@ -1,633 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Logger = require('logger')
|
||||
local Point = require('point')
|
||||
local Util = require('util')
|
||||
|
||||
if device and device.wireless_modem then
|
||||
Logger.setWirelessLogging()
|
||||
end
|
||||
|
||||
local args = { ... }
|
||||
local options = {
|
||||
chunks = { arg = 'c', type = 'number', value = -1,
|
||||
desc = 'Number of chunks to mine' },
|
||||
depth = { arg = 'd', type = 'number', value = 9000,
|
||||
desc = 'Mining depth' },
|
||||
-- enderChest = { arg = 'e', type = 'flag', value = false,
|
||||
-- desc = 'Use ender chest' },
|
||||
resume = { arg = 'r', type = 'flag', value = false,
|
||||
desc = 'Resume mining' },
|
||||
fortunePick = { arg = 'p', type = 'string', value = nil,
|
||||
desc = 'Pick to use with CCTweaks toolhost' },
|
||||
setTrash = { arg = 's', type = 'flag', value = false,
|
||||
desc = 'Set trash items' },
|
||||
help = { arg = 'h', type = 'flag', value = false,
|
||||
desc = 'Displays the options' },
|
||||
}
|
||||
|
||||
local fortuneBlocks = {
|
||||
[ 'minecraft:redstone_ore' ] = true,
|
||||
[ 'minecraft:lapis_ore' ] = true,
|
||||
[ 'minecraft:coal_ore' ] = true,
|
||||
[ 'minecraft:diamond_ore' ] = true,
|
||||
[ 'minecraft:emerald_ore' ] = true,
|
||||
}
|
||||
|
||||
local MIN_FUEL = 7500
|
||||
local LOW_FUEL = 1500
|
||||
local MAX_FUEL = 100000
|
||||
|
||||
if not term.isColor() then
|
||||
MAX_FUEL = 20000
|
||||
end
|
||||
|
||||
local mining = {
|
||||
diameter = 1,
|
||||
chunkIndex = 0,
|
||||
chunks = -1,
|
||||
}
|
||||
|
||||
local trash
|
||||
local boreDirection
|
||||
|
||||
function getChunkCoordinates(diameter, index, x, z)
|
||||
local dirs = { -- circumference of grid
|
||||
{ xd = 0, zd = 1, heading = 1 }, -- south
|
||||
{ xd = -1, zd = 0, heading = 2 },
|
||||
{ xd = 0, zd = -1, heading = 3 },
|
||||
{ xd = 1, zd = 0, heading = 0 } -- east
|
||||
}
|
||||
-- always move east when entering the next diameter
|
||||
if index == 0 then
|
||||
dirs[4].x = x + 16
|
||||
dirs[4].z = z
|
||||
return dirs[4]
|
||||
end
|
||||
dir = dirs[math.floor(index / (diameter - 1)) + 1]
|
||||
dir.x = x + dir.xd * 16
|
||||
dir.z = z + dir.zd * 16
|
||||
return dir
|
||||
end
|
||||
|
||||
function getBoreLocations(x, z)
|
||||
|
||||
local locations = {}
|
||||
|
||||
while true do
|
||||
local a = math.abs(z)
|
||||
local b = math.abs(x)
|
||||
|
||||
if x > 0 and z > 0 or
|
||||
x < 0 and z < 0 then
|
||||
-- rotate coords
|
||||
a = math.abs(x)
|
||||
b = math.abs(z)
|
||||
end
|
||||
if (a % 5 == 0 and b % 5 == 0) or
|
||||
(a % 5 == 2 and b % 5 == 1) or
|
||||
(a % 5 == 4 and b % 5 == 2) or
|
||||
(a % 5 == 1 and b % 5 == 3) or
|
||||
(a % 5 == 3 and b % 5 == 4) then
|
||||
table.insert(locations, { x = x, z = z, y = 0 })
|
||||
end
|
||||
if z % 2 == 0 then -- forward dir
|
||||
if (x + 1) % 16 == 0 then
|
||||
z = z + 1
|
||||
else
|
||||
x = x + 1
|
||||
end
|
||||
else
|
||||
if (x - 1) % 16 == 15 then
|
||||
if (z + 1) % 16 == 0 then
|
||||
break
|
||||
end
|
||||
z = z + 1
|
||||
else
|
||||
x = x - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return locations
|
||||
end
|
||||
|
||||
-- get the bore location closest to the miner
|
||||
local function getClosestLocation(points, b)
|
||||
local key = 1
|
||||
local leastMoves = 9000
|
||||
for k,pt in pairs(points) do
|
||||
|
||||
local moves = Point.calculateMoves(turtle.point, pt)
|
||||
|
||||
if moves < leastMoves then
|
||||
key = k
|
||||
leastMoves = moves
|
||||
if leastMoves == 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
return table.remove(points, key)
|
||||
end
|
||||
|
||||
function getCornerOf(c)
|
||||
return math.floor(c.x / 16) * 16, math.floor(c.z / 16) * 16
|
||||
end
|
||||
|
||||
function nextChunk()
|
||||
|
||||
local x, z = getCornerOf({ x = mining.x, z = mining.z })
|
||||
local points = math.pow(mining.diameter, 2) - math.pow(mining.diameter-2, 2)
|
||||
mining.chunkIndex = mining.chunkIndex + 1
|
||||
|
||||
if mining.chunkIndex >= points then
|
||||
mining.diameter = mining.diameter + 2
|
||||
mining.chunkIndex = 0
|
||||
end
|
||||
|
||||
if mining.chunks ~= -1 then
|
||||
local chunks = math.pow(mining.diameter-2, 2) + mining.chunkIndex
|
||||
if chunks >= mining.chunks then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local nc = getChunkCoordinates(mining.diameter, mining.chunkIndex, x, z)
|
||||
mining.locations = getBoreLocations(nc.x, nc.z)
|
||||
|
||||
-- enter next chunk
|
||||
mining.x = nc.x
|
||||
mining.z = nc.z
|
||||
|
||||
Util.writeTable('mining.progress', mining)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function addTrash()
|
||||
|
||||
if not trash then
|
||||
trash = { }
|
||||
end
|
||||
|
||||
local slots = turtle.getFilledSlots()
|
||||
|
||||
for k,slot in pairs(slots) do
|
||||
trash[slot.iddmg] = true
|
||||
end
|
||||
|
||||
trash['minecraft:bucket:0'] = nil
|
||||
Util.writeTable('mining.trash', trash)
|
||||
end
|
||||
|
||||
function log(text)
|
||||
print(text)
|
||||
Logger.log('mineWorker', text)
|
||||
end
|
||||
|
||||
function status(status)
|
||||
turtle.status = status
|
||||
log(status)
|
||||
end
|
||||
|
||||
function refuel()
|
||||
if turtle.getFuelLevel() < MIN_FUEL then
|
||||
local oldStatus = turtle.status
|
||||
status('refueling')
|
||||
|
||||
if turtle.selectSlot('minecraft:coal:0') then
|
||||
local qty = turtle.getItemCount()
|
||||
print('refueling ' .. qty)
|
||||
turtle.refuel(qty)
|
||||
end
|
||||
if turtle.getFuelLevel() < MIN_FUEL then
|
||||
log('desperate fueling')
|
||||
|
||||
turtle.eachFilledSlot(function(slot)
|
||||
if turtle.getFuelLevel() < MIN_FUEL then
|
||||
turtle.select(slot.index)
|
||||
turtle.refuel(64)
|
||||
end
|
||||
end)
|
||||
end
|
||||
log('Fuel: ' .. turtle.getFuelLevel())
|
||||
status(oldStatus)
|
||||
end
|
||||
|
||||
turtle.select(1)
|
||||
end
|
||||
|
||||
function enderChestUnload()
|
||||
log('unloading')
|
||||
turtle.select(1)
|
||||
if not Util.tryTimed(5, function()
|
||||
turtle.digDown()
|
||||
return turtle.placeDown()
|
||||
end) then
|
||||
log('placedown failed')
|
||||
else
|
||||
turtle.reconcileInventory(slots, turtle.dropDown)
|
||||
|
||||
turtle.select(1)
|
||||
turtle.drop(64)
|
||||
turtle.digDown()
|
||||
end
|
||||
end
|
||||
|
||||
function safeGoto(x, z, y, h)
|
||||
local oldStatus = turtle.status
|
||||
while not turtle.pathfind({ x = x, z = z, y = y, heading = h }) do
|
||||
--status('stuck')
|
||||
if turtle.abort then
|
||||
return false
|
||||
end
|
||||
--os.sleep(1)
|
||||
end
|
||||
turtle.status = oldStatus
|
||||
return true
|
||||
end
|
||||
|
||||
function safeGotoY(y)
|
||||
local oldStatus = turtle.status
|
||||
while not turtle.gotoY(y) do
|
||||
status('stuck')
|
||||
if turtle.abort then
|
||||
return false
|
||||
end
|
||||
os.sleep(1)
|
||||
end
|
||||
turtle.status = oldStatus
|
||||
return true
|
||||
end
|
||||
|
||||
function makeWalkableTunnel(action, tpt, pt)
|
||||
if action ~= 'turn' and not Point.compare(tpt, { x = 0, z = 0 }) then -- not at source
|
||||
if not Point.compare(tpt, pt) then -- not at dest
|
||||
local r, block = turtle.inspectUp()
|
||||
if r and not turtle.isTurtleAtSide('top') then
|
||||
if block.name ~= 'minecraft:cobblestone' and
|
||||
block.name ~= 'minecraft:chest' then
|
||||
turtle.digUp()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function normalChestUnload()
|
||||
local oldStatus = turtle.status
|
||||
status('unloading')
|
||||
local pt = Util.shallowCopy(turtle.point)
|
||||
safeGotoY(0)
|
||||
|
||||
turtle.setMoveCallback(function(action, tpt)
|
||||
makeWalkableTunnel(action, tpt, { x = pt.x, z = pt.z })
|
||||
end)
|
||||
|
||||
safeGoto(0, 0)
|
||||
if not turtle.detectUp() then
|
||||
error('no chest')
|
||||
end
|
||||
local slots = turtle.getFilledSlots()
|
||||
for _,slot in pairs(slots) do
|
||||
if not trash[slot.iddmg] and
|
||||
slot.iddmg ~= 'minecraft:bucket:0' and
|
||||
slot.id ~= 'minecraft:diamond_pickaxe' and
|
||||
slot.id ~= 'cctweaks:toolHost' then
|
||||
if slot.id ~= options.fortunePick.value then
|
||||
turtle.select(slot.index)
|
||||
turtle.dropUp(64)
|
||||
end
|
||||
end
|
||||
end
|
||||
turtle.select(1)
|
||||
safeGoto(pt.x, pt.z, 0, pt.heading)
|
||||
|
||||
turtle.clearMoveCallback()
|
||||
|
||||
safeGotoY(pt.y)
|
||||
status(oldStatus)
|
||||
end
|
||||
|
||||
function ejectTrash()
|
||||
|
||||
local cobbleSlotCount = 0
|
||||
|
||||
turtle.eachFilledSlot(function(slot)
|
||||
if slot.iddmg == 'minecraft:cobblestone:0' then
|
||||
cobbleSlotCount = cobbleSlotCount + 1
|
||||
end
|
||||
|
||||
if trash[slot.iddmg] then
|
||||
-- retain 1 slot with cobble in order to indicate active mining
|
||||
if slot.iddmg ~= 'minecraft:cobblestone:0' or cobbleSlotCount > 1 then
|
||||
turtle.select(slot.index)
|
||||
turtle.dropDown(64)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function mineable(action)
|
||||
local r, block = action.inspect()
|
||||
if not r then
|
||||
return false
|
||||
end
|
||||
|
||||
if block.name == 'minecraft:chest' then
|
||||
collectDrops(action.suck)
|
||||
end
|
||||
|
||||
if turtle.getFuelLevel() < (MAX_FUEL - 1000) then
|
||||
if block.name == 'minecraft:lava' or block.name == 'minecraft:flowing_lava' then
|
||||
if turtle.selectSlot('minecraft:bucket:0') then
|
||||
if action.place() then
|
||||
log('Lava! ' .. turtle.getFuelLevel())
|
||||
turtle.refuel()
|
||||
log(turtle.getFuelLevel())
|
||||
end
|
||||
turtle.select(1)
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if action.side == 'bottom' then
|
||||
return block.name
|
||||
end
|
||||
|
||||
if trash[block.name .. ':0'] then
|
||||
return false
|
||||
end
|
||||
|
||||
return block.name
|
||||
end
|
||||
|
||||
function fortuneDig(action, blockName)
|
||||
if options.fortunePick.value and fortuneBlocks[blockName] then
|
||||
turtle.selectSlot('cctweaks:toolHost')
|
||||
turtle.equipRight()
|
||||
turtle.selectSlot(options.fortunePick.value)
|
||||
repeat until not turtle.dig()
|
||||
turtle.selectSlot('minecraft:diamond_pickaxe')
|
||||
turtle.equipRight()
|
||||
turtle.select(1)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function mine(action)
|
||||
local blockName = mineable(action)
|
||||
if blockName then
|
||||
checkSpace()
|
||||
--collectDrops(action.suck)
|
||||
if not fortuneDig(action, blockName) then
|
||||
action.dig()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function bore()
|
||||
|
||||
local loc = turtle.point
|
||||
local level = loc.y
|
||||
|
||||
turtle.select(1)
|
||||
status('boring down')
|
||||
boreDirection = 'down'
|
||||
|
||||
while true do
|
||||
if turtle.abort then
|
||||
status('aborting')
|
||||
return false
|
||||
end
|
||||
if loc.y <= -mining.depth then
|
||||
break
|
||||
end
|
||||
|
||||
if turtle.point.y < -2 then
|
||||
-- turtle.setDigPolicy(turtle.digPolicies.turtleSafe)
|
||||
end
|
||||
|
||||
mine(turtle.getAction('down'))
|
||||
if not Util.tryTimed(3, turtle.down) then
|
||||
break
|
||||
end
|
||||
|
||||
if loc.y < level - 1 then
|
||||
mine(turtle.getAction('forward'))
|
||||
turtle.turnRight()
|
||||
mine(turtle.getAction('forward'))
|
||||
end
|
||||
end
|
||||
|
||||
boreDirection = 'up'
|
||||
status('boring up')
|
||||
|
||||
turtle.turnRight()
|
||||
mine(turtle.getAction('forward'))
|
||||
|
||||
turtle.turnRight()
|
||||
mine(turtle.getAction('forward'))
|
||||
|
||||
turtle.turnLeft()
|
||||
|
||||
while true do
|
||||
if turtle.abort then
|
||||
status('aborting')
|
||||
return false
|
||||
end
|
||||
|
||||
if turtle.point.y > -2 then
|
||||
-- turtle.setDigPolicy(turtle.digPolicies.turtleSafe)
|
||||
end
|
||||
|
||||
while not Util.tryTimed(3, turtle.up) do
|
||||
status('stuck')
|
||||
end
|
||||
if turtle.status == 'stuck' then
|
||||
status('boring up')
|
||||
end
|
||||
|
||||
if loc.y >= level - 1 then
|
||||
break
|
||||
end
|
||||
|
||||
mine(turtle.getAction('forward'))
|
||||
turtle.turnLeft()
|
||||
mine(turtle.getAction('forward'))
|
||||
end
|
||||
|
||||
if turtle.getFuelLevel() < LOW_FUEL then
|
||||
refuel()
|
||||
local veryMinFuel = Point.turtleDistance(turtle.point, { x = 0, y = 0, z = 0}) + 512
|
||||
if turtle.getFuelLevel() < veryMinFuel then
|
||||
log('Not enough fuel to continue')
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function checkSpace()
|
||||
if turtle.getItemCount(16) > 0 then
|
||||
refuel()
|
||||
local oldStatus = turtle.status
|
||||
status('condensing')
|
||||
ejectTrash()
|
||||
turtle.condense()
|
||||
local lastSlot = 16
|
||||
if boreDirection == 'down' then
|
||||
lastSlot = 15
|
||||
end
|
||||
if turtle.getItemCount(lastSlot) > 0 then
|
||||
unload()
|
||||
end
|
||||
status(oldStatus)
|
||||
turtle.select(1)
|
||||
end
|
||||
end
|
||||
|
||||
function collectDrops(suckAction)
|
||||
for i = 1, 50 do
|
||||
if not suckAction() then
|
||||
break
|
||||
end
|
||||
checkSpace()
|
||||
end
|
||||
end
|
||||
|
||||
function Point.compare(pta, ptb)
|
||||
if pta.x == ptb.x and pta.z == ptb.z then
|
||||
if pta.y and ptb.y then
|
||||
return pta.y == ptb.y
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function inspect(action, name)
|
||||
local r, block = action.inspect()
|
||||
if r and block.name == name then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function boreCommand()
|
||||
local pt = getClosestLocation(mining.locations, turtle.point)
|
||||
|
||||
turtle.setMoveCallback(function(action, tpt)
|
||||
makeWalkableTunnel(action, tpt, pt)
|
||||
end)
|
||||
|
||||
safeGotoY(0)
|
||||
safeGoto(pt.x, pt.z, 0)
|
||||
|
||||
turtle.clearMoveCallback()
|
||||
|
||||
-- location is either mined, currently being mined or is the
|
||||
-- dropoff point for a turtle
|
||||
if inspect(turtle.getAction('up'), 'minecraft:cobblestone') or
|
||||
inspect(turtle.getAction('up'), 'minecraft:chest') or
|
||||
inspect(turtle.getAction('down'), 'minecraft:cobblestone') then
|
||||
return true
|
||||
end
|
||||
|
||||
turtle.digUp()
|
||||
turtle.placeUp('minecraft:cobblestone:0')
|
||||
|
||||
local success = bore()
|
||||
|
||||
safeGotoY(0) -- may have aborted
|
||||
turtle.digUp()
|
||||
|
||||
if success then
|
||||
turtle.placeDown('minecraft:cobblestone:0') -- cap with cobblestone to indicate this spot was mined out
|
||||
end
|
||||
|
||||
return success
|
||||
end
|
||||
|
||||
if not Util.getOptions(options, args) then
|
||||
return
|
||||
end
|
||||
|
||||
mining.depth = options.depth.value
|
||||
mining.chunks = options.chunks.value
|
||||
|
||||
unload = normalChestUnload
|
||||
--if options.enderChest.value then
|
||||
-- unload = enderChestUnload
|
||||
--end
|
||||
|
||||
mining.x = 0
|
||||
mining.z = 0
|
||||
mining.locations = getBoreLocations(0, 0)
|
||||
trash = Util.readTable('mining.trash')
|
||||
|
||||
if options.resume.value then
|
||||
mining = Util.readTable('mining.progress')
|
||||
elseif fs.exists('mining.progress') then
|
||||
print('use -r to resume')
|
||||
read()
|
||||
end
|
||||
|
||||
if not trash or options.setTrash.value then
|
||||
print('Add trash blocks, press enter when ready')
|
||||
read()
|
||||
addTrash()
|
||||
end
|
||||
|
||||
if not turtle.getSlot('minecraft:bucket:0') or
|
||||
not turtle.getSlot('minecraft:cobblestone:0') then
|
||||
print('Add bucket and cobblestone, press enter when ready')
|
||||
read()
|
||||
end
|
||||
|
||||
if options.fortunePick.value then
|
||||
local s = turtle.getSlot(options.fortunePick.value)
|
||||
if not s then
|
||||
error('fortunePick not found: ' .. options.fortunePick.value)
|
||||
end
|
||||
if not turtle.getSlot('cctweaks:toolHost:0') then
|
||||
error('CCTweaks tool host not found')
|
||||
end
|
||||
trash[s.iddmg] = nil
|
||||
trash['minecraft:diamond_pickaxe:0'] = nil
|
||||
trash['cctweaks:toolHost:0'] = nil
|
||||
end
|
||||
|
||||
_G._p = trash
|
||||
|
||||
local function main()
|
||||
repeat
|
||||
while #mining.locations > 0 do
|
||||
status('searching')
|
||||
if not boreCommand() then
|
||||
return
|
||||
end
|
||||
Util.writeTable('mining.progress', mining)
|
||||
end
|
||||
until not nextChunk()
|
||||
end
|
||||
|
||||
turtle.run(function()
|
||||
turtle.reset()
|
||||
turtle.setPolicy(turtle.policies.digAttack)
|
||||
turtle.setDigPolicy(turtle.digPolicies.turtleSafe)
|
||||
|
||||
unload()
|
||||
status('mining')
|
||||
|
||||
local s, m = pcall(function() main() end)
|
||||
if not s and m then
|
||||
printError(m)
|
||||
end
|
||||
|
||||
safeGotoY(0)
|
||||
safeGoto(0, 0, 0, 0)
|
||||
unload()
|
||||
turtle.reset()
|
||||
end)
|
@@ -1,174 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local ChestAdapter = require('chestAdapter18')
|
||||
local Event = require('event')
|
||||
local MEAdapter = require('meAdapter')
|
||||
local RefinedAdapter = require('refinedAdapter')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
local storage = RefinedAdapter()
|
||||
if not storage:isValid() then
|
||||
storage = MEAdapter()
|
||||
if not storage:isValid() then
|
||||
storage = ChestAdapter()
|
||||
end
|
||||
end
|
||||
|
||||
if not storage:isValid() then
|
||||
error('Not connected to a storage device')
|
||||
end
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Storage Activity')
|
||||
UI:configure('StorageActivity', ...)
|
||||
|
||||
local changedPage = UI.Page({
|
||||
grid = UI.Grid({
|
||||
columns = {
|
||||
{ heading = 'Qty', key = 'count', width = 5 },
|
||||
{ heading = 'Change', key = 'change', width = 6 },
|
||||
{ heading = 'Name', key = 'displayName', width = UI.term.width - 15 },
|
||||
},
|
||||
sortColumn = 'displayName',
|
||||
rey = -6,
|
||||
}),
|
||||
buttons = UI.Window({
|
||||
ry = -4,
|
||||
height = 5,
|
||||
backgroundColor = colors.gray,
|
||||
prevButton = UI.Button({
|
||||
event = 'previous',
|
||||
backgroundColor = colors.lightGray,
|
||||
x = 2,
|
||||
y = 2,
|
||||
height = 3,
|
||||
width = 5,
|
||||
text = ' < '
|
||||
}),
|
||||
resetButton = UI.Button({
|
||||
event = 'reset',
|
||||
backgroundColor = colors.lightGray,
|
||||
x = 8,
|
||||
y = 2,
|
||||
height = 3,
|
||||
rex = -8,
|
||||
text = 'Reset'
|
||||
}),
|
||||
nextButton = UI.Button({
|
||||
event = 'next',
|
||||
backgroundColor = colors.lightGray,
|
||||
rx = -5,
|
||||
y = 2,
|
||||
height = 3,
|
||||
width = 5,
|
||||
text = ' > '
|
||||
})
|
||||
}),
|
||||
accelerators = {
|
||||
q = 'quit',
|
||||
}
|
||||
})
|
||||
|
||||
function changedPage.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
|
||||
local ind = '+'
|
||||
if row.change < 0 then
|
||||
ind = ''
|
||||
end
|
||||
row.change = ind .. Util.toBytes(row.change)
|
||||
row.count = Util.toBytes(row.count)
|
||||
|
||||
return row
|
||||
end
|
||||
|
||||
function changedPage:eventHandler(event)
|
||||
|
||||
if event.type == 'reset' then
|
||||
self.lastItems = nil
|
||||
self.grid:setValues({ })
|
||||
self.grid:clear()
|
||||
self.grid:draw()
|
||||
|
||||
elseif event.type == 'next' then
|
||||
self.grid:nextPage()
|
||||
|
||||
elseif event.type == 'previous' then
|
||||
self.grid:previousPage()
|
||||
|
||||
elseif event.type == 'quit' then
|
||||
Event.exitPullEvents()
|
||||
|
||||
else
|
||||
return UI.Page.eventHandler(self, event)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function uniqueKey(item)
|
||||
return table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
||||
end
|
||||
|
||||
function changedPage:refresh()
|
||||
local t = storage:listItems()
|
||||
|
||||
if not t or Util.empty(t) then
|
||||
self:clear()
|
||||
self:centeredWrite(math.ceil(self.height/2), 'Communication failure')
|
||||
return
|
||||
end
|
||||
|
||||
for k,v in pairs(t) do
|
||||
t[k] = Util.shallowCopy(v)
|
||||
end
|
||||
|
||||
if not self.lastItems then
|
||||
self.lastItems = t
|
||||
self.grid:setValues({ })
|
||||
else
|
||||
local changedItems = {}
|
||||
for _,v in pairs(self.lastItems) do
|
||||
found = false
|
||||
for k2,v2 in pairs(t) do
|
||||
if uniqueKey(v) == uniqueKey(v2) then
|
||||
if v.count ~= v2.count then
|
||||
local c = Util.shallowCopy(v2)
|
||||
c.lastCount = v.count
|
||||
table.insert(changedItems, c)
|
||||
end
|
||||
table.remove(t, k2)
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
-- New item
|
||||
if not found then
|
||||
local c = Util.shallowCopy(v)
|
||||
c.lastCount = v.count
|
||||
c.count = 0
|
||||
table.insert(changedItems, c)
|
||||
end
|
||||
end
|
||||
-- No items left
|
||||
for k,v in pairs(t) do
|
||||
v.lastCount = 0
|
||||
table.insert(changedItems, v)
|
||||
end
|
||||
|
||||
for k,v in pairs(changedItems) do
|
||||
v.change = v.count - v.lastCount
|
||||
end
|
||||
|
||||
self.grid:setValues(changedItems)
|
||||
end
|
||||
self.grid:draw()
|
||||
end
|
||||
|
||||
Event.onInterval(5, function()
|
||||
changedPage:refresh()
|
||||
changedPage:sync()
|
||||
end)
|
||||
|
||||
UI:setPage(changedPage)
|
||||
UI:pullEvents()
|
@@ -1,901 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Config = require('config')
|
||||
local Event = require('event')
|
||||
local Logger = require('logger')
|
||||
local ME = require('me')
|
||||
local UI = require('ui')
|
||||
local Util = require('util')
|
||||
|
||||
-- Must be a crafty turtle with duck antenna !
|
||||
-- 3 wide monitor (any side of turtle)
|
||||
|
||||
-- Config location is /sys/config/storageMonitor
|
||||
-- adjust directions in that file if needed
|
||||
|
||||
local config = {
|
||||
trashDirection = 'up', -- trash /chest in relation to interface
|
||||
turtleDirection = 'down', -- turtle in relation to interface
|
||||
noCraftingStorage = 'false' -- no ME crafting (or ability to tell if powered - use with caution)
|
||||
}
|
||||
|
||||
Config.load('storageMonitor', config)
|
||||
|
||||
if not device.tileinterface then
|
||||
error('ME interface not found')
|
||||
end
|
||||
|
||||
local duckAntenna
|
||||
|
||||
if device.workbench then
|
||||
|
||||
local oppositeSide = {
|
||||
[ 'left' ] = 'right',
|
||||
[ 'right' ] = 'left'
|
||||
}
|
||||
|
||||
local duckAntennaSide = oppositeSide[device.workbench.side]
|
||||
duckAntenna = peripheral.wrap(duckAntennaSide)
|
||||
end
|
||||
--if not device.monitor then
|
||||
-- error('Monitor not found')
|
||||
--end
|
||||
|
||||
ME.setDevice(device.tileinterface)
|
||||
|
||||
local jobListGrid
|
||||
local craftingPaused = false
|
||||
|
||||
multishell.setTitle(multishell.getCurrent(), 'Storage Manager')
|
||||
|
||||
Logger.disable()
|
||||
|
||||
function getItem(items, inItem, ignore_dmg)
|
||||
for _,item in pairs(items) do
|
||||
if item.id == inItem.id then
|
||||
if ignore_dmg and ignore_dmg == 'yes' then
|
||||
return item
|
||||
elseif item.dmg == inItem.dmg and item.nbt_hash == inItem.nbt_hash then
|
||||
return item
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function uniqueKey(item)
|
||||
local key = item.id .. ':' .. item.dmg
|
||||
if item.nbt_hash then
|
||||
key = key .. ':' .. item.nbt_hash
|
||||
end
|
||||
return key
|
||||
end
|
||||
|
||||
function mergeResources(t)
|
||||
local resources = Util.readTable('resource.limits')
|
||||
resources = resources or { }
|
||||
|
||||
for _,item in pairs(t) do
|
||||
item.has_recipe = false
|
||||
end
|
||||
|
||||
for _,v in pairs(resources) do
|
||||
local item = getItem(t, v)
|
||||
if item then
|
||||
item.limit = tonumber(v.limit)
|
||||
item.low = tonumber(v.low)
|
||||
item.auto = v.auto
|
||||
item.ignore_dmg = v.ignore_dmg
|
||||
else
|
||||
v.qty = 0
|
||||
v.limit = tonumber(v.limit)
|
||||
v.low = tonumber(v.low)
|
||||
v.auto = v.auto
|
||||
v.ignore_dmg = v.ignore_dmg
|
||||
table.insert(t, v)
|
||||
end
|
||||
end
|
||||
|
||||
recipes = Util.readTable('recipes') or { }
|
||||
|
||||
for _,v in pairs(recipes) do
|
||||
local item = getItem(t, v)
|
||||
if item then
|
||||
item.has_recipe = true
|
||||
else
|
||||
v.qty = 0
|
||||
v.limit = nil
|
||||
v.low = nil
|
||||
v.has_recipe = true
|
||||
v.auto = 'no'
|
||||
v.ignore_dmg = 'no'
|
||||
v.has_recipe = 'true'
|
||||
table.insert(t, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function filterItems(t, filter)
|
||||
local r = {}
|
||||
if filter then
|
||||
filter = filter:lower()
|
||||
for k,v in pairs(t) do
|
||||
if string.find(v.lname, filter) then
|
||||
table.insert(r, v)
|
||||
end
|
||||
end
|
||||
else
|
||||
return t
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
function sumItems(items)
|
||||
local t = {}
|
||||
|
||||
for _,item in pairs(items) do
|
||||
local key = uniqueKey(item)
|
||||
local summedItem = t[key]
|
||||
if summedItem then
|
||||
summedItem.qty = summedItem.qty + item.qty
|
||||
else
|
||||
summedItem = Util.shallowCopy(item)
|
||||
t[key] = summedItem
|
||||
end
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function isGridClear()
|
||||
for i = 1, 16 do
|
||||
if turtle.getItemCount(i) ~= 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function clearGrid()
|
||||
for i = 1, 16 do
|
||||
local count = turtle.getItemCount(i)
|
||||
if count > 0 then
|
||||
ME.insert(i, count, config.turtleDirection)
|
||||
if turtle.getItemCount(i) ~= 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function turtleCraft(recipe, originalItem)
|
||||
|
||||
for k,v in pairs(recipe.ingredients) do
|
||||
|
||||
-- ugh
|
||||
local dmg = v.dmg
|
||||
|
||||
if v.max_dmg and v.max_dmg > 0 then
|
||||
local item = ME.getItemDetail({ id = v.id, nbt_hash = v.nbt_hash }, false)
|
||||
if item then
|
||||
dmg = item.dmg
|
||||
end
|
||||
end
|
||||
|
||||
if not ME.extract(v.id, dmg, v.nbt_hash, v.qty, config.turtleDirection, k) then
|
||||
clearGrid()
|
||||
originalItem.status = v.name .. ' (extract failed)'
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if not turtle.craft() then
|
||||
clearGrid()
|
||||
return false
|
||||
end
|
||||
|
||||
clearGrid()
|
||||
return true
|
||||
end
|
||||
|
||||
function craftItem(items, recipes, item, originalItem, itemList)
|
||||
|
||||
local key = uniqueKey(item)
|
||||
local recipe = recipes[key]
|
||||
|
||||
if recipe then
|
||||
|
||||
if not isGridClear() then
|
||||
return
|
||||
end
|
||||
|
||||
local summedItems = sumItems(recipe.ingredients)
|
||||
|
||||
for i = 1, math.ceil(item.qty / recipe.qty) do
|
||||
|
||||
local failed = false -- try to craft all components (use all CPUs available)
|
||||
|
||||
for _,ingredient in pairs(summedItems) do
|
||||
local ignore_dmg = 'no'
|
||||
if ingredient.max_dmg and ingredient.max_dmg > 0 then
|
||||
ignore_dmg = 'yes'
|
||||
end
|
||||
local qty = ME.getItemCount(ingredient.id, ingredient.dmg, ingredient.nbt_hash, ignore_dmg)
|
||||
if qty < ingredient.qty then
|
||||
originalItem.status = ingredient.name .. ' (crafting)'
|
||||
ingredient.qty = ingredient.qty - qty
|
||||
if not craftItem(items, recipes, ingredient, originalItem, itemList) then
|
||||
failed = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if failed then
|
||||
return false
|
||||
end
|
||||
|
||||
if not failed and not turtleCraft(recipe, originalItem) then
|
||||
Logger.debug('turtle failed to craft ' .. item.name)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
else
|
||||
|
||||
local meItem = getItem(items, item)
|
||||
if not meItem or not meItem.is_craftable then
|
||||
|
||||
if item.id == originalItem.id and item.dmg == originalItem.dmg then
|
||||
originalItem.status = '(not craftable)'
|
||||
else
|
||||
originalItem.status = item.name .. ' (missing)'
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
if item.id == originalItem.id and item.dmg == originalItem.dmg then
|
||||
item.meCraft = true
|
||||
return false
|
||||
end
|
||||
|
||||
-- find it in the list of items to be crafted
|
||||
for _,v in pairs(itemList) do
|
||||
if v.id == item.id and v.dmg == item.dmg and v.nbt_hash == item.nbt_hash then
|
||||
v.qty = item.qty + v.qty
|
||||
return false
|
||||
end
|
||||
end
|
||||
-- add to the item list
|
||||
table.insert(itemList, {
|
||||
id = item.id,
|
||||
dmg = item.dmg,
|
||||
nbt_hash = item.nbt_hash,
|
||||
qty = item.qty,
|
||||
name = item.name,
|
||||
meCraft = true,
|
||||
status = ''
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function craftItems(itemList)
|
||||
|
||||
local recipes = Util.readTable('recipes') or { }
|
||||
local items = ME.getAvailableItems()
|
||||
|
||||
-- turtle craft anything we can, build up list for ME items
|
||||
local keys = Util.keys(itemList)
|
||||
for _,key in pairs(keys) do
|
||||
local item = itemList[key]
|
||||
craftItem(items, recipes, item, item, itemList)
|
||||
end
|
||||
|
||||
-- second pass is to request crafting from ME with aggregated items
|
||||
for _,item in pairs(itemList) do
|
||||
if item.meCraft then
|
||||
|
||||
local alreadyCrafting = false
|
||||
local jobList = ME.getJobList()
|
||||
|
||||
for _,v in pairs(jobList) do
|
||||
if v.id == item.id and v.dmg == item.dmg and v.nbt_hash == item.nbt_hash then
|
||||
alreadyCrafting = true
|
||||
end
|
||||
end
|
||||
|
||||
if alreadyCrafting then
|
||||
item.status = '(crafting)'
|
||||
elseif not ME.isCPUAvailable() then
|
||||
item.status = '(waiting)'
|
||||
else
|
||||
item.status = '(failed)'
|
||||
|
||||
local qty = item.qty
|
||||
while qty >= 1 do -- try to request smaller quantities until successful
|
||||
if ME.craft(item.id, item.dmg, item.nbt_hash, qty) then
|
||||
item.status = '(crafting)'
|
||||
break -- successfully requested crafting
|
||||
end
|
||||
qty = math.floor(qty / 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- AE 1 (obsolete)
|
||||
function isCrafting(jobList, id, dmg)
|
||||
for _, job in pairs(jobList) do
|
||||
if job.id == id and job.dmg == dmg then
|
||||
return job
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local nullDevice = {
|
||||
setCursorPos = function(...) end,
|
||||
write = function(...) end,
|
||||
getSize = function() return 13, 20 end,
|
||||
isColor = function() return false end,
|
||||
setBackgroundColor = function(...) end,
|
||||
setTextColor = function(...) end,
|
||||
clear = function(...) end,
|
||||
}
|
||||
|
||||
local function jobMonitor(jobList)
|
||||
|
||||
local mon
|
||||
|
||||
if device.monitor then
|
||||
mon = UI.Device({
|
||||
deviceType = 'monitor',
|
||||
textScale = .5,
|
||||
})
|
||||
else
|
||||
mon = UI.Device({
|
||||
device = nullDevice
|
||||
})
|
||||
end
|
||||
|
||||
jobListGrid = UI.Grid({
|
||||
parent = mon,
|
||||
sortColumn = 'name',
|
||||
columns = {
|
||||
{ heading = 'Qty', key = 'qty', width = 6 },
|
||||
{ heading = 'Crafting', key = 'name', width = mon.width / 2 - 10 },
|
||||
{ heading = 'Status', key = 'status', width = mon.width - 10 },
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
function getAutocraftItems(items)
|
||||
local t = Util.readTable('resource.limits') or { }
|
||||
local itemList = { }
|
||||
|
||||
for _,res in pairs(t) do
|
||||
|
||||
if res.auto and res.auto == 'yes' then
|
||||
res.qty = 4 -- this could be higher to increase autocrafting speed
|
||||
table.insert(itemList, res)
|
||||
end
|
||||
end
|
||||
return itemList
|
||||
end
|
||||
|
||||
local function getItemWithQty(items, res, ignore_dmg)
|
||||
|
||||
local item = getItem(items, res, ignore_dmg)
|
||||
|
||||
if item then
|
||||
|
||||
if ignore_dmg and ignore_dmg == 'yes' then
|
||||
local qty = 0
|
||||
|
||||
for _,v in pairs(items) do
|
||||
if item.id == v.id and item.nbt_hash == v.nbt_hash then
|
||||
if item.max_dmg > 0 or item.dmg == v.dmg then
|
||||
qty = qty + v.qty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
item.qty = qty
|
||||
end
|
||||
end
|
||||
|
||||
return item
|
||||
end
|
||||
|
||||
function watchResources(items)
|
||||
|
||||
local itemList = { }
|
||||
|
||||
local t = Util.readTable('resource.limits') or { }
|
||||
for k, res in pairs(t) do
|
||||
local item = getItemWithQty(items, res, res.ignore_dmg)
|
||||
res.limit = tonumber(res.limit)
|
||||
res.low = tonumber(res.low)
|
||||
if not item then
|
||||
item = {
|
||||
id = res.id,
|
||||
dmg = res.dmg,
|
||||
nbt_hash = res.nbt_hash,
|
||||
name = res.name,
|
||||
qty = 0
|
||||
}
|
||||
end
|
||||
|
||||
if res.limit and item.qty > res.limit then
|
||||
Logger.debug("Purging " .. item.qty-res.limit .. " " .. res.name)
|
||||
if not ME.extract(item.id, item.dmg, item.nbt_hash, item.qty - res.limit, config.trashDirection) then
|
||||
Logger.debug('Failed to purge ' .. res.name)
|
||||
end
|
||||
|
||||
elseif res.low and item.qty < res.low then
|
||||
if res.ignore_dmg and res.ignore_dmg == 'yes' then
|
||||
item.dmg = 0
|
||||
end
|
||||
table.insert(itemList, {
|
||||
id = item.id,
|
||||
dmg = item.dmg,
|
||||
nbt_hash = item.nbt_hash,
|
||||
qty = res.low - item.qty,
|
||||
name = item.name,
|
||||
status = ''
|
||||
})
|
||||
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
|
||||
},
|
||||
idField = UI.Text {
|
||||
x = 5, y = 3, width = UI.term.width - 10,
|
||||
},
|
||||
form = UI.Form {
|
||||
x = 4, y = 4, height = 8, rex = -4,
|
||||
[1] = UI.TextEntry {
|
||||
width = 7,
|
||||
backgroundColor = colors.gray,
|
||||
backgroundFocusColor = colors.gray,
|
||||
formLabel = 'Min', formKey = 'low', help = 'Craft if below min'
|
||||
},
|
||||
[2] = UI.TextEntry {
|
||||
width = 7,
|
||||
backgroundColor = colors.gray,
|
||||
backgroundFocusColor = colors.gray,
|
||||
formLabel = 'Max', formKey = 'limit', help = 'Eject if above max'
|
||||
},
|
||||
[3] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'Autocraft', formKey = 'auto',
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = 'yes' },
|
||||
{ name = 'No', value = 'no' },
|
||||
},
|
||||
help = 'Craft until out of ingredients'
|
||||
},
|
||||
[4] = UI.Chooser {
|
||||
width = 7,
|
||||
formLabel = 'Ignore Dmg', formKey = 'ignore_dmg',
|
||||
nochoice = 'No',
|
||||
choices = {
|
||||
{ name = 'Yes', value = 'yes' },
|
||||
{ name = 'No', value = 'no' },
|
||||
},
|
||||
help = 'Ignore damage of item'
|
||||
},
|
||||
},
|
||||
statusBar = UI.StatusBar { }
|
||||
}
|
||||
|
||||
function itemPage:enable()
|
||||
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 { }
|
||||
for k,v in pairs(t) do
|
||||
if v.id == values.id and v.dmg == values.dmg then
|
||||
table.remove(t, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
local keys = { 'name', 'auto', 'id', 'low', 'dmg', 'max_dmg', 'nbt_hash', 'limit', 'ignore_dmg' }
|
||||
local filtered = { }
|
||||
for _,key in pairs(keys) do
|
||||
filtered[key] = values[key]
|
||||
end
|
||||
|
||||
table.insert(t, 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 = 'Learn', event = 'learn' },
|
||||
{ text = 'Forget', event = 'forget' },
|
||||
},
|
||||
},
|
||||
grid = UI.Grid {
|
||||
y = 2, height = UI.term.height - 2,
|
||||
columns = {
|
||||
{ heading = 'Name', key = 'name' , width = 22 },
|
||||
{ heading = 'Qty', key = 'qty' , width = 5 },
|
||||
{ heading = 'Min', key = 'low' , width = 4 },
|
||||
{ heading = 'Max', key = 'limit', width = 4 },
|
||||
},
|
||||
sortColumn = 'name',
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
backgroundColor = colors.gray,
|
||||
width = UI.term.width,
|
||||
filterText = UI.Text {
|
||||
x = 2, width = 6,
|
||||
value = 'Filter',
|
||||
},
|
||||
filter = UI.TextEntry {
|
||||
x = 9, width = 19,
|
||||
limit = 50,
|
||||
},
|
||||
refresh = UI.Button {
|
||||
x = 31, width = 8,
|
||||
text = 'Refresh',
|
||||
event = 'refresh',
|
||||
},
|
||||
},
|
||||
accelerators = {
|
||||
r = 'refresh',
|
||||
q = 'quit',
|
||||
}
|
||||
}
|
||||
|
||||
function listingPage.grid:getRowTextColor(row, selected)
|
||||
if row.is_craftable then
|
||||
return colors.yellow
|
||||
end
|
||||
if row.has_recipe then
|
||||
if selected then
|
||||
return colors.blue
|
||||
end
|
||||
return colors.lightBlue
|
||||
end
|
||||
return UI.Grid:getRowTextColor(row, selected)
|
||||
end
|
||||
|
||||
function listingPage.grid:getDisplayValues(row)
|
||||
row = Util.shallowCopy(row)
|
||||
row.qty = Util.toBytes(row.qty)
|
||||
if row.low then
|
||||
row.low = Util.toBytes(row.low)
|
||||
end
|
||||
if row.limit then
|
||||
row.limit = Util.toBytes(row.limit)
|
||||
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
|
||||
Event.exitPullEvents()
|
||||
|
||||
elseif event.type == 'grid_select' then
|
||||
local selected = event.selected
|
||||
itemPage.form:setValues(selected)
|
||||
itemPage.titleBar.title = selected.name
|
||||
itemPage.idField.value = selected.id
|
||||
UI:setPage('item')
|
||||
|
||||
elseif event.type == 'refresh' then
|
||||
self:refresh()
|
||||
self.grid:draw()
|
||||
|
||||
elseif event.type == 'learn' then
|
||||
if not duckAntenna then
|
||||
self.statusBar:timedStatus('Missing peripherals', 3)
|
||||
else
|
||||
UI:setPage('craft')
|
||||
end
|
||||
|
||||
elseif event.type == 'forget' then
|
||||
local item = self.grid:getSelected()
|
||||
if item then
|
||||
local recipes = Util.readTable('recipes') or { }
|
||||
local key = uniqueKey(item)
|
||||
local recipe = recipes[key]
|
||||
|
||||
if recipe then
|
||||
recipes[key] = nil
|
||||
Util.writeTable('recipes', recipes)
|
||||
end
|
||||
|
||||
local resources = Util.readTable('resource.limits') or { }
|
||||
for k,v in pairs(resources) do
|
||||
if v.id == item.id and v.dmg == item.dmg then
|
||||
table.remove(resources, k)
|
||||
Util.writeTable('resource.limits', resources)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
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 = ME.getAvailableItems('all')
|
||||
|
||||
mergeResources(self.allItems)
|
||||
|
||||
Util.each(self.allItems, function(item)
|
||||
item.lname = item.name:lower()
|
||||
end)
|
||||
|
||||
self:applyFilter()
|
||||
end
|
||||
|
||||
function listingPage:applyFilter()
|
||||
local t = filterItems(self.allItems, self.filter)
|
||||
self.grid:setValues(t)
|
||||
end
|
||||
|
||||
-- without duck antenna
|
||||
local function getTurtleInventory()
|
||||
local inventory = { }
|
||||
for i = 1,16 do
|
||||
if turtle.getItemCount(i) > 0 then
|
||||
turtle.select(i)
|
||||
local item = turtle.getItemDetail()
|
||||
inventory[i] = {
|
||||
id = item.name,
|
||||
dmg = item.damage,
|
||||
qty = item.count,
|
||||
name = item.name,
|
||||
}
|
||||
end
|
||||
end
|
||||
return inventory
|
||||
end
|
||||
|
||||
-- Strip off color prefix
|
||||
local function safeString(text)
|
||||
|
||||
local val = text:byte(1)
|
||||
|
||||
if val < 32 or val > 128 then
|
||||
|
||||
local newText = {}
|
||||
for i = 4, #text do
|
||||
local val = text:byte(i)
|
||||
newText[i - 3] = (val > 31 and val < 127) and val or 63
|
||||
end
|
||||
return string.char(unpack(newText))
|
||||
end
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
local function filter(t, filter)
|
||||
local keys = Util.keys(t)
|
||||
for _,key in pairs(keys) do
|
||||
if not Util.key(filter, key) then
|
||||
t[key] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function learnRecipe(page)
|
||||
local t = Util.readTable('recipes') or { }
|
||||
local recipe = { }
|
||||
local ingredients = duckAntenna.getAllStacks(false) -- getTurtleInventory()
|
||||
if ingredients then
|
||||
turtle.select(1)
|
||||
if turtle.craft() then
|
||||
recipe = duckAntenna.getAllStacks(false) -- getTurtleInventory()
|
||||
if recipe and recipe[1] then
|
||||
recipe = recipe[1]
|
||||
local key = uniqueKey(recipe)
|
||||
|
||||
clearGrid()
|
||||
|
||||
recipe.name = safeString(recipe.display_name)
|
||||
filter(recipe, { 'name', 'id', 'dmg', 'nbt_hash', 'qty', 'max_size' })
|
||||
|
||||
for _,ingredient in pairs(ingredients) do
|
||||
ingredient.name = safeString(ingredient.display_name)
|
||||
filter(ingredient, { 'name', 'id', 'dmg', 'nbt_hash', 'qty', 'max_size', 'max_dmg' })
|
||||
|
||||
if ingredient.max_dmg > 0 then -- let's try this...
|
||||
ingredient.dmg = 0
|
||||
end
|
||||
end
|
||||
recipe.ingredients = ingredients
|
||||
recipe.ignore_dmg = 'no'
|
||||
|
||||
t[key] = recipe
|
||||
|
||||
Util.writeTable('recipes', t)
|
||||
listingPage.statusBar.filter:setValue(recipe.name)
|
||||
listingPage.statusBar:timedStatus('Learned: ' .. recipe.name, 3)
|
||||
listingPage.filter = recipe.name
|
||||
listingPage:refresh()
|
||||
listingPage.grid:draw()
|
||||
|
||||
return true
|
||||
end
|
||||
else
|
||||
page.statusBar:timedStatus('Failed to craft', 3)
|
||||
end
|
||||
else
|
||||
page.statusBar:timedStatus('No recipe defined', 3)
|
||||
end
|
||||
end
|
||||
|
||||
craftPage = UI.Dialog {
|
||||
height = 7, width = UI.term.width - 6,
|
||||
backgroundColor = colors.lightGray,
|
||||
titleBar = UI.TitleBar {
|
||||
title = 'Learn Recipe',
|
||||
previousPage = true,
|
||||
},
|
||||
idField = UI.Text {
|
||||
x = 5,
|
||||
y = 3,
|
||||
width = UI.term.width - 10,
|
||||
value = 'Place recipe in turtle'
|
||||
},
|
||||
accept = UI.Button {
|
||||
rx = -13, ry = -2,
|
||||
text = 'Ok', event = 'accept',
|
||||
},
|
||||
cancel = UI.Button {
|
||||
rx = -8, ry = -2,
|
||||
text = 'Cancel', event = 'cancel'
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
status = 'Crafting paused'
|
||||
}
|
||||
}
|
||||
|
||||
function craftPage:enable()
|
||||
craftingPaused = true
|
||||
self:focusFirst()
|
||||
UI.Dialog.enable(self)
|
||||
end
|
||||
|
||||
function craftPage:disable()
|
||||
craftingPaused = false
|
||||
UI.Dialog.disable(self)
|
||||
end
|
||||
|
||||
function craftPage:eventHandler(event)
|
||||
if event.type == 'cancel' then
|
||||
UI:setPreviousPage()
|
||||
elseif event.type == 'accept' then
|
||||
if learnRecipe(self) then
|
||||
UI:setPreviousPage()
|
||||
end
|
||||
else
|
||||
return UI.Dialog.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
UI:setPages({
|
||||
listing = listingPage,
|
||||
item = itemPage,
|
||||
craft = craftPage,
|
||||
})
|
||||
|
||||
UI:setPage(listingPage)
|
||||
listingPage:setFocus(listingPage.statusBar.filter)
|
||||
|
||||
clearGrid()
|
||||
jobMonitor()
|
||||
jobListGrid:draw()
|
||||
jobListGrid:sync()
|
||||
|
||||
Event.onInterval(5, function()
|
||||
|
||||
if not craftingPaused then
|
||||
|
||||
local items = ME.getAvailableItems()
|
||||
|
||||
if Util.size(items) == 0 then
|
||||
jobListGrid.parent:clear()
|
||||
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'No items in system')
|
||||
jobListGrid:sync()
|
||||
|
||||
elseif config.noCraftingStorage ~= 'true' and #ME.getCraftingCPUs() <= 0 then -- only way to determine if AE is online
|
||||
jobListGrid.parent:clear()
|
||||
jobListGrid.parent:centeredWrite(math.ceil(jobListGrid.parent.height/2), 'Power failure')
|
||||
jobListGrid:sync()
|
||||
|
||||
else
|
||||
local itemList = watchResources(items)
|
||||
jobListGrid:setValues(itemList)
|
||||
jobListGrid:draw()
|
||||
jobListGrid:sync()
|
||||
craftItems(itemList)
|
||||
jobListGrid:update()
|
||||
jobListGrid:draw()
|
||||
jobListGrid:sync()
|
||||
|
||||
itemList = getAutocraftItems(items) -- autocrafted items don't show on job monitor
|
||||
craftItems(itemList)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
UI:pullEvents()
|
||||
jobListGrid.parent:reset()
|
@@ -1,438 +0,0 @@
|
||||
requireInjector(getfenv(1))
|
||||
|
||||
local Event = require('event')
|
||||
local Logger = require('logger')
|
||||
local MEProvider = require('meProvider')
|
||||
local Message = require('message')
|
||||
local Point = require('point')
|
||||
local TableDB = require('tableDB')
|
||||
local Util = require('util')
|
||||
|
||||
--[[
|
||||
A supplier turtle for the builder turtle. For larger builds, use
|
||||
ender modems.
|
||||
|
||||
Setup:
|
||||
|
||||
1. chest or ME interface at level 0 (bottom of build area)
|
||||
2. builder turtle on top facing the build area
|
||||
3. If facing the build turtle, the supplier turtle is to the right
|
||||
pointing at the chest/interface
|
||||
]]--
|
||||
|
||||
local ChestProvider = require('chestProvider')
|
||||
if Util.getVersion() == 1.8 then
|
||||
ChestProvider = require('chestProvider18')
|
||||
end
|
||||
|
||||
if not device.wireless_modem then
|
||||
error('No wireless modem detected')
|
||||
end
|
||||
|
||||
Logger.filter('modem_send', 'event', 'ui')
|
||||
Logger.setWirelessLogging()
|
||||
|
||||
local __BUILDER_ID = 6
|
||||
local itemInfoDB
|
||||
|
||||
local Builder = {
|
||||
version = '1.70',
|
||||
ccVersion = nil,
|
||||
slots = { },
|
||||
index = 1,
|
||||
fuelItem = { id = 'minecraft:coal', dmg = 0 },
|
||||
resupplying = true,
|
||||
ready = true,
|
||||
}
|
||||
|
||||
--[[-- maxStackDB --]]--
|
||||
local maxStackDB = TableDB({
|
||||
fileName = 'maxstack.db',
|
||||
tabledef = {
|
||||
autokeys = false,
|
||||
type = 'simple',
|
||||
columns = {
|
||||
{ label = 'Key', type = 'key', length = 8 },
|
||||
{ label = 'Quantity', type = 'number', length = 2 }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function maxStackDB:get(id, dmg)
|
||||
return self.data[id .. ':' .. dmg] or 64
|
||||
end
|
||||
|
||||
function Builder:dumpInventory()
|
||||
|
||||
local success = true
|
||||
|
||||
for i = 1, 16 do
|
||||
local qty = turtle.getItemCount(i)
|
||||
if qty > 0 then
|
||||
self.itemProvider:insert(i, qty)
|
||||
end
|
||||
if turtle.getItemCount(i) ~= 0 then
|
||||
success = false
|
||||
end
|
||||
end
|
||||
turtle.select(1)
|
||||
|
||||
return success
|
||||
end
|
||||
|
||||
function Builder:dumpInventoryWithCheck()
|
||||
while not self:dumpInventory() do
|
||||
Builder:log('Unable to dump inventory')
|
||||
print('Provider is full or missing - make space or replace')
|
||||
print('Press enter to continue')
|
||||
--turtle.setHeading(0)
|
||||
self.ready = false
|
||||
read()
|
||||
end
|
||||
self.ready = true
|
||||
end
|
||||
|
||||
function Builder:autocraft(supplies)
|
||||
local t = { }
|
||||
|
||||
for i,s in pairs(supplies) do
|
||||
local key = s.id .. ':' .. s.dmg
|
||||
local item = t[key]
|
||||
if not item then
|
||||
item = {
|
||||
id = s.id,
|
||||
dmg = s.dmg,
|
||||
qty = 0,
|
||||
}
|
||||
t[key] = item
|
||||
end
|
||||
item.qty = item.qty + (s.need-s.qty)
|
||||
end
|
||||
|
||||
Builder.itemProvider:craftItems(t)
|
||||
end
|
||||
|
||||
function Builder:refuel()
|
||||
while turtle.getFuelLevel() < 4000 and self.fuelItem do
|
||||
Builder:log('Refueling')
|
||||
turtle.select(1)
|
||||
self.itemProvider:provide(self.fuelItem, 64, 1)
|
||||
if turtle.getItemCount(1) == 0 then
|
||||
Builder:log('Out of fuel, add coal to chest/ME system')
|
||||
--turtle.setHeading(0)
|
||||
os.sleep(5)
|
||||
else
|
||||
turtle.refuel(64)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Builder:log(...)
|
||||
Logger.log('supplier', ...)
|
||||
Util.print(...)
|
||||
end
|
||||
|
||||
function Builder:getSupplies()
|
||||
|
||||
Builder.itemProvider:refresh()
|
||||
|
||||
local t = { }
|
||||
for _,s in ipairs(self.slots) do
|
||||
if s.need > 0 then
|
||||
local item = Builder.itemProvider:getItemInfo(s.id, s.dmg)
|
||||
if item then
|
||||
if item.name then
|
||||
s.name = item.name
|
||||
end
|
||||
|
||||
local qty = math.min(s.need-s.qty, item.qty)
|
||||
|
||||
if qty + s.qty > item.max_size then
|
||||
maxStackDB:add({ s.id, s.dmg }, item.max_size)
|
||||
maxStackDB.dirty = true
|
||||
maxStackDB:flush()
|
||||
qty = item.max_size
|
||||
s.need = qty
|
||||
end
|
||||
if qty > 0 then
|
||||
self.itemProvider:provide(item, qty, s.index)
|
||||
s.qty = turtle.getItemCount(s.index)
|
||||
end
|
||||
end
|
||||
end
|
||||
if s.qty < s.need then
|
||||
table.insert(t, s)
|
||||
local name = s.name or s.id .. ':' .. s.dmg
|
||||
local item = itemInfoDB:get({ s.id, s.dmg })
|
||||
if item then
|
||||
name = item.displayName
|
||||
end
|
||||
|
||||
Builder:log('Need %d %s', s.need - s.qty, name)
|
||||
end
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
local function moveTowardsX(dx)
|
||||
|
||||
local direction = dx - turtle.point.x
|
||||
local move
|
||||
|
||||
if direction == 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
if direction > 0 and turtle.point.heading == 0 or
|
||||
direction < 0 and turtle.point.heading == 2 then
|
||||
move = turtle.forward
|
||||
else
|
||||
move = turtle.back
|
||||
end
|
||||
|
||||
return move()
|
||||
end
|
||||
|
||||
local function moveTowardsZ(dz)
|
||||
|
||||
local direction = dz - turtle.point.z
|
||||
local move
|
||||
|
||||
if direction == 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
if direction > 0 and turtle.point.heading == 1 or
|
||||
direction < 0 and turtle.point.heading == 3 then
|
||||
move = turtle.forward
|
||||
else
|
||||
move = turtle.back
|
||||
end
|
||||
|
||||
return move()
|
||||
end
|
||||
|
||||
function Builder:finish()
|
||||
|
||||
Builder.resupplying = true
|
||||
Builder.ready = false
|
||||
if turtle.gotoLocation('supplies') then
|
||||
turtle.setHeading(1)
|
||||
os.sleep(.1) -- random 'Computer is not connected' error...
|
||||
Builder:dumpInventory()
|
||||
Event.exitPullEvents()
|
||||
print('Finished')
|
||||
end
|
||||
end
|
||||
|
||||
function Builder:gotoBuilder()
|
||||
|
||||
if Builder.lastPoint then
|
||||
turtle.status = 'tracking'
|
||||
while true do
|
||||
local pt = Point.copy(Builder.lastPoint)
|
||||
pt.y = pt.y + 3
|
||||
if turtle.point.y ~= pt.y then
|
||||
turtle.gotoY(pt.y)
|
||||
else
|
||||
local distance = Point.turtleDistance(turtle.point, pt)
|
||||
if distance <= 3 then
|
||||
Builder:log('Synchronized')
|
||||
break
|
||||
end
|
||||
|
||||
if turtle.point.heading % 2 == 0 then
|
||||
if turtle.point.x == pt.x then
|
||||
turtle.headTowardsZ(pt.z)
|
||||
moveTowardsZ(pt.z)
|
||||
else
|
||||
moveTowardsX(pt.x)
|
||||
end
|
||||
elseif turtle.point.z ~= pt.z then
|
||||
moveTowardsZ(pt.z)
|
||||
else
|
||||
turtle.headTowardsX(pt.x)
|
||||
moveTowardsX(pt.x)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Message.addHandler('builder',
|
||||
function(h, id, msg, distance)
|
||||
if not id or id ~= __BUILDER_ID then
|
||||
return
|
||||
end
|
||||
|
||||
if not Builder.resupplying then
|
||||
local pt = msg.contents
|
||||
pt.y = pt.y + 3
|
||||
|
||||
turtle.status = 'supervising'
|
||||
turtle.gotoYfirst(pt)
|
||||
end
|
||||
end)
|
||||
|
||||
Message.addHandler('supplyList',
|
||||
function(h, id, msg, distance)
|
||||
if not id or id ~= __BUILDER_ID then
|
||||
return
|
||||
end
|
||||
|
||||
turtle.status = 'resupplying'
|
||||
Builder.resupplying = true
|
||||
Builder.slots = msg.contents.slots
|
||||
Builder.slotUid = msg.contents.uid
|
||||
|
||||
Builder:log('Received supply list ' .. Builder.slotUid)
|
||||
|
||||
os.sleep(0)
|
||||
if not turtle.gotoLocation('supplies') then
|
||||
Builder:log('Failed to go to supply location')
|
||||
self.ready = false
|
||||
Event.exitPullEvents()
|
||||
end
|
||||
turtle.setHeading(1)
|
||||
os.sleep(.2) -- random 'Computer is not connected' error...
|
||||
Builder:dumpInventoryWithCheck()
|
||||
Builder:refuel()
|
||||
|
||||
while true do
|
||||
local supplies = Builder:getSupplies()
|
||||
if #supplies == 0 then
|
||||
break
|
||||
end
|
||||
Builder:autocraft(supplies)
|
||||
turtle.status = 'waiting'
|
||||
os.sleep(5)
|
||||
end
|
||||
Builder:log('Got all supplies')
|
||||
os.sleep(0)
|
||||
Builder:gotoBuilder()
|
||||
Builder.resupplying = false
|
||||
end)
|
||||
|
||||
Message.addHandler('needSupplies',
|
||||
function(h, id, msg, distance)
|
||||
if not id or id ~= __BUILDER_ID then
|
||||
return
|
||||
end
|
||||
|
||||
if Builder.resupplying or msg.contents.uid ~= Builder.slotUid then
|
||||
|
||||
Builder:log('No supplies ready')
|
||||
|
||||
Message.send(__BUILDER_ID, 'gotSupplies')
|
||||
else
|
||||
turtle.status = 'supplying'
|
||||
Builder:log('Supplying')
|
||||
os.sleep(0)
|
||||
|
||||
local pt = msg.contents.point
|
||||
pt.y = turtle.getPoint().y
|
||||
pt.heading = nil
|
||||
if not turtle.gotoYfirst(pt) then -- location of builder
|
||||
Builder.resupplying = true
|
||||
Message.send(__BUILDER_ID, 'gotSupplies')
|
||||
os.sleep(0)
|
||||
if not turtle.gotoLocation('supplies') then
|
||||
Builder:log('failed to go to supply location')
|
||||
--self.ready = false
|
||||
Event.exitPullEvents()
|
||||
end
|
||||
turtle.setHeading(1)
|
||||
return
|
||||
end
|
||||
pt.y = pt.y - 2 -- location where builder should go for the chest to be above
|
||||
|
||||
turtle.select(15)
|
||||
turtle.placeDown()
|
||||
os.sleep(.1) -- random computer not connected error
|
||||
local p = ChestProvider({ direction = 'up', wrapSide = 'bottom' })
|
||||
for i = 1, 16 do
|
||||
p:insert(i, 64)
|
||||
end
|
||||
|
||||
Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true, point = pt })
|
||||
|
||||
Message.waitForMessage('thanks', 5, __BUILDER_ID)
|
||||
--os.sleep(0)
|
||||
|
||||
--p.condenseItems()
|
||||
for i = 1, 16 do
|
||||
p:extract(i, 64)
|
||||
end
|
||||
turtle.digDown()
|
||||
turtle.status = 'waiting'
|
||||
end
|
||||
end)
|
||||
|
||||
Message.addHandler('finished',
|
||||
function(h, id)
|
||||
if not id or id ~= __BUILDER_ID then
|
||||
return
|
||||
end
|
||||
Builder:finish()
|
||||
end)
|
||||
|
||||
Event.on('turtle_abort',
|
||||
function()
|
||||
turtle.abort = false
|
||||
turtle.status = 'aborting'
|
||||
Builder:finish()
|
||||
end)
|
||||
|
||||
local function onTheWay() -- parallel routine
|
||||
while true do
|
||||
local e, side, _id, id, msg, distance = os.pullEvent('modem_message')
|
||||
if Builder.ready then
|
||||
if id == __BUILDER_ID and msg and msg.type then
|
||||
if msg.type == 'needSupplies' then
|
||||
Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true })
|
||||
elseif msg.type == 'builder' then
|
||||
Builder.lastPoint = msg.contents
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local args = {...}
|
||||
if #args < 2 then
|
||||
error('syntax: <builder id> <facing>')
|
||||
end
|
||||
|
||||
__BUILDER_ID = tonumber(args[1])
|
||||
|
||||
maxStackDB:load()
|
||||
|
||||
itemInfoDB = TableDB({
|
||||
fileName = 'items.db'
|
||||
})
|
||||
|
||||
itemInfoDB:load()
|
||||
|
||||
Builder.itemProvider = MEProvider({ direction = args[2] })
|
||||
if not Builder.itemProvider:isValid() then
|
||||
local sides = {
|
||||
east = 'west',
|
||||
west = 'east',
|
||||
north = 'south',
|
||||
south = 'north',
|
||||
}
|
||||
|
||||
Builder.itemProvider = ChestProvider({ direction = sides[args[2]], wrapSide = 'front' })
|
||||
if not Builder.itemProvider:isValid() then
|
||||
error('A chest or ME interface must be in front of turtle')
|
||||
end
|
||||
end
|
||||
|
||||
turtle.run(function()
|
||||
turtle.setPoint({ x = -1, z = -2, y = -1, heading = 1 })
|
||||
|
||||
turtle.saveLocation('supplies')
|
||||
|
||||
Event.pullEvents(onTheWay)
|
||||
end)
|
@@ -1,98 +0,0 @@
|
||||
function doCommand(command, moves)
|
||||
--[[
|
||||
if command == 'sl' then
|
||||
local pt = GPS.getPoint()
|
||||
if pt then
|
||||
turtle.storeLocation(moves, pt)
|
||||
end
|
||||
return
|
||||
end
|
||||
--]]
|
||||
|
||||
local function format(value)
|
||||
if type(value) == 'boolean' then
|
||||
if value then return 'true' end
|
||||
return 'false'
|
||||
end
|
||||
if type(value) ~= 'table' then
|
||||
return value
|
||||
end
|
||||
local str
|
||||
for k,v in pairs(value) do
|
||||
if not str then
|
||||
str = '{ '
|
||||
else
|
||||
str = str .. ', '
|
||||
end
|
||||
str = str .. k .. '=' .. tostring(v)
|
||||
end
|
||||
if str then
|
||||
str = str .. ' }'
|
||||
else
|
||||
str = '{ }'
|
||||
end
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
local function runCommand(fn, arg)
|
||||
local r = { fn(arg) }
|
||||
if r[2] then
|
||||
print(format(r[1]) .. ': ' .. format(r[2]))
|
||||
elseif r[1] then
|
||||
print(format(r[1]))
|
||||
end
|
||||
return r[1]
|
||||
end
|
||||
|
||||
local cmds = {
|
||||
[ 's' ] = turtle.select,
|
||||
[ 'rf' ] = turtle.refuel,
|
||||
[ 'gh' ] = function() turtle.pathfind({ x = 0, y = 0, z = 0, heading = 0}) end,
|
||||
}
|
||||
|
||||
local repCmds = {
|
||||
[ 'u' ] = turtle.up,
|
||||
[ 'd' ] = turtle.down,
|
||||
[ 'f' ] = turtle.forward,
|
||||
[ 'r' ] = turtle.turnRight,
|
||||
[ 'l' ] = turtle.turnLeft,
|
||||
[ 'ta' ] = turtle.turnAround,
|
||||
[ 'DD' ] = turtle.digDown,
|
||||
[ 'DU' ] = turtle.digUp,
|
||||
[ 'D' ] = turtle.dig,
|
||||
[ 'p' ] = turtle.place,
|
||||
[ 'pu' ] = turtle.placeUp,
|
||||
[ 'pd' ] = turtle.placeDown,
|
||||
[ 'b' ] = turtle.back,
|
||||
[ 'gfl' ] = turtle.getFuelLevel,
|
||||
[ 'gp' ] = turtle.getPoint,
|
||||
[ 'R' ] = function() turtle.setPoint({x = 0, y = 0, z = 0, heading = 0}) return turtle.point end
|
||||
}
|
||||
|
||||
if cmds[command] then
|
||||
runCommand(cmds[command], moves)
|
||||
elseif repCmds[command] then
|
||||
for i = 1, moves do
|
||||
if not runCommand(repCmds[command]) then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local args = {...}
|
||||
|
||||
if #args > 0 then
|
||||
doCommand(args[1], args[2] or 1)
|
||||
else
|
||||
print('Enter command (q to quit):')
|
||||
while true do
|
||||
local cmd = read()
|
||||
if cmd == 'q' then break
|
||||
end
|
||||
args = { }
|
||||
cmd:gsub('%w+', function(w) table.insert(args, w) end)
|
||||
doCommand(args[1], args[2] or 1)
|
||||
end
|
||||
end
|
@@ -1,709 +0,0 @@
|
||||
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
|
||||
(10 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 cobblestone line. If the program is stopped before that time,
|
||||
place the turtle in the original position before restarting the program.
|
||||
]]--
|
||||
|
||||
local ChestAdapter = require('chestAdapter18')
|
||||
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 chestAdapter = ChestAdapter({
|
||||
wrapSide = 'top',
|
||||
direction = 'down',
|
||||
})
|
||||
if not chestAdapter:isValid() then
|
||||
print('invalid chestAdapter')
|
||||
read()
|
||||
end
|
||||
-- turtle.emptyInventory(turtle.dropUp)
|
||||
|
||||
Util.print('Crafting %d %s', (qty or 1), item)
|
||||
success = Craft.craftRecipe(recipes[item], qty or 1, chestAdapter)
|
||||
|
||||
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, 4 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_GOOD 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 turtle.getFuelLevel() > 100 and
|
||||
Craft.canCraft(TORCH, 4, turtle.getSummedInventory()) 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())
|
||||
turtle.select(1)
|
||||
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 < -20 then
|
||||
error('lost')
|
||||
end
|
||||
end
|
||||
while inspect(turtle.inspectDown) == COBBLESTONE do
|
||||
pt.z = pt.z - 1
|
||||
turtle.pathfind(pt)
|
||||
if pt.z < -20 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