1
0
mirror of https://github.com/kepler155c/opus synced 2025-10-23 11:47:39 +00:00

reorganization

This commit is contained in:
kepler155c@gmail.com
2017-09-15 01:08:04 -04:00
parent b5ee5db16b
commit 64c68f2662
54 changed files with 175 additions and 15800 deletions

View File

@@ -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,

View File

@@ -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', ...)

View File

@@ -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

View File

@@ -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')

File diff suppressed because it is too large Load Diff

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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")

View File

@@ -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()

View File

@@ -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'

View File

@@ -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)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View File

@@ -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)