spaces->tab, equipper improvements, supertreefarm rewrite, follow improvements, sensor cleanup, milo multiple items allowed in recipes, remote canvas access
This commit is contained in:
@@ -15,37 +15,37 @@ local os = _G.os
|
|||||||
local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
|
||||||
local function sixBitToBase64(input)
|
local function sixBitToBase64(input)
|
||||||
return _sub(alphabet, input+1, input+1)
|
return _sub(alphabet, input+1, input+1)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function base64ToSixBit(input)
|
local function base64ToSixBit(input)
|
||||||
for i=1, 64 do
|
for i=1, 64 do
|
||||||
if input == _sub(alphabet, i, i) then
|
if input == _sub(alphabet, i, i) then
|
||||||
return i-1
|
return i-1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function octetToBase64(o1, o2, o3)
|
local function octetToBase64(o1, o2, o3)
|
||||||
local i1 = sixBitToBase64(_brshift(_band(o1, 0xFC), 2))
|
local i1 = sixBitToBase64(_brshift(_band(o1, 0xFC), 2))
|
||||||
local i2
|
local i2
|
||||||
local i3 = "="
|
local i3 = "="
|
||||||
local i4 = "="
|
local i4 = "="
|
||||||
if o2 then
|
if o2 then
|
||||||
i2 = sixBitToBase64(_bor( _blshift(_band(o1, 3), 4), _brshift(_band(o2, 0xF0), 4) ))
|
i2 = sixBitToBase64(_bor( _blshift(_band(o1, 3), 4), _brshift(_band(o2, 0xF0), 4) ))
|
||||||
if not o3 then
|
if not o3 then
|
||||||
i3 = sixBitToBase64(_blshift(_band(o2, 0x0F), 2))
|
i3 = sixBitToBase64(_blshift(_band(o2, 0x0F), 2))
|
||||||
else
|
else
|
||||||
i3 = sixBitToBase64(_bor( _blshift(_band(o2, 0x0F), 2), _brshift(_band(o3, 0xC0), 6) ))
|
i3 = sixBitToBase64(_bor( _blshift(_band(o2, 0x0F), 2), _brshift(_band(o3, 0xC0), 6) ))
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
i2 = sixBitToBase64(_blshift(_band(o1, 3), 4))
|
i2 = sixBitToBase64(_blshift(_band(o1, 3), 4))
|
||||||
end
|
end
|
||||||
if o3 then
|
if o3 then
|
||||||
i4 = sixBitToBase64(_band(o3, 0x3F))
|
i4 = sixBitToBase64(_band(o3, 0x3F))
|
||||||
end
|
end
|
||||||
|
|
||||||
return i1..i2..i3..i4
|
return i1..i2..i3..i4
|
||||||
end
|
end
|
||||||
|
|
||||||
-- octet 1 needs characters 1/2
|
-- octet 1 needs characters 1/2
|
||||||
@@ -53,61 +53,61 @@ end
|
|||||||
-- octet 3 needs characters 3/4
|
-- octet 3 needs characters 3/4
|
||||||
|
|
||||||
local function base64ToThreeOctet(s1)
|
local function base64ToThreeOctet(s1)
|
||||||
local c1 = base64ToSixBit(_sub(s1, 1, 1))
|
local c1 = base64ToSixBit(_sub(s1, 1, 1))
|
||||||
local c2 = base64ToSixBit(_sub(s1, 2, 2))
|
local c2 = base64ToSixBit(_sub(s1, 2, 2))
|
||||||
local c3
|
local c3
|
||||||
local c4
|
local c4
|
||||||
local o1
|
local o1
|
||||||
local o2
|
local o2
|
||||||
local o3
|
local o3
|
||||||
if _sub(s1, 3, 3) == "=" then
|
if _sub(s1, 3, 3) == "=" then
|
||||||
c3 = nil
|
c3 = nil
|
||||||
c4 = nil
|
c4 = nil
|
||||||
elseif _sub(s1, 4, 4) == "=" then
|
elseif _sub(s1, 4, 4) == "=" then
|
||||||
c3 = base64ToSixBit(_sub(s1, 3, 3))
|
c3 = base64ToSixBit(_sub(s1, 3, 3))
|
||||||
c4 = nil
|
c4 = nil
|
||||||
else
|
else
|
||||||
c3 = base64ToSixBit(_sub(s1, 3, 3))
|
c3 = base64ToSixBit(_sub(s1, 3, 3))
|
||||||
c4 = base64ToSixBit(_sub(s1, 4, 4))
|
c4 = base64ToSixBit(_sub(s1, 4, 4))
|
||||||
end
|
end
|
||||||
o1 = _bor( _blshift(c1, 2), _brshift(_band( c2, 0x30 ), 4) )
|
o1 = _bor( _blshift(c1, 2), _brshift(_band( c2, 0x30 ), 4) )
|
||||||
if c3 then
|
if c3 then
|
||||||
o2 = _bor( _blshift(_band(c2, 0x0F), 4), _brshift(_band( c3, 0x3C ), 2) )
|
o2 = _bor( _blshift(_band(c2, 0x0F), 4), _brshift(_band( c3, 0x3C ), 2) )
|
||||||
else
|
else
|
||||||
o2 = nil
|
o2 = nil
|
||||||
end
|
end
|
||||||
if c4 then
|
if c4 then
|
||||||
o3 = _bor( _blshift(_band(c3, 3), 6), c4 )
|
o3 = _bor( _blshift(_band(c3, 3), 6), c4 )
|
||||||
else
|
else
|
||||||
o3 = nil
|
o3 = nil
|
||||||
end
|
end
|
||||||
return o1, o2, o3
|
return o1, o2, o3
|
||||||
end
|
end
|
||||||
|
|
||||||
local function splitIntoBlocks(bytes)
|
local function splitIntoBlocks(bytes)
|
||||||
local blockNum = 1
|
local blockNum = 1
|
||||||
local blocks = {}
|
local blocks = {}
|
||||||
for i=1, #bytes, 3 do
|
for i=1, #bytes, 3 do
|
||||||
blocks[blockNum] = {bytes[i], bytes[i+1], bytes[i+2]}
|
blocks[blockNum] = {bytes[i], bytes[i+1], bytes[i+2]}
|
||||||
--[[
|
--[[
|
||||||
if #blocks[blockNum] < 3 then
|
if #blocks[blockNum] < 3 then
|
||||||
for j=#blocks[blockNum]+1, 3 do
|
for j=#blocks[blockNum]+1, 3 do
|
||||||
table.insert(blocks[blockNum], 0)
|
table.insert(blocks[blockNum], 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
]]
|
]]
|
||||||
blockNum = blockNum+1
|
blockNum = blockNum+1
|
||||||
end
|
end
|
||||||
return blocks
|
return blocks
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base64.encode(bytes)
|
function Base64.encode(bytes)
|
||||||
local blocks = splitIntoBlocks(bytes)
|
local blocks = splitIntoBlocks(bytes)
|
||||||
local output = ""
|
local output = ""
|
||||||
for i=1, #blocks do
|
for i=1, #blocks do
|
||||||
output = output..octetToBase64( unpack(blocks[i]) )
|
output = output..octetToBase64( unpack(blocks[i]) )
|
||||||
end
|
end
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
local function Throttle()
|
local function Throttle()
|
||||||
@@ -123,32 +123,32 @@ local function Throttle()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Base64.decode(str)
|
function Base64.decode(str)
|
||||||
local bytes = {}
|
local bytes = {}
|
||||||
local blocks = {}
|
local blocks = {}
|
||||||
local blockNum = 1
|
local blockNum = 1
|
||||||
local throttle = Throttle()
|
local throttle = Throttle()
|
||||||
for i=1, #str, 4 do
|
for i=1, #str, 4 do
|
||||||
blocks[blockNum] = _sub(str, i, i+3)
|
blocks[blockNum] = _sub(str, i, i+3)
|
||||||
blockNum = blockNum+1
|
blockNum = blockNum+1
|
||||||
end
|
end
|
||||||
for i=1, #blocks do
|
for i=1, #blocks do
|
||||||
local o1, o2, o3 = base64ToThreeOctet(blocks[i])
|
local o1, o2, o3 = base64ToThreeOctet(blocks[i])
|
||||||
table.insert(bytes, o1)
|
table.insert(bytes, o1)
|
||||||
table.insert(bytes, o2)
|
table.insert(bytes, o2)
|
||||||
table.insert(bytes, o3)
|
table.insert(bytes, o3)
|
||||||
throttle()
|
throttle()
|
||||||
end
|
end
|
||||||
-- Remove padding:
|
-- Remove padding:
|
||||||
--[[
|
--[[
|
||||||
for i=#bytes, 1, -1 do
|
for i=#bytes, 1, -1 do
|
||||||
if bytes[i] ~= 0 then
|
if bytes[i] ~= 0 then
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
bytes[i] = nil
|
bytes[i] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
]]
|
]]
|
||||||
return bytes
|
return bytes
|
||||||
end
|
end
|
||||||
|
|
||||||
return Base64
|
return Base64
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -9,10 +9,10 @@ local turtle = _G.turtle
|
|||||||
|
|
||||||
local Builder = class()
|
local Builder = class()
|
||||||
Util.merge(Builder, {
|
Util.merge(Builder, {
|
||||||
isCommandComputer = not turtle,
|
isCommandComputer = not turtle,
|
||||||
loc = { },
|
loc = { },
|
||||||
index = 1,
|
index = 1,
|
||||||
mode = 'build',
|
mode = 'build',
|
||||||
})
|
})
|
||||||
|
|
||||||
local BUILDER_DIR = 'usr/builder'
|
local BUILDER_DIR = 'usr/builder'
|
||||||
@@ -20,82 +20,82 @@ local BUILDER_DIR = 'usr/builder'
|
|||||||
local blockInfo = Blocks()
|
local blockInfo = Blocks()
|
||||||
|
|
||||||
function Builder:getBlockCounts()
|
function Builder:getBlockCounts()
|
||||||
local blocks = { }
|
local blocks = { }
|
||||||
|
|
||||||
for k = self.index, #self.schematic.blocks do
|
for k = self.index, #self.schematic.blocks do
|
||||||
local b = self.schematic.blocks[k]
|
local b = self.schematic.blocks[k]
|
||||||
local key = tostring(b.id) .. ':' .. b.dmg
|
local key = tostring(b.id) .. ':' .. b.dmg
|
||||||
local block = blocks[key]
|
local block = blocks[key]
|
||||||
if not block then
|
if not block then
|
||||||
block = Util.shallowCopy(b)
|
block = Util.shallowCopy(b)
|
||||||
block.qty = 0
|
block.qty = 0
|
||||||
block.need = 0
|
block.need = 0
|
||||||
blocks[key] = block
|
blocks[key] = block
|
||||||
end
|
end
|
||||||
block.need = block.need + 1
|
block.need = block.need + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
return blocks
|
return blocks
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:substituteBlocks(throttle)
|
function Builder:substituteBlocks(throttle)
|
||||||
for _,b in pairs(self.schematic.blocks) do
|
for _,b in pairs(self.schematic.blocks) do
|
||||||
|
|
||||||
-- replace schematic block type with substitution
|
-- replace schematic block type with substitution
|
||||||
local pb = blockInfo:getPlaceableBlock(b.id, b.dmg)
|
local pb = blockInfo:getPlaceableBlock(b.id, b.dmg)
|
||||||
|
|
||||||
Util.merge(b, pb)
|
Util.merge(b, pb)
|
||||||
|
|
||||||
b.odmg = pb.odmg or pb.dmg
|
b.odmg = pb.odmg or pb.dmg
|
||||||
|
|
||||||
local sub = self.subDB:get({ b.id, b.dmg })
|
local sub = self.subDB:get({ b.id, b.dmg })
|
||||||
if sub then
|
if sub then
|
||||||
b.id, b.dmg = self.subDB:extract(sub)
|
b.id, b.dmg = self.subDB:extract(sub)
|
||||||
end
|
end
|
||||||
throttle()
|
throttle()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:reloadSchematic(throttle)
|
function Builder:reloadSchematic(throttle)
|
||||||
self.schematic:reload(throttle)
|
self.schematic:reload(throttle)
|
||||||
self:substituteBlocks(throttle)
|
self:substituteBlocks(throttle)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:log(...)
|
function Builder:log(...)
|
||||||
Util.print(...)
|
Util.print(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:dumpInventory()
|
function Builder:dumpInventory()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:logBlock(index, b)
|
function Builder:logBlock(index, b)
|
||||||
local bdir = b.direction or ''
|
local bdir = b.direction or ''
|
||||||
local logText = string.format('%d %s:%d (x:%d,z:%d:y:%d) %s',
|
local logText = string.format('%d %s:%d (x:%d,z:%d:y:%d) %s',
|
||||||
index, b.id, b.dmg, b.x, b.z, b.y, bdir)
|
index, b.id, b.dmg, b.x, b.z, b.y, bdir)
|
||||||
self:log(logText)
|
self:log(logText)
|
||||||
-- self:log(b.index) -- unique identifier of block
|
-- self:log(b.index) -- unique identifier of block
|
||||||
|
|
||||||
if device.wireless_modem then
|
if device.wireless_modem then
|
||||||
Message.broadcast('builder', { x = b.x, y = b.y, z = b.z, heading = b.heading })
|
Message.broadcast('builder', { x = b.x, y = b.y, z = b.z, heading = b.heading })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:saveProgress(index)
|
function Builder:saveProgress(index)
|
||||||
Util.writeTable(
|
Util.writeTable(
|
||||||
fs.combine(BUILDER_DIR, self.schematic.filename .. '.progress'),
|
fs.combine(BUILDER_DIR, self.schematic.filename .. '.progress'),
|
||||||
{ index = index, loc = self.loc }
|
{ index = index, loc = self.loc }
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:loadProgress(filename)
|
function Builder:loadProgress(filename)
|
||||||
local progress = Util.readTable(fs.combine(BUILDER_DIR, filename))
|
local progress = Util.readTable(fs.combine(BUILDER_DIR, filename))
|
||||||
if progress then
|
if progress then
|
||||||
self.index = progress.index
|
self.index = progress.index
|
||||||
if self.index > #self.schematic.blocks then
|
if self.index > #self.schematic.blocks then
|
||||||
self.index = 1
|
self.index = 1
|
||||||
end
|
end
|
||||||
self.loc = progress.loc or { }
|
self.loc = progress.loc or { }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Builder
|
return Builder
|
||||||
|
|||||||
@@ -8,77 +8,77 @@ local os = _G.os
|
|||||||
local read = _G.read
|
local read = _G.read
|
||||||
|
|
||||||
function Builder:begin()
|
function Builder:begin()
|
||||||
local direction = 1
|
local direction = 1
|
||||||
local last = #self.schematic.blocks
|
local last = #self.schematic.blocks
|
||||||
local throttle = Util.throttle()
|
local throttle = Util.throttle()
|
||||||
|
|
||||||
local cx, cy, cz = commands.getBlockPosition()
|
local cx, cy, cz = commands.getBlockPosition()
|
||||||
if self.loc.x then
|
if self.loc.x then
|
||||||
cx, cy, cz = self.loc.rx, self.loc.ry, self.loc.rz
|
cx, cy, cz = self.loc.rx, self.loc.ry, self.loc.rz
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.mode == 'destroy' then
|
if self.mode == 'destroy' then
|
||||||
direction = -1
|
direction = -1
|
||||||
last = 1
|
last = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
for i = self.index, last, direction do
|
for i = self.index, last, direction do
|
||||||
self.index = i
|
self.index = i
|
||||||
|
|
||||||
local b = self.schematic:getComputedBlock(i)
|
local b = self.schematic:getComputedBlock(i)
|
||||||
|
|
||||||
if b.id ~= 'minecraft:air' then
|
if b.id ~= 'minecraft:air' then
|
||||||
|
|
||||||
self:logBlock(self.index, b)
|
self:logBlock(self.index, b)
|
||||||
|
|
||||||
local id = b.id
|
local id = b.id
|
||||||
if self.mode == 'destroy' then
|
if self.mode == 'destroy' then
|
||||||
id = 'minecraft:air'
|
id = 'minecraft:air'
|
||||||
end
|
end
|
||||||
|
|
||||||
local function placeBlock(bid, dmg, x, y, z)
|
local function placeBlock(bid, dmg, x, y, z)
|
||||||
local command = table.concat({
|
local command = table.concat({
|
||||||
"setblock",
|
"setblock",
|
||||||
cx + x + 1,
|
cx + x + 1,
|
||||||
cy + y,
|
cy + y,
|
||||||
cz + z + 1,
|
cz + z + 1,
|
||||||
bid,
|
bid,
|
||||||
dmg,
|
dmg,
|
||||||
}, ' ')
|
}, ' ')
|
||||||
|
|
||||||
commands.execAsync(command)
|
commands.execAsync(command)
|
||||||
|
|
||||||
local result = { os.pullEvent("task_complete") }
|
local result = { os.pullEvent("task_complete") }
|
||||||
if not result[4] then
|
if not result[4] then
|
||||||
Util.print(result[5])
|
Util.print(result[5])
|
||||||
if self.mode ~= 'destroy' then
|
if self.mode ~= 'destroy' then
|
||||||
read()
|
read()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
placeBlock(id, b.odmg, b.x, b.y, b.z)
|
placeBlock(id, b.odmg, b.x, b.y, b.z)
|
||||||
|
|
||||||
if b.twoHigh then
|
if b.twoHigh then
|
||||||
local _, topBlock = self.schematic:findIndexAt(b.x, b.z, b.y + 1, true)
|
local _, topBlock = self.schematic:findIndexAt(b.x, b.z, b.y + 1, true)
|
||||||
if topBlock then
|
if topBlock then
|
||||||
placeBlock(id, topBlock.odmg, b.x, b.y + 1, b.z)
|
placeBlock(id, topBlock.odmg, b.x, b.y + 1, b.z)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.mode == 'destroy' then
|
if self.mode == 'destroy' then
|
||||||
self:saveProgress(math.max(self.index, 1))
|
self:saveProgress(math.max(self.index, 1))
|
||||||
else
|
else
|
||||||
self:saveProgress(self.index + 1)
|
self:saveProgress(self.index + 1)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
throttle() -- sleep in case there are a large # of skipped blocks
|
throttle() -- sleep in case there are a large # of skipped blocks
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
fs.delete(self.schematic.filename .. '.progress')
|
fs.delete(self.schematic.filename .. '.progress')
|
||||||
print('Finished')
|
print('Finished')
|
||||||
Event.exitPullEvents()
|
Event.exitPullEvents()
|
||||||
end
|
end
|
||||||
|
|
||||||
return Builder
|
return Builder
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1136
builder/builder.lua
1136
builder/builder.lua
File diff suppressed because it is too large
Load Diff
@@ -10,390 +10,390 @@ local os = _G.os
|
|||||||
local turtle = _G.turtle
|
local turtle = _G.turtle
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
A supplier turtle for the builder turtle. For larger builds, use
|
A supplier turtle for the builder turtle. For larger builds, use
|
||||||
ender modems.
|
ender modems.
|
||||||
|
|
||||||
Setup:
|
Setup:
|
||||||
|
|
||||||
1. chest or ME interface at level 0 (bottom of build area)
|
1. chest or ME interface at level 0 (bottom of build area)
|
||||||
2. builder turtle on top facing the build area
|
2. builder turtle on top facing the build area
|
||||||
3. If facing the build turtle, the supplier turtle is to the right
|
3. If facing the build turtle, the supplier turtle is to the right
|
||||||
pointing at the chest/interface
|
pointing at the chest/interface
|
||||||
]]--
|
]]--
|
||||||
|
|
||||||
local ChestProvider = require('core.chestProvider')
|
local ChestProvider = require('core.chestProvider')
|
||||||
if Util.getVersion() == 1.8 then
|
if Util.getVersion() == 1.8 then
|
||||||
ChestProvider = require('core.chestProvider18')
|
ChestProvider = require('core.chestProvider18')
|
||||||
end
|
end
|
||||||
|
|
||||||
if not device.wireless_modem then
|
if not device.wireless_modem then
|
||||||
error('No wireless modem detected')
|
error('No wireless modem detected')
|
||||||
end
|
end
|
||||||
|
|
||||||
local __BUILDER_ID = 6
|
local __BUILDER_ID = 6
|
||||||
local itemInfoDB
|
local itemInfoDB
|
||||||
|
|
||||||
local Builder = {
|
local Builder = {
|
||||||
version = '1.70',
|
version = '1.70',
|
||||||
ccVersion = nil,
|
ccVersion = nil,
|
||||||
slots = { },
|
slots = { },
|
||||||
index = 1,
|
index = 1,
|
||||||
fuelItem = { id = 'minecraft:coal', dmg = 0 },
|
fuelItem = { id = 'minecraft:coal', dmg = 0 },
|
||||||
resupplying = true,
|
resupplying = true,
|
||||||
ready = true,
|
ready = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
--[[-- maxStackDB --]]--
|
--[[-- maxStackDB --]]--
|
||||||
local maxStackDB = TableDB({
|
local maxStackDB = TableDB({
|
||||||
fileName = 'maxstack.db',
|
fileName = 'maxstack.db',
|
||||||
tabledef = {
|
tabledef = {
|
||||||
autokeys = false,
|
autokeys = false,
|
||||||
type = 'simple',
|
type = 'simple',
|
||||||
columns = {
|
columns = {
|
||||||
{ label = 'Key', type = 'key', length = 8 },
|
{ label = 'Key', type = 'key', length = 8 },
|
||||||
{ label = 'Quantity', type = 'number', length = 2 }
|
{ label = 'Quantity', type = 'number', length = 2 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function maxStackDB:get(id, dmg)
|
function maxStackDB:get(id, dmg)
|
||||||
return self.data[id .. ':' .. dmg] or 64
|
return self.data[id .. ':' .. dmg] or 64
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:dumpInventory()
|
function Builder:dumpInventory()
|
||||||
|
|
||||||
local success = true
|
local success = true
|
||||||
|
|
||||||
for i = 1, 16 do
|
for i = 1, 16 do
|
||||||
local qty = turtle.getItemCount(i)
|
local qty = turtle.getItemCount(i)
|
||||||
if qty > 0 then
|
if qty > 0 then
|
||||||
self.itemProvider:insert(i, qty)
|
self.itemProvider:insert(i, qty)
|
||||||
end
|
end
|
||||||
if turtle.getItemCount(i) ~= 0 then
|
if turtle.getItemCount(i) ~= 0 then
|
||||||
success = false
|
success = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
turtle.select(1)
|
turtle.select(1)
|
||||||
|
|
||||||
return success
|
return success
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:dumpInventoryWithCheck()
|
function Builder:dumpInventoryWithCheck()
|
||||||
while not self:dumpInventory() do
|
while not self:dumpInventory() do
|
||||||
Builder:log('Unable to dump inventory')
|
Builder:log('Unable to dump inventory')
|
||||||
print('Provider is full or missing - make space or replace')
|
print('Provider is full or missing - make space or replace')
|
||||||
print('Press enter to continue')
|
print('Press enter to continue')
|
||||||
--turtle.setHeading(0)
|
--turtle.setHeading(0)
|
||||||
self.ready = false
|
self.ready = false
|
||||||
_G.read()
|
_G.read()
|
||||||
end
|
end
|
||||||
self.ready = true
|
self.ready = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:autocraft(supplies)
|
function Builder:autocraft(supplies)
|
||||||
local t = { }
|
local t = { }
|
||||||
|
|
||||||
for _,s in pairs(supplies) do
|
for _,s in pairs(supplies) do
|
||||||
local key = s.id .. ':' .. s.dmg
|
local key = s.id .. ':' .. s.dmg
|
||||||
local item = t[key]
|
local item = t[key]
|
||||||
if not item then
|
if not item then
|
||||||
item = {
|
item = {
|
||||||
id = s.id,
|
id = s.id,
|
||||||
dmg = s.dmg,
|
dmg = s.dmg,
|
||||||
qty = 0,
|
qty = 0,
|
||||||
}
|
}
|
||||||
t[key] = item
|
t[key] = item
|
||||||
end
|
end
|
||||||
item.qty = item.qty + (s.need-s.qty)
|
item.qty = item.qty + (s.need-s.qty)
|
||||||
end
|
end
|
||||||
|
|
||||||
Builder.itemProvider:craftItems(t)
|
Builder.itemProvider:craftItems(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:refuel()
|
function Builder:refuel()
|
||||||
while turtle.getFuelLevel() < 4000 and self.fuelItem do
|
while turtle.getFuelLevel() < 4000 and self.fuelItem do
|
||||||
Builder:log('Refueling')
|
Builder:log('Refueling')
|
||||||
turtle.select(1)
|
turtle.select(1)
|
||||||
self.itemProvider:provide(self.fuelItem, 64, 1)
|
self.itemProvider:provide(self.fuelItem, 64, 1)
|
||||||
if turtle.getItemCount(1) == 0 then
|
if turtle.getItemCount(1) == 0 then
|
||||||
Builder:log('Out of fuel, add coal to chest/ME system')
|
Builder:log('Out of fuel, add coal to chest/ME system')
|
||||||
--turtle.setHeading(0)
|
--turtle.setHeading(0)
|
||||||
os.sleep(5)
|
os.sleep(5)
|
||||||
else
|
else
|
||||||
turtle.refuel(64)
|
turtle.refuel(64)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:log(...)
|
function Builder:log(...)
|
||||||
Util.print(...)
|
Util.print(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:getSupplies()
|
function Builder:getSupplies()
|
||||||
|
|
||||||
Builder.itemProvider:refresh()
|
Builder.itemProvider:refresh()
|
||||||
|
|
||||||
local t = { }
|
local t = { }
|
||||||
for _,s in ipairs(self.slots) do
|
for _,s in ipairs(self.slots) do
|
||||||
if s.need > 0 then
|
if s.need > 0 then
|
||||||
local item = Builder.itemProvider:getItemInfo(s)
|
local item = Builder.itemProvider:getItemInfo(s)
|
||||||
if item then
|
if item then
|
||||||
if item.name then
|
if item.name then
|
||||||
s.name = item.name
|
s.name = item.name
|
||||||
end
|
end
|
||||||
|
|
||||||
local qty = math.min(s.need-s.qty, item.qty)
|
local qty = math.min(s.need-s.qty, item.qty)
|
||||||
|
|
||||||
if qty + s.qty > item.max_size then
|
if qty + s.qty > item.max_size then
|
||||||
maxStackDB:add({ s.id, s.dmg }, item.max_size)
|
maxStackDB:add({ s.id, s.dmg }, item.max_size)
|
||||||
maxStackDB.dirty = true
|
maxStackDB.dirty = true
|
||||||
maxStackDB:flush()
|
maxStackDB:flush()
|
||||||
qty = item.max_size
|
qty = item.max_size
|
||||||
s.need = qty
|
s.need = qty
|
||||||
end
|
end
|
||||||
if qty > 0 then
|
if qty > 0 then
|
||||||
self.itemProvider:provide(item, qty, s.index)
|
self.itemProvider:provide(item, qty, s.index)
|
||||||
s.qty = turtle.getItemCount(s.index)
|
s.qty = turtle.getItemCount(s.index)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if s.qty < s.need then
|
if s.qty < s.need then
|
||||||
table.insert(t, s)
|
table.insert(t, s)
|
||||||
local name = s.name or s.id .. ':' .. s.dmg
|
local name = s.name or s.id .. ':' .. s.dmg
|
||||||
local item = itemInfoDB:get({ s.id, s.dmg })
|
local item = itemInfoDB:get({ s.id, s.dmg })
|
||||||
if item then
|
if item then
|
||||||
name = item.displayName
|
name = item.displayName
|
||||||
end
|
end
|
||||||
|
|
||||||
Builder:log('Need %d %s', s.need - s.qty, name)
|
Builder:log('Need %d %s', s.need - s.qty, name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
local function moveTowardsX(dx)
|
local function moveTowardsX(dx)
|
||||||
|
|
||||||
local direction = dx - turtle.point.x
|
local direction = dx - turtle.point.x
|
||||||
local move
|
local move
|
||||||
|
|
||||||
if direction == 0 then
|
if direction == 0 then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if direction > 0 and turtle.point.heading == 0 or
|
if direction > 0 and turtle.point.heading == 0 or
|
||||||
direction < 0 and turtle.point.heading == 2 then
|
direction < 0 and turtle.point.heading == 2 then
|
||||||
move = turtle.forward
|
move = turtle.forward
|
||||||
else
|
else
|
||||||
move = turtle.back
|
move = turtle.back
|
||||||
end
|
end
|
||||||
|
|
||||||
return move()
|
return move()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function moveTowardsZ(dz)
|
local function moveTowardsZ(dz)
|
||||||
|
|
||||||
local direction = dz - turtle.point.z
|
local direction = dz - turtle.point.z
|
||||||
local move
|
local move
|
||||||
|
|
||||||
if direction == 0 then
|
if direction == 0 then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if direction > 0 and turtle.point.heading == 1 or
|
if direction > 0 and turtle.point.heading == 1 or
|
||||||
direction < 0 and turtle.point.heading == 3 then
|
direction < 0 and turtle.point.heading == 3 then
|
||||||
move = turtle.forward
|
move = turtle.forward
|
||||||
else
|
else
|
||||||
move = turtle.back
|
move = turtle.back
|
||||||
end
|
end
|
||||||
|
|
||||||
return move()
|
return move()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:finish()
|
function Builder:finish()
|
||||||
Builder.resupplying = true
|
Builder.resupplying = true
|
||||||
Builder.ready = false
|
Builder.ready = false
|
||||||
if turtle.gotoLocation('supplies') then
|
if turtle.gotoLocation('supplies') then
|
||||||
turtle.setHeading(1)
|
turtle.setHeading(1)
|
||||||
os.sleep(.1) -- random 'Computer is not connected' error...
|
os.sleep(.1) -- random 'Computer is not connected' error...
|
||||||
Builder:dumpInventory()
|
Builder:dumpInventory()
|
||||||
Event.exitPullEvents()
|
Event.exitPullEvents()
|
||||||
print('Finished')
|
print('Finished')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Builder:gotoBuilder()
|
function Builder:gotoBuilder()
|
||||||
if Builder.lastPoint then
|
if Builder.lastPoint then
|
||||||
turtle.setStatus('tracking')
|
turtle.setStatus('tracking')
|
||||||
while true do
|
while true do
|
||||||
local pt = Point.copy(Builder.lastPoint)
|
local pt = Point.copy(Builder.lastPoint)
|
||||||
pt.y = pt.y + 3
|
pt.y = pt.y + 3
|
||||||
if turtle.point.y ~= pt.y then
|
if turtle.point.y ~= pt.y then
|
||||||
turtle.gotoY(pt.y)
|
turtle.gotoY(pt.y)
|
||||||
else
|
else
|
||||||
local distance = Point.turtleDistance(turtle.point, pt)
|
local distance = Point.turtleDistance(turtle.point, pt)
|
||||||
if distance <= 3 then
|
if distance <= 3 then
|
||||||
Builder:log('Synchronized')
|
Builder:log('Synchronized')
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
if turtle.point.heading % 2 == 0 then
|
if turtle.point.heading % 2 == 0 then
|
||||||
if turtle.point.x == pt.x then
|
if turtle.point.x == pt.x then
|
||||||
turtle.headTowardsZ(pt.z)
|
turtle.headTowardsZ(pt.z)
|
||||||
moveTowardsZ(pt.z)
|
moveTowardsZ(pt.z)
|
||||||
else
|
else
|
||||||
moveTowardsX(pt.x)
|
moveTowardsX(pt.x)
|
||||||
end
|
end
|
||||||
elseif turtle.point.z ~= pt.z then
|
elseif turtle.point.z ~= pt.z then
|
||||||
moveTowardsZ(pt.z)
|
moveTowardsZ(pt.z)
|
||||||
else
|
else
|
||||||
turtle.headTowardsX(pt.x)
|
turtle.headTowardsX(pt.x)
|
||||||
moveTowardsX(pt.x)
|
moveTowardsX(pt.x)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Message.addHandler('builder',
|
Message.addHandler('builder',
|
||||||
function(_, id, msg)
|
function(_, id, msg)
|
||||||
if not id or id ~= __BUILDER_ID then
|
if not id or id ~= __BUILDER_ID then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if not Builder.resupplying then
|
if not Builder.resupplying then
|
||||||
local pt = msg.contents
|
local pt = msg.contents
|
||||||
pt.y = pt.y + 3
|
pt.y = pt.y + 3
|
||||||
|
|
||||||
turtle.setStatus('supervising')
|
turtle.setStatus('supervising')
|
||||||
turtle.gotoYfirst(pt)
|
turtle.gotoYfirst(pt)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Message.addHandler('supplyList',
|
Message.addHandler('supplyList',
|
||||||
function(_, id, msg)
|
function(_, id, msg)
|
||||||
if not id or id ~= __BUILDER_ID then
|
if not id or id ~= __BUILDER_ID then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
turtle.setStatus('resupplying')
|
turtle.setStatus('resupplying')
|
||||||
Builder.resupplying = true
|
Builder.resupplying = true
|
||||||
Builder.slots = msg.contents.slots
|
Builder.slots = msg.contents.slots
|
||||||
Builder.slotUid = msg.contents.uid
|
Builder.slotUid = msg.contents.uid
|
||||||
|
|
||||||
Builder:log('Received supply list ' .. Builder.slotUid)
|
Builder:log('Received supply list ' .. Builder.slotUid)
|
||||||
|
|
||||||
os.sleep(0)
|
os.sleep(0)
|
||||||
if not turtle.gotoLocation('supplies') then
|
if not turtle.gotoLocation('supplies') then
|
||||||
Builder:log('Failed to go to supply location')
|
Builder:log('Failed to go to supply location')
|
||||||
Builder.ready = false
|
Builder.ready = false
|
||||||
Event.exitPullEvents()
|
Event.exitPullEvents()
|
||||||
end
|
end
|
||||||
turtle.setHeading(1)
|
turtle.setHeading(1)
|
||||||
os.sleep(.2) -- random 'Computer is not connected' error...
|
os.sleep(.2) -- random 'Computer is not connected' error...
|
||||||
Builder:dumpInventoryWithCheck()
|
Builder:dumpInventoryWithCheck()
|
||||||
Builder:refuel()
|
Builder:refuel()
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local supplies = Builder:getSupplies()
|
local supplies = Builder:getSupplies()
|
||||||
if #supplies == 0 then
|
if #supplies == 0 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
Builder:autocraft(supplies)
|
Builder:autocraft(supplies)
|
||||||
turtle.setStatus('waiting')
|
turtle.setStatus('waiting')
|
||||||
os.sleep(5)
|
os.sleep(5)
|
||||||
end
|
end
|
||||||
Builder:log('Got all supplies')
|
Builder:log('Got all supplies')
|
||||||
os.sleep(0)
|
os.sleep(0)
|
||||||
Builder:gotoBuilder()
|
Builder:gotoBuilder()
|
||||||
Builder.resupplying = false
|
Builder.resupplying = false
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Message.addHandler('needSupplies',
|
Message.addHandler('needSupplies',
|
||||||
function(_, id, msg)
|
function(_, id, msg)
|
||||||
if not id or id ~= __BUILDER_ID then
|
if not id or id ~= __BUILDER_ID then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if Builder.resupplying or msg.contents.uid ~= Builder.slotUid then
|
if Builder.resupplying or msg.contents.uid ~= Builder.slotUid then
|
||||||
Builder:log('No supplies ready')
|
Builder:log('No supplies ready')
|
||||||
Message.send(__BUILDER_ID, 'gotSupplies')
|
Message.send(__BUILDER_ID, 'gotSupplies')
|
||||||
else
|
else
|
||||||
turtle.setStatus('supplying')
|
turtle.setStatus('supplying')
|
||||||
Builder:log('Supplying')
|
Builder:log('Supplying')
|
||||||
os.sleep(0)
|
os.sleep(0)
|
||||||
|
|
||||||
local pt = msg.contents.point
|
local pt = msg.contents.point
|
||||||
pt.y = turtle.getPoint().y
|
pt.y = turtle.getPoint().y
|
||||||
pt.heading = nil
|
pt.heading = nil
|
||||||
if not turtle.gotoYfirst(pt) then -- location of builder
|
if not turtle.gotoYfirst(pt) then -- location of builder
|
||||||
Builder.resupplying = true
|
Builder.resupplying = true
|
||||||
Message.send(__BUILDER_ID, 'gotSupplies')
|
Message.send(__BUILDER_ID, 'gotSupplies')
|
||||||
os.sleep(0)
|
os.sleep(0)
|
||||||
if not turtle.gotoLocation('supplies') then
|
if not turtle.gotoLocation('supplies') then
|
||||||
Builder:log('failed to go to supply location')
|
Builder:log('failed to go to supply location')
|
||||||
Event.exitPullEvents()
|
Event.exitPullEvents()
|
||||||
end
|
end
|
||||||
turtle.setHeading(1)
|
turtle.setHeading(1)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
pt.y = pt.y - 2 -- location where builder should go for the chest to be above
|
pt.y = pt.y - 2 -- location where builder should go for the chest to be above
|
||||||
|
|
||||||
turtle.select(15)
|
turtle.select(15)
|
||||||
turtle.placeDown()
|
turtle.placeDown()
|
||||||
os.sleep(.1) -- random computer not connected error
|
os.sleep(.1) -- random computer not connected error
|
||||||
local p = ChestProvider({ direction = 'up', wrapSide = 'bottom' })
|
local p = ChestProvider({ direction = 'up', wrapSide = 'bottom' })
|
||||||
for i = 1, 16 do
|
for i = 1, 16 do
|
||||||
p:insert(i, 64)
|
p:insert(i, 64)
|
||||||
end
|
end
|
||||||
|
|
||||||
Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true, point = pt })
|
Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true, point = pt })
|
||||||
|
|
||||||
Message.waitForMessage('thanks', 5, __BUILDER_ID)
|
Message.waitForMessage('thanks', 5, __BUILDER_ID)
|
||||||
--os.sleep(0)
|
--os.sleep(0)
|
||||||
|
|
||||||
--p.condenseItems()
|
--p.condenseItems()
|
||||||
for i = 1, 16 do
|
for i = 1, 16 do
|
||||||
p:extract(i, 64)
|
p:extract(i, 64)
|
||||||
end
|
end
|
||||||
turtle.digDown()
|
turtle.digDown()
|
||||||
turtle.setStatus('waiting')
|
turtle.setStatus('waiting')
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Message.addHandler('finished',
|
Message.addHandler('finished',
|
||||||
function(_, id)
|
function(_, id)
|
||||||
if not id or id ~= __BUILDER_ID then
|
if not id or id ~= __BUILDER_ID then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
Builder:finish()
|
Builder:finish()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.on('turtle_abort',
|
Event.on('turtle_abort',
|
||||||
function()
|
function()
|
||||||
turtle.abort(false)
|
turtle.abort(false)
|
||||||
turtle.setStatus('aborting')
|
turtle.setStatus('aborting')
|
||||||
Builder:finish()
|
Builder:finish()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local function onTheWay() -- parallel routine
|
local function onTheWay() -- parallel routine
|
||||||
while true do
|
while true do
|
||||||
local _, _, _, id, msg, _ = os.pullEvent('modem_message')
|
local _, _, _, id, msg, _ = os.pullEvent('modem_message')
|
||||||
if Builder.ready then
|
if Builder.ready then
|
||||||
if id == __BUILDER_ID and msg and msg.type then
|
if id == __BUILDER_ID and msg and msg.type then
|
||||||
if msg.type == 'needSupplies' then
|
if msg.type == 'needSupplies' then
|
||||||
Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true })
|
Message.send(__BUILDER_ID, 'gotSupplies', { supplies = true })
|
||||||
elseif msg.type == 'builder' then
|
elseif msg.type == 'builder' then
|
||||||
Builder.lastPoint = msg.contents
|
Builder.lastPoint = msg.contents
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local args = {...}
|
local args = {...}
|
||||||
if #args < 2 then
|
if #args < 2 then
|
||||||
error('syntax: <builder id> <facing>')
|
error('syntax: <builder id> <facing>')
|
||||||
end
|
end
|
||||||
|
|
||||||
__BUILDER_ID = tonumber(args[1])
|
__BUILDER_ID = tonumber(args[1])
|
||||||
@@ -401,30 +401,30 @@ __BUILDER_ID = tonumber(args[1])
|
|||||||
maxStackDB:load()
|
maxStackDB:load()
|
||||||
|
|
||||||
itemInfoDB = TableDB({
|
itemInfoDB = TableDB({
|
||||||
fileName = 'items.db'
|
fileName = 'items.db'
|
||||||
})
|
})
|
||||||
|
|
||||||
itemInfoDB:load()
|
itemInfoDB:load()
|
||||||
|
|
||||||
Builder.itemProvider = MEProvider({ direction = args[2] })
|
Builder.itemProvider = MEProvider({ direction = args[2] })
|
||||||
if not Builder.itemProvider:isValid() then
|
if not Builder.itemProvider:isValid() then
|
||||||
local sides = {
|
local sides = {
|
||||||
east = 'west',
|
east = 'west',
|
||||||
west = 'east',
|
west = 'east',
|
||||||
north = 'south',
|
north = 'south',
|
||||||
south = 'north',
|
south = 'north',
|
||||||
}
|
}
|
||||||
|
|
||||||
Builder.itemProvider = ChestProvider({ direction = sides[args[2]], wrapSide = 'front' })
|
Builder.itemProvider = ChestProvider({ direction = sides[args[2]], wrapSide = 'front' })
|
||||||
if not Builder.itemProvider:isValid() then
|
if not Builder.itemProvider:isValid() then
|
||||||
error('A chest or ME interface must be in front of turtle')
|
error('A chest or ME interface must be in front of turtle')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
turtle.run(function()
|
turtle.run(function()
|
||||||
turtle.setPoint({ x = -1, z = -2, y = -1, heading = 1 })
|
turtle.setPoint({ x = -1, z = -2, y = -1, heading = 1 })
|
||||||
|
|
||||||
turtle.saveLocation('supplies')
|
turtle.saveLocation('supplies')
|
||||||
|
|
||||||
Event.pullEvents(onTheWay)
|
Event.pullEvents(onTheWay)
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ local function Syntax(msg)
|
|||||||
* Entity sensor
|
* Entity sensor
|
||||||
* Introspection module
|
* Introspection module
|
||||||
]])
|
]])
|
||||||
error(msg)
|
error(msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
local neural = device['neuralInterface'] or Syntax('Must be run on a neural interface')
|
local neural = device['neuralInterface'] or Syntax('Must be run on a neural interface')
|
||||||
|
|
||||||
local function assertModule(module, name)
|
local function assertModule(module, name)
|
||||||
if not neural.hasModule(module) then
|
if not neural.hasModule(module) then
|
||||||
Syntax('Missing: ' .. name)
|
Syntax('Missing: ' .. name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assertModule('plethora:glasses', 'Overlay glasses')
|
assertModule('plethora:glasses', 'Overlay glasses')
|
||||||
assertModule('plethora:sensor', 'Entity sensor')
|
assertModule('plethora:sensor', 'Entity sensor')
|
||||||
@@ -31,102 +31,102 @@ local BUILDER_DIR = 'usr/builder'
|
|||||||
|
|
||||||
--[[-- SubDB --]]--
|
--[[-- SubDB --]]--
|
||||||
local subDB = TableDB({
|
local subDB = TableDB({
|
||||||
fileName = fs.combine(BUILDER_DIR, 'sub.db'),
|
fileName = fs.combine(BUILDER_DIR, 'sub.db'),
|
||||||
})
|
})
|
||||||
|
|
||||||
function subDB:load()
|
function subDB:load()
|
||||||
if fs.exists(self.fileName) then
|
if fs.exists(self.fileName) then
|
||||||
TableDB.load(self)
|
TableDB.load(self)
|
||||||
elseif not Builder.isCommandComputer then
|
elseif not Builder.isCommandComputer then
|
||||||
self:seedDB()
|
self:seedDB()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function subDB:seedDB()
|
function subDB:seedDB()
|
||||||
self.data = {
|
self.data = {
|
||||||
[ "minecraft:redstone_wire:0" ] = "minecraft:redstone:0",
|
[ "minecraft:redstone_wire:0" ] = "minecraft:redstone:0",
|
||||||
[ "minecraft:wall_sign:0" ] = "minecraft:sign:0",
|
[ "minecraft:wall_sign:0" ] = "minecraft:sign:0",
|
||||||
[ "minecraft:standing_sign:0" ] = "minecraft:sign:0",
|
[ "minecraft:standing_sign:0" ] = "minecraft:sign:0",
|
||||||
[ "minecraft:potatoes:0" ] = "minecraft:potato:0",
|
[ "minecraft:potatoes:0" ] = "minecraft:potato:0",
|
||||||
[ "minecraft:unlit_redstone_torch:0" ] = "minecraft:redstone_torch:0",
|
[ "minecraft:unlit_redstone_torch:0" ] = "minecraft:redstone_torch:0",
|
||||||
[ "minecraft:powered_repeater:0" ] = "minecraft:repeater:0",
|
[ "minecraft:powered_repeater:0" ] = "minecraft:repeater:0",
|
||||||
[ "minecraft:unpowered_repeater:0" ] = "minecraft:repeater:0",
|
[ "minecraft:unpowered_repeater:0" ] = "minecraft:repeater:0",
|
||||||
[ "minecraft:carrots:0" ] = "minecraft:carrot:0",
|
[ "minecraft:carrots:0" ] = "minecraft:carrot:0",
|
||||||
[ "minecraft:cocoa:0" ] = "minecraft:dye:3",
|
[ "minecraft:cocoa:0" ] = "minecraft:dye:3",
|
||||||
[ "minecraft:unpowered_comparator:0" ] = "minecraft:comparator:0",
|
[ "minecraft:unpowered_comparator:0" ] = "minecraft:comparator:0",
|
||||||
[ "minecraft:powered_comparator:0" ] = "minecraft:comparator:0",
|
[ "minecraft:powered_comparator:0" ] = "minecraft:comparator:0",
|
||||||
[ "minecraft:piston_head:0" ] = "minecraft:air:0",
|
[ "minecraft:piston_head:0" ] = "minecraft:air:0",
|
||||||
[ "minecraft:piston_extension:0" ] = "minecraft:air:0",
|
[ "minecraft:piston_extension:0" ] = "minecraft:air:0",
|
||||||
[ "minecraft:portal:0" ] = "minecraft:air:0",
|
[ "minecraft:portal:0" ] = "minecraft:air:0",
|
||||||
[ "minecraft:double_wooden_slab:0" ] = "minecraft:planks:0",
|
[ "minecraft:double_wooden_slab:0" ] = "minecraft:planks:0",
|
||||||
[ "minecraft:double_wooden_slab:1" ] = "minecraft:planks:1",
|
[ "minecraft:double_wooden_slab:1" ] = "minecraft:planks:1",
|
||||||
[ "minecraft:double_wooden_slab:2" ] = "minecraft:planks:2",
|
[ "minecraft:double_wooden_slab:2" ] = "minecraft:planks:2",
|
||||||
[ "minecraft:double_wooden_slab:3" ] = "minecraft:planks:3",
|
[ "minecraft:double_wooden_slab:3" ] = "minecraft:planks:3",
|
||||||
[ "minecraft:double_wooden_slab:4" ] = "minecraft:planks:4",
|
[ "minecraft:double_wooden_slab:4" ] = "minecraft:planks:4",
|
||||||
[ "minecraft:double_wooden_slab:5" ] = "minecraft:planks:5",
|
[ "minecraft:double_wooden_slab:5" ] = "minecraft:planks:5",
|
||||||
[ "minecraft:lit_redstone_lamp:0" ] = "minecraft:redstone_lamp:0",
|
[ "minecraft:lit_redstone_lamp:0" ] = "minecraft:redstone_lamp:0",
|
||||||
[ "minecraft:double_stone_slab:1" ] = "minecraft:sandstone:0",
|
[ "minecraft:double_stone_slab:1" ] = "minecraft:sandstone:0",
|
||||||
[ "minecraft:double_stone_slab:2" ] = "minecraft:planks:0",
|
[ "minecraft:double_stone_slab:2" ] = "minecraft:planks:0",
|
||||||
[ "minecraft:double_stone_slab:3" ] = "minecraft:cobblestone:0",
|
[ "minecraft:double_stone_slab:3" ] = "minecraft:cobblestone:0",
|
||||||
[ "minecraft:double_stone_slab:4" ] = "minecraft:brick_block:0",
|
[ "minecraft:double_stone_slab:4" ] = "minecraft:brick_block:0",
|
||||||
[ "minecraft:double_stone_slab:5" ] = "minecraft:stonebrick:0",
|
[ "minecraft:double_stone_slab:5" ] = "minecraft:stonebrick:0",
|
||||||
[ "minecraft:double_stone_slab:6" ] = "minecraft:nether_brick:0",
|
[ "minecraft:double_stone_slab:6" ] = "minecraft:nether_brick:0",
|
||||||
[ "minecraft:double_stone_slab:7" ] = "minecraft:quartz_block:0",
|
[ "minecraft:double_stone_slab:7" ] = "minecraft:quartz_block:0",
|
||||||
[ "minecraft:double_stone_slab:9" ] = "minecraft:sandstone:2",
|
[ "minecraft:double_stone_slab:9" ] = "minecraft:sandstone:2",
|
||||||
[ "minecraft:double_stone_slab2:0" ] = "minecraft:sandstone:0",
|
[ "minecraft:double_stone_slab2:0" ] = "minecraft:sandstone:0",
|
||||||
[ "minecraft:stone_slab:2" ] = "minecraft:wooden_slab:0",
|
[ "minecraft:stone_slab:2" ] = "minecraft:wooden_slab:0",
|
||||||
[ "minecraft:wheat:0" ] = "minecraft:wheat_seeds:0",
|
[ "minecraft:wheat:0" ] = "minecraft:wheat_seeds:0",
|
||||||
[ "minecraft:flowing_water:0" ] = "minecraft:air:0",
|
[ "minecraft:flowing_water:0" ] = "minecraft:air:0",
|
||||||
[ "minecraft:lit_furnace:0" ] = "minecraft:furnace:0",
|
[ "minecraft:lit_furnace:0" ] = "minecraft:furnace:0",
|
||||||
[ "minecraft:wall_banner:0" ] = "minecraft:banner:0",
|
[ "minecraft:wall_banner:0" ] = "minecraft:banner:0",
|
||||||
[ "minecraft:standing_banner:0" ] = "minecraft:banner:0",
|
[ "minecraft:standing_banner:0" ] = "minecraft:banner:0",
|
||||||
[ "minecraft:tripwire:0" ] = "minecraft:string:0",
|
[ "minecraft:tripwire:0" ] = "minecraft:string:0",
|
||||||
[ "minecraft:pumpkin_stem:0" ] = "minecraft:pumpkin_seeds:0",
|
[ "minecraft:pumpkin_stem:0" ] = "minecraft:pumpkin_seeds:0",
|
||||||
}
|
}
|
||||||
self.dirty = true
|
self.dirty = true
|
||||||
self:flush()
|
self:flush()
|
||||||
end
|
end
|
||||||
|
|
||||||
function subDB:add(s)
|
function subDB:add(s)
|
||||||
TableDB.add(self, { s.id, s.dmg }, table.concat({ s.sid, s.sdmg }, ':'))
|
TableDB.add(self, { s.id, s.dmg }, table.concat({ s.sid, s.sdmg }, ':'))
|
||||||
self:flush()
|
self:flush()
|
||||||
end
|
end
|
||||||
|
|
||||||
function subDB:remove(s)
|
function subDB:remove(s)
|
||||||
-- TODO: tableDB.remove should take table key
|
-- TODO: tableDB.remove should take table key
|
||||||
TableDB.remove(self, s.id .. ':' .. s.dmg)
|
TableDB.remove(self, s.id .. ':' .. s.dmg)
|
||||||
self:flush()
|
self:flush()
|
||||||
end
|
end
|
||||||
|
|
||||||
function subDB:extract(s)
|
function subDB:extract(s)
|
||||||
local id, dmg = s:match('(.+):(%d+)')
|
local id, dmg = s:match('(.+):(%d+)')
|
||||||
return id, tonumber(dmg)
|
return id, tonumber(dmg)
|
||||||
end
|
end
|
||||||
|
|
||||||
function subDB:getSubstitutedItem(id, dmg)
|
function subDB:getSubstitutedItem(id, dmg)
|
||||||
local sub = TableDB.get(self, { id, dmg })
|
local sub = TableDB.get(self, { id, dmg })
|
||||||
if sub then
|
if sub then
|
||||||
id, dmg = self:extract(sub)
|
id, dmg = self:extract(sub)
|
||||||
end
|
end
|
||||||
return { id = id, dmg = dmg }
|
return { id = id, dmg = dmg }
|
||||||
end
|
end
|
||||||
|
|
||||||
function subDB:lookupBlocksForSub(sid, sdmg)
|
function subDB:lookupBlocksForSub(sid, sdmg)
|
||||||
local t = { }
|
local t = { }
|
||||||
for k,v in pairs(self.data) do
|
for k,v in pairs(self.data) do
|
||||||
local id, dmg = self:extract(v)
|
local id, dmg = self:extract(v)
|
||||||
if id == sid and dmg == sdmg then
|
if id == sid and dmg == sdmg then
|
||||||
id, dmg = self:extract(k)
|
id, dmg = self:extract(k)
|
||||||
t[k] = { id = id, dmg = dmg, sid = sid, sdmg = sdmg }
|
t[k] = { id = id, dmg = dmg, sid = sid, sdmg = sdmg }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[-- startup logic --]]--
|
--[[-- startup logic --]]--
|
||||||
local args = {...}
|
local args = {...}
|
||||||
if #args < 1 then
|
if #args < 1 then
|
||||||
error('supply file name')
|
error('supply file name')
|
||||||
end
|
end
|
||||||
|
|
||||||
subDB:load()
|
subDB:load()
|
||||||
@@ -142,18 +142,18 @@ Builder:substituteBlocks(Util.throttle())
|
|||||||
local cn = neural.canvas3d().create()
|
local cn = neural.canvas3d().create()
|
||||||
local pos = neural.getMetaOwner().withinBlock
|
local pos = neural.getMetaOwner().withinBlock
|
||||||
|
|
||||||
cn.recenter({-(pos.x + .5), -(pos.y + 2) + .5, -(pos.z + .5) })
|
cn.recenter({-pos.x + .5, -(pos.y + 2) + .5, -pos.z + .5 })
|
||||||
|
|
||||||
for i = 1, #Builder.schematic.blocks do
|
for i = 1, #Builder.schematic.blocks do
|
||||||
local b = Builder.schematic:getComputedBlock(i)
|
local b = Builder.schematic:getComputedBlock(i)
|
||||||
if b.id ~= "minecraft:air" and b.id ~= 'minecraft:water' then
|
if b.id ~= "minecraft:air" and b.id ~= 'minecraft:water' then
|
||||||
local s, m = pcall(function()
|
local s, m = pcall(function()
|
||||||
cn.addItem({ b.x, b.y, b.z }, b.id, b.dmg)
|
cn.addItem({ b.x, b.y, b.z }, b.id, b.dmg)
|
||||||
end)
|
end)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G.printError(m)
|
_G.printError(m)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
pcall(_G.read)
|
pcall(_G.read)
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ local fs = _G.fs
|
|||||||
local peripheral = _G.peripheral
|
local peripheral = _G.peripheral
|
||||||
|
|
||||||
if ccemux then
|
if ccemux then
|
||||||
-- add a System setup tab
|
-- add a System setup tab
|
||||||
fs.mount('sys/apps/system/ccemux.lua', 'linkfs', 'packages/ccemux/system/ccemux.lua')
|
fs.mount('sys/apps/system/ccemux.lua', 'linkfs', 'packages/ccemux/system/ccemux.lua')
|
||||||
|
|
||||||
local Config = require('config')
|
local Config = require('config')
|
||||||
|
|
||||||
for k,v in pairs(Config.load('ccemux')) do
|
for k,v in pairs(Config.load('ccemux')) do
|
||||||
if not peripheral.getType(k) then
|
if not peripheral.getType(k) then
|
||||||
ccemux.attach(k, v.type, v.args)
|
ccemux.attach(k, v.type, v.args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -15,18 +15,18 @@ local REGISTRY_DIR = 'usr/.registry'
|
|||||||
-- FIX SOMEDAY
|
-- FIX SOMEDAY
|
||||||
local function registerApp(app, key)
|
local function registerApp(app, key)
|
||||||
|
|
||||||
app.key = SHA1.sha1(key)
|
app.key = SHA1.sha1(key)
|
||||||
Util.writeTable(fs.combine(REGISTRY_DIR, app.key), app)
|
Util.writeTable(fs.combine(REGISTRY_DIR, app.key), app)
|
||||||
os.queueEvent('os_register_app')
|
os.queueEvent('os_register_app')
|
||||||
end
|
end
|
||||||
|
|
||||||
local function unregisterApp(key)
|
local function unregisterApp(key)
|
||||||
|
|
||||||
local filename = fs.combine(REGISTRY_DIR, SHA1.sha1(key))
|
local filename = fs.combine(REGISTRY_DIR, SHA1.sha1(key))
|
||||||
if fs.exists(filename) then
|
if fs.exists(filename) then
|
||||||
fs.delete(filename)
|
fs.delete(filename)
|
||||||
os.queueEvent('os_register_app')
|
os.queueEvent('os_register_app')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -40,366 +40,366 @@ local APP_DIR = 'usr/apps'
|
|||||||
|
|
||||||
local sources = {
|
local sources = {
|
||||||
|
|
||||||
{ text = "STD Default",
|
{ text = "STD Default",
|
||||||
event = 'source',
|
event = 'source',
|
||||||
url = "http://pastebin.com/raw/zVws7eLq" }, --stock
|
url = "http://pastebin.com/raw/zVws7eLq" }, --stock
|
||||||
--[[
|
--[[
|
||||||
{ text = "Discover",
|
{ text = "Discover",
|
||||||
event = 'source',
|
event = 'source',
|
||||||
generateName = true,
|
generateName = true,
|
||||||
url = "http://pastebin.com/raw/9bXfCz6M" }, --owned by dannysmc95
|
url = "http://pastebin.com/raw/9bXfCz6M" }, --owned by dannysmc95
|
||||||
|
|
||||||
{ text = "Opus",
|
{ text = "Opus",
|
||||||
event = 'source',
|
event = 'source',
|
||||||
url = "http://pastebin.com/raw/ajQ91Rmn" },
|
url = "http://pastebin.com/raw/ajQ91Rmn" },
|
||||||
]]
|
]]
|
||||||
}
|
}
|
||||||
|
|
||||||
shell.setDir(APP_DIR)
|
shell.setDir(APP_DIR)
|
||||||
|
|
||||||
local function downloadApp(app)
|
local function downloadApp(app)
|
||||||
local h
|
local h
|
||||||
|
|
||||||
if type(app.url) == "table" then
|
if type(app.url) == "table" then
|
||||||
h = contextualGet(app.url[1])
|
h = contextualGet(app.url[1])
|
||||||
else
|
else
|
||||||
h = http.get(app.url)
|
h = http.get(app.url)
|
||||||
end
|
end
|
||||||
|
|
||||||
if h then
|
if h then
|
||||||
local contents = h.readAll()
|
local contents = h.readAll()
|
||||||
h:close()
|
h:close()
|
||||||
return contents
|
return contents
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function runApp(app, checkExists, ...)
|
local function runApp(app, checkExists, ...)
|
||||||
|
|
||||||
local path, fn
|
local path, fn
|
||||||
local args = { ... }
|
local args = { ... }
|
||||||
|
|
||||||
if checkExists and fs.exists(fs.combine(APP_DIR, app.name)) then
|
if checkExists and fs.exists(fs.combine(APP_DIR, app.name)) then
|
||||||
path = fs.combine(APP_DIR, app.name)
|
path = fs.combine(APP_DIR, app.name)
|
||||||
else
|
else
|
||||||
local program = downloadApp(app)
|
local program = downloadApp(app)
|
||||||
|
|
||||||
fn = function()
|
fn = function()
|
||||||
|
|
||||||
if not program then
|
if not program then
|
||||||
error('Failed to download')
|
error('Failed to download')
|
||||||
end
|
end
|
||||||
|
|
||||||
local fn = loadstring(program, app.name)
|
local fn = loadstring(program, app.name)
|
||||||
|
|
||||||
if not fn then
|
if not fn then
|
||||||
error('Failed to download')
|
error('Failed to download')
|
||||||
end
|
end
|
||||||
|
|
||||||
setfenv(fn, sandboxEnv)
|
setfenv(fn, sandboxEnv)
|
||||||
fn(unpack(args))
|
fn(unpack(args))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
multishell.openTab({
|
multishell.openTab({
|
||||||
title = app.name,
|
title = app.name,
|
||||||
env = sandboxEnv,
|
env = sandboxEnv,
|
||||||
path = path,
|
path = path,
|
||||||
fn = fn,
|
fn = fn,
|
||||||
focused = true,
|
focused = true,
|
||||||
})
|
})
|
||||||
|
|
||||||
return true, 'Running program'
|
return true, 'Running program'
|
||||||
end
|
end
|
||||||
|
|
||||||
local installApp = function(app)
|
local installApp = function(app)
|
||||||
|
|
||||||
local program = downloadApp(app)
|
local program = downloadApp(app)
|
||||||
if not program then
|
if not program then
|
||||||
return false, "Failed to download"
|
return false, "Failed to download"
|
||||||
end
|
end
|
||||||
|
|
||||||
local fullPath = fs.combine(APP_DIR, app.name)
|
local fullPath = fs.combine(APP_DIR, app.name)
|
||||||
Util.writeFile(fullPath, program)
|
Util.writeFile(fullPath, program)
|
||||||
return true, 'Installed as ' .. fullPath
|
return true, 'Installed as ' .. fullPath
|
||||||
end
|
end
|
||||||
|
|
||||||
local viewApp = function(app)
|
local viewApp = function(app)
|
||||||
|
|
||||||
local program = downloadApp(app)
|
local program = downloadApp(app)
|
||||||
if not program then
|
if not program then
|
||||||
return false, "Failed to download"
|
return false, "Failed to download"
|
||||||
end
|
end
|
||||||
|
|
||||||
Util.writeFile('/.source', program)
|
Util.writeFile('/.source', program)
|
||||||
shell.openForegroundTab('edit /.source')
|
shell.openForegroundTab('edit /.source')
|
||||||
fs.delete('/.source')
|
fs.delete('/.source')
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local getSourceListing = function(source)
|
local getSourceListing = function(source)
|
||||||
local contents = http.get(source.url)
|
local contents = http.get(source.url)
|
||||||
if contents then
|
if contents then
|
||||||
|
|
||||||
local fn = loadstring(contents.readAll(), source.text)
|
local fn = loadstring(contents.readAll(), source.text)
|
||||||
contents.close()
|
contents.close()
|
||||||
|
|
||||||
local env = { std = { } }
|
local env = { std = { } }
|
||||||
setmetatable(env, { __index = _G })
|
setmetatable(env, { __index = _G })
|
||||||
setfenv(fn, env)
|
setfenv(fn, env)
|
||||||
fn()
|
fn()
|
||||||
|
|
||||||
if env.contextualGet then
|
if env.contextualGet then
|
||||||
contextualGet = env.contextualGet
|
contextualGet = env.contextualGet
|
||||||
end
|
end
|
||||||
|
|
||||||
source.storeURLs = env.std.storeURLs
|
source.storeURLs = env.std.storeURLs
|
||||||
source.storeCatagoryNames = env.std.storeCatagoryNames
|
source.storeCatagoryNames = env.std.storeCatagoryNames
|
||||||
|
|
||||||
if source.storeURLs and source.storeCatagoryNames then
|
if source.storeURLs and source.storeCatagoryNames then
|
||||||
for k,v in pairs(source.storeURLs) do
|
for k,v in pairs(source.storeURLs) do
|
||||||
if source.generateName then
|
if source.generateName then
|
||||||
v.name = v.title:match('(%w+)')
|
v.name = v.title:match('(%w+)')
|
||||||
if not v.name or #v.name == 0 then
|
if not v.name or #v.name == 0 then
|
||||||
v.name = tostring(k)
|
v.name = tostring(k)
|
||||||
else
|
else
|
||||||
v.name = v.name:lower()
|
v.name = v.name:lower()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
v.name = k
|
v.name = k
|
||||||
end
|
end
|
||||||
v.categoryName = source.storeCatagoryNames[v.catagory]
|
v.categoryName = source.storeCatagoryNames[v.catagory]
|
||||||
v.ltitle = v.title:lower()
|
v.ltitle = v.title:lower()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local appPage = UI.Page {
|
local appPage = UI.Page {
|
||||||
menuBar = UI.MenuBar {
|
menuBar = UI.MenuBar {
|
||||||
-- showBackButton = not pocket,
|
-- showBackButton = not pocket,
|
||||||
buttons = {
|
buttons = {
|
||||||
{ text = '\027', event = 'back' },
|
{ text = '\027', event = 'back' },
|
||||||
{ text = 'Install', event = 'install' },
|
{ text = 'Install', event = 'install' },
|
||||||
{ text = 'Run', event = 'run' },
|
{ text = 'Run', event = 'run' },
|
||||||
{ text = 'View', event = 'view' },
|
{ text = 'View', event = 'view' },
|
||||||
{ text = 'Remove', event = 'uninstall', name = 'removeButton' },
|
{ text = 'Remove', event = 'uninstall', name = 'removeButton' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
container = UI.Window {
|
container = UI.Window {
|
||||||
x = 2, y = 3, ex = -2, ey = -3,
|
x = 2, y = 3, ex = -2, ey = -3,
|
||||||
viewport = UI.Viewport(),
|
viewport = UI.Viewport(),
|
||||||
},
|
},
|
||||||
notification = UI.Notification(),
|
notification = UI.Notification(),
|
||||||
accelerators = {
|
accelerators = {
|
||||||
q = 'back',
|
q = 'back',
|
||||||
backspace = 'back',
|
backspace = 'back',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function appPage.container.viewport:draw()
|
function appPage.container.viewport:draw()
|
||||||
local app = self.parent.parent.app
|
local app = self.parent.parent.app
|
||||||
local str = string.format(
|
local str = string.format(
|
||||||
'%s \nBy: %s \nCategory: %s\nFile name: %s\n\n%s',
|
'%s \nBy: %s \nCategory: %s\nFile name: %s\n\n%s',
|
||||||
Ansi.yellow .. app.title .. Ansi.reset,
|
Ansi.yellow .. app.title .. Ansi.reset,
|
||||||
app.creator,
|
app.creator,
|
||||||
app.categoryName, app.name,
|
app.categoryName, app.name,
|
||||||
Ansi.yellow .. app.description .. Ansi.reset)
|
Ansi.yellow .. app.description .. Ansi.reset)
|
||||||
|
|
||||||
self:clear()
|
self:clear()
|
||||||
self:setCursorPos(1, 1)
|
self:setCursorPos(1, 1)
|
||||||
self:print(str)
|
self:print(str)
|
||||||
self.ymax = self.cursorY
|
self.ymax = self.cursorY
|
||||||
|
|
||||||
if appPage.notification.enabled then
|
if appPage.notification.enabled then
|
||||||
appPage.notification:draw()
|
appPage.notification:draw()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function appPage:enable(source, app)
|
function appPage:enable(source, app)
|
||||||
self.source = source
|
self.source = source
|
||||||
self.app = app
|
self.app = app
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
|
|
||||||
self.container.viewport:setScrollPosition(0)
|
self.container.viewport:setScrollPosition(0)
|
||||||
if fs.exists(fs.combine(APP_DIR, app.name)) then
|
if fs.exists(fs.combine(APP_DIR, app.name)) then
|
||||||
self.menuBar.removeButton:enable('Remove')
|
self.menuBar.removeButton:enable('Remove')
|
||||||
else
|
else
|
||||||
self.menuBar.removeButton:disable('Remove')
|
self.menuBar.removeButton:disable('Remove')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function appPage:eventHandler(event)
|
function appPage:eventHandler(event)
|
||||||
if event.type == 'back' then
|
if event.type == 'back' then
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
|
|
||||||
elseif event.type == 'run' then
|
elseif event.type == 'run' then
|
||||||
self.notification:info('Running program', 3)
|
self.notification:info('Running program', 3)
|
||||||
self:sync()
|
self:sync()
|
||||||
runApp(self.app, true)
|
runApp(self.app, true)
|
||||||
|
|
||||||
elseif event.type == 'view' then
|
elseif event.type == 'view' then
|
||||||
self.notification:info('Downloading program', 3)
|
self.notification:info('Downloading program', 3)
|
||||||
self:sync()
|
self:sync()
|
||||||
viewApp(self.app)
|
viewApp(self.app)
|
||||||
|
|
||||||
elseif event.type == 'uninstall' then
|
elseif event.type == 'uninstall' then
|
||||||
if self.app.runOnly then
|
if self.app.runOnly then
|
||||||
runApp(self.app, false, 'uninstall')
|
runApp(self.app, false, 'uninstall')
|
||||||
else
|
else
|
||||||
fs.delete(fs.combine(APP_DIR, self.app.name))
|
fs.delete(fs.combine(APP_DIR, self.app.name))
|
||||||
self.notification:success("Uninstalled " .. self.app.name, 3)
|
self.notification:success("Uninstalled " .. self.app.name, 3)
|
||||||
self:focusFirst(self)
|
self:focusFirst(self)
|
||||||
self.menuBar.removeButton:disable('Remove')
|
self.menuBar.removeButton:disable('Remove')
|
||||||
self.menuBar:draw()
|
self.menuBar:draw()
|
||||||
|
|
||||||
unregisterApp(self.app.creator .. '.' .. self.app.name)
|
unregisterApp(self.app.creator .. '.' .. self.app.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'install' then
|
elseif event.type == 'install' then
|
||||||
self.notification:info("Installing", 3)
|
self.notification:info("Installing", 3)
|
||||||
self:sync()
|
self:sync()
|
||||||
local s, m
|
local s, m
|
||||||
if self.app.runOnly then
|
if self.app.runOnly then
|
||||||
s,m = runApp(self.app, false)
|
s,m = runApp(self.app, false)
|
||||||
else
|
else
|
||||||
s,m = installApp(self.app)
|
s,m = installApp(self.app)
|
||||||
end
|
end
|
||||||
if s then
|
if s then
|
||||||
self.notification:success(m, 3)
|
self.notification:success(m, 3)
|
||||||
|
|
||||||
if not self.app.runOnly then
|
if not self.app.runOnly then
|
||||||
self.menuBar.removeButton:enable('Remove')
|
self.menuBar.removeButton:enable('Remove')
|
||||||
self.menuBar:draw()
|
self.menuBar:draw()
|
||||||
|
|
||||||
local category = 'Apps'
|
local category = 'Apps'
|
||||||
if self.app.catagoryName == 'Game' then
|
if self.app.catagoryName == 'Game' then
|
||||||
category = 'Games'
|
category = 'Games'
|
||||||
end
|
end
|
||||||
|
|
||||||
registerApp({
|
registerApp({
|
||||||
run = fs.combine(APP_DIR, self.app.name),
|
run = fs.combine(APP_DIR, self.app.name),
|
||||||
title = self.app.title,
|
title = self.app.title,
|
||||||
category = category,
|
category = category,
|
||||||
icon = self.app.icon,
|
icon = self.app.icon,
|
||||||
}, self.app.creator .. '.' .. self.app.name)
|
}, self.app.creator .. '.' .. self.app.name)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self.notification:error(m, 3)
|
self.notification:error(m, 3)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local categoryPage = UI.Page {
|
local categoryPage = UI.Page {
|
||||||
menuBar = UI.MenuBar {
|
menuBar = UI.MenuBar {
|
||||||
buttons = {
|
buttons = {
|
||||||
{ text = 'Catalog', dropdown = sources },
|
{ text = 'Catalog', dropdown = sources },
|
||||||
{ text = 'Category', name = 'categoryButton', dropdown = { } },
|
{ text = 'Category', name = 'categoryButton', dropdown = { } },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = 2, ey = -2,
|
y = 2, ey = -2,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Title', key = 'title' },
|
{ heading = 'Title', key = 'title' },
|
||||||
},
|
},
|
||||||
sortColumn = 'title',
|
sortColumn = 'title',
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar(),
|
statusBar = UI.StatusBar(),
|
||||||
accelerators = {
|
accelerators = {
|
||||||
l = 'lua',
|
l = 'lua',
|
||||||
q = 'quit',
|
q = 'quit',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function categoryPage:setCategory(source, name, index)
|
function categoryPage:setCategory(source, name, index)
|
||||||
self.grid.values = { }
|
self.grid.values = { }
|
||||||
for _,v in pairs(source.storeURLs) do
|
for _,v in pairs(source.storeURLs) do
|
||||||
if index == 0 or index == v.catagory then
|
if index == 0 or index == v.catagory then
|
||||||
table.insert(self.grid.values, v)
|
table.insert(self.grid.values, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.statusBar:setStatus(string.format('%s: %s', source.text, name))
|
self.statusBar:setStatus(string.format('%s: %s', source.text, name))
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
self.grid:setIndex(1)
|
self.grid:setIndex(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
function categoryPage:setSource(source)
|
function categoryPage:setSource(source)
|
||||||
|
|
||||||
if not source.categoryMenu then
|
if not source.categoryMenu then
|
||||||
|
|
||||||
self.statusBar:setStatus('Loading...')
|
self.statusBar:setStatus('Loading...')
|
||||||
self.statusBar:draw()
|
self.statusBar:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
|
|
||||||
getSourceListing(source)
|
getSourceListing(source)
|
||||||
|
|
||||||
if not source.storeURLs then
|
if not source.storeURLs then
|
||||||
error('Unable to download application list')
|
error('Unable to download application list')
|
||||||
end
|
end
|
||||||
|
|
||||||
local buttons = { }
|
local buttons = { }
|
||||||
for k,v in Util.spairs(source.storeCatagoryNames,
|
for k,v in Util.spairs(source.storeCatagoryNames,
|
||||||
function(a, b) return a:lower() < b:lower() end) do
|
function(a, b) return a:lower() < b:lower() end) do
|
||||||
|
|
||||||
if v ~= 'Operating System' then
|
if v ~= 'Operating System' then
|
||||||
table.insert(buttons, {
|
table.insert(buttons, {
|
||||||
text = v,
|
text = v,
|
||||||
event = 'category',
|
event = 'category',
|
||||||
index = k,
|
index = k,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
source.categoryMenu = UI.DropMenu({
|
source.categoryMenu = UI.DropMenu({
|
||||||
buttons = buttons,
|
buttons = buttons,
|
||||||
})
|
})
|
||||||
source.index, source.name = Util.first(source.storeCatagoryNames)
|
source.index, source.name = Util.first(source.storeCatagoryNames)
|
||||||
|
|
||||||
categoryPage.menuBar.categoryButton:add({
|
categoryPage.menuBar.categoryButton:add({
|
||||||
categoryMenu = source.categoryMenu
|
categoryMenu = source.categoryMenu
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
self.source = source
|
self.source = source
|
||||||
self.menuBar.categoryButton.dropmenu = source.categoryMenu
|
self.menuBar.categoryButton.dropmenu = source.categoryMenu
|
||||||
categoryPage:setCategory(source, source.name, source.index)
|
categoryPage:setCategory(source, source.name, source.index)
|
||||||
end
|
end
|
||||||
|
|
||||||
function categoryPage.grid:sortCompare(a, b)
|
function categoryPage.grid:sortCompare(a, b)
|
||||||
return a.ltitle < b.ltitle
|
return a.ltitle < b.ltitle
|
||||||
end
|
end
|
||||||
|
|
||||||
function categoryPage.grid:getRowTextColor(row, selected)
|
function categoryPage.grid:getRowTextColor(row, selected)
|
||||||
if fs.exists(fs.combine(APP_DIR, row.name)) then
|
if fs.exists(fs.combine(APP_DIR, row.name)) then
|
||||||
return colors.orange
|
return colors.orange
|
||||||
end
|
end
|
||||||
return UI.Grid:getRowTextColor(row, selected)
|
return UI.Grid:getRowTextColor(row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
function categoryPage:eventHandler(event)
|
function categoryPage:eventHandler(event)
|
||||||
|
|
||||||
if event.type == 'grid_select' or event.type == 'select' then
|
if event.type == 'grid_select' or event.type == 'select' then
|
||||||
UI:setPage(appPage, self.source, self.grid:getSelected())
|
UI:setPage(appPage, self.source, self.grid:getSelected())
|
||||||
|
|
||||||
elseif event.type == 'category' then
|
elseif event.type == 'category' then
|
||||||
self:setCategory(self.source, event.button.text, event.button.index)
|
self:setCategory(self.source, event.button.text, event.button.index)
|
||||||
self:setFocus(self.grid)
|
self:setFocus(self.grid)
|
||||||
self:draw()
|
self:draw()
|
||||||
|
|
||||||
elseif event.type == 'source' then
|
elseif event.type == 'source' then
|
||||||
self:setFocus(self.grid)
|
self:setFocus(self.grid)
|
||||||
self:setSource(event.button)
|
self:setSource(event.button)
|
||||||
self:draw()
|
self:draw()
|
||||||
|
|
||||||
elseif event.type == 'quit' then
|
elseif event.type == 'quit' then
|
||||||
UI:exitPullEvents()
|
UI:exitPullEvents()
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
print("Retrieving catalog list")
|
print("Retrieving catalog list")
|
||||||
|
|||||||
@@ -10,196 +10,196 @@ local peripheral = _G.peripheral
|
|||||||
|
|
||||||
--[[ -- PeripheralsPage -- ]] --
|
--[[ -- PeripheralsPage -- ]] --
|
||||||
local peripheralsPage = UI.Page {
|
local peripheralsPage = UI.Page {
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
ey = -2,
|
ey = -2,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Type', key = 'type' },
|
{ heading = 'Type', key = 'type' },
|
||||||
{ heading = 'Side', key = 'side' },
|
{ heading = 'Side', key = 'side' },
|
||||||
},
|
},
|
||||||
sortColumn = 'type',
|
sortColumn = 'type',
|
||||||
autospace = true,
|
autospace = true,
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar {
|
statusBar = UI.StatusBar {
|
||||||
values = 'Select peripheral',
|
values = 'Select peripheral',
|
||||||
},
|
},
|
||||||
accelerators = {
|
accelerators = {
|
||||||
q = 'quit',
|
q = 'quit',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function peripheralsPage.grid:draw()
|
function peripheralsPage.grid:draw()
|
||||||
local sides = peripheral.getNames()
|
local sides = peripheral.getNames()
|
||||||
|
|
||||||
Util.clear(self.values)
|
Util.clear(self.values)
|
||||||
for _,side in pairs(sides) do
|
for _,side in pairs(sides) do
|
||||||
table.insert(self.values, {
|
table.insert(self.values, {
|
||||||
type = peripheral.getType(side),
|
type = peripheral.getType(side),
|
||||||
side = side
|
side = side
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
self:update()
|
self:update()
|
||||||
self:adjustWidth()
|
self:adjustWidth()
|
||||||
UI.Grid.draw(self)
|
UI.Grid.draw(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function peripheralsPage:updatePeripherals()
|
function peripheralsPage:updatePeripherals()
|
||||||
if UI:getCurrentPage() == self then
|
if UI:getCurrentPage() == self then
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function peripheralsPage:eventHandler(event)
|
function peripheralsPage:eventHandler(event)
|
||||||
if event.type == 'quit' then
|
if event.type == 'quit' then
|
||||||
Event.exitPullEvents()
|
Event.exitPullEvents()
|
||||||
|
|
||||||
elseif event.type == 'grid_select' then
|
elseif event.type == 'grid_select' then
|
||||||
UI:setPage('methods', event.selected)
|
UI:setPage('methods', event.selected)
|
||||||
|
|
||||||
end
|
end
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[ -- MethodsPage -- ]] --
|
--[[ -- MethodsPage -- ]] --
|
||||||
local methodsPage = UI.Page {
|
local methodsPage = UI.Page {
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
doc = UI.TextArea {
|
doc = UI.TextArea {
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
x = 2, y = 2, ex = -1, ey = -7,
|
x = 2, y = 2, ex = -1, ey = -7,
|
||||||
},
|
},
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = -6, ey = -2,
|
y = -6, ey = -2,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Name', key = 'name', width = UI.term.width }
|
{ heading = 'Name', key = 'name', width = UI.term.width }
|
||||||
},
|
},
|
||||||
sortColumn = 'name',
|
sortColumn = 'name',
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar {
|
statusBar = UI.StatusBar {
|
||||||
status = 'q to return',
|
status = 'q to return',
|
||||||
},
|
},
|
||||||
accelerators = {
|
accelerators = {
|
||||||
q = 'back',
|
q = 'back',
|
||||||
backspace = 'back',
|
backspace = 'back',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function methodsPage:enable(p)
|
function methodsPage:enable(p)
|
||||||
|
|
||||||
self.peripheral = p or self.peripheral
|
self.peripheral = p or self.peripheral
|
||||||
|
|
||||||
p = peripheral.wrap(self.peripheral.side)
|
p = peripheral.wrap(self.peripheral.side)
|
||||||
if p.getDocs then
|
if p.getDocs then
|
||||||
-- plethora
|
-- plethora
|
||||||
self.grid.values = { }
|
self.grid.values = { }
|
||||||
for k,v in pairs(p.getDocs()) do
|
for k,v in pairs(p.getDocs()) do
|
||||||
table.insert(self.grid.values, {
|
table.insert(self.grid.values, {
|
||||||
name = k,
|
name = k,
|
||||||
doc = v,
|
doc = v,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
elseif not p.getAdvancedMethodsData then
|
elseif not p.getAdvancedMethodsData then
|
||||||
-- computercraft
|
-- computercraft
|
||||||
self.grid.values = { }
|
self.grid.values = { }
|
||||||
for name in pairs(p) do
|
for name in pairs(p) do
|
||||||
table.insert(self.grid.values, {
|
table.insert(self.grid.values, {
|
||||||
name = name,
|
name = name,
|
||||||
noext = true,
|
noext = true,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- open peripherals
|
-- open peripherals
|
||||||
self.grid.values = p.getAdvancedMethodsData()
|
self.grid.values = p.getAdvancedMethodsData()
|
||||||
for name,f in pairs(self.grid.values) do
|
for name,f in pairs(self.grid.values) do
|
||||||
f.name = name
|
f.name = name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
self.grid:setIndex(1)
|
self.grid:setIndex(1)
|
||||||
|
|
||||||
self.doc:setText(self:getDocumentation())
|
self.doc:setText(self:getDocumentation())
|
||||||
|
|
||||||
self.statusBar:setStatus(self.peripheral.type)
|
self.statusBar:setStatus(self.peripheral.type)
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
|
|
||||||
self:setFocus(self.grid)
|
self:setFocus(self.grid)
|
||||||
end
|
end
|
||||||
|
|
||||||
function methodsPage:eventHandler(event)
|
function methodsPage:eventHandler(event)
|
||||||
if event.type == 'back' then
|
if event.type == 'back' then
|
||||||
UI:setPage(peripheralsPage)
|
UI:setPage(peripheralsPage)
|
||||||
return true
|
return true
|
||||||
elseif event.type == 'grid_focus_row' then
|
elseif event.type == 'grid_focus_row' then
|
||||||
self.doc:setText(self:getDocumentation())
|
self.doc:setText(self:getDocumentation())
|
||||||
end
|
end
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
function methodsPage:getDocumentation()
|
function methodsPage:getDocumentation()
|
||||||
|
|
||||||
local method = self.grid:getSelected()
|
local method = self.grid:getSelected()
|
||||||
|
|
||||||
if method.noext then -- computercraft docs
|
if method.noext then -- computercraft docs
|
||||||
return 'No documentation'
|
return 'No documentation'
|
||||||
end
|
end
|
||||||
|
|
||||||
if method.doc then -- plethora docs
|
if method.doc then -- plethora docs
|
||||||
return Ansi.yellow .. method.doc
|
return Ansi.yellow .. method.doc
|
||||||
end
|
end
|
||||||
|
|
||||||
-- open peripherals docs
|
-- open peripherals docs
|
||||||
local sb = { }
|
local sb = { }
|
||||||
if method.description then
|
if method.description then
|
||||||
table.insert(sb, method.description .. '\n\n')
|
table.insert(sb, method.description .. '\n\n')
|
||||||
end
|
end
|
||||||
|
|
||||||
if method.returnTypes ~= '()' then
|
if method.returnTypes ~= '()' then
|
||||||
table.insert(sb, Ansi.yellow .. method.returnTypes .. ' ')
|
table.insert(sb, Ansi.yellow .. method.returnTypes .. ' ')
|
||||||
end
|
end
|
||||||
table.insert(sb, Ansi.blue .. method.name .. Ansi.reset .. '(')
|
table.insert(sb, Ansi.blue .. method.name .. Ansi.reset .. '(')
|
||||||
|
|
||||||
for k,arg in ipairs(method.args) do
|
for k,arg in ipairs(method.args) do
|
||||||
if arg.optional then
|
if arg.optional then
|
||||||
table.insert(sb, Ansi.orange .. string.format('[%s]', arg.name))
|
table.insert(sb, Ansi.orange .. string.format('[%s]', arg.name))
|
||||||
else
|
else
|
||||||
table.insert(sb, Ansi.green .. arg.name)
|
table.insert(sb, Ansi.green .. arg.name)
|
||||||
end
|
end
|
||||||
if k < #method.args then
|
if k < #method.args then
|
||||||
table.insert(sb, ',')
|
table.insert(sb, ',')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.insert(sb, Ansi.reset .. ')')
|
table.insert(sb, Ansi.reset .. ')')
|
||||||
|
|
||||||
Util.filterInplace(method.args, function(a) return #a.description > 0 end)
|
Util.filterInplace(method.args, function(a) return #a.description > 0 end)
|
||||||
if #method.args > 0 then
|
if #method.args > 0 then
|
||||||
table.insert(sb, '\n\n')
|
table.insert(sb, '\n\n')
|
||||||
for k,arg in ipairs(method.args) do
|
for k,arg in ipairs(method.args) do
|
||||||
if arg.optional then
|
if arg.optional then
|
||||||
table.insert(sb, Ansi.orange)
|
table.insert(sb, Ansi.orange)
|
||||||
else
|
else
|
||||||
table.insert(sb, Ansi.green)
|
table.insert(sb, Ansi.green)
|
||||||
end
|
end
|
||||||
table.insert(sb, arg.name .. Ansi.reset .. ': ' .. arg.description)
|
table.insert(sb, arg.name .. Ansi.reset .. ': ' .. arg.description)
|
||||||
if k ~= #method.args then
|
if k ~= #method.args then
|
||||||
table.insert(sb, '\n\n')
|
table.insert(sb, '\n\n')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return table.concat(sb)
|
return table.concat(sb)
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.on('peripheral', function()
|
Event.on('peripheral', function()
|
||||||
peripheralsPage:updatePeripherals()
|
peripheralsPage:updatePeripherals()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.on('peripheral_detach', function()
|
Event.on('peripheral_detach', function()
|
||||||
peripheralsPage:updatePeripherals()
|
peripheralsPage:updatePeripherals()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
UI:setPage(peripheralsPage)
|
UI:setPage(peripheralsPage)
|
||||||
|
|
||||||
UI:setPages({
|
UI:setPages({
|
||||||
methods = methodsPage,
|
methods = methodsPage,
|
||||||
})
|
})
|
||||||
|
|
||||||
UI:pullEvents()
|
UI:pullEvents()
|
||||||
|
|||||||
@@ -10,230 +10,230 @@ local peripheral = _G.peripheral
|
|||||||
|
|
||||||
local drives = { }
|
local drives = { }
|
||||||
peripheral.find('drive', function(n, v)
|
peripheral.find('drive', function(n, v)
|
||||||
if not drives.left then
|
if not drives.left then
|
||||||
drives.left = Util.shallowCopy(v)
|
drives.left = Util.shallowCopy(v)
|
||||||
drives.left.name = n
|
drives.left.name = n
|
||||||
else
|
else
|
||||||
drives.right = Util.shallowCopy(v)
|
drives.right = Util.shallowCopy(v)
|
||||||
drives.right.name = n
|
drives.right.name = n
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if not (drives.left and drives.right) then
|
if not (drives.left and drives.right) then
|
||||||
error('Two drives are required')
|
error('Two drives are required')
|
||||||
end
|
end
|
||||||
|
|
||||||
local COPY_LEFT = 1
|
local COPY_LEFT = 1
|
||||||
local COPY_RIGHT = 2
|
local COPY_RIGHT = 2
|
||||||
local directions = {
|
local directions = {
|
||||||
[ COPY_LEFT ] = { text = '-->>' },
|
[ COPY_LEFT ] = { text = '-->>' },
|
||||||
[ COPY_RIGHT ] = { text = '<<--' },
|
[ COPY_RIGHT ] = { text = '<<--' },
|
||||||
}
|
}
|
||||||
|
|
||||||
local config = Config.load('DiskCopy', {
|
local config = Config.load('DiskCopy', {
|
||||||
eject = true,
|
eject = true,
|
||||||
automatic = false,
|
automatic = false,
|
||||||
copyDir = COPY_LEFT
|
copyDir = COPY_LEFT
|
||||||
})
|
})
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
linfo = UI.Window {
|
linfo = UI.Window {
|
||||||
x = 2, y = 2, ey = 5, width = 18,
|
x = 2, y = 2, ey = 5, width = 18,
|
||||||
},
|
},
|
||||||
rinfo = UI.Window {
|
rinfo = UI.Window {
|
||||||
x = -19, y = 2, ey = 5, width = 18,
|
x = -19, y = 2, ey = 5, width = 18,
|
||||||
},
|
},
|
||||||
dir = UI.Button {
|
dir = UI.Button {
|
||||||
x = 17, y = 7, width = 6,
|
x = 17, y = 7, width = 6,
|
||||||
event = 'change_dir',
|
event = 'change_dir',
|
||||||
},
|
},
|
||||||
progress = UI.ProgressBar {
|
progress = UI.ProgressBar {
|
||||||
x = 2, ex = -2, y = -4,
|
x = 2, ex = -2, y = -4,
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
},
|
},
|
||||||
ejectText = UI.Text {
|
ejectText = UI.Text {
|
||||||
x = 2, y = -2,
|
x = 2, y = -2,
|
||||||
value = 'Eject'
|
value = 'Eject'
|
||||||
},
|
},
|
||||||
eject = UI.Checkbox {
|
eject = UI.Checkbox {
|
||||||
x = 8, y = -2,
|
x = 8, y = -2,
|
||||||
},
|
},
|
||||||
automaticText = UI.Text {
|
automaticText = UI.Text {
|
||||||
x = 12, y = -2,
|
x = 12, y = -2,
|
||||||
value = 'Copy automatically'
|
value = 'Copy automatically'
|
||||||
},
|
},
|
||||||
automatic = UI.Checkbox {
|
automatic = UI.Checkbox {
|
||||||
x = 31, y = -2,
|
x = 31, y = -2,
|
||||||
},
|
},
|
||||||
copyButton = UI.Button {
|
copyButton = UI.Button {
|
||||||
x = -7, y = -2,
|
x = -7, y = -2,
|
||||||
text = 'Copy',
|
text = 'Copy',
|
||||||
event = 'copy',
|
event = 'copy',
|
||||||
inactive = true,
|
inactive = true,
|
||||||
},
|
},
|
||||||
warning = UI.Text {
|
warning = UI.Text {
|
||||||
x = 2, ex = -2, y = -5,
|
x = 2, ex = -2, y = -5,
|
||||||
align = 'center',
|
align = 'center',
|
||||||
textColor = colors.orange,
|
textColor = colors.orange,
|
||||||
},
|
},
|
||||||
notification = UI.Notification { },
|
notification = UI.Notification { },
|
||||||
}
|
}
|
||||||
|
|
||||||
function page:enable()
|
function page:enable()
|
||||||
Util.merge(self.dir, directions[config.copyDir])
|
Util.merge(self.dir, directions[config.copyDir])
|
||||||
|
|
||||||
self.eject.value = config.eject
|
self.eject.value = config.eject
|
||||||
self.automatic.value = config.automatic
|
self.automatic.value = config.automatic
|
||||||
|
|
||||||
self.dir.x = math.floor((self.width / 2) - 3) + 1
|
self.dir.x = math.floor((self.width / 2) - 3) + 1
|
||||||
|
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function isValid(drive)
|
local function isValid(drive)
|
||||||
return drive.isDiskPresent() and drive.getMountPath()
|
return drive.isDiskPresent() and drive.getMountPath()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function needsLabel(drive)
|
local function needsLabel(drive)
|
||||||
return drive.isDiskPresent() and not drive.getMountPath() and not drive.getAudioTitle()
|
return drive.isDiskPresent() and not drive.getMountPath() and not drive.getAudioTitle()
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:drawInfo(drive, textArea)
|
function page:drawInfo(drive, textArea)
|
||||||
local function getLabel()
|
local function getLabel()
|
||||||
return not drive.isDiskPresent() and 'empty' or
|
return not drive.isDiskPresent() and 'empty' or
|
||||||
not drive.getMountPath() and 'invalid' or
|
not drive.getMountPath() and 'invalid' or
|
||||||
drive.getDiskLabel() or 'unlabeled'
|
drive.getDiskLabel() or 'unlabeled'
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getUsed()
|
local function getUsed()
|
||||||
return isValid(drive) and fs.getSize(drive.getMountPath(), true) or 0
|
return isValid(drive) and fs.getSize(drive.getMountPath(), true) or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getFree()
|
local function getFree()
|
||||||
return isValid(drive) and fs.getFreeSpace(drive.getMountPath()) or 0
|
return isValid(drive) and fs.getFreeSpace(drive.getMountPath()) or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
textArea:setCursorPos(1, 1)
|
textArea:setCursorPos(1, 1)
|
||||||
textArea:print(string.format('Drive: %s%s%s\nLabel: %s%s%s\nUsed: %s%s%s\nFree: %s%s%s',
|
textArea:print(string.format('Drive: %s%s%s\nLabel: %s%s%s\nUsed: %s%s%s\nFree: %s%s%s',
|
||||||
Ansi.yellow, drive.name, Ansi.reset,
|
Ansi.yellow, drive.name, Ansi.reset,
|
||||||
isValid(drive) and Ansi.yellow or Ansi.orange, getLabel():sub(1, 10), Ansi.reset,
|
isValid(drive) and Ansi.yellow or Ansi.orange, getLabel():sub(1, 10), Ansi.reset,
|
||||||
Ansi.yellow, Util.toBytes(getUsed()), Ansi.reset,
|
Ansi.yellow, Util.toBytes(getUsed()), Ansi.reset,
|
||||||
Ansi.yellow, Util.toBytes(getFree()), Ansi.reset))
|
Ansi.yellow, Util.toBytes(getFree()), Ansi.reset))
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:scan()
|
function page:scan()
|
||||||
local showWarning = needsLabel(drives.left) or needsLabel(drives.right)
|
local showWarning = needsLabel(drives.left) or needsLabel(drives.right)
|
||||||
local valid = isValid(drives.left) and isValid(drives.right)
|
local valid = isValid(drives.left) and isValid(drives.right)
|
||||||
|
|
||||||
self.warning.value = showWarning and 'Computers must be labeled'
|
self.warning.value = showWarning and 'Computers must be labeled'
|
||||||
self.copyButton.inactive = not valid
|
self.copyButton.inactive = not valid
|
||||||
|
|
||||||
self:draw()
|
self:draw()
|
||||||
self.progress:centeredWrite(1, 'Analyzing Disks..')
|
self.progress:centeredWrite(1, 'Analyzing Disks..')
|
||||||
self.progress:sync()
|
self.progress:sync()
|
||||||
|
|
||||||
self:drawInfo(drives.left, self.linfo)
|
self:drawInfo(drives.left, self.linfo)
|
||||||
self:drawInfo(drives.right, self.rinfo)
|
self:drawInfo(drives.right, self.rinfo)
|
||||||
|
|
||||||
self.progress:clear()
|
self.progress:clear()
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:copy()
|
function page:copy()
|
||||||
local sdrive = config.copyDir == COPY_LEFT and drives.left or drives.right
|
local sdrive = config.copyDir == COPY_LEFT and drives.left or drives.right
|
||||||
local tdrive = config.copyDir == COPY_LEFT and drives.right or drives.left
|
local tdrive = config.copyDir == COPY_LEFT and drives.right or drives.left
|
||||||
|
|
||||||
local throttle = Util.throttle()
|
local throttle = Util.throttle()
|
||||||
local sourceFiles, targetFiles = { }, { }
|
local sourceFiles, targetFiles = { }, { }
|
||||||
|
|
||||||
local function getListing(mountPath, path, files)
|
local function getListing(mountPath, path, files)
|
||||||
for _,f in pairs(fs.list(path)) do
|
for _,f in pairs(fs.list(path)) do
|
||||||
local file = fs.combine(path, f)
|
local file = fs.combine(path, f)
|
||||||
if not fs.isReadOnly(file) then
|
if not fs.isReadOnly(file) then
|
||||||
files[string.sub(file, #mountPath + 1)] = true
|
files[string.sub(file, #mountPath + 1)] = true
|
||||||
if fs.isDir(file) then
|
if fs.isDir(file) then
|
||||||
getListing(mountPath, file, files)
|
getListing(mountPath, file, files)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
throttle()
|
throttle()
|
||||||
end
|
end
|
||||||
|
|
||||||
self.progress:centeredWrite(1, 'Computing..')
|
self.progress:centeredWrite(1, 'Computing..')
|
||||||
self.progress:sync()
|
self.progress:sync()
|
||||||
|
|
||||||
getListing(sdrive.getMountPath(), sdrive.getMountPath(), sourceFiles)
|
getListing(sdrive.getMountPath(), sdrive.getMountPath(), sourceFiles)
|
||||||
getListing(tdrive.getMountPath(), tdrive.getMountPath(), targetFiles)
|
getListing(tdrive.getMountPath(), tdrive.getMountPath(), targetFiles)
|
||||||
|
|
||||||
local copied = 0
|
local copied = 0
|
||||||
local totalFiles = Util.size(sourceFiles)
|
local totalFiles = Util.size(sourceFiles)
|
||||||
|
|
||||||
local function rawCopy(source, target)
|
local function rawCopy(source, target)
|
||||||
if fs.isDir(source) then
|
if fs.isDir(source) then
|
||||||
copied = copied + 1
|
copied = copied + 1
|
||||||
if not fs.exists(target) then
|
if not fs.exists(target) then
|
||||||
fs.makeDir(target)
|
fs.makeDir(target)
|
||||||
end
|
end
|
||||||
for _,f in pairs(fs.list(source)) do
|
for _,f in pairs(fs.list(source)) do
|
||||||
rawCopy(fs.combine(source, f), fs.combine(target, f))
|
rawCopy(fs.combine(source, f), fs.combine(target, f))
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
if fs.exists(target) then
|
if fs.exists(target) then
|
||||||
fs.delete(target)
|
fs.delete(target)
|
||||||
end
|
end
|
||||||
|
|
||||||
fs.copy(source, target)
|
fs.copy(source, target)
|
||||||
copied = copied + 1
|
copied = copied + 1
|
||||||
self.progress.value = copied * 100 / totalFiles
|
self.progress.value = copied * 100 / totalFiles
|
||||||
self.progress:draw()
|
self.progress:draw()
|
||||||
self.progress:sync()
|
self.progress:sync()
|
||||||
end
|
end
|
||||||
throttle()
|
throttle()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function cleanup()
|
local function cleanup()
|
||||||
for k in pairs(targetFiles) do
|
for k in pairs(targetFiles) do
|
||||||
if not sourceFiles[k] then
|
if not sourceFiles[k] then
|
||||||
fs.delete(fs.combine(tdrive.getMountPath(), k))
|
fs.delete(fs.combine(tdrive.getMountPath(), k))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self.progress:clear()
|
self.progress:clear()
|
||||||
rawCopy(sdrive.getMountPath(), tdrive.getMountPath())
|
rawCopy(sdrive.getMountPath(), tdrive.getMountPath())
|
||||||
cleanup()
|
cleanup()
|
||||||
self.progress:centeredWrite(1, 'Copy Complete', colors.lime, colors.black)
|
self.progress:centeredWrite(1, 'Copy Complete', colors.lime, colors.black)
|
||||||
self.progress:sync()
|
self.progress:sync()
|
||||||
|
|
||||||
self.progress.value = 0
|
self.progress.value = 0
|
||||||
self.progress:clear()
|
self.progress:clear()
|
||||||
|
|
||||||
self:scan()
|
self:scan()
|
||||||
|
|
||||||
if config.eject then
|
if config.eject then
|
||||||
tdrive.ejectDisk()
|
tdrive.ejectDisk()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'change_dir' then
|
if event.type == 'change_dir' then
|
||||||
config.copyDir = (config.copyDir) % 2 + 1
|
config.copyDir = (config.copyDir) % 2 + 1
|
||||||
Util.merge(self.dir, directions[config.copyDir])
|
Util.merge(self.dir, directions[config.copyDir])
|
||||||
Config.update('DiskCopy', config)
|
Config.update('DiskCopy', config)
|
||||||
self.dir:draw()
|
self.dir:draw()
|
||||||
|
|
||||||
elseif event.type == 'copy' then
|
elseif event.type == 'copy' then
|
||||||
self:copy()
|
self:copy()
|
||||||
|
|
||||||
elseif event.type == 'checkbox_change' then
|
elseif event.type == 'checkbox_change' then
|
||||||
if event.element == self.eject then
|
if event.element == self.eject then
|
||||||
config.eject = not not event.checked
|
config.eject = not not event.checked
|
||||||
elseif event.element == self.automatic then
|
elseif event.element == self.automatic then
|
||||||
config.automatic = not not event.checked
|
config.automatic = not not event.checked
|
||||||
end
|
end
|
||||||
|
|
||||||
Config.update('DiskCopy', config)
|
Config.update('DiskCopy', config)
|
||||||
event.element:draw()
|
event.element:draw()
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
@@ -242,22 +242,22 @@ function page:eventHandler(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
Event.on("disk", function()
|
Event.on("disk", function()
|
||||||
page:scan()
|
page:scan()
|
||||||
page:sync()
|
page:sync()
|
||||||
|
|
||||||
if config.automatic and not page.copyButton.inactive then
|
if config.automatic and not page.copyButton.inactive then
|
||||||
page:copy()
|
page:copy()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.on("disk_eject", function()
|
Event.on("disk_eject", function()
|
||||||
page:scan()
|
page:scan()
|
||||||
page:sync()
|
page:sync()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.onTimeout(.2, function()
|
Event.onTimeout(.2, function()
|
||||||
page:scan()
|
page:scan()
|
||||||
page:sync()
|
page:sync()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
UI:setPage(page)
|
UI:setPage(page)
|
||||||
|
|||||||
@@ -8,131 +8,131 @@ local os = _G.os
|
|||||||
UI:configure('Events', ...)
|
UI:configure('Events', ...)
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
menuBar = UI.MenuBar {
|
menuBar = UI.MenuBar {
|
||||||
buttons = {
|
buttons = {
|
||||||
{ text = 'Filter', event = 'filter' },
|
{ text = 'Filter', event = 'filter' },
|
||||||
{ text = 'Reset', event = 'reset' },
|
{ text = 'Reset', event = 'reset' },
|
||||||
{ text = 'Pause ', event = 'toggle', name = 'pauseButton' },
|
{ text = 'Pause ', event = 'toggle', name = 'pauseButton' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
grid = UI.Grid {
|
grid = UI.Grid {
|
||||||
y = 2,
|
y = 2,
|
||||||
columns = {
|
columns = {
|
||||||
{ key = 'event' },
|
{ key = 'event' },
|
||||||
{ key = 'p1' },
|
{ key = 'p1' },
|
||||||
{ key = 'p2' },
|
{ key = 'p2' },
|
||||||
{ key = 'p3' },
|
{ key = 'p3' },
|
||||||
{ key = 'p4' },
|
{ key = 'p4' },
|
||||||
{ key = 'p5' },
|
{ key = 'p5' },
|
||||||
},
|
},
|
||||||
autospace = true,
|
autospace = true,
|
||||||
disableHeader = true,
|
disableHeader = true,
|
||||||
},
|
},
|
||||||
accelerators = {
|
accelerators = {
|
||||||
f = 'filter',
|
f = 'filter',
|
||||||
p = 'toggle',
|
p = 'toggle',
|
||||||
r = 'reset',
|
r = 'reset',
|
||||||
c = 'clear',
|
c = 'clear',
|
||||||
q = 'quit',
|
q = 'quit',
|
||||||
},
|
},
|
||||||
filtered = { },
|
filtered = { },
|
||||||
}
|
}
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
|
|
||||||
if event.type == 'filter' then
|
if event.type == 'filter' then
|
||||||
local entry = self.grid:getSelected()
|
local entry = self.grid:getSelected()
|
||||||
self.filtered[entry.event] = true
|
self.filtered[entry.event] = true
|
||||||
|
|
||||||
elseif event.type == 'toggle' then
|
elseif event.type == 'toggle' then
|
||||||
self.paused = not self.paused
|
self.paused = not self.paused
|
||||||
if self.paused then
|
if self.paused then
|
||||||
self.menuBar.pauseButton.text = 'Resume'
|
self.menuBar.pauseButton.text = 'Resume'
|
||||||
else
|
else
|
||||||
self.menuBar.pauseButton.text = 'Pause '
|
self.menuBar.pauseButton.text = 'Pause '
|
||||||
end
|
end
|
||||||
self.menuBar:draw()
|
self.menuBar:draw()
|
||||||
|
|
||||||
elseif event.type == 'grid_select' then
|
elseif event.type == 'grid_select' then
|
||||||
multishell.openTab({
|
multishell.openTab({
|
||||||
path = 'sys/apps/Lua.lua',
|
path = 'sys/apps/Lua.lua',
|
||||||
args = { event.selected },
|
args = { event.selected },
|
||||||
focused = true,
|
focused = true,
|
||||||
})
|
})
|
||||||
|
|
||||||
elseif event.type == 'reset' then
|
elseif event.type == 'reset' then
|
||||||
self.filtered = { }
|
self.filtered = { }
|
||||||
self.grid:setValues({ })
|
self.grid:setValues({ })
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
if self.paused then
|
if self.paused then
|
||||||
self:emit({ type = 'toggle' })
|
self:emit({ type = 'toggle' })
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'clear' then
|
elseif event.type == 'clear' then
|
||||||
self.grid:setValues({ })
|
self.grid:setValues({ })
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
|
|
||||||
elseif event.type == 'quit' then
|
elseif event.type == 'quit' then
|
||||||
UI:exitPullEvents()
|
UI:exitPullEvents()
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
elseif event.type == 'focus_change' then
|
elseif event.type == 'focus_change' then
|
||||||
if event.focused == self.grid then
|
if event.focused == self.grid then
|
||||||
if not self.paused then
|
if not self.paused then
|
||||||
self:emit({ type = 'toggle' })
|
self:emit({ type = 'toggle' })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
--]]
|
--]]
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:getDisplayValues(row)
|
function page.grid:getDisplayValues(row)
|
||||||
row = Util.shallowCopy(row)
|
row = Util.shallowCopy(row)
|
||||||
|
|
||||||
local function tovalue(s)
|
local function tovalue(s)
|
||||||
if type(s) == 'table' then
|
if type(s) == 'table' then
|
||||||
return 'table'
|
return 'table'
|
||||||
end
|
end
|
||||||
return s
|
return s
|
||||||
end
|
end
|
||||||
|
|
||||||
for k,v in pairs(row) do
|
for k,v in pairs(row) do
|
||||||
row[k] = tovalue(v)
|
row[k] = tovalue(v)
|
||||||
end
|
end
|
||||||
|
|
||||||
return row
|
return row
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:draw()
|
function page.grid:draw()
|
||||||
self:adjustWidth()
|
self:adjustWidth()
|
||||||
UI.Grid.draw(self)
|
UI.Grid.draw(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.addRoutine(function()
|
Event.addRoutine(function()
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local e = { os.pullEvent() }
|
local e = { os.pullEvent() }
|
||||||
if not page.paused and not page.filtered[e[1]] then
|
if not page.paused and not page.filtered[e[1]] then
|
||||||
table.insert(page.grid.values, 1, {
|
table.insert(page.grid.values, 1, {
|
||||||
event = e[1],
|
event = e[1],
|
||||||
p1 = e[2],
|
p1 = e[2],
|
||||||
p2 = e[3],
|
p2 = e[3],
|
||||||
p3 = e[4],
|
p3 = e[4],
|
||||||
p4 = e[5],
|
p4 = e[5],
|
||||||
p5 = e[6],
|
p5 = e[6],
|
||||||
})
|
})
|
||||||
if #page.grid.values > page.grid.height then
|
if #page.grid.values > page.grid.height then
|
||||||
table.remove(page.grid.values, #page.grid.values)
|
table.remove(page.grid.values, #page.grid.values)
|
||||||
end
|
end
|
||||||
page.grid:update()
|
page.grid:update()
|
||||||
page.grid:draw()
|
page.grid:draw()
|
||||||
page:sync()
|
page:sync()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
UI:setPage(page)
|
UI:setPage(page)
|
||||||
|
|||||||
@@ -15,32 +15,64 @@ local gpt = GPS.getPoint() or error('GPS not found')
|
|||||||
local pts, blocks
|
local pts, blocks
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
mode = UI.Chooser {
|
menuBar = UI.MenuBar {
|
||||||
x = 13, y = -1,
|
buttons = {
|
||||||
choices = {
|
{ text = 'Range', event = 'range' },
|
||||||
{ name = 'No breaking', value = 'digNone' },
|
{ text = 'Stop', event = 'stop' },
|
||||||
{ name = 'Destructive', value = 'turtleSafe' },
|
},
|
||||||
},
|
mode = UI.Chooser {
|
||||||
value = 'digNone',
|
x = -16,
|
||||||
},
|
choices = {
|
||||||
grid = UI.ScrollingGrid {
|
{ name = 'No breaking', value = 'digNone' },
|
||||||
y = 2, ey = -2,
|
{ name = 'Destructive', value = 'turtleSafe' },
|
||||||
columns = {
|
},
|
||||||
{ heading = 'Label', key = 'label' },
|
value = 'digNone',
|
||||||
{ heading = 'Dist', key = 'distance' },
|
},
|
||||||
{ heading = 'Status', key = 'status' },
|
},
|
||||||
{ heading = 'Fuel', key = 'fuel' },
|
grid = UI.ScrollingGrid {
|
||||||
},
|
y = 2, ey = -2,
|
||||||
sortColumn = 'distance',
|
columns = {
|
||||||
autospace = true,
|
{ heading = 'Label', key = 'label' },
|
||||||
},
|
{ heading = 'Dist', key = 'distance' },
|
||||||
|
{ heading = 'Status', key = 'status' },
|
||||||
|
{ heading = 'Fuel', key = 'fuel' },
|
||||||
|
},
|
||||||
|
sortColumn = 'distance',
|
||||||
|
autospace = true,
|
||||||
|
},
|
||||||
|
range = UI.SlideOut {
|
||||||
|
y = -7, height = 7,
|
||||||
|
backgroundColor = colors.cyan,
|
||||||
|
titleBar = UI.TitleBar {
|
||||||
|
event = 'cancel',
|
||||||
|
title = 'Enter range',
|
||||||
|
},
|
||||||
|
notice = UI.TextArea {
|
||||||
|
x = 2, ex = -2, y = 3, ey = 4,
|
||||||
|
value =
|
||||||
|
[[Select all turtles within a specified range]],
|
||||||
|
},
|
||||||
|
entry = UI.TextEntry {
|
||||||
|
y = 6, x = 2, ex = 10,
|
||||||
|
limit = 4,
|
||||||
|
shadowText = 'range',
|
||||||
|
accelerators = {
|
||||||
|
enter = 'select_range',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
button = UI.Button {
|
||||||
|
x = 12, y = 6,
|
||||||
|
text = 'Apply',
|
||||||
|
event = 'select_range',
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function page.grid:getRowTextColor(row, selected)
|
function page.grid:getRowTextColor(row, selected)
|
||||||
if swarm.pool[row.id] then
|
if swarm.pool[row.id] then
|
||||||
return colors.yellow
|
return colors.yellow
|
||||||
end
|
end
|
||||||
return UI.ScrollingGrid.getRowTextColor(self, row, selected)
|
return UI.ScrollingGrid.getRowTextColor(self, row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:getDisplayValues(row)
|
function page.grid:getDisplayValues(row)
|
||||||
@@ -55,130 +87,155 @@ function page.grid:getDisplayValues(row)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function page:enable()
|
function page:enable()
|
||||||
local function update()
|
local function update()
|
||||||
local t = { }
|
local t = { }
|
||||||
for _,v in pairs(network) do
|
for _,v in pairs(network) do
|
||||||
if v.fuel and v.active and v.fuel > 0 and v.distance then
|
if v.fuel and v.active and v.fuel > 0 and v.distance then
|
||||||
table.insert(t, v)
|
table.insert(t, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.grid:setValues(t)
|
self.grid:setValues(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.onInterval(3, function()
|
Event.onInterval(3, function()
|
||||||
update()
|
update()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
update()
|
update()
|
||||||
|
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function follow(member)
|
local function follow(member)
|
||||||
local turtle = member.turtle
|
local turtle = member.turtle
|
||||||
turtle.reset()
|
turtle.reset()
|
||||||
turtle.set({
|
turtle.set({
|
||||||
digPolicy = page.mode.value,
|
digPolicy = page.menuBar.mode.value,
|
||||||
status = 'Following',
|
status = 'Following',
|
||||||
})
|
})
|
||||||
|
|
||||||
if not turtle.enableGPS(nil, true) then
|
if not turtle.enableGPS(nil, true) then
|
||||||
error('turtle: No GPS found')
|
error('turtle: No GPS found')
|
||||||
end
|
end
|
||||||
|
|
||||||
member.snmp = Socket.connect(member.id, 161)
|
member.snmp = Socket.connect(member.id, 161)
|
||||||
member.snmp.co = coroutine.running()
|
member.snmp.co = coroutine.running()
|
||||||
|
|
||||||
local pt
|
local pt
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
while pt and Point.same(gpt, pt) do
|
while pt and Point.same(gpt, pt) do
|
||||||
os.sleep(.5)
|
os.sleep(.5)
|
||||||
end
|
end
|
||||||
pt = Point.copy(gpt)
|
pt = Point.copy(gpt)
|
||||||
|
|
||||||
local cpt = Point.closest(turtle.getPoint(), pts)
|
local cpt = Point.closest(turtle.getPoint(), pts)
|
||||||
|
|
||||||
turtle.abort(false)
|
turtle.abort(false)
|
||||||
if turtle.pathfind(cpt, { blocks = blocks }) then
|
if turtle.pathfind(cpt, { blocks = blocks }) then
|
||||||
turtle.headTowards(pt)
|
turtle.headTowards(pt)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function swarm:onRemove(member, status, message)
|
function swarm:onRemove(member, status, message)
|
||||||
if member.socket then
|
if member.socket then
|
||||||
pcall(function()
|
pcall(function()
|
||||||
member.turtle.set({ status = 'idle' })
|
member.turtle.set({ status = 'idle' })
|
||||||
member.turtle.abort(true)
|
member.turtle.abort(true)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
if member.snmp then
|
if member.snmp then
|
||||||
member.snmp:close()
|
member.snmp:close()
|
||||||
member.snmp = nil
|
member.snmp = nil
|
||||||
end
|
end
|
||||||
if not status then
|
if not status then
|
||||||
_G._syslog(message)
|
_G._syslog(message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'grid_select' then
|
if event.type == 'grid_select' then
|
||||||
if not swarm.pool[event.selected.id] then
|
if not swarm.pool[event.selected.id] then
|
||||||
swarm:add(event.selected.id)
|
swarm:add(event.selected.id)
|
||||||
swarm:run(follow)
|
swarm:run(follow)
|
||||||
else
|
else
|
||||||
swarm:remove(event.selected.id)
|
swarm:remove(event.selected.id)
|
||||||
end
|
end
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
|
|
||||||
elseif event.type == 'choice_change' then
|
elseif event.type == 'choice_change' then
|
||||||
local script = string.format('turtle.set({ digPolicy = "%s"})', event.value)
|
local script = string.format('turtle.set({ digPolicy = "%s"})', event.value)
|
||||||
for _, member in pairs(swarm.pool) do
|
for _, member in pairs(swarm.pool) do
|
||||||
member.snmp:write({ type = 'scriptEx', args = script })
|
member.snmp:write({ type = 'scriptEx', args = script })
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
elseif event.type == 'stop' then
|
||||||
return UI.Page.eventHandler(self, event)
|
for id in pairs(swarm.pool) do
|
||||||
end
|
swarm:remove(id)
|
||||||
return true
|
end
|
||||||
|
|
||||||
|
elseif event.type == 'range' then
|
||||||
|
self.range:show()
|
||||||
|
|
||||||
|
elseif event.type == 'cancel' then
|
||||||
|
self.range:hide()
|
||||||
|
|
||||||
|
elseif event.type == 'select_range' then
|
||||||
|
local range = tonumber(self.range.entry.value)
|
||||||
|
|
||||||
|
if range and range > 0 then
|
||||||
|
for id, v in pairs(network) do
|
||||||
|
if not swarm.pool[id] then
|
||||||
|
if v.fuel and v.active and v.fuel > 0 and v.distance and v.distance <= range then
|
||||||
|
swarm:add(id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
swarm:run(follow)
|
||||||
|
self.range:hide()
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return UI.Page.eventHandler(self, event)
|
||||||
|
end
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.addRoutine(function()
|
Event.addRoutine(function()
|
||||||
while true do
|
while true do
|
||||||
local pt = GPS.getPoint()
|
local pt = GPS.getPoint()
|
||||||
if not pts or (pt and not Point.same(pt, gpt)) then
|
if not pts or (pt and not Point.same(pt, gpt)) then
|
||||||
gpt = pt
|
gpt = pt
|
||||||
pts = {
|
pts = {
|
||||||
{ x = pt.x + 2, z = pt.z, y = pt.y },
|
{ x = pt.x + 2, z = pt.z, y = pt.y },
|
||||||
{ x = pt.x - 2, z = pt.z, y = pt.y },
|
{ x = pt.x - 2, z = pt.z, y = pt.y },
|
||||||
{ x = pt.x, z = pt.z + 2, y = pt.y },
|
{ x = pt.x, z = pt.z + 2, y = pt.y },
|
||||||
{ x = pt.x, z = pt.z - 2, y = pt.y },
|
{ x = pt.x, z = pt.z - 2, y = pt.y },
|
||||||
}
|
}
|
||||||
blocks = { }
|
blocks = { }
|
||||||
|
|
||||||
local function addBlocks(tpt)
|
local function addBlocks(tpt)
|
||||||
table.insert(blocks, tpt)
|
table.insert(blocks, tpt)
|
||||||
local apts = Point.adjacentPoints(tpt)
|
local apts = Point.adjacentPoints(tpt)
|
||||||
for _,apt in pairs(apts) do
|
for _,apt in pairs(apts) do
|
||||||
table.insert(blocks, apt)
|
table.insert(blocks, apt)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- don't run into player
|
-- don't run into player
|
||||||
addBlocks(pt)
|
addBlocks(pt)
|
||||||
addBlocks(Point.above(pt))
|
addBlocks(Point.above(pt))
|
||||||
|
|
||||||
for _, member in pairs(swarm.pool) do
|
for _, member in pairs(swarm.pool) do
|
||||||
if member.snmp then
|
if member.snmp then
|
||||||
member.snmp:write({ type = 'scriptEx', args = 'turtle.abort(true)' })
|
member.snmp:write({ type = 'scriptEx', args = 'turtle.abort(true)' })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
os.sleep(1)
|
os.sleep(1)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
UI:setPage(page)
|
UI:setPage(page)
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ local config = { }
|
|||||||
Config.load('Turtles', config)
|
Config.load('Turtles', config)
|
||||||
|
|
||||||
local options = {
|
local options = {
|
||||||
turtle = { arg = 'i', type = 'number', value = config.id or -1,
|
turtle = { arg = 'i', type = 'number', value = config.id or -1,
|
||||||
desc = 'Turtle ID' },
|
desc = 'Turtle ID' },
|
||||||
tab = { arg = 's', type = 'string', value = config.tab or 'Sel',
|
tab = { arg = 's', type = 'string', value = config.tab or 'Sel',
|
||||||
desc = 'Selected tab to display' },
|
desc = 'Selected tab to display' },
|
||||||
help = { arg = 'h', type = 'flag', value = false,
|
help = { arg = 'h', type = 'flag', value = false,
|
||||||
desc = 'Displays the options' },
|
desc = 'Displays the options' },
|
||||||
}
|
}
|
||||||
|
|
||||||
local SCRIPTS_PATH = 'packages/common/etc/scripts'
|
local SCRIPTS_PATH = 'packages/common/etc/scripts'
|
||||||
@@ -35,343 +35,343 @@ local nullTerm = Terminal.getNullTerm(term.current())
|
|||||||
local socket
|
local socket
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
coords = UI.Window {
|
coords = UI.Window {
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
height = 3,
|
height = 3,
|
||||||
},
|
},
|
||||||
tabs = UI.Tabs {
|
tabs = UI.Tabs {
|
||||||
x = 1, y = 4, ey = -2,
|
x = 1, y = 4, ey = -2,
|
||||||
scripts = UI.ScrollingGrid {
|
scripts = UI.ScrollingGrid {
|
||||||
tabTitle = 'Run',
|
tabTitle = 'Run',
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = '', key = 'label' },
|
{ heading = '', key = 'label' },
|
||||||
},
|
},
|
||||||
disableHeader = true,
|
disableHeader = true,
|
||||||
sortColumn = 'label',
|
sortColumn = 'label',
|
||||||
autospace = true,
|
autospace = true,
|
||||||
},
|
},
|
||||||
turtles = UI.ScrollingGrid {
|
turtles = UI.ScrollingGrid {
|
||||||
tabTitle = 'Select',
|
tabTitle = 'Select',
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'label', key = 'label' },
|
{ heading = 'label', key = 'label' },
|
||||||
{ heading = 'Dist', key = 'distance' },
|
{ heading = 'Dist', key = 'distance' },
|
||||||
{ heading = 'Status', key = 'status' },
|
{ heading = 'Status', key = 'status' },
|
||||||
{ heading = 'Fuel', key = 'fuel' },
|
{ heading = 'Fuel', key = 'fuel' },
|
||||||
},
|
},
|
||||||
disableHeader = true,
|
disableHeader = true,
|
||||||
sortColumn = 'label',
|
sortColumn = 'label',
|
||||||
autospace = true,
|
autospace = true,
|
||||||
},
|
},
|
||||||
inventory = UI.ScrollingGrid {
|
inventory = UI.ScrollingGrid {
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
tabTitle = 'Inv',
|
tabTitle = 'Inv',
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = '', key = 'index', width = 2 },
|
{ heading = '', key = 'index', width = 2 },
|
||||||
{ heading = '', key = 'qty', width = 2 },
|
{ heading = '', key = 'qty', width = 2 },
|
||||||
{ heading = 'Inventory', key = 'id', width = UI.term.width - 7 },
|
{ heading = 'Inventory', key = 'id', width = UI.term.width - 7 },
|
||||||
},
|
},
|
||||||
disableHeader = true,
|
disableHeader = true,
|
||||||
sortColumn = 'index',
|
sortColumn = 'index',
|
||||||
},
|
},
|
||||||
--[[
|
--[[
|
||||||
policy = UI.ScrollingGrid {
|
policy = UI.ScrollingGrid {
|
||||||
tabTitle = 'Mod',
|
tabTitle = 'Mod',
|
||||||
backgroundColor = UI.TabBar.defaults.selectedBackgroundColor,
|
backgroundColor = UI.TabBar.defaults.selectedBackgroundColor,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'label', key = 'label' },
|
{ heading = 'label', key = 'label' },
|
||||||
},
|
},
|
||||||
values = policies,
|
values = policies,
|
||||||
disableHeader = true,
|
disableHeader = true,
|
||||||
sortColumn = 'label',
|
sortColumn = 'label',
|
||||||
autospace = true,
|
autospace = true,
|
||||||
},
|
},
|
||||||
]]
|
]]
|
||||||
action = UI.Window {
|
action = UI.Window {
|
||||||
tabTitle = 'Action',
|
tabTitle = 'Action',
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
moveUp = UI.Button {
|
moveUp = UI.Button {
|
||||||
x = 5, y = 2,
|
x = 5, y = 2,
|
||||||
text = 'up',
|
text = 'up',
|
||||||
fn = 'turtle.up',
|
fn = 'turtle.up',
|
||||||
},
|
},
|
||||||
moveDown = UI.Button {
|
moveDown = UI.Button {
|
||||||
x = 5, y = 4,
|
x = 5, y = 4,
|
||||||
text = 'dn',
|
text = 'dn',
|
||||||
fn = 'turtle.down',
|
fn = 'turtle.down',
|
||||||
},
|
},
|
||||||
moveForward = UI.Button {
|
moveForward = UI.Button {
|
||||||
x = 9, y = 3,
|
x = 9, y = 3,
|
||||||
text = 'f',
|
text = 'f',
|
||||||
fn = 'turtle.forward',
|
fn = 'turtle.forward',
|
||||||
},
|
},
|
||||||
moveBack = UI.Button {
|
moveBack = UI.Button {
|
||||||
x = 2, y = 3,
|
x = 2, y = 3,
|
||||||
text = 'b',
|
text = 'b',
|
||||||
fn = 'turtle.back',
|
fn = 'turtle.back',
|
||||||
},
|
},
|
||||||
turnLeft = UI.Button {
|
turnLeft = UI.Button {
|
||||||
x = 2, y = 6,
|
x = 2, y = 6,
|
||||||
text = 'lt',
|
text = 'lt',
|
||||||
fn = 'turtle.turnLeft',
|
fn = 'turtle.turnLeft',
|
||||||
},
|
},
|
||||||
turnRight = UI.Button {
|
turnRight = UI.Button {
|
||||||
x = 8, y = 6,
|
x = 8, y = 6,
|
||||||
text = 'rt',
|
text = 'rt',
|
||||||
fn = 'turtle.turnRight',
|
fn = 'turtle.turnRight',
|
||||||
},
|
},
|
||||||
info = UI.TextArea {
|
info = UI.TextArea {
|
||||||
x = 15, y = 2,
|
x = 15, y = 2,
|
||||||
inactive = true,
|
inactive = true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar {
|
statusBar = UI.StatusBar {
|
||||||
values = { },
|
values = { },
|
||||||
columns = {
|
columns = {
|
||||||
{ key = 'status' },
|
{ key = 'status' },
|
||||||
{ key = 'distance', width = 6 },
|
{ key = 'distance', width = 6 },
|
||||||
{ key = 'fuel', width = 6 },
|
{ key = 'fuel', width = 6 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
notification = UI.Notification(),
|
notification = UI.Notification(),
|
||||||
accelerators = {
|
accelerators = {
|
||||||
q = 'quit',
|
q = 'quit',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function page:enable(turtle)
|
function page:enable(turtle)
|
||||||
self.turtle = turtle
|
self.turtle = turtle
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:runFunction(script, nowrap)
|
function page:runFunction(script, nowrap)
|
||||||
for _ = 1, 2 do
|
for _ = 1, 2 do
|
||||||
if not socket then
|
if not socket then
|
||||||
socket = Socket.connect(self.turtle.id, 161)
|
socket = Socket.connect(self.turtle.id, 161)
|
||||||
end
|
end
|
||||||
|
|
||||||
if socket then
|
if socket then
|
||||||
if not nowrap then
|
if not nowrap then
|
||||||
script = 'turtle.run(' .. script .. ')'
|
script = 'turtle.run(' .. script .. ')'
|
||||||
end
|
end
|
||||||
if socket:write({ type = 'scriptEx', args = script }) then
|
if socket:write({ type = 'scriptEx', args = script }) then
|
||||||
local t = socket:read(3)
|
local t = socket:read(3)
|
||||||
if t then
|
if t then
|
||||||
return table.unpack(t)
|
return table.unpack(t)
|
||||||
end
|
end
|
||||||
return false, 'Socket timeout'
|
return false, 'Socket timeout'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
socket = nil
|
socket = nil
|
||||||
end
|
end
|
||||||
self.notification:error('Unable to connect')
|
self.notification:error('Unable to connect')
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:runScript(scriptName)
|
function page:runScript(scriptName)
|
||||||
if self.turtle then
|
if self.turtle then
|
||||||
self.notification:info('Connecting')
|
self.notification:info('Connecting')
|
||||||
self:sync()
|
self:sync()
|
||||||
|
|
||||||
local cmd = string.format('Script %d %s', self.turtle.id, scriptName)
|
local cmd = string.format('Script %d %s', self.turtle.id, scriptName)
|
||||||
local ot = term.redirect(nullTerm)
|
local ot = term.redirect(nullTerm)
|
||||||
pcall(function() shell.run(cmd) end)
|
pcall(function() shell.run(cmd) end)
|
||||||
term.redirect(ot)
|
term.redirect(ot)
|
||||||
self.notification:success('Sent')
|
self.notification:success('Sent')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.coords:draw()
|
function page.coords:draw()
|
||||||
local t = self.parent.turtle
|
local t = self.parent.turtle
|
||||||
self:clear()
|
self:clear()
|
||||||
if t then
|
if t then
|
||||||
self:setCursorPos(2, 2)
|
self:setCursorPos(2, 2)
|
||||||
local ind = 'GPS'
|
local ind = 'GPS'
|
||||||
if not t.point.gps then
|
if not t.point.gps then
|
||||||
ind = 'REL'
|
ind = 'REL'
|
||||||
end
|
end
|
||||||
self:print(string.format('%s : %d,%d,%d',
|
self:print(string.format('%s : %d,%d,%d',
|
||||||
ind, t.point.x, t.point.y, t.point.z))
|
ind, t.point.x, t.point.y, t.point.z))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[ Inventory Tab ]]--
|
--[[ Inventory Tab ]]--
|
||||||
function page.tabs.inventory:getRowTextColor(row, selected)
|
function page.tabs.inventory:getRowTextColor(row, selected)
|
||||||
if page.turtle and row.selected then
|
if page.turtle and row.selected then
|
||||||
return colors.yellow
|
return colors.yellow
|
||||||
end
|
end
|
||||||
return UI.ScrollingGrid.getRowTextColor(self, row, selected)
|
return UI.ScrollingGrid.getRowTextColor(self, row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.tabs.inventory:draw()
|
function page.tabs.inventory:draw()
|
||||||
local t = page.turtle
|
local t = page.turtle
|
||||||
Util.clear(self.values)
|
Util.clear(self.values)
|
||||||
if t then
|
if t then
|
||||||
for _,v in ipairs(t.inventory) do
|
for _,v in ipairs(t.inventory) do
|
||||||
if v.qty > 0 then
|
if v.qty > 0 then
|
||||||
table.insert(self.values, v)
|
table.insert(self.values, v)
|
||||||
if v.index == t.slotIndex then
|
if v.index == t.slotIndex then
|
||||||
v.selected = true
|
v.selected = true
|
||||||
end
|
end
|
||||||
if v.id then
|
if v.id then
|
||||||
v.id = itemDB:getName(v)
|
v.id = itemDB:getName(v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:adjustWidth()
|
self:adjustWidth()
|
||||||
self:update()
|
self:update()
|
||||||
UI.ScrollingGrid.draw(self)
|
UI.ScrollingGrid.draw(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.tabs.inventory:eventHandler(event)
|
function page.tabs.inventory:eventHandler(event)
|
||||||
if event.type == 'grid_select' then
|
if event.type == 'grid_select' then
|
||||||
local fn = string.format('turtle.select(%d)', event.selected.index)
|
local fn = string.format('turtle.select(%d)', event.selected.index)
|
||||||
page:runFunction(fn)
|
page:runFunction(fn)
|
||||||
else
|
else
|
||||||
return UI.ScrollingGrid.eventHandler(self, event)
|
return UI.ScrollingGrid.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.tabs.scripts:draw()
|
function page.tabs.scripts:draw()
|
||||||
|
|
||||||
Util.clear(self.values)
|
Util.clear(self.values)
|
||||||
local files = fs.list(SCRIPTS_PATH)
|
local files = fs.list(SCRIPTS_PATH)
|
||||||
for _,path in pairs(files) do
|
for _,path in pairs(files) do
|
||||||
table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) })
|
table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) })
|
||||||
end
|
end
|
||||||
self:update()
|
self:update()
|
||||||
UI.ScrollingGrid.draw(self)
|
UI.ScrollingGrid.draw(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.tabs.scripts:eventHandler(event)
|
function page.tabs.scripts:eventHandler(event)
|
||||||
if event.type == 'grid_select' then
|
if event.type == 'grid_select' then
|
||||||
page:runScript(event.selected.label)
|
page:runScript(event.selected.label)
|
||||||
else
|
else
|
||||||
return UI.ScrollingGrid.eventHandler(self, event)
|
return UI.ScrollingGrid.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.tabs.turtles:getDisplayValues(row)
|
function page.tabs.turtles:getDisplayValues(row)
|
||||||
row = Util.shallowCopy(row)
|
row = Util.shallowCopy(row)
|
||||||
if row.fuel then
|
if row.fuel then
|
||||||
row.fuel = Util.toBytes(row.fuel)
|
row.fuel = Util.toBytes(row.fuel)
|
||||||
end
|
end
|
||||||
if row.distance then
|
if row.distance then
|
||||||
row.distance = Util.round(row.distance, 1)
|
row.distance = Util.round(row.distance, 1)
|
||||||
end
|
end
|
||||||
return row
|
return row
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.tabs.turtles:draw()
|
function page.tabs.turtles:draw()
|
||||||
Util.clear(self.values)
|
Util.clear(self.values)
|
||||||
for _,v in pairs(network) do
|
for _,v in pairs(network) do
|
||||||
if v.fuel then
|
if v.fuel then
|
||||||
table.insert(self.values, v)
|
table.insert(self.values, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:update()
|
self:update()
|
||||||
UI.ScrollingGrid.draw(self)
|
UI.ScrollingGrid.draw(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.tabs.turtles:eventHandler(event)
|
function page.tabs.turtles:eventHandler(event)
|
||||||
if event.type == 'grid_select' then
|
if event.type == 'grid_select' then
|
||||||
page.turtle = event.selected
|
page.turtle = event.selected
|
||||||
config.id = event.selected.id
|
config.id = event.selected.id
|
||||||
Config.update('Turtles', config)
|
Config.update('Turtles', config)
|
||||||
multishell.setTitle(multishell.getCurrent(), page.turtle.label)
|
multishell.setTitle(multishell.getCurrent(), page.turtle.label)
|
||||||
if socket then
|
if socket then
|
||||||
socket:close()
|
socket:close()
|
||||||
socket = nil
|
socket = nil
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return UI.ScrollingGrid.eventHandler(self, event)
|
return UI.ScrollingGrid.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.statusBar:draw()
|
function page.statusBar:draw()
|
||||||
local t = self.parent.turtle
|
local t = self.parent.turtle
|
||||||
if t then
|
if t then
|
||||||
self.values.status = t.status
|
self.values.status = t.status
|
||||||
self.values.distance = Util.round(t.distance, 2)
|
self.values.distance = Util.round(t.distance, 2)
|
||||||
self.values.fuel = Util.toBytes(t.fuel)
|
self.values.fuel = Util.toBytes(t.fuel)
|
||||||
end
|
end
|
||||||
UI.StatusBar.draw(self)
|
UI.StatusBar.draw(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:showBlocks()
|
function page:showBlocks()
|
||||||
local script = [[
|
local script = [[
|
||||||
local function inspect(direction)
|
local function inspect(direction)
|
||||||
local s,b = turtle['inspect' .. (direction or '')]()
|
local s,b = turtle['inspect' .. (direction or '')]()
|
||||||
if not s then
|
if not s then
|
||||||
return 'minecraft:air:0'
|
return 'minecraft:air:0'
|
||||||
end
|
end
|
||||||
return string.format('%s:%d', b.name, b.metadata)
|
return string.format('%s:%d', b.name, b.metadata)
|
||||||
end
|
end
|
||||||
|
|
||||||
local bu, bf, bd = inspect('Up'), inspect(), inspect('Down')
|
local bu, bf, bd = inspect('Up'), inspect(), inspect('Down')
|
||||||
return string.format('%s\n%s\n%s', bu, bf, bd)
|
return string.format('%s\n%s\n%s', bu, bf, bd)
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local s, m = self:runFunction(script, true)
|
local s, m = self:runFunction(script, true)
|
||||||
self.tabs.action.info:setText(s or m)
|
self.tabs.action.info:setText(s or m)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'quit' then
|
if event.type == 'quit' then
|
||||||
UI:exitPullEvents()
|
UI:exitPullEvents()
|
||||||
|
|
||||||
elseif event.type == 'tab_select' then
|
elseif event.type == 'tab_select' then
|
||||||
config.tab = event.button.text
|
config.tab = event.button.text
|
||||||
Config.update('Turtles', config)
|
Config.update('Turtles', config)
|
||||||
|
|
||||||
elseif event.type == 'button_press' then
|
elseif event.type == 'button_press' then
|
||||||
if event.button.fn then
|
if event.button.fn then
|
||||||
self:runFunction(event.button.fn, event.button.nowrap)
|
self:runFunction(event.button.fn, event.button.nowrap)
|
||||||
self:showBlocks()
|
self:showBlocks()
|
||||||
elseif event.button.script then
|
elseif event.button.script then
|
||||||
self:runScript(event.button.script)
|
self:runScript(event.button.script)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:enable()
|
function page:enable()
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
-- self.tabs:activateTab(page.tabs.turtles)
|
-- self.tabs:activateTab(page.tabs.turtles)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not Util.getOptions(options, { ... }, true) then
|
if not Util.getOptions(options, { ... }, true) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if options.turtle.value >= 0 then
|
if options.turtle.value >= 0 then
|
||||||
for _ = 1, 10 do
|
for _ = 1, 10 do
|
||||||
page.turtle = _G.network[options.turtle.value]
|
page.turtle = _G.network[options.turtle.value]
|
||||||
if page.turtle then
|
if page.turtle then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
os.sleep(1)
|
os.sleep(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.onInterval(1, function()
|
Event.onInterval(1, function()
|
||||||
if page.turtle then
|
if page.turtle then
|
||||||
local t = _G.network[page.turtle.id]
|
local t = _G.network[page.turtle.id]
|
||||||
page.turtle = t
|
page.turtle = t
|
||||||
page:draw()
|
page:draw()
|
||||||
page:sync()
|
page:sync()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if config.tab then
|
if config.tab then
|
||||||
page.tabs.tabBar:selectTab(config.tab)
|
page.tabs.tabBar:selectTab(config.tab)
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:setPage(page)
|
UI:setPage(page)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
local c = function(shell, nIndex, sText)
|
local c = function(shell, nIndex, sText)
|
||||||
if nIndex == 1 then
|
if nIndex == 1 then
|
||||||
return _G.fs.complete(sText, shell.dir(), true, false)
|
return _G.fs.complete(sText, shell.dir(), true, false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
_ENV.shell.setCompletionFunction("packages/common/edit.lua", c)
|
_ENV.shell.setCompletionFunction("packages/common/edit.lua", c)
|
||||||
|
|||||||
45
common/canvasClient.lua
Normal file
45
common/canvasClient.lua
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
local Point = require('point')
|
||||||
|
local Util = require('util')
|
||||||
|
|
||||||
|
local device = _G.device
|
||||||
|
local os = _G.os
|
||||||
|
local turtle = _G.turtle
|
||||||
|
|
||||||
|
local function convert(blocks, reference)
|
||||||
|
if not reference then
|
||||||
|
return blocks
|
||||||
|
end
|
||||||
|
|
||||||
|
local rotated = {
|
||||||
|
[0] = 0,
|
||||||
|
[1] = 3,
|
||||||
|
[2] = 2,
|
||||||
|
[3] = 1,
|
||||||
|
}
|
||||||
|
return Util.reduce(blocks, function(acc, b)
|
||||||
|
local c = Util.shallowCopy(b)
|
||||||
|
Point.rotate(c, rotated[reference.heading])
|
||||||
|
c.x = c.x + reference.x
|
||||||
|
c.y = c.y + reference.y
|
||||||
|
c.z = c.z + reference.z
|
||||||
|
table.insert(acc, c)
|
||||||
|
return acc
|
||||||
|
end, { })
|
||||||
|
end
|
||||||
|
|
||||||
|
local function broadcast(blocks, displayType, source)
|
||||||
|
if device.wireless_modem then
|
||||||
|
device.wireless_modem.transmit(3773, os.getComputerID(), {
|
||||||
|
type = displayType,
|
||||||
|
data = convert(blocks, source),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local _, msg = os.pullEvent('canvas')
|
||||||
|
|
||||||
|
local reference = turtle and turtle.getState().reference
|
||||||
|
|
||||||
|
broadcast(msg.data, msg.type, reference)
|
||||||
|
end
|
||||||
2044
common/edit.lua
2044
common/edit.lua
File diff suppressed because it is too large
Load Diff
@@ -22,37 +22,37 @@ local scanner = device['plethora:scanner'] or
|
|||||||
-- hud
|
-- hud
|
||||||
local canvas = glasses and glasses.canvas()
|
local canvas = glasses and glasses.canvas()
|
||||||
if canvas then
|
if canvas then
|
||||||
local lh
|
local lh
|
||||||
|
|
||||||
local function addText(x, y, text, color)
|
local function addText(x, y, text, color)
|
||||||
local th = canvas.group.addText({ x, y }, text, color or 0xa0a0a0FF)
|
local th = canvas.group.addText({ x, y }, text, color or 0xa0a0a0FF)
|
||||||
lh = lh or th.getLineHeight()
|
lh = lh or th.getLineHeight()
|
||||||
th.setShadow(true)
|
th.setShadow(true)
|
||||||
th.setScale(.75)
|
th.setScale(.75)
|
||||||
return th
|
return th
|
||||||
end
|
end
|
||||||
|
|
||||||
canvas.group = canvas.addGroup({ 4, 90 })
|
canvas.group = canvas.addGroup({ 4, 90 })
|
||||||
canvas.group.bg = canvas.group.addRectangle(0, 0, 80, 10, 0x40404080)
|
canvas.group.bg = canvas.group.addRectangle(0, 0, 80, 10, 0x40404080)
|
||||||
canvas.group.addLines(
|
canvas.group.addLines(
|
||||||
{ 0, 0 },
|
{ 0, 0 },
|
||||||
{ 80, 0 },
|
{ 80, 0 },
|
||||||
{ 80, 10 },
|
{ 80, 10 },
|
||||||
{ 0, 10 },
|
{ 0, 10 },
|
||||||
{ 0, 0 },
|
{ 0, 0 },
|
||||||
0x202020FF,
|
0x202020FF,
|
||||||
2)
|
2)
|
||||||
addText(20, 2, 'Swarm Miner', 0xc0c0c0FF)
|
addText(20, 2, 'Swarm Miner', 0xc0c0c0FF)
|
||||||
|
|
||||||
local y = 15
|
local y = 15
|
||||||
addText(3, y, 'Turtles')
|
addText(3, y, 'Turtles')
|
||||||
canvas.turtles = addText(60, y, '')
|
canvas.turtles = addText(60, y, '')
|
||||||
canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4)
|
canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4)
|
||||||
|
|
||||||
y = y + lh + 5
|
y = y + lh + 5
|
||||||
addText(3, y, 'Queue')
|
addText(3, y, 'Queue')
|
||||||
canvas.queue = addText(60, y, '')
|
canvas.queue = addText(60, y, '')
|
||||||
canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4)
|
canvas.group.addLine({ 0, y + lh - 2 }, { 80, y + lh - 2 }, 0x404040FF, 4)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- container
|
-- container
|
||||||
@@ -62,19 +62,19 @@ local box, offset
|
|||||||
local paused = false
|
local paused = false
|
||||||
|
|
||||||
local function inBox(pt)
|
local function inBox(pt)
|
||||||
if not box or not box.ex then
|
if not box or not box.ex then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return Point.inBox(pt, box)
|
return Point.inBox(pt, box)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function locate()
|
local function locate()
|
||||||
for _ = 1, 3 do
|
for _ = 1, 3 do
|
||||||
local pt = GPS.getPoint()
|
local pt = GPS.getPoint()
|
||||||
if pt then
|
if pt then
|
||||||
return pt
|
return pt
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local spt = GPS.getPoint() or error('GPS failure')
|
local spt = GPS.getPoint() or error('GPS failure')
|
||||||
@@ -88,7 +88,7 @@ local abort
|
|||||||
local function hijackTurtle(remoteId)
|
local function hijackTurtle(remoteId)
|
||||||
local socket, msg = Socket.connect(remoteId, 188)
|
local socket, msg = Socket.connect(remoteId, 188)
|
||||||
|
|
||||||
if not socket then
|
if not socket then
|
||||||
error(msg)
|
error(msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -111,240 +111,240 @@ local function hijackTurtle(remoteId)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function getNextPoint(turtle)
|
local function getNextPoint(turtle)
|
||||||
if not paused then
|
if not paused then
|
||||||
local pt = Point.closest(turtle.getPoint(), queue)
|
local pt = Point.closest(turtle.getPoint(), queue)
|
||||||
if pt then
|
if pt then
|
||||||
turtle.pt = pt
|
turtle.pt = pt
|
||||||
queue[pt.pkey] = nil
|
queue[pt.pkey] = nil
|
||||||
return pt
|
return pt
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function run(member, point)
|
local function run(member, point)
|
||||||
Event.addRoutine(function()
|
Event.addRoutine(function()
|
||||||
local turtle, socket
|
local turtle, socket
|
||||||
local _, m = pcall(function()
|
local _, m = pcall(function()
|
||||||
member.active = true
|
member.active = true
|
||||||
turtle, socket = hijackTurtle(member.id)
|
turtle, socket = hijackTurtle(member.id)
|
||||||
|
|
||||||
local function emptySlots(retain, pt)
|
local function emptySlots(retain, pt)
|
||||||
local slots = turtle.getFilledSlots()
|
local slots = turtle.getFilledSlots()
|
||||||
for _,slot in pairs(slots) do
|
for _,slot in pairs(slots) do
|
||||||
if not retain[slot.key] then
|
if not retain[slot.key] then
|
||||||
turtle.select(slot.index)
|
turtle.select(slot.index)
|
||||||
if pt then
|
if pt then
|
||||||
turtle.dropAt(pt, 64)
|
turtle.dropAt(pt, 64)
|
||||||
else
|
else
|
||||||
turtle.dropUp(64)
|
turtle.dropUp(64)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function dropOff()
|
local function dropOff()
|
||||||
-- go to 2 above chest
|
-- go to 2 above chest
|
||||||
local topPoint = Point.copy(chestPoint)
|
local topPoint = Point.copy(chestPoint)
|
||||||
topPoint.y = topPoint.y + 2
|
topPoint.y = topPoint.y + 2
|
||||||
turtle.gotoY(topPoint.y)
|
turtle.gotoY(topPoint.y)
|
||||||
while not turtle.go(topPoint) do
|
while not turtle.go(topPoint) do
|
||||||
os.sleep(.5)
|
os.sleep(.5)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- path to chest
|
-- path to chest
|
||||||
local box = Point.makeBox(
|
local box = Point.makeBox(
|
||||||
{ x = chestPoint.x - 3, y = chestPoint.y + 3, z = chestPoint.z - 3 },
|
{ x = chestPoint.x - 3, y = chestPoint.y + 3, z = chestPoint.z - 3 },
|
||||||
{ x = chestPoint.x + 3, y = chestPoint.y, z = chestPoint.z + 3 }
|
{ x = chestPoint.x + 3, y = chestPoint.y, z = chestPoint.z + 3 }
|
||||||
)
|
)
|
||||||
turtle.set({
|
turtle.set({
|
||||||
movementStrategy = 'pathing',
|
movementStrategy = 'pathing',
|
||||||
pathingBox = Point.normalizeBox(box),
|
pathingBox = Point.normalizeBox(box),
|
||||||
digPolicy = 'digNone',
|
digPolicy = 'digNone',
|
||||||
})
|
})
|
||||||
while not turtle.moveAgainst(chestPoint) do
|
while not turtle.moveAgainst(chestPoint) do
|
||||||
os.sleep(.5)
|
os.sleep(.5)
|
||||||
end
|
end
|
||||||
emptySlots({ }, chestPoint)
|
emptySlots({ }, chestPoint)
|
||||||
|
|
||||||
-- path to 3 above chest
|
-- path to 3 above chest
|
||||||
turtle.pathfind(Point.above(topPoint))
|
turtle.pathfind(Point.above(topPoint))
|
||||||
turtle.set({
|
turtle.set({
|
||||||
movementStrategy = 'goto',
|
movementStrategy = 'goto',
|
||||||
digPolicy = 'blacklist',
|
digPolicy = 'blacklist',
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
if turtle then
|
if turtle then
|
||||||
turtles[member.id] = turtle
|
turtles[member.id] = turtle
|
||||||
|
|
||||||
turtle.reset()
|
turtle.reset()
|
||||||
turtle.set({
|
turtle.set({
|
||||||
attackPolicy = 'attack',
|
attackPolicy = 'attack',
|
||||||
digPolicy = 'blacklist',
|
digPolicy = 'blacklist',
|
||||||
blacklist = {
|
blacklist = {
|
||||||
'turtle',
|
'turtle',
|
||||||
'chest',
|
'chest',
|
||||||
'shulker',
|
'shulker',
|
||||||
},
|
},
|
||||||
movementStrategy = 'goto',
|
movementStrategy = 'goto',
|
||||||
point = point,
|
point = point,
|
||||||
})
|
})
|
||||||
turtle.select(1)
|
turtle.select(1)
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
local pt = getNextPoint(turtle)
|
local pt = getNextPoint(turtle)
|
||||||
if pt then
|
if pt then
|
||||||
member.status = 'digging'
|
member.status = 'digging'
|
||||||
|
|
||||||
if blockTypes[pt.key] == true then
|
if blockTypes[pt.key] == true then
|
||||||
if turtle.moveAgainst(pt) then
|
if turtle.moveAgainst(pt) then
|
||||||
local index = turtle.selectOpenSlot()
|
local index = turtle.selectOpenSlot()
|
||||||
if turtle.digAt(pt, pt.name) then
|
if turtle.digAt(pt, pt.name) then
|
||||||
local slot = turtle.getSlot(index)
|
local slot = turtle.getSlot(index)
|
||||||
if slot.count > 0 then
|
if slot.count > 0 then
|
||||||
blockTypes[pt.key] = slot.key
|
blockTypes[pt.key] = slot.key
|
||||||
if slot.key ~= pt.key then
|
if slot.key ~= pt.key then
|
||||||
blockTypes[slot.key] = true
|
blockTypes[slot.key] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
turtle.select(1)
|
turtle.select(1)
|
||||||
else
|
else
|
||||||
turtle.digAt(pt, pt.name)
|
turtle.digAt(pt, pt.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
if turtle.getItemCount(15) > 0 then
|
if turtle.getItemCount(15) > 0 then
|
||||||
member.status = 'ejecting trash'
|
member.status = 'ejecting trash'
|
||||||
emptySlots(blockTypes)
|
emptySlots(blockTypes)
|
||||||
turtle.condense()
|
turtle.condense()
|
||||||
if turtle.getItemCount(15) > 0 then
|
if turtle.getItemCount(15) > 0 then
|
||||||
member.status = 'dropping off'
|
member.status = 'dropping off'
|
||||||
if not chestPoint then
|
if not chestPoint then
|
||||||
member.abort = true
|
member.abort = true
|
||||||
member.status = 'full'
|
member.status = 'full'
|
||||||
else
|
else
|
||||||
dropOff()
|
dropOff()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
turtle.select(1)
|
turtle.select(1)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
member.status = 'waiting'
|
member.status = 'waiting'
|
||||||
os.sleep(1)
|
os.sleep(1)
|
||||||
end
|
end
|
||||||
if member.fuel < 100 then
|
if member.fuel < 100 then
|
||||||
member.status = 'out of fuel'
|
member.status = 'out of fuel'
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
until member.abort
|
until member.abort
|
||||||
end
|
end
|
||||||
|
|
||||||
emptySlots(blockTypes)
|
emptySlots(blockTypes)
|
||||||
|
|
||||||
if chestPoint then
|
if chestPoint then
|
||||||
dropOff()
|
dropOff()
|
||||||
while not turtle.go(Point.above(spt)) do
|
while not turtle.go(Point.above(spt)) do
|
||||||
os.sleep(.5)
|
os.sleep(.5)
|
||||||
end
|
end
|
||||||
--if turtle.selectSlotWithQuantity(0) then
|
--if turtle.selectSlotWithQuantity(0) then
|
||||||
--turtle.set({ digPolicy = 'dig' })
|
--turtle.set({ digPolicy = 'dig' })
|
||||||
--end
|
--end
|
||||||
while not turtle.go(spt) do
|
while not turtle.go(spt) do
|
||||||
os.sleep(.5)
|
os.sleep(.5)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
turtle.gotoY(spt.y)
|
turtle.gotoY(spt.y)
|
||||||
while not turtle.go(spt) do
|
while not turtle.go(spt) do
|
||||||
os.sleep(.5)
|
os.sleep(.5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
turtles[member.id] = nil
|
turtles[member.id] = nil
|
||||||
member.status = m
|
member.status = m
|
||||||
member.active = false
|
member.active = false
|
||||||
if socket then
|
if socket then
|
||||||
socket:close()
|
socket:close()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function drawContainer(pos)
|
local function drawContainer(pos)
|
||||||
if canvas3d then
|
if canvas3d then
|
||||||
canvas3d.clear()
|
canvas3d.clear()
|
||||||
|
|
||||||
local function addBox(b)
|
local function addBox(b)
|
||||||
canvas3d.addBox(
|
canvas3d.addBox(
|
||||||
b.x - offset.x + .25,
|
b.x - offset.x + .25,
|
||||||
b.y - offset.y + .25 ,
|
b.y - offset.y + .25 ,
|
||||||
b.z - offset.z + .25 ,
|
b.z - offset.z + .25 ,
|
||||||
.5, .5, .5).setDepthTested(false)
|
.5, .5, .5).setDepthTested(false)
|
||||||
end
|
end
|
||||||
if box and box.ex then
|
if box and box.ex then
|
||||||
addBox({ x = box.x, y = box.y, z = box.z })
|
addBox({ x = box.x, y = box.y, z = box.z })
|
||||||
addBox({ x = box.x, y = box.y, z = box.ez })
|
addBox({ x = box.x, y = box.y, z = box.ez })
|
||||||
addBox({ x = box.ex, y = box.y, z = box.z })
|
addBox({ x = box.ex, y = box.y, z = box.z })
|
||||||
addBox({ x = box.ex, y = box.y, z = box.ez })
|
addBox({ x = box.ex, y = box.y, z = box.ez })
|
||||||
addBox({ x = box.x, y = box.ey, z = box.z })
|
addBox({ x = box.x, y = box.ey, z = box.z })
|
||||||
addBox({ x = box.x, y = box.ey, z = box.ez })
|
addBox({ x = box.x, y = box.ey, z = box.ez })
|
||||||
addBox({ x = box.ex, y = box.ey, z = box.z })
|
addBox({ x = box.ex, y = box.ey, z = box.z })
|
||||||
addBox({ x = box.ex, y = box.ey, z = box.ez })
|
addBox({ x = box.ex, y = box.ey, z = box.ez })
|
||||||
elseif box then
|
elseif box then
|
||||||
canvas3d.recenter({ -(pos.x % 1), -(pos.y % 1), -(pos.z % 1) })
|
canvas3d.recenter({ -(pos.x % 1), -(pos.y % 1), -(pos.z % 1) })
|
||||||
addBox(box)
|
addBox(box)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local pauseResume = {
|
local pauseResume = {
|
||||||
{ text = 'Pause', event = 'pause' },
|
{ text = 'Pause', event = 'pause' },
|
||||||
{ text = 'Resume', event = 'resume' },
|
{ text = 'Resume', event = 'resume' },
|
||||||
}
|
}
|
||||||
local containerText = {
|
local containerText = {
|
||||||
[[Set a corner to contain mining area]],
|
[[Set a corner to contain mining area]],
|
||||||
[[Set ending corner]],
|
[[Set ending corner]],
|
||||||
[[Set again to clear]],
|
[[Set again to clear]],
|
||||||
}
|
}
|
||||||
|
|
||||||
local containTab = UI.Tab {
|
local containTab = UI.Tab {
|
||||||
tabTitle = 'Contain',
|
tabTitle = 'Contain',
|
||||||
button = UI.Button {
|
button = UI.Button {
|
||||||
x = 2, y = 2,
|
x = 2, y = 2,
|
||||||
text = 'Set corner',
|
text = 'Set corner',
|
||||||
event = 'contain'
|
event = 'contain'
|
||||||
},
|
},
|
||||||
textArea = UI.TextArea {
|
textArea = UI.TextArea {
|
||||||
x = 2, y = 4,
|
x = 2, y = 4,
|
||||||
value = containerText[1],
|
value = containerText[1],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local blocksTab = UI.Tab {
|
local blocksTab = UI.Tab {
|
||||||
tabTitle = 'Blocks',
|
tabTitle = 'Blocks',
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = 1,
|
y = 1,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Count', key = 'count', width = 6, align = 'right' },
|
{ heading = 'Count', key = 'count', width = 6, align = 'right' },
|
||||||
{ heading = 'Name', key = 'displayName' },
|
{ heading = 'Name', key = 'displayName' },
|
||||||
},
|
},
|
||||||
sortColumn = 'displayName',
|
sortColumn = 'displayName',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local turtlesTab = UI.Tab {
|
local turtlesTab = UI.Tab {
|
||||||
tabTitle = 'Turtles',
|
tabTitle = 'Turtles',
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = 1,
|
y = 1,
|
||||||
values = pool,
|
values = pool,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'ID', key = 'id', width = 5, },
|
{ heading = 'ID', key = 'id', width = 5, },
|
||||||
{ heading = ' Fuel', key = 'fuel', width = 5, align = 'right' },
|
{ heading = ' Fuel', key = 'fuel', width = 5, align = 'right' },
|
||||||
{ heading = ' Dist', key = 'distance', width = 5, align = 'right' },
|
{ heading = ' Dist', key = 'distance', width = 5, align = 'right' },
|
||||||
{ heading = 'Status', key = 'status' },
|
{ heading = 'Status', key = 'status' },
|
||||||
},
|
},
|
||||||
sortColumn = 'label',
|
sortColumn = 'label',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
@@ -354,264 +354,264 @@ local page = UI.Page {
|
|||||||
{ text = 'Abort', event = 'abort' },
|
{ text = 'Abort', event = 'abort' },
|
||||||
pauseResume[1],
|
pauseResume[1],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tabs = UI.Tabs {
|
tabs = UI.Tabs {
|
||||||
y = 2, ey = -2,
|
y = 2, ey = -2,
|
||||||
[1] = blocksTab,
|
[1] = blocksTab,
|
||||||
[2] = turtlesTab,
|
[2] = turtlesTab,
|
||||||
[3] = containTab,
|
[3] = containTab,
|
||||||
},
|
},
|
||||||
info = UI.Window {
|
info = UI.Window {
|
||||||
y = -1,
|
y = -1,
|
||||||
backgroundColor = colors.blue,
|
backgroundColor = colors.blue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function page.info:draw()
|
function page.info:draw()
|
||||||
self:clear()
|
self:clear()
|
||||||
self:write(2, 1, 'Turtles: ' .. Util.size(turtles))
|
self:write(2, 1, 'Turtles: ' .. Util.size(turtles))
|
||||||
if not chestPoint then
|
if not chestPoint then
|
||||||
self:write(16, 1, 'No chest')
|
self:write(16, 1, 'No chest')
|
||||||
end
|
end
|
||||||
self:write(28, 1, 'Queue: ' .. Util.size(queue))
|
self:write(28, 1, 'Queue: ' .. Util.size(queue))
|
||||||
end
|
end
|
||||||
|
|
||||||
function turtlesTab.grid:getDisplayValues(row)
|
function turtlesTab.grid:getDisplayValues(row)
|
||||||
row = Util.shallowCopy(row)
|
row = Util.shallowCopy(row)
|
||||||
row.distance = row.distance and Util.round(row.distance, 1)
|
row.distance = row.distance and Util.round(row.distance, 1)
|
||||||
row.fuel = row.fuel and row.fuel > 0 and Util.toBytes(row.fuel) or ''
|
row.fuel = row.fuel and row.fuel > 0 and Util.toBytes(row.fuel) or ''
|
||||||
return row
|
return row
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:scan()
|
function page:scan()
|
||||||
local gpt = GPS.getPoint()
|
local gpt = GPS.getPoint()
|
||||||
if not gpt then
|
if not gpt then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local rawBlocks = scanner:scan()
|
local rawBlocks = scanner:scan()
|
||||||
local candidates = { }
|
local candidates = { }
|
||||||
|
|
||||||
self.totals = Util.reduce(rawBlocks,
|
self.totals = Util.reduce(rawBlocks,
|
||||||
function(acc, b)
|
function(acc, b)
|
||||||
b.key = table.concat({ b.name, b.metadata }, ':')
|
b.key = table.concat({ b.name, b.metadata }, ':')
|
||||||
local entry = acc[b.key]
|
local entry = acc[b.key]
|
||||||
if not entry then
|
if not entry then
|
||||||
b.displayName = itemDB:getName(b.key)
|
b.displayName = itemDB:getName(b.key)
|
||||||
b.count = 1
|
b.count = 1
|
||||||
acc[b.key] = b
|
acc[b.key] = b
|
||||||
else
|
else
|
||||||
entry.count = entry.count + 1
|
entry.count = entry.count + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if b.name == 'computercraft:turtle_advanced' or
|
if b.name == 'computercraft:turtle_advanced' or
|
||||||
b.name == 'computercraft:turtle_expanded' or
|
b.name == 'computercraft:turtle_expanded' or
|
||||||
b.name == 'computercraft:turtle' then
|
b.name == 'computercraft:turtle' then
|
||||||
table.insert(candidates, b)
|
table.insert(candidates, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
if b.name == 'minecraft:chest' or b.name:find('shulker') then
|
if b.name == 'minecraft:chest' or b.name:find('shulker') then
|
||||||
chestPoint = b
|
chestPoint = b
|
||||||
end
|
end
|
||||||
|
|
||||||
-- add relevant blocks to queue
|
-- add relevant blocks to queue
|
||||||
b.x = gpt.x + b.x
|
b.x = gpt.x + b.x
|
||||||
b.y = gpt.y + b.y
|
b.y = gpt.y + b.y
|
||||||
b.z = gpt.z + b.z
|
b.z = gpt.z + b.z
|
||||||
b.pkey = table.concat({ b.x, b.y, b.z }, ':')
|
b.pkey = table.concat({ b.x, b.y, b.z }, ':')
|
||||||
if blockTypes[b.key] and inBox(b) then
|
if blockTypes[b.key] and inBox(b) then
|
||||||
if not Util.any(turtles, function(t)
|
if not Util.any(turtles, function(t)
|
||||||
return t.pt and t.pt.pkey == b.pkey
|
return t.pt and t.pt.pkey == b.pkey
|
||||||
end) then
|
end) then
|
||||||
queue[b.pkey] = b
|
queue[b.pkey] = b
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
queue[b.pkey] = nil
|
queue[b.pkey] = nil
|
||||||
end
|
end
|
||||||
return acc
|
return acc
|
||||||
end,
|
end,
|
||||||
{ })
|
{ })
|
||||||
|
|
||||||
for _, b in pairs(candidates) do
|
for _, b in pairs(candidates) do
|
||||||
local v = scanner.getBlockMeta(b.x - gpt.x, b.y - gpt.y, b.z - gpt.z)
|
local v = scanner.getBlockMeta(b.x - gpt.x, b.y - gpt.y, b.z - gpt.z)
|
||||||
if v and v.computer then
|
if v and v.computer then
|
||||||
local member = pool[v.computer.id]
|
local member = pool[v.computer.id]
|
||||||
if not member then
|
if not member then
|
||||||
member = {
|
member = {
|
||||||
id = v.computer.id,
|
id = v.computer.id,
|
||||||
label = v.computer.label,
|
label = v.computer.label,
|
||||||
}
|
}
|
||||||
pool[v.computer.id] = member
|
pool[v.computer.id] = member
|
||||||
end
|
end
|
||||||
|
|
||||||
member.fuel = v.turtle.fuel
|
member.fuel = v.turtle.fuel
|
||||||
member.distance = 0
|
member.distance = 0
|
||||||
|
|
||||||
if not v.computer.isOn then
|
if not v.computer.isOn then
|
||||||
member.status = 'Powered off'
|
member.status = 'Powered off'
|
||||||
elseif v.turtle.fuel < 100 and not member.active then
|
elseif v.turtle.fuel < 100 and not member.active then
|
||||||
member.status = 'Not enough fuel'
|
member.status = 'Not enough fuel'
|
||||||
elseif not member.active and not member.abort then
|
elseif not member.active and not member.abort then
|
||||||
local pt = Point.copy(b)
|
local pt = Point.copy(b)
|
||||||
pt.heading = Point.facings[v.state.facing].heading
|
pt.heading = Point.facings[v.state.facing].heading
|
||||||
run(member, pt)
|
run(member, pt)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function blocksTab.grid:getDisplayValues(row)
|
function blocksTab.grid:getDisplayValues(row)
|
||||||
row = Util.shallowCopy(row)
|
row = Util.shallowCopy(row)
|
||||||
row.count = Util.toBytes(row.count) .. ' '
|
row.count = Util.toBytes(row.count) .. ' '
|
||||||
return row
|
return row
|
||||||
end
|
end
|
||||||
|
|
||||||
function blocksTab.grid:getRowTextColor(row, selected)
|
function blocksTab.grid:getRowTextColor(row, selected)
|
||||||
return blockTypes[row.key] and
|
return blockTypes[row.key] and
|
||||||
colors.yellow or
|
colors.yellow or
|
||||||
UI.Grid.getRowTextColor(self, row, selected)
|
UI.Grid.getRowTextColor(self, row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
function blocksTab:eventHandler(event)
|
function blocksTab:eventHandler(event)
|
||||||
if event.type == 'grid_select' then
|
if event.type == 'grid_select' then
|
||||||
local key = event.selected.key
|
local key = event.selected.key
|
||||||
if blockTypes[key] then
|
if blockTypes[key] then
|
||||||
for k,v in pairs(queue) do
|
for k,v in pairs(queue) do
|
||||||
if v.key == key then
|
if v.key == key then
|
||||||
queue[k] = nil
|
queue[k] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
blockTypes[key] = nil
|
blockTypes[key] = nil
|
||||||
else
|
else
|
||||||
blockTypes[key] = true
|
blockTypes[key] = true
|
||||||
end
|
end
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'scan' then
|
if event.type == 'scan' then
|
||||||
blocksTab.grid:setValues(self.totals)
|
blocksTab.grid:setValues(self.totals)
|
||||||
blocksTab.grid:draw()
|
blocksTab.grid:draw()
|
||||||
self.tabs:selectTab(blocksTab)
|
self.tabs:selectTab(blocksTab)
|
||||||
|
|
||||||
elseif event.type == 'pause' then
|
elseif event.type == 'pause' then
|
||||||
paused = true
|
paused = true
|
||||||
Util.merge(event.button, pauseResume[2])
|
Util.merge(event.button, pauseResume[2])
|
||||||
event.button:draw()
|
event.button:draw()
|
||||||
|
|
||||||
elseif event.type == 'resume' then
|
elseif event.type == 'resume' then
|
||||||
paused = false
|
paused = false
|
||||||
Util.merge(event.button, pauseResume[1])
|
Util.merge(event.button, pauseResume[1])
|
||||||
event.button:draw()
|
event.button:draw()
|
||||||
|
|
||||||
elseif event.type == 'contain' then
|
elseif event.type == 'contain' then
|
||||||
local pt = { gps.locate() }
|
local pt = { gps.locate() }
|
||||||
local pos = {
|
local pos = {
|
||||||
x = pt[1],
|
x = pt[1],
|
||||||
y = pt[2],
|
y = pt[2],
|
||||||
z = pt[3],
|
z = pt[3],
|
||||||
}
|
}
|
||||||
|
|
||||||
if not box then
|
if not box then
|
||||||
offset = {
|
offset = {
|
||||||
x = math.floor(pos.x),
|
x = math.floor(pos.x),
|
||||||
y = math.floor(pos.y),
|
y = math.floor(pos.y),
|
||||||
z = math.floor(pos.z),
|
z = math.floor(pos.z),
|
||||||
}
|
}
|
||||||
box = {
|
box = {
|
||||||
x = math.floor(pos.x),
|
x = math.floor(pos.x),
|
||||||
y = math.floor(pos.y) - 1,
|
y = math.floor(pos.y) - 1,
|
||||||
z = math.floor(pos.z),
|
z = math.floor(pos.z),
|
||||||
}
|
}
|
||||||
containTab.textArea.value = containerText[2]
|
containTab.textArea.value = containerText[2]
|
||||||
elseif not box.ex then
|
elseif not box.ex then
|
||||||
box.ex = math.floor(pos.x)
|
box.ex = math.floor(pos.x)
|
||||||
box.ey = math.floor(pos.y) - 1
|
box.ey = math.floor(pos.y) - 1
|
||||||
box.ez = math.floor(pos.z)
|
box.ez = math.floor(pos.z)
|
||||||
box = Point.normalizeBox(box)
|
box = Point.normalizeBox(box)
|
||||||
containTab.textArea.value = containerText[3]
|
containTab.textArea.value = containerText[3]
|
||||||
else
|
else
|
||||||
box = nil
|
box = nil
|
||||||
containTab.textArea.value = containerText[1]
|
containTab.textArea.value = containerText[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
containTab.textArea:draw()
|
containTab.textArea:draw()
|
||||||
drawContainer(pos)
|
drawContainer(pos)
|
||||||
|
|
||||||
elseif event.type == 'abort' then
|
elseif event.type == 'abort' then
|
||||||
for _, v in pairs(pool) do
|
for _, v in pairs(pool) do
|
||||||
v.abort = true
|
v.abort = true
|
||||||
v.status = 'aborting'
|
v.status = 'aborting'
|
||||||
end
|
end
|
||||||
spt = Point.above(locate())
|
spt = Point.above(locate())
|
||||||
abort = true
|
abort = true
|
||||||
end
|
end
|
||||||
|
|
||||||
UI.Page.eventHandler(self, event)
|
UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.onInterval(5, function()
|
Event.onInterval(5, function()
|
||||||
if not abort and not paused then
|
if not abort and not paused then
|
||||||
|
|
||||||
--local meta = scanner.getMetaOwner()
|
--local meta = scanner.getMetaOwner()
|
||||||
--if meta.isSneaking then
|
--if meta.isSneaking then
|
||||||
page:scan()
|
page:scan()
|
||||||
-- Sound.play('entity.bobber.throw', .6)
|
-- Sound.play('entity.bobber.throw', .6)
|
||||||
--end
|
--end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.onInterval(1, function()
|
Event.onInterval(1, function()
|
||||||
for id,v in pairs(network) do
|
for id,v in pairs(network) do
|
||||||
if v.fuel then
|
if v.fuel then
|
||||||
if pool[id] then
|
if pool[id] then
|
||||||
pool[id].fuel = v.fuel
|
pool[id].fuel = v.fuel
|
||||||
pool[id].distance = v.distance
|
pool[id].distance = v.distance
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if abort and Util.size(turtles) == 0 then
|
if abort and Util.size(turtles) == 0 then
|
||||||
Event.exitPullEvents()
|
Event.exitPullEvents()
|
||||||
end
|
end
|
||||||
|
|
||||||
if turtlesTab.enabled then
|
if turtlesTab.enabled then
|
||||||
turtlesTab.grid:update()
|
turtlesTab.grid:update()
|
||||||
turtlesTab.grid:draw()
|
turtlesTab.grid:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
page.info:draw()
|
page.info:draw()
|
||||||
page.info:sync()
|
page.info:sync()
|
||||||
|
|
||||||
if canvas then
|
if canvas then
|
||||||
canvas.turtles.setText(tostring(Util.size(turtles)))
|
canvas.turtles.setText(tostring(Util.size(turtles)))
|
||||||
canvas.queue.setText(tostring(Util.size(queue)))
|
canvas.queue.setText(tostring(Util.size(queue)))
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.onTimeout(.1, function()
|
Event.onTimeout(.1, function()
|
||||||
page:scan()
|
page:scan()
|
||||||
blocksTab.grid:setValues(page.totals)
|
blocksTab.grid:setValues(page.totals)
|
||||||
blocksTab.grid:draw()
|
blocksTab.grid:draw()
|
||||||
page:sync()
|
page:sync()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
UI:setPage(page)
|
UI:setPage(page)
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
Event.onTerminate(function()
|
Event.onTerminate(function()
|
||||||
spt = Point.above(locate())
|
spt = Point.above(locate())
|
||||||
for _, v in pairs(pool) do
|
for _, v in pairs(pool) do
|
||||||
v.status = 'aborting'
|
v.status = 'aborting'
|
||||||
v.abort = true
|
v.abort = true
|
||||||
end
|
end
|
||||||
abort = true
|
abort = true
|
||||||
end)
|
end)
|
||||||
]]
|
]]
|
||||||
|
|
||||||
Event.pullEvents()
|
Event.pullEvents()
|
||||||
|
|
||||||
if canvas then
|
if canvas then
|
||||||
canvas3d.clear()
|
canvas3d.clear()
|
||||||
canvas.group.remove()
|
canvas.group.remove()
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,154 +8,154 @@ local os = _G.os
|
|||||||
local ChestAdapter = class()
|
local ChestAdapter = class()
|
||||||
|
|
||||||
local convertNames = {
|
local convertNames = {
|
||||||
name = 'id',
|
name = 'id',
|
||||||
damage = 'dmg',
|
damage = 'dmg',
|
||||||
maxCount = 'max_size',
|
maxCount = 'max_size',
|
||||||
count = 'qty',
|
count = 'qty',
|
||||||
displayName = 'display_name',
|
displayName = 'display_name',
|
||||||
maxDamage = 'max_dmg',
|
maxDamage = 'max_dmg',
|
||||||
nbtHash = 'nbt_hash',
|
nbtHash = 'nbt_hash',
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Strip off color prefix
|
-- Strip off color prefix
|
||||||
local function safeString(text)
|
local function safeString(text)
|
||||||
|
|
||||||
local val = text:byte(1)
|
local val = text:byte(1)
|
||||||
|
|
||||||
if val < 32 or val > 128 then
|
if val < 32 or val > 128 then
|
||||||
|
|
||||||
local newText = {}
|
local newText = {}
|
||||||
for i = 4, #text do
|
for i = 4, #text do
|
||||||
val = text:byte(i)
|
val = text:byte(i)
|
||||||
newText[i - 3] = (val > 31 and val < 127) and val or 63
|
newText[i - 3] = (val > 31 and val < 127) and val or 63
|
||||||
end
|
end
|
||||||
return string.char(unpack(newText))
|
return string.char(unpack(newText))
|
||||||
end
|
end
|
||||||
|
|
||||||
return text
|
return text
|
||||||
end
|
end
|
||||||
|
|
||||||
local function convertItem(item)
|
local function convertItem(item)
|
||||||
for k,v in pairs(convertNames) do
|
for k,v in pairs(convertNames) do
|
||||||
item[k] = item[v]
|
item[k] = item[v]
|
||||||
item[v] = nil
|
item[v] = nil
|
||||||
end
|
end
|
||||||
item.displayName = safeString(item.displayName)
|
item.displayName = safeString(item.displayName)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:init(args)
|
function ChestAdapter:init(args)
|
||||||
local defaults = {
|
local defaults = {
|
||||||
name = 'chest',
|
name = 'chest',
|
||||||
}
|
}
|
||||||
Util.merge(self, defaults)
|
Util.merge(self, defaults)
|
||||||
Util.merge(self, args)
|
Util.merge(self, args)
|
||||||
|
|
||||||
local chest
|
local chest
|
||||||
if not self.side then
|
if not self.side then
|
||||||
chest = Peripheral.getByMethod('getAllStacks')
|
chest = Peripheral.getByMethod('getAllStacks')
|
||||||
else
|
else
|
||||||
chest = Peripheral.getBySide(self.side)
|
chest = Peripheral.getBySide(self.side)
|
||||||
if chest and not chest.getAllStacks then
|
if chest and not chest.getAllStacks then
|
||||||
chest = nil
|
chest = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if chest then
|
if chest then
|
||||||
Util.merge(self, chest)
|
Util.merge(self, chest)
|
||||||
|
|
||||||
if chest.listAvailableItems then
|
if chest.listAvailableItems then
|
||||||
self.list = chest.listAvailableItems
|
self.list = chest.listAvailableItems
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:isValid()
|
function ChestAdapter:isValid()
|
||||||
return not not self.getAllStacks
|
return not not self.getAllStacks
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:refresh(throttle)
|
function ChestAdapter:refresh(throttle)
|
||||||
return self:listItems(throttle)
|
return self:listItems(throttle)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- provide a consolidated list of items
|
-- provide a consolidated list of items
|
||||||
function ChestAdapter:listItems(throttle)
|
function ChestAdapter:listItems(throttle)
|
||||||
local cache = { }
|
local cache = { }
|
||||||
local items = { }
|
local items = { }
|
||||||
throttle = throttle or Util.throttle()
|
throttle = throttle or Util.throttle()
|
||||||
|
|
||||||
-- getAllStacks sometimes fails
|
-- getAllStacks sometimes fails
|
||||||
local s, m = pcall(function()
|
local s, m = pcall(function()
|
||||||
for _,v in pairs(self.getAllStacks(false)) do
|
for _,v in pairs(self.getAllStacks(false)) do
|
||||||
if v.qty > 0 then
|
if v.qty > 0 then
|
||||||
convertItem(v)
|
convertItem(v)
|
||||||
local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')
|
local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')
|
||||||
|
|
||||||
local entry = cache[key]
|
local entry = cache[key]
|
||||||
if not entry then
|
if not entry then
|
||||||
entry = itemDB:get(v) or itemDB:add(v)
|
entry = itemDB:get(v) or itemDB:add(v)
|
||||||
entry = Util.shallowCopy(entry)
|
entry = Util.shallowCopy(entry)
|
||||||
entry.count = 0
|
entry.count = 0
|
||||||
cache[key] = entry
|
cache[key] = entry
|
||||||
table.insert(items, entry)
|
table.insert(items, entry)
|
||||||
end
|
end
|
||||||
entry.count = entry.count + v.count
|
entry.count = entry.count + v.count
|
||||||
throttle()
|
throttle()
|
||||||
end
|
end
|
||||||
itemDB:flush()
|
itemDB:flush()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
if s then
|
if s then
|
||||||
if not Util.empty(items) then
|
if not Util.empty(items) then
|
||||||
self.cache = cache
|
self.cache = cache
|
||||||
return items
|
return items
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
_G._syslog(m)
|
_G._syslog(m)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:getItemInfo(item)
|
function ChestAdapter:getItemInfo(item)
|
||||||
if not self.cache then
|
if not self.cache then
|
||||||
self:listItems()
|
self:listItems()
|
||||||
end
|
end
|
||||||
local key = table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
local key = table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
||||||
return self.cache[key]
|
return self.cache[key]
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:provide(item, qty, slot, direction)
|
function ChestAdapter:provide(item, qty, slot, direction)
|
||||||
pcall(function()
|
pcall(function()
|
||||||
for key,stack in Util.rpairs(self.getAllStacks(false)) do
|
for key,stack in Util.rpairs(self.getAllStacks(false)) do
|
||||||
if stack.id == item.name and
|
if stack.id == item.name and
|
||||||
(not item.damage or stack.dmg == item.damage) and
|
(not item.damage or stack.dmg == item.damage) and
|
||||||
(not item.nbtHash or stack.nbt_hash == item.nbtHash) then
|
(not item.nbtHash or stack.nbt_hash == item.nbtHash) then
|
||||||
local amount = math.min(qty, stack.qty)
|
local amount = math.min(qty, stack.qty)
|
||||||
if amount > 0 then
|
if amount > 0 then
|
||||||
self.pushItemIntoSlot(direction or self.direction, key, amount, slot)
|
self.pushItemIntoSlot(direction or self.direction, key, amount, slot)
|
||||||
end
|
end
|
||||||
qty = qty - amount
|
qty = qty - amount
|
||||||
if qty <= 0 then
|
if qty <= 0 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:extract(slot, qty, toSlot)
|
function ChestAdapter:extract(slot, qty, toSlot)
|
||||||
if toSlot then
|
if toSlot then
|
||||||
self.pushItemIntoSlot(self.direction, slot, qty, toSlot)
|
self.pushItemIntoSlot(self.direction, slot, qty, toSlot)
|
||||||
else
|
else
|
||||||
self.pushItem(self.direction, slot, qty)
|
self.pushItem(self.direction, slot, qty)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:insert(slot, qty, toSlot)
|
function ChestAdapter:insert(slot, qty, toSlot)
|
||||||
-- toSlot not tested ...
|
-- toSlot not tested ...
|
||||||
local s, m = pcall(self.pullItem, self.direction, slot, qty, toSlot)
|
local s, m = pcall(self.pullItem, self.direction, slot, qty, toSlot)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
os.sleep(1)
|
os.sleep(1)
|
||||||
pcall(self.pullItem, self.direction, slot, qty, toSlot)
|
pcall(self.pullItem, self.direction, slot, qty, toSlot)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return ChestAdapter
|
return ChestAdapter
|
||||||
|
|||||||
@@ -6,159 +6,159 @@ local Peripheral = require('peripheral')
|
|||||||
local ChestAdapter = class()
|
local ChestAdapter = class()
|
||||||
|
|
||||||
function ChestAdapter:init(args)
|
function ChestAdapter:init(args)
|
||||||
local defaults = {
|
local defaults = {
|
||||||
name = 'chest',
|
name = 'chest',
|
||||||
adapter = 'ChestAdapter18'
|
adapter = 'ChestAdapter18'
|
||||||
}
|
}
|
||||||
Util.merge(self, defaults)
|
Util.merge(self, defaults)
|
||||||
Util.merge(self, args)
|
Util.merge(self, args)
|
||||||
|
|
||||||
local chest
|
local chest
|
||||||
if not self.side then
|
if not self.side then
|
||||||
chest = Peripheral.getByMethod('list') or
|
chest = Peripheral.getByMethod('list') or
|
||||||
Peripheral.getByMethod('listAvailableItems')
|
Peripheral.getByMethod('listAvailableItems')
|
||||||
else
|
else
|
||||||
chest = Peripheral.getBySide(self.side)
|
chest = Peripheral.getBySide(self.side)
|
||||||
if chest and not chest.list and not chest.listAvailableItems then
|
if chest and not chest.list and not chest.listAvailableItems then
|
||||||
chest = nil
|
chest = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if chest then
|
if chest then
|
||||||
Util.merge(self, chest)
|
Util.merge(self, chest)
|
||||||
|
|
||||||
if chest.listAvailableItems then
|
if chest.listAvailableItems then
|
||||||
self.list = chest.listAvailableItems
|
self.list = chest.listAvailableItems
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:isValid()
|
function ChestAdapter:isValid()
|
||||||
return not not self.list
|
return not not self.list
|
||||||
end
|
end
|
||||||
|
|
||||||
-- handle both AE/RS and generic inventory
|
-- handle both AE/RS and generic inventory
|
||||||
function ChestAdapter:getItemDetails(index, item)
|
function ChestAdapter:getItemDetails(index, item)
|
||||||
if self.getItemMeta then
|
if self.getItemMeta then
|
||||||
local s, detail = pcall(self.getItemMeta, index)
|
local s, detail = pcall(self.getItemMeta, index)
|
||||||
if not s or not detail or detail.name ~= item.name then
|
if not s or not detail or detail.name ~= item.name then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return detail
|
return detail
|
||||||
else
|
else
|
||||||
local detail = self.findItems(item)
|
local detail = self.findItems(item)
|
||||||
if detail and #detail > 0 then
|
if detail and #detail > 0 then
|
||||||
return detail[1].getMetadata()
|
return detail[1].getMetadata()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:getCachedItemDetails(item, k)
|
function ChestAdapter:getCachedItemDetails(item, k)
|
||||||
local cached = itemDB:get(item)
|
local cached = itemDB:get(item)
|
||||||
if cached then
|
if cached then
|
||||||
return cached
|
return cached
|
||||||
end
|
end
|
||||||
|
|
||||||
local detail = self:getItemDetails(k, item)
|
local detail = self:getItemDetails(k, item)
|
||||||
if detail then
|
if detail then
|
||||||
return itemDB:add(detail)
|
return itemDB:add(detail)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:refresh(throttle)
|
function ChestAdapter:refresh(throttle)
|
||||||
return self:listItems(throttle)
|
return self:listItems(throttle)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- provide a consolidated list of items
|
-- provide a consolidated list of items
|
||||||
function ChestAdapter:listItems(throttle)
|
function ChestAdapter:listItems(throttle)
|
||||||
for _ = 1, 5 do
|
for _ = 1, 5 do
|
||||||
local list = self:listItemsInternal(throttle)
|
local list = self:listItemsInternal(throttle)
|
||||||
if list then
|
if list then
|
||||||
return list
|
return list
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
error('Error accessing inventory: ' .. self.direction)
|
error('Error accessing inventory: ' .. self.direction)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:listItemsInternal(throttle)
|
function ChestAdapter:listItemsInternal(throttle)
|
||||||
local cache = { }
|
local cache = { }
|
||||||
local items = { }
|
local items = { }
|
||||||
throttle = throttle or Util.throttle()
|
throttle = throttle or Util.throttle()
|
||||||
|
|
||||||
for k,v in pairs(self.list()) do
|
for k,v in pairs(self.list()) do
|
||||||
if v.count > 0 then
|
if v.count > 0 then
|
||||||
local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')
|
local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')
|
||||||
|
|
||||||
local entry = cache[key]
|
local entry = cache[key]
|
||||||
if not entry then
|
if not entry then
|
||||||
entry = self:getCachedItemDetails(v, k)
|
entry = self:getCachedItemDetails(v, k)
|
||||||
if not entry then
|
if not entry then
|
||||||
return -- Inventory has changed
|
return -- Inventory has changed
|
||||||
end
|
end
|
||||||
entry = Util.shallowCopy(entry)
|
entry = Util.shallowCopy(entry)
|
||||||
entry.count = 0
|
entry.count = 0
|
||||||
cache[key] = entry
|
cache[key] = entry
|
||||||
table.insert(items, entry)
|
table.insert(items, entry)
|
||||||
end
|
end
|
||||||
|
|
||||||
if entry then
|
if entry then
|
||||||
entry.count = entry.count + v.count
|
entry.count = entry.count + v.count
|
||||||
end
|
end
|
||||||
throttle()
|
throttle()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
itemDB:flush()
|
itemDB:flush()
|
||||||
|
|
||||||
self.cache = cache
|
self.cache = cache
|
||||||
return items
|
return items
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:getItemInfo(item)
|
function ChestAdapter:getItemInfo(item)
|
||||||
if not self.cache then
|
if not self.cache then
|
||||||
self:listItems()
|
self:listItems()
|
||||||
end
|
end
|
||||||
local key = table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
local key = table.concat({ item.name, item.damage, item.nbtHash }, ':')
|
||||||
local items = self.cache or { }
|
local items = self.cache or { }
|
||||||
return items[key]
|
return items[key]
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:getPercentUsed()
|
function ChestAdapter:getPercentUsed()
|
||||||
if self.cache and self.getDrawerCount then
|
if self.cache and self.getDrawerCount then
|
||||||
return math.floor(Util.size(self.cache) / self.getDrawerCount() * 100)
|
return math.floor(Util.size(self.cache) / self.getDrawerCount() * 100)
|
||||||
end
|
end
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:provide(item, qty, slot, direction)
|
function ChestAdapter:provide(item, qty, slot, direction)
|
||||||
local total = 0
|
local total = 0
|
||||||
|
|
||||||
local _, m = pcall(function()
|
local _, m = pcall(function()
|
||||||
local stacks = self.list()
|
local stacks = self.list()
|
||||||
for key,stack in Util.rpairs(stacks) do
|
for key,stack in Util.rpairs(stacks) do
|
||||||
if stack.name == item.name and
|
if stack.name == item.name and
|
||||||
stack.damage == item.damage and
|
stack.damage == item.damage and
|
||||||
stack.nbtHash == item.nbtHash then
|
stack.nbtHash == item.nbtHash then
|
||||||
local amount = math.min(qty, stack.count)
|
local amount = math.min(qty, stack.count)
|
||||||
if amount > 0 then
|
if amount > 0 then
|
||||||
amount = self.pushItems(direction or self.direction, key, amount, slot)
|
amount = self.pushItems(direction or self.direction, key, amount, slot)
|
||||||
end
|
end
|
||||||
qty = qty - amount
|
qty = qty - amount
|
||||||
total = total + amount
|
total = total + amount
|
||||||
if qty <= 0 then
|
if qty <= 0 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
return total, m
|
return total, m
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:extract(slot, qty, toSlot, direction)
|
function ChestAdapter:extract(slot, qty, toSlot, direction)
|
||||||
return self.pushItems(direction or self.direction, slot, qty, toSlot)
|
return self.pushItems(direction or self.direction, slot, qty, toSlot)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ChestAdapter:insert(slot, qty, toSlot, direction)
|
function ChestAdapter:insert(slot, qty, toSlot, direction)
|
||||||
return self.pullItems(direction or self.direction, slot, qty, toSlot)
|
return self.pullItems(direction or self.direction, slot, qty, toSlot)
|
||||||
end
|
end
|
||||||
|
|
||||||
return ChestAdapter
|
return ChestAdapter
|
||||||
|
|||||||
@@ -6,277 +6,277 @@ local Util = require('util')
|
|||||||
local itemDB = TableDB({ fileName = 'usr/config/items.db' })
|
local itemDB = TableDB({ fileName = 'usr/config/items.db' })
|
||||||
|
|
||||||
local function safeString(text)
|
local function safeString(text)
|
||||||
local val = text:byte(1)
|
local val = text:byte(1)
|
||||||
|
|
||||||
if val < 32 or val > 128 then
|
if val < 32 or val > 128 then
|
||||||
|
|
||||||
local newText = { }
|
local newText = { }
|
||||||
local skip = 0
|
local skip = 0
|
||||||
for i = 1, #text do
|
for i = 1, #text do
|
||||||
val = text:byte(i)
|
val = text:byte(i)
|
||||||
if val == 167 then
|
if val == 167 then
|
||||||
skip = 2
|
skip = 2
|
||||||
end
|
end
|
||||||
if skip > 0 then
|
if skip > 0 then
|
||||||
skip = skip - 1
|
skip = skip - 1
|
||||||
else
|
else
|
||||||
if val >= 32 and val <= 128 then
|
if val >= 32 and val <= 128 then
|
||||||
newText[#newText + 1] = val
|
newText[#newText + 1] = val
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return string.char(unpack(newText))
|
return string.char(unpack(newText))
|
||||||
end
|
end
|
||||||
|
|
||||||
return text
|
return text
|
||||||
end
|
end
|
||||||
|
|
||||||
function itemDB:makeKey(item)
|
function itemDB:makeKey(item)
|
||||||
if not item then error('itemDB:makeKey: item is required', 2) end
|
if not item then error('itemDB:makeKey: item is required', 2) end
|
||||||
return table.concat({ item.name, item.damage or '*', item.nbtHash }, ':')
|
return table.concat({ item.name, item.damage or '*', item.nbtHash }, ':')
|
||||||
end
|
end
|
||||||
|
|
||||||
function itemDB:splitKey(key, item)
|
function itemDB:splitKey(key, item)
|
||||||
item = item or { }
|
item = item or { }
|
||||||
|
|
||||||
local t = Util.split(key, '(.-):')
|
local t = Util.split(key, '(.-):')
|
||||||
if #t[#t] > 8 then
|
if #t[#t] > 8 then
|
||||||
item.nbtHash = table.remove(t)
|
item.nbtHash = table.remove(t)
|
||||||
end
|
end
|
||||||
local damage = table.remove(t)
|
local damage = table.remove(t)
|
||||||
if damage ~= '*' then
|
if damage ~= '*' then
|
||||||
item.damage = tonumber(damage)
|
item.damage = tonumber(damage)
|
||||||
end
|
end
|
||||||
item.name = table.concat(t, ':')
|
item.name = table.concat(t, ':')
|
||||||
|
|
||||||
return item
|
return item
|
||||||
end
|
end
|
||||||
|
|
||||||
function itemDB:get(key, populateFn)
|
function itemDB:get(key, populateFn)
|
||||||
if not key then error('itemDB:get: key is required', 2) end
|
if not key then error('itemDB:get: key is required', 2) end
|
||||||
if type(key) == 'string' then
|
if type(key) == 'string' then
|
||||||
key = self:splitKey(key)
|
key = self:splitKey(key)
|
||||||
else
|
else
|
||||||
key = Util.shallowCopy(key)
|
key = Util.shallowCopy(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
local item = self:_get(key)
|
local item = self:_get(key)
|
||||||
if not item and populateFn then
|
if not item and populateFn then
|
||||||
item = populateFn()
|
item = populateFn()
|
||||||
if item then
|
if item then
|
||||||
item = self:add(item)
|
item = self:add(item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return item and Util.merge(key, item)
|
return item and Util.merge(key, item)
|
||||||
end
|
end
|
||||||
|
|
||||||
function itemDB:_get(key)
|
function itemDB:_get(key)
|
||||||
if not key then error('itemDB:get: key is required', 2) end
|
if not key then error('itemDB:get: key is required', 2) end
|
||||||
if type(key) == 'string' then
|
if type(key) == 'string' then
|
||||||
key = self:splitKey(key)
|
key = self:splitKey(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
local item = TableDB.get(self, self:makeKey(key))
|
local item = TableDB.get(self, self:makeKey(key))
|
||||||
if item then
|
if item then
|
||||||
return item
|
return item
|
||||||
end
|
end
|
||||||
|
|
||||||
-- try finding an item that has damage values
|
-- try finding an item that has damage values
|
||||||
if type(key.damage) == 'number' then
|
if type(key.damage) == 'number' then
|
||||||
item = TableDB.get(self, self:makeKey({ name = key.name, nbtHash = key.nbtHash }))
|
item = TableDB.get(self, self:makeKey({ name = key.name, nbtHash = key.nbtHash }))
|
||||||
if item and item.maxDamage then
|
if item and item.maxDamage then
|
||||||
item = Util.shallowCopy(item)
|
item = Util.shallowCopy(item)
|
||||||
item.damage = key.damage
|
item.damage = key.damage
|
||||||
if item.maxDamage > 0 and type(item.damage) == 'number' and item.damage > 0 then
|
if item.maxDamage > 0 and type(item.damage) == 'number' and item.damage > 0 then
|
||||||
item.displayName = string.format('%s (damage: %s)', item.displayName, item.damage)
|
item.displayName = string.format('%s (damage: %s)', item.displayName, item.damage)
|
||||||
end
|
end
|
||||||
return item
|
return item
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
for k,item in pairs(self.data) do
|
for k,item in pairs(self.data) do
|
||||||
if key.name == item.name and
|
if key.name == item.name and
|
||||||
key.nbtHash == key.nbtHash and
|
key.nbtHash == key.nbtHash and
|
||||||
item.maxDamage > 0 then
|
item.maxDamage > 0 then
|
||||||
item = Util.shallowCopy(item)
|
item = Util.shallowCopy(item)
|
||||||
item.nbtHash = key.nbtHash
|
item.nbtHash = key.nbtHash
|
||||||
return item
|
return item
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if key.nbtHash then
|
if key.nbtHash then
|
||||||
item = self:get({ name = key.name, damage = key.damage })
|
item = self:get({ name = key.name, damage = key.damage })
|
||||||
|
|
||||||
if item and item.ignoreNBT then
|
if item and item.ignoreNBT then
|
||||||
item = Util.shallowCopy(item)
|
item = Util.shallowCopy(item)
|
||||||
item.nbtHash = key.nbtHash
|
item.nbtHash = key.nbtHash
|
||||||
item.damage = key.damage
|
item.damage = key.damage
|
||||||
return item
|
return item
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function formatTime(t)
|
local function formatTime(t)
|
||||||
local m = math.floor(t/60)
|
local m = math.floor(t/60)
|
||||||
local s = t % 60
|
local s = t % 60
|
||||||
if s < 10 then
|
if s < 10 then
|
||||||
s = '0' .. s
|
s = '0' .. s
|
||||||
end
|
end
|
||||||
|
|
||||||
return m .. ':' .. s
|
return m .. ':' .. s
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
If the base item contains an NBT hash, then the NBT hash uniquely
|
If the base item contains an NBT hash, then the NBT hash uniquely
|
||||||
identifies this item.
|
identifies this item.
|
||||||
]]--
|
]]--
|
||||||
function itemDB:add(baseItem)
|
function itemDB:add(baseItem)
|
||||||
local nItem = {
|
local nItem = {
|
||||||
name = baseItem.name,
|
name = baseItem.name,
|
||||||
damage = baseItem.damage,
|
damage = baseItem.damage,
|
||||||
nbtHash = baseItem.nbtHash,
|
nbtHash = baseItem.nbtHash,
|
||||||
}
|
}
|
||||||
-- if detail.maxDamage > 0 then
|
-- if detail.maxDamage > 0 then
|
||||||
-- nItem.damage = '*'
|
-- nItem.damage = '*'
|
||||||
-- end
|
-- end
|
||||||
|
|
||||||
nItem.displayName = safeString(baseItem.displayName)
|
nItem.displayName = safeString(baseItem.displayName)
|
||||||
nItem.maxCount = baseItem.maxCount
|
nItem.maxCount = baseItem.maxCount
|
||||||
nItem.maxDamage = baseItem.maxDamage
|
nItem.maxDamage = baseItem.maxDamage
|
||||||
|
|
||||||
-- enchanted items
|
-- enchanted items
|
||||||
if baseItem.enchantments then
|
if baseItem.enchantments then
|
||||||
if nItem.name == 'minecraft:enchanted_book' then
|
if nItem.name == 'minecraft:enchanted_book' then
|
||||||
nItem.displayName = 'Book: '
|
nItem.displayName = 'Book: '
|
||||||
else
|
else
|
||||||
nItem.displayName = nItem.displayName .. ': '
|
nItem.displayName = nItem.displayName .. ': '
|
||||||
end
|
end
|
||||||
for k, v in ipairs(baseItem.enchantments) do
|
for k, v in ipairs(baseItem.enchantments) do
|
||||||
if k > 1 then
|
if k > 1 then
|
||||||
nItem.displayName = nItem.displayName .. ', '
|
nItem.displayName = nItem.displayName .. ', '
|
||||||
end
|
end
|
||||||
nItem.displayName = nItem.displayName .. v.fullName
|
nItem.displayName = nItem.displayName .. v.fullName
|
||||||
end
|
end
|
||||||
|
|
||||||
-- turtles / computers / etc
|
-- turtles / computers / etc
|
||||||
elseif baseItem.computer then
|
elseif baseItem.computer then
|
||||||
-- a turtle's NBT is updated constantly
|
-- a turtle's NBT is updated constantly
|
||||||
-- update the cache with the new NBT
|
-- update the cache with the new NBT
|
||||||
if baseItem.computer.id then
|
if baseItem.computer.id then
|
||||||
Map.removeMatches(self.data, { name = nItem.name, displayName = nItem.displayName })
|
Map.removeMatches(self.data, { name = nItem.name, displayName = nItem.displayName })
|
||||||
end
|
end
|
||||||
nItem.displayName = baseItem.computer.label or baseItem.displayName
|
nItem.displayName = baseItem.computer.label or baseItem.displayName
|
||||||
|
|
||||||
-- disks
|
-- disks
|
||||||
elseif baseItem.media then
|
elseif baseItem.media then
|
||||||
-- don't ignore nbt... as disks can be labeled
|
-- don't ignore nbt... as disks can be labeled
|
||||||
if baseItem.media.recordTitle then
|
if baseItem.media.recordTitle then
|
||||||
nItem.displayName = nItem.displayName .. ': ' .. baseItem.media.recordTitle
|
nItem.displayName = nItem.displayName .. ': ' .. baseItem.media.recordTitle
|
||||||
end
|
end
|
||||||
|
|
||||||
-- potions
|
-- potions
|
||||||
elseif nItem.name == 'minecraft:potion' or nItem.name == 'minecraft:lingering_potion' then
|
elseif nItem.name == 'minecraft:potion' or nItem.name == 'minecraft:lingering_potion' then
|
||||||
if baseItem.effects then
|
if baseItem.effects then
|
||||||
local effect = baseItem.effects[1]
|
local effect = baseItem.effects[1]
|
||||||
if effect.amplifier == 1 then
|
if effect.amplifier == 1 then
|
||||||
nItem.displayName = nItem.displayName .. ' II'
|
nItem.displayName = nItem.displayName .. ' II'
|
||||||
end
|
end
|
||||||
if effect.duration and effect.duration > 0 then
|
if effect.duration and effect.duration > 0 then
|
||||||
nItem.displayName = string.format('%s (%s)', nItem.displayName, formatTime(effect.duration))
|
nItem.displayName = string.format('%s (%s)', nItem.displayName, formatTime(effect.duration))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
for k,item in pairs(self.data) do
|
for k,item in pairs(self.data) do
|
||||||
if nItem.name == item.name and
|
if nItem.name == item.name and
|
||||||
nItem.displayName == item.displayName then
|
nItem.displayName == item.displayName then
|
||||||
|
|
||||||
if nItem.nbtHash ~= item.nbtHash and nItem.damage ~= item.damage then
|
if nItem.nbtHash ~= item.nbtHash and nItem.damage ~= item.damage then
|
||||||
nItem.damage = '*'
|
nItem.damage = '*'
|
||||||
nItem.nbtHash = nil
|
nItem.nbtHash = nil
|
||||||
nItem.ignoreNBT = true
|
nItem.ignoreNBT = true
|
||||||
self.data[k] = nil
|
self.data[k] = nil
|
||||||
break
|
break
|
||||||
elseif nItem.damage ~= item.damage then
|
elseif nItem.damage ~= item.damage then
|
||||||
nItem.damage = '*'
|
nItem.damage = '*'
|
||||||
self.data[k] = nil
|
self.data[k] = nil
|
||||||
break
|
break
|
||||||
elseif nItem.nbtHash ~= item.nbtHash then
|
elseif nItem.nbtHash ~= item.nbtHash then
|
||||||
nItem.nbtHash = nil
|
nItem.nbtHash = nil
|
||||||
nItem.ignoreNBT = true
|
nItem.ignoreNBT = true
|
||||||
self.data[k] = nil
|
self.data[k] = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
TableDB.add(self, self:makeKey(nItem), nItem)
|
TableDB.add(self, self:makeKey(nItem), nItem)
|
||||||
nItem = Util.shallowCopy(nItem)
|
nItem = Util.shallowCopy(nItem)
|
||||||
nItem.damage = baseItem.damage
|
nItem.damage = baseItem.damage
|
||||||
nItem.nbtHash = baseItem.nbtHash
|
nItem.nbtHash = baseItem.nbtHash
|
||||||
|
|
||||||
return nItem
|
return nItem
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Accepts: "minecraft:stick:0" or { name = 'minecraft:stick', damage = 0 }
|
-- Accepts: "minecraft:stick:0" or { name = 'minecraft:stick', damage = 0 }
|
||||||
function itemDB:getName(item)
|
function itemDB:getName(item)
|
||||||
if type(item) == 'string' then
|
if type(item) == 'string' then
|
||||||
item = self:splitKey(item)
|
item = self:splitKey(item)
|
||||||
end
|
end
|
||||||
|
|
||||||
local detail = self:get(item)
|
local detail = self:get(item)
|
||||||
if detail then
|
if detail then
|
||||||
return detail.displayName
|
return detail.displayName
|
||||||
end
|
end
|
||||||
|
|
||||||
-- fallback to nameDB
|
-- fallback to nameDB
|
||||||
local strId = self:makeKey(item)
|
local strId = self:makeKey(item)
|
||||||
local name = nameDB.data[strId]
|
local name = nameDB.data[strId]
|
||||||
if not name and not item.damage then
|
if not name and not item.damage then
|
||||||
name = nameDB.data[self:makeKey({ name = item.name, damage = 0, nbtHash = item.nbtHash })]
|
name = nameDB.data[self:makeKey({ name = item.name, damage = 0, nbtHash = item.nbtHash })]
|
||||||
end
|
end
|
||||||
return name or strId
|
return name or strId
|
||||||
end
|
end
|
||||||
|
|
||||||
function itemDB:getMaxCount(item)
|
function itemDB:getMaxCount(item)
|
||||||
local detail = self:get(item)
|
local detail = self:get(item)
|
||||||
return detail and detail.maxCount or 64
|
return detail and detail.maxCount or 64
|
||||||
end
|
end
|
||||||
|
|
||||||
function itemDB:load()
|
function itemDB:load()
|
||||||
TableDB.load(self)
|
TableDB.load(self)
|
||||||
|
|
||||||
for key,item in pairs(self.data) do
|
for key,item in pairs(self.data) do
|
||||||
self:splitKey(key, item)
|
self:splitKey(key, item)
|
||||||
item.maxDamage = item.maxDamage or 0
|
item.maxDamage = item.maxDamage or 0
|
||||||
item.maxCount = item.maxCount or 64
|
item.maxCount = item.maxCount or 64
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function itemDB:flush()
|
function itemDB:flush()
|
||||||
if self.dirty then
|
if self.dirty then
|
||||||
|
|
||||||
local t = { }
|
local t = { }
|
||||||
for k,v in pairs(self.data) do
|
for k,v in pairs(self.data) do
|
||||||
v = Util.shallowCopy(v)
|
v = Util.shallowCopy(v)
|
||||||
v.name = nil
|
v.name = nil
|
||||||
v.damage = nil
|
v.damage = nil
|
||||||
v.nbtHash = nil
|
v.nbtHash = nil
|
||||||
v.count = nil -- wipe out previously saved counts - temporary
|
v.count = nil -- wipe out previously saved counts - temporary
|
||||||
if v.maxDamage == 0 then
|
if v.maxDamage == 0 then
|
||||||
v.maxDamage = nil
|
v.maxDamage = nil
|
||||||
end
|
end
|
||||||
if v.maxCount == 64 then
|
if v.maxCount == 64 then
|
||||||
v.maxCount = nil
|
v.maxCount = nil
|
||||||
end
|
end
|
||||||
t[k] = v
|
t[k] = v
|
||||||
end
|
end
|
||||||
|
|
||||||
Util.writeTable(self.fileName, t)
|
Util.writeTable(self.fileName, t)
|
||||||
self.dirty = false
|
self.dirty = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
itemDB:load()
|
itemDB:load()
|
||||||
|
|||||||
@@ -6,249 +6,249 @@ local Util = require('util')
|
|||||||
local os = _G.os
|
local os = _G.os
|
||||||
|
|
||||||
local convertNames = {
|
local convertNames = {
|
||||||
name = 'id',
|
name = 'id',
|
||||||
damage = 'dmg',
|
damage = 'dmg',
|
||||||
maxCount = 'max_size',
|
maxCount = 'max_size',
|
||||||
count = 'qty',
|
count = 'qty',
|
||||||
displayName = 'display_name',
|
displayName = 'display_name',
|
||||||
maxDamage = 'max_dmg',
|
maxDamage = 'max_dmg',
|
||||||
nbtHash = 'nbt_hash',
|
nbtHash = 'nbt_hash',
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Strip off color prefix
|
-- Strip off color prefix
|
||||||
local function safeString(text)
|
local function safeString(text)
|
||||||
|
|
||||||
local val = text:byte(1)
|
local val = text:byte(1)
|
||||||
|
|
||||||
if val < 32 or val > 128 then
|
if val < 32 or val > 128 then
|
||||||
|
|
||||||
local newText = {}
|
local newText = {}
|
||||||
for i = 4, #text do
|
for i = 4, #text do
|
||||||
val = text:byte(i)
|
val = text:byte(i)
|
||||||
newText[i - 3] = (val > 31 and val < 127) and val or 63
|
newText[i - 3] = (val > 31 and val < 127) and val or 63
|
||||||
end
|
end
|
||||||
return string.char(unpack(newText))
|
return string.char(unpack(newText))
|
||||||
end
|
end
|
||||||
|
|
||||||
return text
|
return text
|
||||||
end
|
end
|
||||||
|
|
||||||
local function convertItem(item)
|
local function convertItem(item)
|
||||||
for k,v in pairs(convertNames) do
|
for k,v in pairs(convertNames) do
|
||||||
item[k] = item[v]
|
item[k] = item[v]
|
||||||
item[v] = nil
|
item[v] = nil
|
||||||
end
|
end
|
||||||
item.displayName = safeString(item.displayName)
|
item.displayName = safeString(item.displayName)
|
||||||
end
|
end
|
||||||
|
|
||||||
local MEAdapter = class()
|
local MEAdapter = class()
|
||||||
|
|
||||||
function MEAdapter:init(args)
|
function MEAdapter:init(args)
|
||||||
local defaults = {
|
local defaults = {
|
||||||
items = { },
|
items = { },
|
||||||
name = 'ME',
|
name = 'ME',
|
||||||
jobList = { },
|
jobList = { },
|
||||||
}
|
}
|
||||||
Util.merge(self, defaults)
|
Util.merge(self, defaults)
|
||||||
Util.merge(self, args)
|
Util.merge(self, args)
|
||||||
|
|
||||||
local chest
|
local chest
|
||||||
|
|
||||||
if not self.side then
|
if not self.side then
|
||||||
chest = Peripheral.getByMethod('getAvailableItems')
|
chest = Peripheral.getByMethod('getAvailableItems')
|
||||||
else
|
else
|
||||||
chest = Peripheral.getBySide(self.side)
|
chest = Peripheral.getBySide(self.side)
|
||||||
if chest and not chest.getAvailableItems then
|
if chest and not chest.getAvailableItems then
|
||||||
chest = nil
|
chest = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if chest then
|
if chest then
|
||||||
Util.merge(self, chest)
|
Util.merge(self, chest)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:isValid()
|
function MEAdapter:isValid()
|
||||||
return self.getAvailableItems and self.getAvailableItems()
|
return self.getAvailableItems and self.getAvailableItems()
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:refresh()
|
function MEAdapter:refresh()
|
||||||
self.items = nil
|
self.items = nil
|
||||||
local hasItems, failed
|
local hasItems, failed
|
||||||
|
|
||||||
local s, m = pcall(function()
|
local s, m = pcall(function()
|
||||||
self.items = self.getAvailableItems('all')
|
self.items = self.getAvailableItems('all')
|
||||||
for _,v in pairs(self.items) do
|
for _,v in pairs(self.items) do
|
||||||
Util.merge(v, v.item)
|
Util.merge(v, v.item)
|
||||||
convertItem(v)
|
convertItem(v)
|
||||||
|
|
||||||
-- if power has been interrupted, the list will still be returned
|
-- if power has been interrupted, the list will still be returned
|
||||||
-- but all items will have a 0 quantity
|
-- but all items will have a 0 quantity
|
||||||
-- ensure that the list is valid
|
-- ensure that the list is valid
|
||||||
if not hasItems then
|
if not hasItems then
|
||||||
hasItems = v.count > 0
|
hasItems = v.count > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
if not v.fingerprint then
|
if not v.fingerprint then
|
||||||
failed = true
|
failed = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
if not itemDB:get(v) then
|
if not itemDB:get(v) then
|
||||||
itemDB:add(v, v)
|
itemDB:add(v, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
itemDB:flush()
|
itemDB:flush()
|
||||||
|
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G._syslog(m)
|
_G._syslog(m)
|
||||||
end
|
end
|
||||||
|
|
||||||
if s and not failed and hasItems and self.items and not Util.empty(self.items) then
|
if s and not failed and hasItems and self.items and not Util.empty(self.items) then
|
||||||
return self.items
|
return self.items
|
||||||
end
|
end
|
||||||
self.items = nil
|
self.items = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:listItems()
|
function MEAdapter:listItems()
|
||||||
self:refresh()
|
self:refresh()
|
||||||
return self.items
|
return self.items
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:getItemInfo(item)
|
function MEAdapter:getItemInfo(item)
|
||||||
for _,i in pairs(self.items) do
|
for _,i in pairs(self.items) do
|
||||||
if item.name == i.name and
|
if item.name == i.name and
|
||||||
item.damage == i.damage and
|
item.damage == i.damage and
|
||||||
item.nbtHash == i.nbtHash then
|
item.nbtHash == i.nbtHash then
|
||||||
return i
|
return i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:isCPUAvailable()
|
function MEAdapter:isCPUAvailable()
|
||||||
local cpus = self.getCraftingCPUs() or { }
|
local cpus = self.getCraftingCPUs() or { }
|
||||||
local available = false
|
local available = false
|
||||||
|
|
||||||
for cpu,v in pairs(cpus) do
|
for cpu,v in pairs(cpus) do
|
||||||
if not v.busy then
|
if not v.busy then
|
||||||
available = true
|
available = true
|
||||||
elseif not self.jobList[cpu] then -- something else is crafting something (don't know what)
|
elseif not self.jobList[cpu] then -- something else is crafting something (don't know what)
|
||||||
return false -- return false since we are in an unknown state
|
return false -- return false since we are in an unknown state
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return available
|
return available
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:craft(item, count)
|
function MEAdapter:craft(item, count)
|
||||||
if not self:isCPUAvailable() then
|
if not self:isCPUAvailable() then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
self:refresh()
|
self:refresh()
|
||||||
|
|
||||||
item = self:getItemInfo(item)
|
item = self:getItemInfo(item)
|
||||||
if item and item.is_craftable then
|
if item and item.is_craftable then
|
||||||
|
|
||||||
local cpus = self.getCraftingCPUs() or { }
|
local cpus = self.getCraftingCPUs() or { }
|
||||||
for cpu,v in pairs(cpus) do
|
for cpu,v in pairs(cpus) do
|
||||||
if not v.busy then
|
if not v.busy then
|
||||||
self.requestCrafting({
|
self.requestCrafting({
|
||||||
id = item.name,
|
id = item.name,
|
||||||
dmg = item.damage,
|
dmg = item.damage,
|
||||||
nbt_hash = item.nbtHash,
|
nbt_hash = item.nbtHash,
|
||||||
},
|
},
|
||||||
count or 1,
|
count or 1,
|
||||||
v.name -- CPUs must be named ! use anvil
|
v.name -- CPUs must be named ! use anvil
|
||||||
)
|
)
|
||||||
|
|
||||||
os.sleep(0) -- needed ?
|
os.sleep(0) -- needed ?
|
||||||
cpus = self.getCraftingCPUs() or { }
|
cpus = self.getCraftingCPUs() or { }
|
||||||
|
|
||||||
if cpus[cpu].busy then
|
if cpus[cpu].busy then
|
||||||
self.jobList[cpu] = {
|
self.jobList[cpu] = {
|
||||||
name = item.name,
|
name = item.name,
|
||||||
damage = item.damage,
|
damage = item.damage,
|
||||||
nbtHash = item.nbtHash,
|
nbtHash = item.nbtHash,
|
||||||
count = count,
|
count = count,
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
break -- only need to try the first available cpu
|
break -- only need to try the first available cpu
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:getJobList()
|
function MEAdapter:getJobList()
|
||||||
local cpus = self.getCraftingCPUs() or { }
|
local cpus = self.getCraftingCPUs() or { }
|
||||||
for cpu,v in pairs(cpus) do
|
for cpu,v in pairs(cpus) do
|
||||||
if not v.busy then
|
if not v.busy then
|
||||||
self.jobList[cpu] = nil
|
self.jobList[cpu] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return self.jobList
|
return self.jobList
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:isCrafting(item)
|
function MEAdapter:isCrafting(item)
|
||||||
for _,v in pairs(self:getJobList()) do
|
for _,v in pairs(self:getJobList()) do
|
||||||
if v.name == item.name and
|
if v.name == item.name and
|
||||||
v.damage == item.damage and
|
v.damage == item.damage and
|
||||||
v.nbtHash == item.nbtHash then
|
v.nbtHash == item.nbtHash then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:craftItems(items)
|
function MEAdapter:craftItems(items)
|
||||||
local cpus = self.getCraftingCPUs() or { }
|
local cpus = self.getCraftingCPUs() or { }
|
||||||
local count = 0
|
local count = 0
|
||||||
|
|
||||||
for _,cpu in pairs(cpus) do
|
for _,cpu in pairs(cpus) do
|
||||||
if cpu.busy then
|
if cpu.busy then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _,item in pairs(items) do
|
for _,item in pairs(items) do
|
||||||
if count >= #cpus then
|
if count >= #cpus then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if not self:isCrafting(item) then
|
if not self:isCrafting(item) then
|
||||||
if self:craft(item, item.count) then
|
if self:craft(item, item.count) then
|
||||||
count = count + 1
|
count = count + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:provide(item, qty, slot, direction)
|
function MEAdapter:provide(item, qty, slot, direction)
|
||||||
return pcall(function()
|
return pcall(function()
|
||||||
for _,stack in pairs(self.getAvailableItems('all')) do
|
for _,stack in pairs(self.getAvailableItems('all')) do
|
||||||
if stack.item.id == item.name and
|
if stack.item.id == item.name and
|
||||||
(not item.damage or stack.item.dmg == item.damage) and
|
(not item.damage or stack.item.dmg == item.damage) and
|
||||||
(not item.nbtHash or stack.item.nbt_hash == item.nbtHash) then
|
(not item.nbtHash or stack.item.nbt_hash == item.nbtHash) then
|
||||||
local amount = math.min(qty, stack.item.qty)
|
local amount = math.min(qty, stack.item.qty)
|
||||||
if amount > 0 then
|
if amount > 0 then
|
||||||
self.exportItem(stack.item, direction or self.direction, amount, slot)
|
self.exportItem(stack.item, direction or self.direction, amount, slot)
|
||||||
end
|
end
|
||||||
qty = qty - amount
|
qty = qty - amount
|
||||||
if qty <= 0 then
|
if qty <= 0 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:insert(slot, qty, toSlot)
|
function MEAdapter:insert(slot, qty, toSlot)
|
||||||
local s, m = pcall(self.pullItem, self.direction, slot, qty, toSlot)
|
local s, m = pcall(self.pullItem, self.direction, slot, qty, toSlot)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
os.sleep(1)
|
os.sleep(1)
|
||||||
pcall(self.pullItem, self.direction, slot, qty, toSlot)
|
pcall(self.pullItem, self.direction, slot, qty, toSlot)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return MEAdapter
|
return MEAdapter
|
||||||
|
|||||||
@@ -8,83 +8,83 @@ local MEAdapter = class(RSAdapter)
|
|||||||
local DEVICE_TYPE = 'appliedenergistics2:interface'
|
local DEVICE_TYPE = 'appliedenergistics2:interface'
|
||||||
|
|
||||||
function MEAdapter:init(args)
|
function MEAdapter:init(args)
|
||||||
local defaults = {
|
local defaults = {
|
||||||
name = 'appliedEnergistics',
|
name = 'appliedEnergistics',
|
||||||
jobList = { },
|
jobList = { },
|
||||||
}
|
}
|
||||||
Util.merge(self, defaults)
|
Util.merge(self, defaults)
|
||||||
Util.merge(self, args)
|
Util.merge(self, args)
|
||||||
|
|
||||||
local controller
|
local controller
|
||||||
if not self.side then
|
if not self.side then
|
||||||
controller = Peripheral.getByType(DEVICE_TYPE)
|
controller = Peripheral.getByType(DEVICE_TYPE)
|
||||||
else
|
else
|
||||||
controller = Peripheral.getBySide(self.side)
|
controller = Peripheral.getBySide(self.side)
|
||||||
end
|
end
|
||||||
|
|
||||||
if controller then
|
if controller then
|
||||||
Util.merge(self, controller)
|
Util.merge(self, controller)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:isValid()
|
function MEAdapter:isValid()
|
||||||
return self.type == DEVICE_TYPE and not not self.findItems
|
return self.type == DEVICE_TYPE and not not self.findItems
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:clearFinished()
|
function MEAdapter:clearFinished()
|
||||||
for _,key in pairs(Util.keys(self.jobList)) do
|
for _,key in pairs(Util.keys(self.jobList)) do
|
||||||
local job = self.jobList[key]
|
local job = self.jobList[key]
|
||||||
if job.info.status() == 'finished' then
|
if job.info.status() == 'finished' then
|
||||||
self.jobList[key] = nil
|
self.jobList[key] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:isCPUAvailable()
|
function MEAdapter:isCPUAvailable()
|
||||||
local cpus = self.getCraftingCPUs() or { }
|
local cpus = self.getCraftingCPUs() or { }
|
||||||
local busy = 0
|
local busy = 0
|
||||||
|
|
||||||
for _,cpu in pairs(cpus) do
|
for _,cpu in pairs(cpus) do
|
||||||
if cpu.busy then
|
if cpu.busy then
|
||||||
busy = busy + 1
|
busy = busy + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:clearFinished()
|
self:clearFinished()
|
||||||
return busy == Util.size(self.jobList) and busy < #cpus
|
return busy == Util.size(self.jobList) and busy < #cpus
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:craft(item, count)
|
function MEAdapter:craft(item, count)
|
||||||
if not self:isCPUAvailable() then
|
if not self:isCPUAvailable() then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local detail = self.findItem(item)
|
local detail = self.findItem(item)
|
||||||
if detail and detail.craft then
|
if detail and detail.craft then
|
||||||
local info = detail.craft(count or 1)
|
local info = detail.craft(count or 1)
|
||||||
if info.status() == 'unknown' then
|
if info.status() == 'unknown' then
|
||||||
self.jobList[info.getId()] = {
|
self.jobList[info.getId()] = {
|
||||||
name = item.name,
|
name = item.name,
|
||||||
damage = item.damage,
|
damage = item.damage,
|
||||||
nbtHash = item.nbtHash,
|
nbtHash = item.nbtHash,
|
||||||
info = info,
|
info = info,
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function MEAdapter:isCrafting(item)
|
function MEAdapter:isCrafting(item)
|
||||||
self:clearFinished()
|
self:clearFinished()
|
||||||
_G._p = self.jobList
|
_G._p = self.jobList
|
||||||
for _,job in pairs(self.jobList) do
|
for _,job in pairs(self.jobList) do
|
||||||
if job.name == item.name and
|
if job.name == item.name and
|
||||||
job.damage == item.damage and
|
job.damage == item.damage and
|
||||||
job.nbtHash == item.nbtHash then
|
job.nbtHash == item.nbtHash then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return MEAdapter
|
return MEAdapter
|
||||||
|
|||||||
@@ -5,91 +5,91 @@ local Message = { }
|
|||||||
local messageHandlers = {}
|
local messageHandlers = {}
|
||||||
|
|
||||||
function Message.enable()
|
function Message.enable()
|
||||||
if not device.wireless_modem.isOpen(os.getComputerID()) then
|
if not device.wireless_modem.isOpen(os.getComputerID()) then
|
||||||
device.wireless_modem.open(os.getComputerID())
|
device.wireless_modem.open(os.getComputerID())
|
||||||
end
|
end
|
||||||
if not device.wireless_modem.isOpen(60000) then
|
if not device.wireless_modem.isOpen(60000) then
|
||||||
device.wireless_modem.open(60000)
|
device.wireless_modem.open(60000)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if device and device.wireless_modem then
|
if device and device.wireless_modem then
|
||||||
Message.enable()
|
Message.enable()
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.on('device_attach', function(event, deviceName)
|
Event.on('device_attach', function(event, deviceName)
|
||||||
if deviceName == 'wireless_modem' then
|
if deviceName == 'wireless_modem' then
|
||||||
Message.enable()
|
Message.enable()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
function Message.addHandler(type, f)
|
function Message.addHandler(type, f)
|
||||||
table.insert(messageHandlers, {
|
table.insert(messageHandlers, {
|
||||||
type = type,
|
type = type,
|
||||||
f = f,
|
f = f,
|
||||||
enabled = true
|
enabled = true
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
function Message.removeHandler(h)
|
function Message.removeHandler(h)
|
||||||
for k,v in pairs(messageHandlers) do
|
for k,v in pairs(messageHandlers) do
|
||||||
if v == h then
|
if v == h then
|
||||||
messageHandlers[k] = nil
|
messageHandlers[k] = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.on('modem_message',
|
Event.on('modem_message',
|
||||||
function(event, side, sendChannel, replyChannel, msg, distance)
|
function(event, side, sendChannel, replyChannel, msg, distance)
|
||||||
if msg and msg.type then -- filter out messages from other systems
|
if msg and msg.type then -- filter out messages from other systems
|
||||||
local id = replyChannel
|
local id = replyChannel
|
||||||
for k,h in pairs(messageHandlers) do
|
for k,h in pairs(messageHandlers) do
|
||||||
if h.type == msg.type then
|
if h.type == msg.type then
|
||||||
-- should provide msg.contents instead of message - type is already known
|
-- should provide msg.contents instead of message - type is already known
|
||||||
h.f(h, id, msg, distance)
|
h.f(h, id, msg, distance)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|
||||||
function Message.send(id, msgType, contents)
|
function Message.send(id, msgType, contents)
|
||||||
if not device.wireless_modem then
|
if not device.wireless_modem then
|
||||||
error('No modem attached', 2)
|
error('No modem attached', 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
if id then
|
if id then
|
||||||
device.wireless_modem.transmit(id, os.getComputerID(), {
|
device.wireless_modem.transmit(id, os.getComputerID(), {
|
||||||
type = msgType, contents = contents
|
type = msgType, contents = contents
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
device.wireless_modem.transmit(60000, os.getComputerID(), {
|
device.wireless_modem.transmit(60000, os.getComputerID(), {
|
||||||
type = msgType, contents = contents
|
type = msgType, contents = contents
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Message.broadcast(t, contents)
|
function Message.broadcast(t, contents)
|
||||||
if not device.wireless_modem then
|
if not device.wireless_modem then
|
||||||
error('No modem attached', 2)
|
error('No modem attached', 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
Message.send(nil, t, contents)
|
Message.send(nil, t, contents)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Message.waitForMessage(msgType, timeout, fromId)
|
function Message.waitForMessage(msgType, timeout, fromId)
|
||||||
local timerId = os.startTimer(timeout)
|
local timerId = os.startTimer(timeout)
|
||||||
repeat
|
repeat
|
||||||
local e, side, _id, id, msg, distance = os.pullEvent()
|
local e, side, _id, id, msg, distance = os.pullEvent()
|
||||||
if e == 'modem_message' then
|
if e == 'modem_message' then
|
||||||
if msg and msg.type and msg.type == msgType then
|
if msg and msg.type and msg.type == msgType then
|
||||||
if not fromId or id == fromId then
|
if not fromId or id == fromId then
|
||||||
return e, id, msg, distance
|
return e, id, msg, distance
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
until e == 'timer' and side == timerId
|
until e == 'timer' and side == timerId
|
||||||
end
|
end
|
||||||
|
|
||||||
return Message
|
return Message
|
||||||
@@ -10,50 +10,50 @@ local USER_DIR = '/usr/etc/names'
|
|||||||
local nameDB = TableDB()
|
local nameDB = TableDB()
|
||||||
|
|
||||||
function nameDB:loadDirectory(directory)
|
function nameDB:loadDirectory(directory)
|
||||||
if fs.exists(directory) then
|
if fs.exists(directory) then
|
||||||
local files = fs.list(directory)
|
local files = fs.list(directory)
|
||||||
table.sort(files)
|
table.sort(files)
|
||||||
|
|
||||||
for _,file in ipairs(files) do
|
for _,file in ipairs(files) do
|
||||||
local mod = file:match('(%S+).json')
|
local mod = file:match('(%S+).json')
|
||||||
if mod then
|
if mod then
|
||||||
local blocks = JSON.decodeFromFile(fs.combine(directory, file))
|
local blocks = JSON.decodeFromFile(fs.combine(directory, file))
|
||||||
|
|
||||||
if not blocks then
|
if not blocks then
|
||||||
error('Unable to read ' .. fs.combine(directory, file))
|
error('Unable to read ' .. fs.combine(directory, file))
|
||||||
end
|
end
|
||||||
|
|
||||||
for strId, block in pairs(blocks) do
|
for strId, block in pairs(blocks) do
|
||||||
strId = string.format('%s:%s', mod, strId)
|
strId = string.format('%s:%s', mod, strId)
|
||||||
if type(block.name) == 'string' then
|
if type(block.name) == 'string' then
|
||||||
self.data[strId .. ':0'] = block.name
|
self.data[strId .. ':0'] = block.name
|
||||||
else
|
else
|
||||||
for nid,name in pairs(block.name) do
|
for nid,name in pairs(block.name) do
|
||||||
self.data[strId .. ':' .. (nid-1)] = name
|
self.data[strId .. ':' .. (nid-1)] = name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif file:match('(%S+).db') then
|
elseif file:match('(%S+).db') then
|
||||||
local names = Util.readTable(fs.combine(directory, file))
|
local names = Util.readTable(fs.combine(directory, file))
|
||||||
if not names then
|
if not names then
|
||||||
error('Unable to read ' .. fs.combine(directory, file))
|
error('Unable to read ' .. fs.combine(directory, file))
|
||||||
end
|
end
|
||||||
for key,name in pairs(names) do
|
for key,name in pairs(names) do
|
||||||
self.data[key] = name
|
self.data[key] = name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function nameDB:load()
|
function nameDB:load()
|
||||||
self:loadDirectory(CORE_DIR)
|
self:loadDirectory(CORE_DIR)
|
||||||
self:loadDirectory(USER_DIR)
|
self:loadDirectory(USER_DIR)
|
||||||
end
|
end
|
||||||
|
|
||||||
function nameDB:getName(strId)
|
function nameDB:getName(strId)
|
||||||
return self.data[strId] or strId
|
return self.data[strId] or strId
|
||||||
end
|
end
|
||||||
|
|
||||||
nameDB:load()
|
nameDB:load()
|
||||||
|
|||||||
32
core/apis/proxy.lua
Normal file
32
core/apis/proxy.lua
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
local Socket = require('socket')
|
||||||
|
|
||||||
|
local Proxy = { }
|
||||||
|
|
||||||
|
function Proxy.create(remoteId, uri)
|
||||||
|
local socket, msg = Socket.connect(remoteId, 188)
|
||||||
|
|
||||||
|
if not socket then
|
||||||
|
error(msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
socket.co = coroutine.running()
|
||||||
|
|
||||||
|
socket:write(uri)
|
||||||
|
local methods = socket:read(2) or error('Timed out')
|
||||||
|
|
||||||
|
local hijack = { }
|
||||||
|
for _,method in pairs(methods) do
|
||||||
|
hijack[method] = function(...)
|
||||||
|
socket:write({ method, ... })
|
||||||
|
local resp = socket:read()
|
||||||
|
if not resp then
|
||||||
|
error('timed out: ' .. method)
|
||||||
|
end
|
||||||
|
return table.unpack(resp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return hijack, socket
|
||||||
|
end
|
||||||
|
|
||||||
|
return Proxy
|
||||||
@@ -6,134 +6,134 @@ local Util = require('util')
|
|||||||
local RefinedAdapter = class()
|
local RefinedAdapter = class()
|
||||||
|
|
||||||
function RefinedAdapter:init(args)
|
function RefinedAdapter:init(args)
|
||||||
local defaults = {
|
local defaults = {
|
||||||
name = 'refinedStorage',
|
name = 'refinedStorage',
|
||||||
}
|
}
|
||||||
Util.merge(self, defaults)
|
Util.merge(self, defaults)
|
||||||
Util.merge(self, args)
|
Util.merge(self, args)
|
||||||
|
|
||||||
local controller
|
local controller
|
||||||
if not self.side then
|
if not self.side then
|
||||||
controller = Peripheral.getByMethod('getCraftingTasks')
|
controller = Peripheral.getByMethod('getCraftingTasks')
|
||||||
else
|
else
|
||||||
controller = Peripheral.getBySide(self.side)
|
controller = Peripheral.getBySide(self.side)
|
||||||
end
|
end
|
||||||
|
|
||||||
if controller then
|
if controller then
|
||||||
Util.merge(self, controller)
|
Util.merge(self, controller)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:isValid()
|
function RefinedAdapter:isValid()
|
||||||
return not not self.getCraftingTasks
|
return not not self.getCraftingTasks
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:getItemDetails(item)
|
function RefinedAdapter:getItemDetails(item)
|
||||||
local detail = self.findItems(item)
|
local detail = self.findItems(item)
|
||||||
if detail and #detail > 0 then
|
if detail and #detail > 0 then
|
||||||
return detail[1].getMetadata()
|
return detail[1].getMetadata()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:getCachedItemDetails(item)
|
function RefinedAdapter:getCachedItemDetails(item)
|
||||||
local cached = itemDB:get(item)
|
local cached = itemDB:get(item)
|
||||||
if cached then
|
if cached then
|
||||||
return cached
|
return cached
|
||||||
end
|
end
|
||||||
|
|
||||||
local detail = self:getItemDetails(item)
|
local detail = self:getItemDetails(item)
|
||||||
if detail then
|
if detail then
|
||||||
return itemDB:add(detail)
|
return itemDB:add(detail)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:refresh(throttle)
|
function RefinedAdapter:refresh(throttle)
|
||||||
return self:listItems(throttle)
|
return self:listItems(throttle)
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:listItems(throttle)
|
function RefinedAdapter:listItems(throttle)
|
||||||
local items = { }
|
local items = { }
|
||||||
throttle = throttle or Util.throttle()
|
throttle = throttle or Util.throttle()
|
||||||
|
|
||||||
local s, m = pcall(function()
|
local s, m = pcall(function()
|
||||||
for _,v in pairs(self.listAvailableItems()) do
|
for _,v in pairs(self.listAvailableItems()) do
|
||||||
--if v.count > 0 then
|
--if v.count > 0 then
|
||||||
local item = self:getCachedItemDetails(v)
|
local item = self:getCachedItemDetails(v)
|
||||||
if item then
|
if item then
|
||||||
item = Util.shallowCopy(item)
|
item = Util.shallowCopy(item)
|
||||||
item.count = v.count
|
item.count = v.count
|
||||||
table.insert(items, item)
|
table.insert(items, item)
|
||||||
end
|
end
|
||||||
--end
|
--end
|
||||||
throttle()
|
throttle()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G._syslog(m)
|
_G._syslog(m)
|
||||||
end
|
end
|
||||||
|
|
||||||
itemDB:flush()
|
itemDB:flush()
|
||||||
if not Util.empty(items) then
|
if not Util.empty(items) then
|
||||||
return items
|
return items
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:getItemInfo(item)
|
function RefinedAdapter:getItemInfo(item)
|
||||||
return self:getItemDetails(item)
|
return self:getItemDetails(item)
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:isCPUAvailable()
|
function RefinedAdapter:isCPUAvailable()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:craft(item, qty)
|
function RefinedAdapter:craft(item, qty)
|
||||||
local detail = self.findItem(item)
|
local detail = self.findItem(item)
|
||||||
if detail then
|
if detail then
|
||||||
return detail.craft(qty)
|
return detail.craft(qty)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:isCrafting(item)
|
function RefinedAdapter:isCrafting(item)
|
||||||
for _,task in pairs(self.getCraftingTasks()) do
|
for _,task in pairs(self.getCraftingTasks()) do
|
||||||
local output = task.getPattern().outputs[1]
|
local output = task.getPattern().outputs[1]
|
||||||
if output.name == item.name and
|
if output.name == item.name and
|
||||||
output.damage == item.damage and
|
output.damage == item.damage and
|
||||||
output.nbtHash == item.nbtHash then
|
output.nbtHash == item.nbtHash then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:provide(item, qty, slot, direction)
|
function RefinedAdapter:provide(item, qty, slot, direction)
|
||||||
return pcall(function()
|
return pcall(function()
|
||||||
for _,stack in pairs(self.listAvailableItems()) do
|
for _,stack in pairs(self.listAvailableItems()) do
|
||||||
if stack.name == item.name and
|
if stack.name == item.name and
|
||||||
(not item.damage or stack.damage == item.damage) and
|
(not item.damage or stack.damage == item.damage) and
|
||||||
(not item.nbtHash or stack.nbtHash == item.nbtHash) then
|
(not item.nbtHash or stack.nbtHash == item.nbtHash) then
|
||||||
local amount = math.min(qty, stack.count)
|
local amount = math.min(qty, stack.count)
|
||||||
if amount > 0 then
|
if amount > 0 then
|
||||||
local detail = self.findItem(item)
|
local detail = self.findItem(item)
|
||||||
if detail then
|
if detail then
|
||||||
return detail.export(direction or self.direction, amount, slot)
|
return detail.export(direction or self.direction, amount, slot)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
qty = qty - amount
|
qty = qty - amount
|
||||||
if qty <= 0 then
|
if qty <= 0 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:extract(slot, qty, toSlot)
|
function RefinedAdapter:extract(slot, qty, toSlot)
|
||||||
self.pushItems(self.direction, slot, qty, toSlot)
|
self.pushItems(self.direction, slot, qty, toSlot)
|
||||||
end
|
end
|
||||||
|
|
||||||
function RefinedAdapter:insert(slot, qty, toSlot)
|
function RefinedAdapter:insert(slot, qty, toSlot)
|
||||||
self.pullItems(self.direction, slot, qty, toSlot)
|
self.pullItems(self.direction, slot, qty, toSlot)
|
||||||
end
|
end
|
||||||
|
|
||||||
return RefinedAdapter
|
return RefinedAdapter
|
||||||
|
|||||||
@@ -1,66 +1,66 @@
|
|||||||
local class = require('class')
|
local class = require('class')
|
||||||
local Event = require('event')
|
local Event = require('event')
|
||||||
local Map = require('map')
|
local Map = require('map')
|
||||||
local Proxy = require('proxy')
|
local Proxy = require('core.proxy')
|
||||||
|
|
||||||
local Swarm = class()
|
local Swarm = class()
|
||||||
function Swarm:init(args)
|
function Swarm:init(args)
|
||||||
self.pool = { }
|
self.pool = { }
|
||||||
Map.merge(self, args)
|
Map.merge(self, args)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Swarm:add(id, args)
|
function Swarm:add(id, args)
|
||||||
local member = Map.shallowCopy(args or { })
|
local member = Map.shallowCopy(args or { })
|
||||||
member.id = id
|
member.id = id
|
||||||
self.pool[id] = member
|
self.pool[id] = member
|
||||||
end
|
end
|
||||||
|
|
||||||
function Swarm:remove(id, s, m)
|
function Swarm:remove(id, s, m)
|
||||||
local member = self.pool[id]
|
local member = self.pool[id]
|
||||||
if member then
|
if member then
|
||||||
self.pool[id] = nil
|
self.pool[id] = nil
|
||||||
self:onRemove(member, s, m)
|
self:onRemove(member, s, m)
|
||||||
if member.socket then
|
if member.socket then
|
||||||
member.socket:close()
|
member.socket:close()
|
||||||
member.socket = nil
|
member.socket = nil
|
||||||
end
|
end
|
||||||
if member.handler then
|
if member.handler then
|
||||||
member.handler:terminate()
|
member.handler:terminate()
|
||||||
member.handler = nil
|
member.handler = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Swarm:run(fn)
|
function Swarm:run(fn)
|
||||||
for id, member in pairs(self.pool) do
|
for id, member in pairs(self.pool) do
|
||||||
if not member.socket then
|
if not member.socket then
|
||||||
member.handler = Event.addRoutine(function()
|
member.handler = Event.addRoutine(function()
|
||||||
local s, m = pcall(function()
|
local s, m = pcall(function()
|
||||||
member.turtle, member.socket = Proxy.create(id, 'turtle')
|
member.turtle, member.socket = Proxy.create(id, 'turtle')
|
||||||
|
|
||||||
fn(member)
|
fn(member)
|
||||||
end)
|
end)
|
||||||
self:remove(id, s, m)
|
self:remove(id, s, m)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Swarm:stop()
|
function Swarm:stop()
|
||||||
for _, member in pairs(self.pool) do
|
for _, member in pairs(self.pool) do
|
||||||
if member.socket then
|
if member.socket then
|
||||||
member.socket:close()
|
member.socket:close()
|
||||||
member.socket = nil
|
member.socket = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Override
|
-- Override
|
||||||
function Swarm:onRemove(member, success, msg)
|
function Swarm:onRemove(member, success, msg)
|
||||||
print('removed from pool: ' .. member.id)
|
print('removed from pool: ' .. member.id)
|
||||||
if not success then
|
if not success then
|
||||||
_G.printError(msg)
|
_G.printError(msg)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return Swarm
|
return Swarm
|
||||||
|
|||||||
@@ -3,47 +3,47 @@ local Util = require('util')
|
|||||||
|
|
||||||
local TableDB = class()
|
local TableDB = class()
|
||||||
function TableDB:init(args)
|
function TableDB:init(args)
|
||||||
local defaults = {
|
local defaults = {
|
||||||
fileName = '',
|
fileName = '',
|
||||||
dirty = false,
|
dirty = false,
|
||||||
data = { },
|
data = { },
|
||||||
}
|
}
|
||||||
Util.merge(defaults, args)
|
Util.merge(defaults, args)
|
||||||
Util.merge(self, defaults)
|
Util.merge(self, defaults)
|
||||||
end
|
end
|
||||||
|
|
||||||
function TableDB:load()
|
function TableDB:load()
|
||||||
local t = Util.readTable(self.fileName)
|
local t = Util.readTable(self.fileName)
|
||||||
if t then
|
if t then
|
||||||
self.data = t.data or t
|
self.data = t.data or t
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function TableDB:add(key, entry)
|
function TableDB:add(key, entry)
|
||||||
if type(key) == 'table' then
|
if type(key) == 'table' then
|
||||||
key = table.concat(key, ':')
|
key = table.concat(key, ':')
|
||||||
end
|
end
|
||||||
self.data[key] = entry
|
self.data[key] = entry
|
||||||
self.dirty = true
|
self.dirty = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function TableDB:get(key)
|
function TableDB:get(key)
|
||||||
if type(key) == 'table' then
|
if type(key) == 'table' then
|
||||||
key = table.concat(key, ':')
|
key = table.concat(key, ':')
|
||||||
end
|
end
|
||||||
return self.data[key]
|
return self.data[key]
|
||||||
end
|
end
|
||||||
|
|
||||||
function TableDB:remove(key)
|
function TableDB:remove(key)
|
||||||
self.data[key] = nil
|
self.data[key] = nil
|
||||||
self.dirty = true
|
self.dirty = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function TableDB:flush()
|
function TableDB:flush()
|
||||||
if self.dirty then
|
if self.dirty then
|
||||||
Util.writeTable(self.fileName, self.data)
|
Util.writeTable(self.fileName, self.data)
|
||||||
self.dirty = false
|
self.dirty = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return TableDB
|
return TableDB
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,60 +6,60 @@ local turtle = _G.turtle
|
|||||||
local CRAFTING_TABLE = 'minecraft:crafting_table'
|
local CRAFTING_TABLE = 'minecraft:crafting_table'
|
||||||
|
|
||||||
local function clearGrid(inventory)
|
local function clearGrid(inventory)
|
||||||
for i = 1, 16 do
|
for i = 1, 16 do
|
||||||
local count = turtle.getItemCount(i)
|
local count = turtle.getItemCount(i)
|
||||||
if count > 0 then
|
if count > 0 then
|
||||||
inventory:insert(i, count)
|
inventory:insert(i, count)
|
||||||
if turtle.getItemCount(i) ~= 0 then
|
if turtle.getItemCount(i) ~= 0 then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function turtle.craftItem(item, count, inventoryInfo)
|
function turtle.craftItem(item, count, inventoryInfo)
|
||||||
local success, msg
|
local success, msg
|
||||||
|
|
||||||
local inventory = Adapter.wrap(inventoryInfo)
|
local inventory = Adapter.wrap(inventoryInfo)
|
||||||
if not inventory then
|
if not inventory then
|
||||||
return false, 'Invalid inventory'
|
return false, 'Invalid inventory'
|
||||||
end
|
end
|
||||||
|
|
||||||
local equipped, side
|
local equipped, side
|
||||||
if not turtle.isEquipped('workbench') then
|
if not turtle.isEquipped('workbench') then
|
||||||
local modemSide = turtle.isEquipped('modem') or 'right'
|
local modemSide = turtle.isEquipped('modem') or 'right'
|
||||||
local osides = { left = 'right', right = 'left' }
|
local osides = { left = 'right', right = 'left' }
|
||||||
side = osides[modemSide]
|
side = osides[modemSide]
|
||||||
if not turtle.select(CRAFTING_TABLE) then
|
if not turtle.select(CRAFTING_TABLE) then
|
||||||
clearGrid(inventory)
|
clearGrid(inventory)
|
||||||
if not turtle.selectOpenSlot() then
|
if not turtle.selectOpenSlot() then
|
||||||
return false, 'Inventory is full'
|
return false, 'Inventory is full'
|
||||||
end
|
end
|
||||||
if not inventory:provide({ name = CRAFTING_TABLE, damage = 0 }, 1) then
|
if not inventory:provide({ name = CRAFTING_TABLE, damage = 0 }, 1) then
|
||||||
return false, 'Missing crafting table'
|
return false, 'Missing crafting table'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local slot = turtle.select(CRAFTING_TABLE)
|
local slot = turtle.select(CRAFTING_TABLE)
|
||||||
turtle.equip(side, CRAFTING_TABLE)
|
turtle.equip(side, CRAFTING_TABLE)
|
||||||
equipped = turtle.getItemDetail(slot.index)
|
equipped = turtle.getItemDetail(slot.index)
|
||||||
end
|
end
|
||||||
|
|
||||||
clearGrid(inventory)
|
clearGrid(inventory)
|
||||||
success, msg = Craft.craftRecipe(item, count or 1, inventory)
|
success, msg = Craft.craftRecipe(item, count or 1, inventory)
|
||||||
|
|
||||||
if equipped then
|
if equipped then
|
||||||
turtle.selectOpenSlot()
|
turtle.selectOpenSlot()
|
||||||
inventory:provide({ name = equipped.name, damage = equipped.damage }, 1)
|
inventory:provide({ name = equipped.name, damage = equipped.damage }, 1)
|
||||||
turtle.equip(side, equipped.name .. ':' .. equipped.damage)
|
turtle.equip(side, equipped.name .. ':' .. equipped.damage)
|
||||||
end
|
end
|
||||||
|
|
||||||
return success, msg
|
return success, msg
|
||||||
end
|
end
|
||||||
|
|
||||||
function turtle.canCraft(item, count, items)
|
function turtle.canCraft(item, count, items)
|
||||||
return Craft.canCraft(item, count, items)
|
return Craft.canCraft(item, count, items)
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -9,167 +9,167 @@ local box = { }
|
|||||||
local oldCallback
|
local oldCallback
|
||||||
|
|
||||||
local function toKey(pt)
|
local function toKey(pt)
|
||||||
return table.concat({ pt.x, pt.y, pt.z }, ':')
|
return table.concat({ pt.x, pt.y, pt.z }, ':')
|
||||||
end
|
end
|
||||||
|
|
||||||
local function addNode(node)
|
local function addNode(node)
|
||||||
for i = 0, 5 do
|
for i = 0, 5 do
|
||||||
local hi = turtle.getHeadingInfo(i)
|
local hi = turtle.getHeadingInfo(i)
|
||||||
local testNode = { x = node.x + hi.xd, y = node.y + hi.yd, z = node.z + hi.zd }
|
local testNode = { x = node.x + hi.xd, y = node.y + hi.yd, z = node.z + hi.zd }
|
||||||
|
|
||||||
if Point.inBox(testNode, box) then
|
if Point.inBox(testNode, box) then
|
||||||
local key = toKey(testNode)
|
local key = toKey(testNode)
|
||||||
if not checkedNodes[key] then
|
if not checkedNodes[key] then
|
||||||
nodes[key] = testNode
|
nodes[key] = testNode
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function dig(action)
|
local function dig(action)
|
||||||
local directions = {
|
local directions = {
|
||||||
top = 'up',
|
top = 'up',
|
||||||
bottom = 'down',
|
bottom = 'down',
|
||||||
}
|
}
|
||||||
|
|
||||||
-- convert to up, down, north, south, east, west
|
-- convert to up, down, north, south, east, west
|
||||||
local direction = directions[action.side] or
|
local direction = directions[action.side] or
|
||||||
turtle.getHeadingInfo(turtle.point.heading).direction
|
turtle.getHeadingInfo(turtle.point.heading).direction
|
||||||
|
|
||||||
local hi = turtle.getHeadingInfo(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 }
|
local node = { x = turtle.point.x + hi.xd, y = turtle.point.y + hi.yd, z = turtle.point.z + hi.zd }
|
||||||
|
|
||||||
if Point.inBox(node, box) then
|
if Point.inBox(node, box) then
|
||||||
|
|
||||||
local key = toKey(node)
|
local key = toKey(node)
|
||||||
checkedNodes[key] = true
|
checkedNodes[key] = true
|
||||||
nodes[key] = nil
|
nodes[key] = nil
|
||||||
|
|
||||||
if action.dig() then
|
if action.dig() then
|
||||||
addNode(node)
|
addNode(node)
|
||||||
repeat until not action.dig() -- sand, etc
|
repeat until not action.dig() -- sand, etc
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function move(action)
|
local function move(action)
|
||||||
if action == 'turn' then
|
if action == 'turn' then
|
||||||
dig(turtle.getAction('forward'))
|
dig(turtle.getAction('forward'))
|
||||||
elseif action == 'up' then
|
elseif action == 'up' then
|
||||||
dig(turtle.getAction('up'))
|
dig(turtle.getAction('up'))
|
||||||
dig(turtle.getAction('forward'))
|
dig(turtle.getAction('forward'))
|
||||||
elseif action == 'down' then
|
elseif action == 'down' then
|
||||||
dig(turtle.getAction('down'))
|
dig(turtle.getAction('down'))
|
||||||
dig(turtle.getAction('forward'))
|
dig(turtle.getAction('forward'))
|
||||||
elseif action == 'back' then
|
elseif action == 'back' then
|
||||||
dig(turtle.getAction('up'))
|
dig(turtle.getAction('up'))
|
||||||
dig(turtle.getAction('down'))
|
dig(turtle.getAction('down'))
|
||||||
end
|
end
|
||||||
|
|
||||||
if oldCallback then
|
if oldCallback then
|
||||||
oldCallback(action)
|
oldCallback(action)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- find the closest block
|
-- find the closest block
|
||||||
-- * favor same plane
|
-- * favor same plane
|
||||||
-- * going backwards only if the dest is above or below
|
-- * going backwards only if the dest is above or below
|
||||||
local function closestPoint(reference, pts)
|
local function closestPoint(reference, pts)
|
||||||
local lpt, lm -- lowest
|
local lpt, lm -- lowest
|
||||||
for _,pt in pairs(pts) do
|
for _,pt in pairs(pts) do
|
||||||
local m = Point.turtleDistance(reference, pt)
|
local m = Point.turtleDistance(reference, pt)
|
||||||
local h = Point.calculateHeading(reference, pt)
|
local h = Point.calculateHeading(reference, pt)
|
||||||
local t = Point.calculateTurns(reference.heading, h)
|
local t = Point.calculateTurns(reference.heading, h)
|
||||||
if pt.y ~= reference.y then -- try and stay on same plane
|
if pt.y ~= reference.y then -- try and stay on same plane
|
||||||
m = m + .01
|
m = m + .01
|
||||||
end
|
end
|
||||||
if t ~= 2 or pt.y == reference.y then
|
if t ~= 2 or pt.y == reference.y then
|
||||||
m = m + t
|
m = m + t
|
||||||
if t > 0 then
|
if t > 0 then
|
||||||
m = m + .01
|
m = m + .01
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not lm or m < lm then
|
if not lm or m < lm then
|
||||||
lpt = pt
|
lpt = pt
|
||||||
lm = m
|
lm = m
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return lpt
|
return lpt
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getAdjacentPoint(pt)
|
local function getAdjacentPoint(pt)
|
||||||
local t = { }
|
local t = { }
|
||||||
table.insert(t, pt)
|
table.insert(t, pt)
|
||||||
for i = 0, 5 do
|
for i = 0, 5 do
|
||||||
local hi = turtle.getHeadingInfo(i)
|
local hi = turtle.getHeadingInfo(i)
|
||||||
local heading
|
local heading
|
||||||
if i < 4 then
|
if i < 4 then
|
||||||
heading = (hi.heading + 2) % 4
|
heading = (hi.heading + 2) % 4
|
||||||
end
|
end
|
||||||
table.insert(t, { x = pt.x + hi.xd, z = pt.z + hi.zd, y = pt.y + hi.yd, heading = heading })
|
table.insert(t, { x = pt.x + hi.xd, z = pt.z + hi.zd, y = pt.y + hi.yd, heading = heading })
|
||||||
end
|
end
|
||||||
|
|
||||||
return closestPoint(turtle.getPoint(), t)
|
return closestPoint(turtle.getPoint(), t)
|
||||||
end
|
end
|
||||||
|
|
||||||
function turtle.level(startPt, endPt, firstPt, verbose)
|
function turtle.level(startPt, endPt, firstPt, verbose)
|
||||||
checkedNodes = { }
|
checkedNodes = { }
|
||||||
nodes = { }
|
nodes = { }
|
||||||
box = { }
|
box = { }
|
||||||
|
|
||||||
box.x = math.min(startPt.x, endPt.x)
|
box.x = math.min(startPt.x, endPt.x)
|
||||||
box.y = math.min(startPt.y, endPt.y)
|
box.y = math.min(startPt.y, endPt.y)
|
||||||
box.z = math.min(startPt.z, endPt.z)
|
box.z = math.min(startPt.z, endPt.z)
|
||||||
box.ex = math.max(startPt.x, endPt.x)
|
box.ex = math.max(startPt.x, endPt.x)
|
||||||
box.ey = math.max(startPt.y, endPt.y)
|
box.ey = math.max(startPt.y, endPt.y)
|
||||||
box.ez = math.max(startPt.z, endPt.z)
|
box.ez = math.max(startPt.z, endPt.z)
|
||||||
|
|
||||||
if not Point.inBox(firstPt, box) then
|
if not Point.inBox(firstPt, box) then
|
||||||
error('Starting point is not in leveling area')
|
error('Starting point is not in leveling area')
|
||||||
end
|
end
|
||||||
|
|
||||||
if not turtle.pathfind(firstPt) then
|
if not turtle.pathfind(firstPt) then
|
||||||
error('failed to reach starting point')
|
error('failed to reach starting point')
|
||||||
end
|
end
|
||||||
|
|
||||||
turtle.set({
|
turtle.set({
|
||||||
digPolicy = dig,
|
digPolicy = dig,
|
||||||
attackPolicy = 'attack',
|
attackPolicy = 'attack',
|
||||||
movePolicy = 'moveAssured',
|
movePolicy = 'moveAssured',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
oldCallback = turtle.getMoveCallback()
|
oldCallback = turtle.getMoveCallback()
|
||||||
turtle.setMoveCallback(move)
|
turtle.setMoveCallback(move)
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
local key = toKey(turtle.point)
|
local key = toKey(turtle.point)
|
||||||
|
|
||||||
checkedNodes[key] = true
|
checkedNodes[key] = true
|
||||||
nodes[key] = nil
|
nodes[key] = nil
|
||||||
|
|
||||||
dig(turtle.getAction('down'))
|
dig(turtle.getAction('down'))
|
||||||
dig(turtle.getAction('up'))
|
dig(turtle.getAction('up'))
|
||||||
dig(turtle.getAction('forward'))
|
dig(turtle.getAction('forward'))
|
||||||
|
|
||||||
if verbose then
|
if verbose then
|
||||||
print(string.format('%d nodes remaining', Util.size(nodes)))
|
print(string.format('%d nodes remaining', Util.size(nodes)))
|
||||||
end
|
end
|
||||||
|
|
||||||
if not next(nodes) then
|
if not next(nodes) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
local node = closestPoint(turtle.point, nodes)
|
local node = closestPoint(turtle.point, nodes)
|
||||||
node = getAdjacentPoint(node)
|
node = getAdjacentPoint(node)
|
||||||
if not turtle.go(node) then
|
if not turtle.go(node) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
until turtle.isAborted()
|
until turtle.isAborted()
|
||||||
|
|
||||||
turtle.resetState()
|
turtle.resetState()
|
||||||
turtle.setMoveCallback(oldCallback)
|
turtle.setMoveCallback(oldCallback)
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ local Runners = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Equipper.equipLeft('minecraft:diamond_sword')
|
Equipper.equipLeft('minecraft:diamond_sword')
|
||||||
local scanner = Equipper.equipRight('plethora:module:2', 'plethora:scanner')
|
local scanner = Equipper.equipRight('plethora:scanner')
|
||||||
|
|
||||||
local facing = scanner.getBlockMeta(0, 0, 0).state.facing
|
local facing = scanner.getBlockMeta(0, 0, 0).state.facing
|
||||||
turtle.point.heading = Point.facings[facing].heading
|
turtle.point.heading = Point.facings[facing].heading
|
||||||
|
|
||||||
local sensor = Equipper.equipRight('plethora:module:3', 'plethora:sensor')
|
local sensor = Equipper.equipRight('plethora:sensor')
|
||||||
|
|
||||||
turtle.setMovementStrategy('goto')
|
turtle.setMovementStrategy('goto')
|
||||||
turtle.set({ attackPolicy = 'attack' })
|
turtle.set({ attackPolicy = 'attack' })
|
||||||
@@ -32,9 +32,9 @@ local function findChests()
|
|||||||
if chest then
|
if chest then
|
||||||
return { chest }
|
return { chest }
|
||||||
end
|
end
|
||||||
Equipper.equipRight('plethora:module:2', 'plethora:scanner')
|
Equipper.equipRight('plethora:scanner')
|
||||||
local chests = scanner.scan()
|
local chests = scanner.scan()
|
||||||
Equipper.equipRight('plethora:module:3', 'plethora:sensor')
|
Equipper.equipRight('plethora:sensor')
|
||||||
|
|
||||||
Util.filterInplace(chests, function(b)
|
Util.filterInplace(chests, function(b)
|
||||||
if b.name == 'minecraft:chest' or
|
if b.name == 'minecraft:chest' or
|
||||||
|
|||||||
373
farms/farmer.lua
373
farms/farmer.lua
@@ -12,238 +12,241 @@ local STARTUP_FILE = 'usr/autorun/farmer.lua'
|
|||||||
local MIN_FUEL = 2500
|
local MIN_FUEL = 2500
|
||||||
|
|
||||||
local FUEL = Util.transpose {
|
local FUEL = Util.transpose {
|
||||||
'minecraft:coal:0',
|
'minecraft:coal:0',
|
||||||
'minecraft:coal:1',
|
'minecraft:coal:1',
|
||||||
'minecraft:blaze_rod:0',
|
'minecraft:blaze_rod:0',
|
||||||
}
|
}
|
||||||
|
|
||||||
local scanner = Equipper.equipRight('plethora:module:2', 'plethora:scanner')
|
local scanner = Equipper.equipRight('plethora:scanner')
|
||||||
|
|
||||||
local crops = Util.readTable(CONFIG_FILE) or {
|
local crops = Util.readTable(CONFIG_FILE) or {
|
||||||
['minecraft:wheat'] =
|
['minecraft:wheat'] =
|
||||||
{ seed = 'minecraft:wheat_seeds', mature = 7, action = 'plant' },
|
{ seed = 'minecraft:wheat_seeds', mature = 7, action = 'plant' },
|
||||||
['minecraft:carrots'] =
|
['minecraft:carrots'] =
|
||||||
{ seed = 'minecraft:carrot', mature = 7, action = 'plant' },
|
{ seed = 'minecraft:carrot', mature = 7, action = 'plant' },
|
||||||
['minecraft:potatoes'] =
|
['minecraft:potatoes'] =
|
||||||
{ seed = 'minecraft:potato', mature = 7, action = 'plant' },
|
{ seed = 'minecraft:potato', mature = 7, action = 'plant' },
|
||||||
['minecraft:beetroots'] =
|
['minecraft:beetroots'] =
|
||||||
{ seed = 'minecraft:beetroot_seeds', mature = 3, action = 'plant' },
|
{ seed = 'minecraft:beetroot_seeds', mature = 3, action = 'plant' },
|
||||||
['minecraft:nether_wart'] =
|
['minecraft:nether_wart'] =
|
||||||
{ seed = 'minecraft:nether_wart', mature = 3, action = 'plant' },
|
{ seed = 'minecraft:nether_wart', mature = 3, action = 'plant' },
|
||||||
['minecraft:cocoa'] =
|
['minecraft:cocoa'] =
|
||||||
{ seed = 'minecraft:dye:3', mature = 8, action = 'pick' },
|
{ seed = 'minecraft:dye:3', mature = 8, action = 'pick' },
|
||||||
['minecraft:reeds'] = { action = 'bash' },
|
['minecraft:reeds'] = { action = 'bash' },
|
||||||
['minecraft:chorus_flower'] = { action = 'bash' },
|
['minecraft:chorus_flower'] = { action = 'bash' },
|
||||||
['minecraft:chorus_plant'] =
|
['minecraft:chorus_plant'] =
|
||||||
{ seed = 'minecraft:chorus_flower', mature = 0, action = 'bash-smash', },
|
{ seed = 'minecraft:chorus_flower', mature = 0, action = 'bash-smash', },
|
||||||
['minecraft:melon_block'] = { action = 'smash' },
|
['minecraft:melon_block'] = { action = 'smash' },
|
||||||
['minecraft:pumpkin'] = { action = 'smash' },
|
['minecraft:pumpkin'] = { action = 'smash' },
|
||||||
['minecraft:chest'] = { action = 'drop' },
|
['minecraft:chest'] = { action = 'drop' },
|
||||||
['minecraft:cactus'] = { action = 'smash' },
|
['minecraft:cactus'] = { action = 'smash' },
|
||||||
}
|
}
|
||||||
|
|
||||||
if not fs.exists(CONFIG_FILE) then
|
if not fs.exists(CONFIG_FILE) then
|
||||||
Util.writeTable(CONFIG_FILE, crops)
|
Util.writeTable(CONFIG_FILE, crops)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not fs.exists(STARTUP_FILE) then
|
if not fs.exists(STARTUP_FILE) then
|
||||||
Util.writeFile(STARTUP_FILE,
|
Util.writeFile(STARTUP_FILE,
|
||||||
[[os.sleep(1)
|
[[os.sleep(1)
|
||||||
shell.openForegroundTab('farmer.lua')]])
|
shell.openForegroundTab('farmer.lua')]])
|
||||||
print('Autorun program created: ' .. STARTUP_FILE)
|
print('Autorun program created: ' .. STARTUP_FILE)
|
||||||
end
|
end
|
||||||
|
|
||||||
local retain = Util.transpose {
|
local retain = Util.transpose {
|
||||||
"minecraft:diamond_pickaxe",
|
"minecraft:diamond_pickaxe",
|
||||||
"plethora:module:2",
|
"plethora:module:2",
|
||||||
"plethora:module:3",
|
"plethora:module:3",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v in pairs(crops) do
|
for _, v in pairs(crops) do
|
||||||
if v.seed then
|
if v.seed then
|
||||||
retain[v.seed] = true
|
retain[v.seed] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function scan()
|
local function scan()
|
||||||
local blocks = scanner.scan()
|
local blocks = scanner.scan()
|
||||||
local summed = turtle.getSummedInventory()
|
local summed = turtle.getSummedInventory()
|
||||||
local doDropOff
|
local doDropOff
|
||||||
|
|
||||||
for _,v in pairs(summed) do
|
for _,v in pairs(summed) do
|
||||||
if v.count > 32 then
|
if v.count > 32 then
|
||||||
doDropOff = true
|
doDropOff = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Util.filterInplace(blocks, function(b)
|
Util.filterInplace(blocks, function(b)
|
||||||
b.action = crops[b.name] and crops[b.name].action
|
b.action = crops[b.name] and crops[b.name].action
|
||||||
|
|
||||||
if b.action == 'bash' then
|
if b.action == 'bash' then
|
||||||
return b.y == 0
|
return b.y == 0
|
||||||
end
|
end
|
||||||
if b.action == 'drop' then
|
if b.action == 'drop' then
|
||||||
return doDropOff and (b.y == -1 or b.y == 1)
|
return doDropOff and (b.y == -1 or b.y == 1)
|
||||||
end
|
end
|
||||||
if b.action == 'bash-smash' then
|
if b.action == 'bash-smash' then
|
||||||
if b.y == -1 then
|
if b.y == -1 then
|
||||||
b.action = 'smash'
|
b.action = 'smash'
|
||||||
end
|
end
|
||||||
if b.y == 0 then
|
if b.y == 0 then
|
||||||
b.action = 'bash'
|
b.action = 'bash'
|
||||||
end
|
end
|
||||||
return b.action ~= 'bash-smash'
|
return b.action ~= 'bash-smash'
|
||||||
end
|
end
|
||||||
|
|
||||||
if b.action == 'smash' then
|
if b.action == 'smash' then
|
||||||
return b.y == -1
|
return b.y == -1
|
||||||
end
|
end
|
||||||
if b.action == 'pick' then
|
if b.action == 'pick' then
|
||||||
return b.y == 0 and b.state.age == 2
|
return b.y == 0 and b.state.age == 2
|
||||||
end
|
end
|
||||||
if b.action == 'bump' then
|
if b.action == 'bump' then
|
||||||
return b.y == 0
|
return b.y == 0
|
||||||
end
|
end
|
||||||
return b.action == 'plant' and
|
if b.action == 'plant' and b.y == -1 then
|
||||||
b.metadata == crops[b.name].mature and
|
if not b.metadata then -- minecraft 1.10
|
||||||
b.y == -1
|
b = scanner.getBlockMeta(b.x, b.y, b.z)
|
||||||
end)
|
end
|
||||||
|
return b.metadata == crops[b.name].mature
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
local harvestCount = 0
|
local harvestCount = 0
|
||||||
for _,b in pairs(blocks) do
|
for _,b in pairs(blocks) do
|
||||||
b.x = b.x + turtle.point.x
|
b.x = b.x + turtle.point.x
|
||||||
b.y = b.y + turtle.point.y
|
b.y = b.y + turtle.point.y
|
||||||
b.z = b.z + turtle.point.z
|
b.z = b.z + turtle.point.z
|
||||||
if b.action ~= 'drop' then
|
if b.action ~= 'drop' then
|
||||||
harvestCount = harvestCount + 1
|
harvestCount = harvestCount + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return blocks, harvestCount
|
return blocks, harvestCount
|
||||||
end
|
end
|
||||||
|
|
||||||
local function harvest(blocks)
|
local function harvest(blocks)
|
||||||
Equipper.equipRight('minecraft:diamond_pickaxe')
|
Equipper.equipRight('minecraft:diamond_pickaxe')
|
||||||
|
|
||||||
local dropped
|
local dropped
|
||||||
|
|
||||||
Point.eachClosest(turtle.point, blocks, function(b)
|
Point.eachClosest(turtle.point, blocks, function(b)
|
||||||
turtle.select(1)
|
turtle.select(1)
|
||||||
|
|
||||||
if b.action == 'bash' then
|
if b.action == 'bash' then
|
||||||
turtle.digForwardAt(b)
|
turtle.digForwardAt(b)
|
||||||
|
|
||||||
elseif b.action == 'drop' and not dropped then
|
elseif b.action == 'drop' and not dropped then
|
||||||
if turtle.go(b.y == 1 and Point.below(b) or Point.above(b)) then
|
if turtle.go(b.y == 1 and Point.below(b) or Point.above(b)) then
|
||||||
local dropFn = b.y == 1 and turtle.dropUp or turtle.dropDown
|
local dropFn = b.y == 1 and turtle.dropUp or turtle.dropDown
|
||||||
local dropDir = b.y == 1 and 'down' or 'up'
|
local dropDir = b.y == 1 and 'down' or 'up'
|
||||||
|
|
||||||
turtle.eachFilledSlot(function(slot)
|
turtle.eachFilledSlot(function(slot)
|
||||||
if not retain[slot.name] and not retain[slot.key] then
|
if not retain[slot.name] and not retain[slot.key] then
|
||||||
turtle.select(slot.index)
|
turtle.select(slot.index)
|
||||||
dropFn()
|
dropFn()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
local summed = turtle.getSummedInventory()
|
local summed = turtle.getSummedInventory()
|
||||||
for k,v in pairs(summed) do
|
for k,v in pairs(summed) do
|
||||||
if v.count > 16 then
|
if v.count > 16 then
|
||||||
dropFn(k, v.count - 16)
|
dropFn(k, v.count - 16)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
dropped = true
|
dropped = true
|
||||||
turtle.condense()
|
turtle.condense()
|
||||||
|
|
||||||
if turtle.getFuelLevel() < MIN_FUEL then
|
if turtle.getFuelLevel() < MIN_FUEL then
|
||||||
local inv = peripheral.wrap('bottom')
|
local inv = peripheral.wrap('bottom')
|
||||||
if inv and inv.list then
|
if inv and inv.list then
|
||||||
for k, v in pairs(inv.list()) do
|
for k, v in pairs(inv.list()) do
|
||||||
if FUEL[table.concat({ v.name, v.damage }, ':')] then
|
if FUEL[table.concat({ v.name, v.damage }, ':')] then
|
||||||
local count = inv.pushItems(dropDir, k, v.count)
|
local count = inv.pushItems(dropDir, k, v.count)
|
||||||
if count > 0 then
|
if count > 0 then
|
||||||
turtle.refuel(v.name, v.count)
|
turtle.refuel(v.name, v.count)
|
||||||
print('Fuel: ' .. turtle.getFuelLevel())
|
print('Fuel: ' .. turtle.getFuelLevel())
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif b.action == 'smash' then
|
elseif b.action == 'smash' then
|
||||||
if turtle.digDownAt(b) then
|
if turtle.digDownAt(b) then
|
||||||
if crops[b.name].seed then
|
if crops[b.name].seed then
|
||||||
turtle.placeDown(crops[b.name].seed)
|
turtle.placeDown(crops[b.name].seed)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif b.action == 'plant' then
|
elseif b.action == 'plant' then
|
||||||
if turtle.digDownAt(b) then
|
if turtle.digDownAt(b) then
|
||||||
turtle.placeDown(crops[b.name].seed)
|
turtle.placeDown(crops[b.name].seed)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif b.action == 'bump' then
|
elseif b.action == 'bump' then
|
||||||
if turtle.faceAgainst(b) then
|
if turtle.faceAgainst(b) then
|
||||||
Equipper.equipRight('plethora:module:3', 'plethora:sensor')
|
Equipper.equipRight('plethora:sensor')
|
||||||
os.sleep(.5)
|
os.sleep(.5)
|
||||||
-- search the ground for the dropped cactus
|
-- search the ground for the dropped cactus
|
||||||
local sensed = peripheral.call('right', 'sense')
|
local sensed = peripheral.call('right', 'sense')
|
||||||
Equipper.equipRight('minecraft:diamond_pickaxe')
|
Equipper.equipRight('minecraft:diamond_pickaxe')
|
||||||
Util.filterInplace(sensed, function(s)
|
Util.filterInplace(sensed, function(s)
|
||||||
if s.displayName == 'item.tile.cactus' then
|
if s.displayName == 'item.tile.cactus' then
|
||||||
s.x = Util.round(s.x) + turtle.point.x
|
s.x = Util.round(s.x) + turtle.point.x
|
||||||
s.z = Util.round(s.z) + turtle.point.z
|
s.z = Util.round(s.z) + turtle.point.z
|
||||||
s.y = -1
|
s.y = -1
|
||||||
if Point.distance(b, s) < 6 then
|
if Point.distance(b, s) < 6 then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
Point.eachClosest(turtle.point, sensed, function(s)
|
Point.eachClosest(turtle.point, sensed, function(s)
|
||||||
turtle.suckDownAt(s)
|
turtle.suckDownAt(s)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif b.action == 'pick' then
|
elseif b.action == 'pick' then
|
||||||
local h = Point.facings[b.state.facing].heading
|
local h = Point.facings[b.state.facing].heading
|
||||||
local hi = Point.headings[(h + 2) % 4] -- opposite heading
|
local hi = Point.headings[(h + 2) % 4] -- opposite heading
|
||||||
|
|
||||||
-- without pathfinding, will be unable to circle log
|
-- without pathfinding, will be unable to circle log
|
||||||
if turtle.go({ x = b.x + hi.xd, z = b.z + hi.zd, heading = h }) then
|
if turtle.go({ x = b.x + hi.xd, z = b.z + hi.zd, heading = h }) then
|
||||||
if turtle.dig() then
|
if turtle.dig() then
|
||||||
turtle.place(crops[b.name].seed)
|
turtle.place(crops[b.name].seed)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
Equipper.equipRight('plethora:module:2', 'plethora:scanner')
|
Equipper.equipRight('plethora:scanner')
|
||||||
end
|
end
|
||||||
|
|
||||||
local s, m = turtle.run(function()
|
local s, m = turtle.run(function()
|
||||||
local facing = scanner.getBlockMeta(0, 0, 0).state.facing
|
local facing = scanner.getBlockMeta(0, 0, 0).state.facing
|
||||||
turtle.point.heading = Point.facings[facing].heading
|
turtle.point.heading = Point.facings[facing].heading
|
||||||
|
|
||||||
turtle.setStatus('farming')
|
turtle.setStatus('farming')
|
||||||
print('Fuel: ' .. turtle.getFuelLevel())
|
print('Fuel: ' .. turtle.getFuelLevel())
|
||||||
|
|
||||||
turtle.setMovementStrategy('goto')
|
turtle.setMovementStrategy('goto')
|
||||||
repeat
|
repeat
|
||||||
local blocks, harvestCount = scan()
|
local blocks, harvestCount = scan()
|
||||||
if harvestCount > 0 then
|
if harvestCount > 0 then
|
||||||
turtle.setStatus('Harvesting')
|
turtle.setStatus('Harvesting')
|
||||||
harvest(blocks)
|
harvest(blocks)
|
||||||
turtle.setStatus('Sleeping')
|
turtle.setStatus('Sleeping')
|
||||||
end
|
end
|
||||||
os.sleep(10)
|
os.sleep(10)
|
||||||
if turtle.getFuelLevel() < 10 then
|
if turtle.getFuelLevel() < 10 then
|
||||||
turtle.setStatus('Out of fuel')
|
turtle.setStatus('Out of fuel')
|
||||||
error('Out of fuel')
|
error('Out of fuel')
|
||||||
end
|
end
|
||||||
until turtle.isAborted()
|
until turtle.isAborted()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if not s and m then
|
if not s and m then
|
||||||
error(m)
|
error(m)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ Requirements
|
|||||||
* Standard Modem
|
* Standard Modem
|
||||||
* Block Scanner
|
* Block Scanner
|
||||||
* Entity Sensor
|
* Entity Sensor
|
||||||
* Crafting Table
|
* Furnace
|
||||||
* Vanilla Chest
|
* Vanilla Chest
|
||||||
|
* Sapling
|
||||||
* GPS
|
* GPS
|
||||||
|
|
||||||
Setup
|
Setup
|
||||||
@@ -19,18 +20,20 @@ Setup
|
|||||||
> package install farms
|
> package install farms
|
||||||
> reboot
|
> reboot
|
||||||
|
|
||||||
|
The turtle will need some fuel initially.
|
||||||
|
|
||||||
The tree farm fits exactly in one chunk. It's best to have a mostly level ground around the center of the farming area as the turtle will only collect saplings that have fallen to the same level as the turtle.
|
The tree farm fits exactly in one chunk. It's best to have a mostly level ground around the center of the farming area as the turtle will only collect saplings that have fallen to the same level as the turtle.
|
||||||
|
|
||||||
To align the turtle perfectly in one chunk, position the turtle 8 blocks diagonally from the bottom left corner.
|
To align the turtle perfectly in one chunk, position the turtle 8 blocks diagonally from the bottom left corner.
|
||||||
|
|
||||||
Place a sapling directly in front of the turtle and place all the required items into the inventory.
|
Place all the required items into the inventory.
|
||||||
|
|
||||||
To start the program, run:
|
To start the program, run:
|
||||||
|
|
||||||
> superTreefarm
|
> superTreefarm
|
||||||
A startup file is created automatically the first time the program is run (usr/autorun/superTreefarm.lua).
|
A startup file is created automatically the first time the program is run (usr/autorun/superTreefarm.lua).
|
||||||
|
|
||||||
If the turtle does not get any saplings from the initial tree, place down another sapling in front of the turtle.
|
If the turtle does not get any saplings from the initial tree, another sapling in the turtle.
|
||||||
|
|
||||||
Tips
|
Tips
|
||||||
====
|
====
|
||||||
|
|||||||
@@ -10,111 +10,117 @@ local turtle = _G.turtle
|
|||||||
local STARTUP_FILE = 'usr/autorun/rancher.lua'
|
local STARTUP_FILE = 'usr/autorun/rancher.lua'
|
||||||
|
|
||||||
local retain = Util.transpose {
|
local retain = Util.transpose {
|
||||||
'minecraft:shears',
|
'minecraft:shears',
|
||||||
'minecraft:wheat',
|
'minecraft:wheat',
|
||||||
'minecraft:diamond_sword',
|
'minecraft:diamond_sword',
|
||||||
'plethora:module:3',
|
'plethora:module:3',
|
||||||
}
|
}
|
||||||
local config = {
|
local config = {
|
||||||
animal = 'Cow',
|
animal = 'Cow',
|
||||||
max_animals = 15,
|
max_animals = 15,
|
||||||
}
|
}
|
||||||
Config.load('rancher', config)
|
Config.load('rancher', config)
|
||||||
|
|
||||||
local ANIMALS = {
|
local ANIMALS = {
|
||||||
Pig = { min = 0, food = 'minecraft:carrot' },
|
Pig = { min = 0, food = 'minecraft:carrot' },
|
||||||
Sheep = { min = .5, food = 'minecraft:wheat' },
|
Sheep = { min = .5, food = 'minecraft:wheat' },
|
||||||
Cow = { min = .5, food = 'minecraft:wheat' },
|
Cow = { min = .5, food = 'minecraft:wheat' },
|
||||||
}
|
}
|
||||||
|
|
||||||
local animal = ANIMALS[config.animal]
|
local animal = ANIMALS[config.animal]
|
||||||
|
|
||||||
Equipper.equipLeft('minecraft:diamond_sword')
|
Equipper.equipLeft('minecraft:diamond_sword')
|
||||||
local sensor = Equipper.equipRight('plethora:module:3', 'plethora:sensor')
|
local sensor = Equipper.equipRight('plethora:sensor')
|
||||||
|
|
||||||
local chest = Adapter({ side = 'bottom', direction = 'up' }) or error('missing chest')
|
local chest = Adapter({ side = 'bottom', direction = 'up' }) or error('missing chest')
|
||||||
|
|
||||||
if not fs.exists(STARTUP_FILE) then
|
if not fs.exists(STARTUP_FILE) then
|
||||||
Util.writeFile(STARTUP_FILE,
|
Util.writeFile(STARTUP_FILE,
|
||||||
[[os.sleep(1)
|
[[os.sleep(1)
|
||||||
shell.openForegroundTab('rancher.lua')]])
|
shell.openForegroundTab('rancher.lua')]])
|
||||||
print('Autorun program created: ' .. STARTUP_FILE)
|
print('Autorun program created: ' .. STARTUP_FILE)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getAnimalCount()
|
local function getAnimalCount()
|
||||||
local blocks = sensor.sense()
|
local blocks = sensor.sense()
|
||||||
|
|
||||||
local grown = 0
|
local grown = 0
|
||||||
local babies = 0
|
local babies = 0
|
||||||
|
|
||||||
Util.filterInplace(blocks, function(v)
|
Util.filterInplace(blocks, function(v)
|
||||||
if v.name == config.animal then
|
if v.name == config.animal then
|
||||||
if v.y > -.5 then grown = grown + 1 end
|
local entity = sensor.getMetaByID(v.id)
|
||||||
if v.y < -.5 then babies = babies + 1 end
|
if entity then
|
||||||
return v.y > -.5
|
if entity.isChild then
|
||||||
end
|
babies = babies + 1
|
||||||
end)
|
else
|
||||||
|
grown = grown + 1
|
||||||
|
end
|
||||||
|
return not entity.isChild
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
Util.print('%d grown, %d babies', grown, babies)
|
Util.print('%d grown, %d babies', grown, babies)
|
||||||
|
|
||||||
return #blocks
|
return #blocks
|
||||||
end
|
end
|
||||||
|
|
||||||
local function butcher()
|
local function butcher()
|
||||||
Equipper.equipRight('minecraft:diamond_sword')
|
Equipper.equipRight('minecraft:diamond_sword')
|
||||||
turtle.select(1)
|
turtle.select(1)
|
||||||
|
|
||||||
turtle.attack()
|
turtle.attack()
|
||||||
for _ = 1, 3 do
|
for _ = 1, 3 do
|
||||||
turtle.turnRight()
|
turtle.turnRight()
|
||||||
turtle.attack()
|
turtle.attack()
|
||||||
end
|
end
|
||||||
Equipper.equipRight('plethora:module:3', 'plethora:sensor')
|
Equipper.equipRight('plethora:sensor')
|
||||||
|
|
||||||
turtle.eachFilledSlot(function(slot)
|
turtle.eachFilledSlot(function(slot)
|
||||||
if not retain[slot.name] then
|
if not retain[slot.name] then
|
||||||
chest:insert(slot.index, 64)
|
chest:insert(slot.index, 64)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function breed()
|
local function breed()
|
||||||
turtle.select(1)
|
turtle.select(1)
|
||||||
|
|
||||||
if config.animal == 'Sheep' then
|
if config.animal == 'Sheep' then
|
||||||
turtle.place('minecraft:shears')
|
turtle.place('minecraft:shears')
|
||||||
end
|
end
|
||||||
turtle.place('minecraft:wheat')
|
turtle.place('minecraft:wheat')
|
||||||
for _ = 1, 3 do
|
for _ = 1, 3 do
|
||||||
turtle.turnRight()
|
turtle.turnRight()
|
||||||
if config.animal == 'Sheep' then
|
if config.animal == 'Sheep' then
|
||||||
turtle.place('minecraft:shears')
|
turtle.place('minecraft:shears')
|
||||||
end
|
end
|
||||||
turtle.place('minecraft:wheat')
|
turtle.place('minecraft:wheat')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local s, m = turtle.run(function()
|
local s, m = turtle.run(function()
|
||||||
print('Configured animal: ' .. config.animal)
|
print('Configured animal: ' .. config.animal)
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
local animalCount = getAnimalCount()
|
local animalCount = getAnimalCount()
|
||||||
if animalCount > config.max_animals then
|
if animalCount > config.max_animals then
|
||||||
turtle.setStatus('Butchering')
|
turtle.setStatus('Butchering')
|
||||||
butcher()
|
butcher()
|
||||||
elseif turtle.getItemCount(animal.food) == 0 then
|
elseif turtle.getItemCount(animal.food) == 0 then
|
||||||
if chest:provide({ name = animal.food, damage = 0 }, 64) == 0 then
|
if chest:provide({ name = animal.food, damage = 0 }, 64) == 0 then
|
||||||
print('Out of ' .. animal.food)
|
print('Out of ' .. animal.food)
|
||||||
turtle.setStatus('Out of food')
|
turtle.setStatus('Out of food')
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
turtle.setStatus('Breeding')
|
turtle.setStatus('Breeding')
|
||||||
breed()
|
breed()
|
||||||
end
|
end
|
||||||
os.sleep(5)
|
os.sleep(5)
|
||||||
until turtle.isAborted()
|
until turtle.isAborted()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if not s and m then
|
if not s and m then
|
||||||
error(m)
|
error(m)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ local STARTUP_FILE = 'usr/autorun/spawner.lua'
|
|||||||
local mobTypes = { }
|
local mobTypes = { }
|
||||||
|
|
||||||
Equipper.equipLeft('minecraft:diamond_sword')
|
Equipper.equipLeft('minecraft:diamond_sword')
|
||||||
local scanner = Equipper.equipRight('plethora:module:2', 'plethora:scanner')
|
local scanner = Equipper.equipRight('plethora:scanner')
|
||||||
|
|
||||||
turtle.reset()
|
turtle.reset()
|
||||||
local facing = scanner.getBlockMeta(0, 0, 0).state.facing
|
local facing = scanner.getBlockMeta(0, 0, 0).state.facing
|
||||||
@@ -28,13 +28,13 @@ Util.filterInplace(data, function(b)
|
|||||||
end)
|
end)
|
||||||
local chest = Point.closest(spawner, data) or error('missing drop off chest')
|
local chest = Point.closest(spawner, data) or error('missing drop off chest')
|
||||||
|
|
||||||
local sensor = Equipper.equipRight('plethora:module:3', 'plethora:sensor')
|
local sensor = Equipper.equipRight('plethora:sensor')
|
||||||
|
|
||||||
if not fs.exists(STARTUP_FILE) then
|
if not fs.exists(STARTUP_FILE) then
|
||||||
Util.writeFile(STARTUP_FILE,
|
Util.writeFile(STARTUP_FILE,
|
||||||
string.format([[os.sleep(1)
|
string.format([[os.sleep(1)
|
||||||
shell.openForegroundTab('spawner.lua %s')]], table.concat({ ... }, ' ')))
|
shell.openForegroundTab('spawner.lua %s')]], table.concat({ ... }, ' ')))
|
||||||
print('Autorun program created: ' .. STARTUP_FILE)
|
print('Autorun program created: ' .. STARTUP_FILE)
|
||||||
end
|
end
|
||||||
|
|
||||||
turtle.setMovementStrategy('goto')
|
turtle.setMovementStrategy('goto')
|
||||||
@@ -70,8 +70,8 @@ local function normalize(b)
|
|||||||
b.z = Util.round(b.z) + turtle.point.z
|
b.z = Util.round(b.z) + turtle.point.z
|
||||||
|
|
||||||
return b.x >= spawner.x - 4 and b.x <= spawner.x + 4 and
|
return b.x >= spawner.x - 4 and b.x <= spawner.x + 4 and
|
||||||
b.y >= spawner.y - 4 and b.y <= spawner.y + 4 and
|
b.y >= spawner.y - 4 and b.y <= spawner.y + 4 and
|
||||||
b.z >= spawner.z - 4 and b.z <= spawner.z + 4
|
b.z >= spawner.z - 4 and b.z <= spawner.z + 4
|
||||||
end
|
end
|
||||||
|
|
||||||
local function aboveAttack(b)
|
local function aboveAttack(b)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1010
farms/treefarm.lua
1010
farms/treefarm.lua
File diff suppressed because it is too large
Load Diff
@@ -3,25 +3,25 @@ local Sound = require('sound')
|
|||||||
local os = _G.os
|
local os = _G.os
|
||||||
|
|
||||||
local tunes = {
|
local tunes = {
|
||||||
{ sound = 'record.11', length = '1:11' },
|
{ sound = 'record.11', length = '1:11' },
|
||||||
{ sound = 'record.13', length = '2:58' },
|
{ sound = 'record.13', length = '2:58' },
|
||||||
{ sound = 'record.blocks', length = '5:45' },
|
{ sound = 'record.blocks', length = '5:45' },
|
||||||
{ sound = 'record.cat', length = '3:05' },
|
{ sound = 'record.cat', length = '3:05' },
|
||||||
{ sound = 'record.chirp', length = '3:05' },
|
{ sound = 'record.chirp', length = '3:05' },
|
||||||
{ sound = 'record.far', length = '2:54' },
|
{ sound = 'record.far', length = '2:54' },
|
||||||
{ sound = 'record.mall', length = '3:17' },
|
{ sound = 'record.mall', length = '3:17' },
|
||||||
{ sound = 'record.mellohi', length = '1:36' },
|
{ sound = 'record.mellohi', length = '1:36' },
|
||||||
{ sound = 'record.stal', length = '2:30' },
|
{ sound = 'record.stal', length = '2:30' },
|
||||||
{ sound = 'record.strad', length = '3:08' },
|
{ sound = 'record.strad', length = '3:08' },
|
||||||
{ sound = 'record.wait', length = '3:58' },
|
{ sound = 'record.wait', length = '3:58' },
|
||||||
{ sound = 'record.ward', length = '4:11' },
|
{ sound = 'record.ward', length = '4:11' },
|
||||||
}
|
}
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local song = tunes[math.random(1, #tunes)]
|
local song = tunes[math.random(1, #tunes)]
|
||||||
Sound.play(song.sound)
|
Sound.play(song.sound)
|
||||||
local min, sec = song.length:match('(%d+):(%d+)')
|
local min, sec = song.length:match('(%d+):(%d+)')
|
||||||
local length = tonumber(min)*60 + tonumber(sec)
|
local length = tonumber(min)*60 + tonumber(sec)
|
||||||
print(string.format('Playing %s (%s)', song.sound, song.length))
|
print(string.format('Playing %s (%s)', song.sound, song.length))
|
||||||
os.sleep(length + 3)
|
os.sleep(length + 3)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ local GPS = require('gps')
|
|||||||
local Util = require('util')
|
local Util = require('util')
|
||||||
|
|
||||||
local args = { ... }
|
local args = { ... }
|
||||||
|
local colors = _G.colors
|
||||||
local fs = _G.fs
|
local fs = _G.fs
|
||||||
local gps = _G.gps
|
local gps = _G.gps
|
||||||
local os = _G.os
|
local os = _G.os
|
||||||
|
|||||||
@@ -16,68 +16,68 @@ local turtle = _G.turtle
|
|||||||
multishell.setTitle(multishell.getCurrent(), 'Milo')
|
multishell.setTitle(multishell.getCurrent(), 'Milo')
|
||||||
|
|
||||||
local function Syntax(msg)
|
local function Syntax(msg)
|
||||||
print([[
|
print([[
|
||||||
Turtle must be provided with:
|
Turtle must be provided with:
|
||||||
* Introspection module (never bound)
|
* Introspection module (never bound)
|
||||||
* Workbench
|
* Workbench
|
||||||
|
|
||||||
Turtle must be connected to:
|
Turtle must be connected to:
|
||||||
* Wired modem (activated)
|
* Wired modem (activated)
|
||||||
]])
|
]])
|
||||||
|
|
||||||
error(msg)
|
error(msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
local modem
|
local modem
|
||||||
for _,v in pairs(device) do
|
for _,v in pairs(device) do
|
||||||
if v.type == 'wired_modem' then
|
if v.type == 'wired_modem' then
|
||||||
if modem then
|
if modem then
|
||||||
Syntax('Only 1 wired modem can be connected')
|
Syntax('Only 1 wired modem can be connected')
|
||||||
end
|
end
|
||||||
modem = v
|
modem = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not modem or not modem.getNameLocal then
|
if not modem or not modem.getNameLocal then
|
||||||
Syntax('Wired modem missing')
|
Syntax('Wired modem missing')
|
||||||
end
|
end
|
||||||
|
|
||||||
if not modem.getNameLocal() then
|
if not modem.getNameLocal() then
|
||||||
Syntax('Wired modem is not active')
|
Syntax('Wired modem is not active')
|
||||||
end
|
end
|
||||||
|
|
||||||
local introspection = device['plethora:introspection'] or
|
local introspection = device['plethora:introspection'] or
|
||||||
turtle.equip('left', 'plethora:module:0') and device['plethora:introspection'] or
|
turtle.equip('left', 'plethora:module:0') and device['plethora:introspection'] or
|
||||||
Syntax('Introspection module missing')
|
Syntax('Introspection module missing')
|
||||||
|
|
||||||
if not device.workbench then
|
if not device.workbench then
|
||||||
turtle.equip('right', 'minecraft:crafting_table:0')
|
turtle.equip('right', 'minecraft:crafting_table:0')
|
||||||
if not device.workbench then
|
if not device.workbench then
|
||||||
Syntax('Workbench missing')
|
Syntax('Workbench missing')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local localName = modem.getNameLocal()
|
local localName = modem.getNameLocal()
|
||||||
|
|
||||||
local context = {
|
local context = {
|
||||||
resources = Util.readTable(Milo.RESOURCE_FILE) or { },
|
resources = Util.readTable(Milo.RESOURCE_FILE) or { },
|
||||||
|
|
||||||
state = { },
|
state = { },
|
||||||
craftingQueue = { },
|
craftingQueue = { },
|
||||||
tasks = { },
|
tasks = { },
|
||||||
queue = { },
|
queue = { },
|
||||||
plugins = { },
|
plugins = { },
|
||||||
loggers = { },
|
loggers = { },
|
||||||
|
|
||||||
taskTimer = 0,
|
taskTimer = 0,
|
||||||
taskCounter = 0,
|
taskCounter = 0,
|
||||||
|
|
||||||
storage = Storage(),
|
storage = Storage(),
|
||||||
turtleInventory = {
|
turtleInventory = {
|
||||||
name = localName,
|
name = localName,
|
||||||
mtype = 'hidden',
|
mtype = 'hidden',
|
||||||
adapter = introspection.getInventory(),
|
adapter = introspection.getInventory(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.storage.nodes[localName] = context.turtleInventory
|
context.storage.nodes[localName] = context.turtleInventory
|
||||||
@@ -88,23 +88,23 @@ context.storage:initStorage()
|
|||||||
context.storage.turtleInventory = context.turtleInventory
|
context.storage.turtleInventory = context.turtleInventory
|
||||||
|
|
||||||
local function loadPlugin(file)
|
local function loadPlugin(file)
|
||||||
local s, plugin = Util.run(_ENV, file, context)
|
local s, plugin = Util.run(_ENV, file, context)
|
||||||
if not s and plugin then
|
if not s and plugin then
|
||||||
_G.printError('Error loading: ' .. file)
|
_G.printError('Error loading: ' .. file)
|
||||||
error(plugin or 'Unknown error')
|
error(plugin or 'Unknown error')
|
||||||
end
|
end
|
||||||
|
|
||||||
if plugin and type(plugin) == 'table' then
|
if plugin and type(plugin) == 'table' then
|
||||||
Milo:registerPlugin(plugin)
|
Milo:registerPlugin(plugin)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function loadDirectory(dir)
|
local function loadDirectory(dir)
|
||||||
for _, file in pairs(fs.list(dir)) do
|
for _, file in pairs(fs.list(dir)) do
|
||||||
if not fs.isDir(fs.combine(dir, file)) then
|
if not fs.isDir(fs.combine(dir, file)) then
|
||||||
loadPlugin(fs.combine(dir, file))
|
loadPlugin(fs.combine(dir, file))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local programDir = fs.getDir(shell.getRunningProgram())
|
local programDir = fs.getDir(shell.getRunningProgram())
|
||||||
@@ -113,17 +113,17 @@ loadDirectory(fs.combine(programDir, 'plugins'))
|
|||||||
loadDirectory(fs.combine(programDir, 'plugins/item'))
|
loadDirectory(fs.combine(programDir, 'plugins/item'))
|
||||||
|
|
||||||
for k in pairs(Milo:getState('plugins') or { }) do
|
for k in pairs(Milo:getState('plugins') or { }) do
|
||||||
loadPlugin(k)
|
loadPlugin(k)
|
||||||
end
|
end
|
||||||
|
|
||||||
table.sort(context.tasks, function(a, b)
|
table.sort(context.tasks, function(a, b)
|
||||||
return a.priority < b.priority
|
return a.priority < b.priority
|
||||||
end)
|
end)
|
||||||
|
|
||||||
_G._syslog('Tasks\n-----')
|
_G._syslog('Tasks\n-----')
|
||||||
for _, task in ipairs(context.tasks) do
|
for _, task in ipairs(context.tasks) do
|
||||||
task.execTime = 0
|
task.execTime = 0
|
||||||
_G._syslog('%d: %s', task.priority, task.name)
|
_G._syslog('%d: %s', task.priority, task.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:clearGrid()
|
Milo:clearGrid()
|
||||||
@@ -132,92 +132,92 @@ UI:setPage(UI:getPage('listing'))
|
|||||||
Sound.play('ui.toast.challenge_complete')
|
Sound.play('ui.toast.challenge_complete')
|
||||||
|
|
||||||
Event.on({ 'milo_cycle', 'milo_queue' }, function(e)
|
Event.on({ 'milo_cycle', 'milo_queue' }, function(e)
|
||||||
if context.storage:isOnline() then
|
if context.storage:isOnline() then
|
||||||
if #context.queue > 0 then
|
if #context.queue > 0 then
|
||||||
local queue = context.queue
|
local queue = context.queue
|
||||||
context.queue = { }
|
context.queue = { }
|
||||||
for _, entry in pairs(queue) do
|
for _, entry in pairs(queue) do
|
||||||
local s, m = pcall(entry.callback, entry.request)
|
local s, m = pcall(entry.callback, entry.request)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G._syslog('callback crashed')
|
_G._syslog('callback crashed')
|
||||||
_G._syslog(m)
|
_G._syslog(m)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if e == 'milo_cycle' and not Milo:isCraftingPaused() then
|
if e == 'milo_cycle' and not Milo:isCraftingPaused() then
|
||||||
local taskTimer = Util.timer()
|
local taskTimer = Util.timer()
|
||||||
Milo:resetCraftingStatus()
|
Milo:resetCraftingStatus()
|
||||||
|
|
||||||
for _, task in ipairs(context.tasks) do
|
for _, task in ipairs(context.tasks) do
|
||||||
local timer = Util.timer()
|
local timer = Util.timer()
|
||||||
local s, m = pcall(function() task:cycle(context) end)
|
local s, m = pcall(function() task:cycle(context) end)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G._syslog(task.name .. ' crashed')
|
_G._syslog(task.name .. ' crashed')
|
||||||
_G._syslog(m)
|
_G._syslog(m)
|
||||||
end
|
end
|
||||||
task.execTime = task.execTime + timer()
|
task.execTime = task.execTime + timer()
|
||||||
end
|
end
|
||||||
context.taskTimer = context.taskTimer + taskTimer()
|
context.taskTimer = context.taskTimer + taskTimer()
|
||||||
context.taskCounter = context.taskCounter + 1
|
context.taskCounter = context.taskCounter + 1
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if context.storage:isOnline() and #context.queue > 0 then
|
if context.storage:isOnline() and #context.queue > 0 then
|
||||||
os.queueEvent('milo_cycle')
|
os.queueEvent('milo_cycle')
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.on('turtle_inventory', function()
|
Event.on('turtle_inventory', function()
|
||||||
Milo:queueRequest({ }, function()
|
Milo:queueRequest({ }, function()
|
||||||
if not Milo:isCraftingPaused() then
|
if not Milo:isCraftingPaused() then
|
||||||
Milo:clearGrid()
|
Milo:clearGrid()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local cycleHandle
|
local cycleHandle
|
||||||
cycleHandle = Event.onInterval(5, function()
|
cycleHandle = Event.onInterval(5, function()
|
||||||
Event.trigger('milo_cycle')
|
Event.trigger('milo_cycle')
|
||||||
if context.taskCounter > 0 then
|
if context.taskCounter > 0 then
|
||||||
--local average = context.taskTimer / context.taskCounter
|
--local average = context.taskTimer / context.taskCounter
|
||||||
--_syslog('Interval: ' .. math.max(5, 2 + average * 3))
|
--_syslog('Interval: ' .. math.max(5, 2 + average * 3))
|
||||||
--cycleHandle.updateInterval(math.max(5, 2 + average * 3))
|
--cycleHandle.updateInterval(math.max(5, 2 + average * 3))
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.on({ 'storage_offline', 'storage_online' }, function()
|
Event.on({ 'storage_offline', 'storage_online' }, function()
|
||||||
if context.storage:isOnline() then
|
if context.storage:isOnline() then
|
||||||
Milo:resumeCrafting({ key = 'storageOnline' })
|
Milo:resumeCrafting({ key = 'storageOnline' })
|
||||||
else
|
else
|
||||||
Milo:pauseCrafting({ key = 'storageOnline', msg = 'Storage offline' })
|
Milo:pauseCrafting({ key = 'storageOnline', msg = 'Storage offline' })
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.on('terminate', function()
|
Event.on('terminate', function()
|
||||||
for _, node in pairs(context.storage.nodes) do
|
for _, node in pairs(context.storage.nodes) do
|
||||||
if node.category == 'display' and node.adapter and node.adapter.clear then
|
if node.category == 'display' and node.adapter and node.adapter.clear then
|
||||||
node.adapter.setBackgroundColor(colors.black)
|
node.adapter.setBackgroundColor(colors.black)
|
||||||
node.adapter.clear()
|
node.adapter.clear()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
os.queueEvent(
|
os.queueEvent(
|
||||||
context.storage:isOnline() and 'storage_online' or 'storage_offline',
|
context.storage:isOnline() and 'storage_online' or 'storage_offline',
|
||||||
context.storage:isOnline())
|
context.storage:isOnline())
|
||||||
|
|
||||||
local oldDebug = _G._syslog
|
local oldDebug = _G._syslog
|
||||||
_G._syslog = function(...)
|
_G._syslog = function(...)
|
||||||
for _,v in pairs(context.loggers) do
|
for _,v in pairs(context.loggers) do
|
||||||
v(...)
|
v(...)
|
||||||
end
|
end
|
||||||
oldDebug(...)
|
oldDebug(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
local s, m = pcall(function()
|
local s, m = pcall(function()
|
||||||
UI:pullEvents()
|
UI:pullEvents()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
_G._syslog = oldDebug
|
_G._syslog = oldDebug
|
||||||
|
|||||||
@@ -13,512 +13,512 @@ local peripheral = _G.peripheral
|
|||||||
local shell = _ENV.shell
|
local shell = _ENV.shell
|
||||||
|
|
||||||
local context = {
|
local context = {
|
||||||
state = Config.load('miloRemote', { displayMode = 0, deposit = true }),
|
state = Config.load('miloRemote', { displayMode = 0, deposit = true }),
|
||||||
responseHandlers = { },
|
responseHandlers = { },
|
||||||
}
|
}
|
||||||
|
|
||||||
local depositMode = {
|
local depositMode = {
|
||||||
[ true ] = { text = '\25', textColor = colors.black, help = 'Deposit enabled' },
|
[ true ] = { text = '\25', textColor = colors.black, help = 'Deposit enabled' },
|
||||||
[ false ] = { text = '\215', textColor = colors.red, help = 'Deposit disabled' },
|
[ false ] = { text = '\215', textColor = colors.red, help = 'Deposit disabled' },
|
||||||
}
|
}
|
||||||
|
|
||||||
local displayModes = {
|
local displayModes = {
|
||||||
[0] = { text = 'A', help = 'Showing all items' },
|
[0] = { text = 'A', help = 'Showing all items' },
|
||||||
[1] = { text = 'I', help = 'Showing inventory items' },
|
[1] = { text = 'I', help = 'Showing inventory items' },
|
||||||
}
|
}
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
menuBar = UI.MenuBar {
|
menuBar = UI.MenuBar {
|
||||||
buttons = {
|
buttons = {
|
||||||
{
|
{
|
||||||
text = 'Refresh',
|
text = 'Refresh',
|
||||||
x = -12,
|
x = -12,
|
||||||
event = 'refresh'
|
event = 'refresh'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name = 'config',
|
name = 'config',
|
||||||
text = '\187',
|
text = '\187',
|
||||||
x = -3,
|
x = -3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
infoBar = UI.StatusBar {
|
infoBar = UI.StatusBar {
|
||||||
x = 1, ex = -16,
|
x = 1, ex = -16,
|
||||||
backgroundColor = colors.lightGray,
|
backgroundColor = colors.lightGray,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
grid = UI.Grid {
|
grid = UI.Grid {
|
||||||
y = 2, ey = -2,
|
y = 2, ey = -2,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = ' Qty', key = 'count' , width = 4, align = 'right' },
|
{ heading = ' Qty', key = 'count' , width = 4, align = 'right' },
|
||||||
{ heading = 'Name', key = 'displayName' },
|
{ heading = 'Name', key = 'displayName' },
|
||||||
},
|
},
|
||||||
values = { },
|
values = { },
|
||||||
sortColumn = context.state.sortColumn or 'count',
|
sortColumn = context.state.sortColumn or 'count',
|
||||||
inverseSort = context.state.inverseSort,
|
inverseSort = context.state.inverseSort,
|
||||||
help = '^(s)tack, ^(a)ll'
|
help = '^(s)tack, ^(a)ll'
|
||||||
},
|
},
|
||||||
statusBar = UI.Window {
|
statusBar = UI.Window {
|
||||||
y = -1,
|
y = -1,
|
||||||
filter = UI.TextEntry {
|
filter = UI.TextEntry {
|
||||||
x = 1, ex = -12,
|
x = 1, ex = -12,
|
||||||
limit = 50,
|
limit = 50,
|
||||||
shadowText = 'filter',
|
shadowText = 'filter',
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
backgroundFocusColor = colors.cyan,
|
backgroundFocusColor = colors.cyan,
|
||||||
accelerators = {
|
accelerators = {
|
||||||
[ 'enter' ] = 'eject',
|
[ 'enter' ] = 'eject',
|
||||||
[ 'up' ] = 'grid_up',
|
[ 'up' ] = 'grid_up',
|
||||||
[ 'down' ] = 'grid_down',
|
[ 'down' ] = 'grid_down',
|
||||||
[ 'control-a' ] = 'eject_all',
|
[ 'control-a' ] = 'eject_all',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
amount = UI.TextEntry {
|
amount = UI.TextEntry {
|
||||||
x = -11, ex = -7,
|
x = -11, ex = -7,
|
||||||
limit = 3,
|
limit = 3,
|
||||||
shadowText = '1',
|
shadowText = '1',
|
||||||
shadowTextColor = colors.gray,
|
shadowTextColor = colors.gray,
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
backgroundFocusColor = colors.black,
|
backgroundFocusColor = colors.black,
|
||||||
accelerators = {
|
accelerators = {
|
||||||
[ 'enter' ] = 'eject_specified',
|
[ 'enter' ] = 'eject_specified',
|
||||||
[ 'control-a' ] = 'eject_all',
|
[ 'control-a' ] = 'eject_all',
|
||||||
},
|
},
|
||||||
help = 'Request amount',
|
help = 'Request amount',
|
||||||
},
|
},
|
||||||
depositToggle = UI.Button {
|
depositToggle = UI.Button {
|
||||||
x = -6,
|
x = -6,
|
||||||
event = 'toggle_deposit',
|
event = 'toggle_deposit',
|
||||||
text = '\215',
|
text = '\215',
|
||||||
},
|
},
|
||||||
display = UI.Button {
|
display = UI.Button {
|
||||||
x = -3,
|
x = -3,
|
||||||
event = 'toggle_display',
|
event = 'toggle_display',
|
||||||
text = displayModes[context.state.displayMode].text,
|
text = displayModes[context.state.displayMode].text,
|
||||||
help = displayModes[context.state.displayMode].help,
|
help = displayModes[context.state.displayMode].help,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
notification = UI.Notification {
|
notification = UI.Notification {
|
||||||
anchor = 'top',
|
anchor = 'top',
|
||||||
},
|
},
|
||||||
accelerators = {
|
accelerators = {
|
||||||
r = 'refresh',
|
r = 'refresh',
|
||||||
[ 'control-r' ] = 'refresh',
|
[ 'control-r' ] = 'refresh',
|
||||||
[ 'control-e' ] = 'eject',
|
[ 'control-e' ] = 'eject',
|
||||||
[ 'control-s' ] = 'eject_stack',
|
[ 'control-s' ] = 'eject_stack',
|
||||||
[ 'control-a' ] = 'eject_all',
|
[ 'control-a' ] = 'eject_all',
|
||||||
|
|
||||||
q = 'quit',
|
q = 'quit',
|
||||||
},
|
},
|
||||||
items = { },
|
items = { },
|
||||||
}
|
}
|
||||||
|
|
||||||
local function getPlayerName()
|
local function getPlayerName()
|
||||||
local neural = peripheral.find('neuralInterface')
|
local neural = peripheral.find('neuralInterface')
|
||||||
|
|
||||||
if neural and neural.getName then
|
if neural and neural.getName then
|
||||||
return neural.getName()
|
return neural.getName()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:getRowTextColor(row, selected)
|
function page.grid:getRowTextColor(row, selected)
|
||||||
if row.is_craftable then
|
if row.is_craftable then
|
||||||
return colors.yellow
|
return colors.yellow
|
||||||
end
|
end
|
||||||
if row.has_recipe then
|
if row.has_recipe then
|
||||||
return colors.cyan
|
return colors.cyan
|
||||||
end
|
end
|
||||||
return UI.Grid:getRowTextColor(row, selected)
|
return UI.Grid:getRowTextColor(row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:getDisplayValues(row)
|
function page.grid:getDisplayValues(row)
|
||||||
row = Util.shallowCopy(row)
|
row = Util.shallowCopy(row)
|
||||||
row.count = row.count > 0 and Util.toBytes(row.count) or ''
|
row.count = row.count > 0 and Util.toBytes(row.count) or ''
|
||||||
return row
|
return row
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:sortCompare(a, b)
|
function page.grid:sortCompare(a, b)
|
||||||
if self.sortColumn ~= 'displayName' then
|
if self.sortColumn ~= 'displayName' then
|
||||||
if a[self.sortColumn] == b[self.sortColumn] then
|
if a[self.sortColumn] == b[self.sortColumn] then
|
||||||
if self.inverseSort then
|
if self.inverseSort then
|
||||||
return a.displayName > b.displayName
|
return a.displayName > b.displayName
|
||||||
end
|
end
|
||||||
return a.displayName < b.displayName
|
return a.displayName < b.displayName
|
||||||
end
|
end
|
||||||
if a[self.sortColumn] == 0 then
|
if a[self.sortColumn] == 0 then
|
||||||
return self.inverseSort
|
return self.inverseSort
|
||||||
end
|
end
|
||||||
if b[self.sortColumn] == 0 then
|
if b[self.sortColumn] == 0 then
|
||||||
return not self.inverseSort
|
return not self.inverseSort
|
||||||
end
|
end
|
||||||
return a[self.sortColumn] < b[self.sortColumn]
|
return a[self.sortColumn] < b[self.sortColumn]
|
||||||
end
|
end
|
||||||
return UI.Grid.sortCompare(self, a, b)
|
return UI.Grid.sortCompare(self, a, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:eventHandler(event)
|
function page.grid:eventHandler(event)
|
||||||
if event.type == 'grid_sort' then
|
if event.type == 'grid_sort' then
|
||||||
context.state.sortColumn = event.sortColumn
|
context.state.sortColumn = event.sortColumn
|
||||||
context.state.inverseSort = event.inverseSort
|
context.state.inverseSort = event.inverseSort
|
||||||
Config.update('miloRemote', context.state)
|
Config.update('miloRemote', context.state)
|
||||||
end
|
end
|
||||||
return UI.Grid.eventHandler(self, event)
|
return UI.Grid.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:transfer(item, count, msg)
|
function page:transfer(item, count, msg)
|
||||||
context:sendRequest({ request = 'transfer', item = item, count = count }, msg)
|
context:sendRequest({ request = 'transfer', item = item, count = count }, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'quit' then
|
if event.type == 'quit' then
|
||||||
UI:exitPullEvents()
|
UI:exitPullEvents()
|
||||||
|
|
||||||
elseif event.type == 'setup' then
|
elseif event.type == 'setup' then
|
||||||
self.setup.form:setValues(context.state)
|
self.setup.form:setValues(context.state)
|
||||||
self.setup:show()
|
self.setup:show()
|
||||||
|
|
||||||
elseif event.type == 'toggle_deposit' then
|
elseif event.type == 'toggle_deposit' then
|
||||||
context.state.deposit = not context.state.deposit
|
context.state.deposit = not context.state.deposit
|
||||||
Util.merge(self.statusBar.depositToggle, depositMode[context.state.deposit])
|
Util.merge(self.statusBar.depositToggle, depositMode[context.state.deposit])
|
||||||
self.statusBar:draw()
|
self.statusBar:draw()
|
||||||
context:setStatus(depositMode[context.state.deposit].help)
|
context:setStatus(depositMode[context.state.deposit].help)
|
||||||
context:notifyInfo(depositMode[context.state.deposit].help)
|
context:notifyInfo(depositMode[context.state.deposit].help)
|
||||||
Config.update('miloRemote', context.state)
|
Config.update('miloRemote', context.state)
|
||||||
|
|
||||||
elseif event.type == 'focus_change' then
|
elseif event.type == 'focus_change' then
|
||||||
context:setStatus(event.focused.help)
|
context:setStatus(event.focused.help)
|
||||||
|
|
||||||
elseif event.type == 'eject' or event.type == 'grid_select' then
|
elseif event.type == 'eject' or event.type == 'grid_select' then
|
||||||
local item = self.grid:getSelected()
|
local item = self.grid:getSelected()
|
||||||
if item then
|
if item then
|
||||||
self:transfer(item, 1, 'requesting 1 ...')
|
self:transfer(item, 1, 'requesting 1 ...')
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'eject_stack' then
|
elseif event.type == 'eject_stack' then
|
||||||
local item = self.grid:getSelected()
|
local item = self.grid:getSelected()
|
||||||
if item then
|
if item then
|
||||||
self:transfer(item, 'stack', 'requesting stack ...')
|
self:transfer(item, 'stack', 'requesting stack ...')
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'eject_all' then
|
elseif event.type == 'eject_all' then
|
||||||
local item = self.grid:getSelected()
|
local item = self.grid:getSelected()
|
||||||
if item then
|
if item then
|
||||||
self:transfer(item, 'all', 'requesting all ...')
|
self:transfer(item, 'all', 'requesting all ...')
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'eject_specified' then
|
elseif event.type == 'eject_specified' then
|
||||||
local item = self.grid:getSelected()
|
local item = self.grid:getSelected()
|
||||||
local count = tonumber(self.statusBar.amount.value)
|
local count = tonumber(self.statusBar.amount.value)
|
||||||
if item and count then
|
if item and count then
|
||||||
self.statusBar.amount:reset()
|
self.statusBar.amount:reset()
|
||||||
self:setFocus(self.statusBar.filter)
|
self:setFocus(self.statusBar.filter)
|
||||||
self:transfer(item, count, 'requesting ' .. count .. ' ...')
|
self:transfer(item, count, 'requesting ' .. count .. ' ...')
|
||||||
else
|
else
|
||||||
Sound.play('entity.villager.no')
|
Sound.play('entity.villager.no')
|
||||||
context:notifyError('nope ...')
|
context:notifyError('nope ...')
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'plugin' then
|
elseif event.type == 'plugin' then
|
||||||
event.button.callback(context)
|
event.button.callback(context)
|
||||||
|
|
||||||
elseif event.type == 'rescan' then
|
elseif event.type == 'rescan' then
|
||||||
self:setFocus(self.statusBar.filter)
|
self:setFocus(self.statusBar.filter)
|
||||||
self:refresh('scan')
|
self:refresh('scan')
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
|
|
||||||
elseif event.type == 'grid_up' then
|
elseif event.type == 'grid_up' then
|
||||||
self.grid:emit({ type = 'scroll_up' })
|
self.grid:emit({ type = 'scroll_up' })
|
||||||
|
|
||||||
elseif event.type == 'grid_down' then
|
elseif event.type == 'grid_down' then
|
||||||
self.grid:emit({ type = 'scroll_down' })
|
self.grid:emit({ type = 'scroll_down' })
|
||||||
|
|
||||||
elseif event.type == 'refresh' then
|
elseif event.type == 'refresh' then
|
||||||
self:setFocus(self.statusBar.filter)
|
self:setFocus(self.statusBar.filter)
|
||||||
self:refresh('list')
|
self:refresh('list')
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
|
|
||||||
elseif event.type == 'toggle_display' then
|
elseif event.type == 'toggle_display' then
|
||||||
context.state.displayMode = (context.state.displayMode + 1) % 2
|
context.state.displayMode = (context.state.displayMode + 1) % 2
|
||||||
Util.merge(event.button, displayModes[context.state.displayMode])
|
Util.merge(event.button, displayModes[context.state.displayMode])
|
||||||
event.button:draw()
|
event.button:draw()
|
||||||
self:applyFilter()
|
self:applyFilter()
|
||||||
context:setStatus(event.button.help)
|
context:setStatus(event.button.help)
|
||||||
context:notifyInfo(event.button.help)
|
context:notifyInfo(event.button.help)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
Config.update('miloRemote', context.state)
|
Config.update('miloRemote', context.state)
|
||||||
|
|
||||||
elseif event.type == 'text_change' and event.element == self.statusBar.filter then
|
elseif event.type == 'text_change' and event.element == self.statusBar.filter then
|
||||||
self.filter = event.text
|
self.filter = event.text
|
||||||
if #self.filter == 0 then
|
if #self.filter == 0 then
|
||||||
self.filter = nil
|
self.filter = nil
|
||||||
end
|
end
|
||||||
self:applyFilter()
|
self:applyFilter()
|
||||||
self.grid:setIndex(1)
|
self.grid:setIndex(1)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
|
|
||||||
else
|
else
|
||||||
UI.Page.eventHandler(self, event)
|
UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:enable()
|
function page:enable()
|
||||||
self:setFocus(self.statusBar.filter)
|
self:setFocus(self.statusBar.filter)
|
||||||
Util.merge(self.statusBar.depositToggle, depositMode[context.state.deposit])
|
Util.merge(self.statusBar.depositToggle, depositMode[context.state.deposit])
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
if not context.state.server then
|
if not context.state.server then
|
||||||
self.setup.form:setValues(context.state)
|
self.setup.form:setValues(context.state)
|
||||||
self.setup:show()
|
self.setup:show()
|
||||||
end
|
end
|
||||||
Event.onTimeout(.1, function()
|
Event.onTimeout(.1, function()
|
||||||
self:refresh('list')
|
self:refresh('list')
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function splitKey(key)
|
local function splitKey(key)
|
||||||
local t = Util.split(key, '(.-):')
|
local t = Util.split(key, '(.-):')
|
||||||
local item = { }
|
local item = { }
|
||||||
if #t[#t] > 8 then
|
if #t[#t] > 8 then
|
||||||
item.nbtHash = table.remove(t)
|
item.nbtHash = table.remove(t)
|
||||||
end
|
end
|
||||||
item.damage = tonumber(table.remove(t))
|
item.damage = tonumber(table.remove(t))
|
||||||
item.name = table.concat(t, ':')
|
item.name = table.concat(t, ':')
|
||||||
return item
|
return item
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:expandList(list)
|
function page:expandList(list)
|
||||||
local t = { }
|
local t = { }
|
||||||
for k,v in pairs(list) do
|
for k,v in pairs(list) do
|
||||||
local item = splitKey(k)
|
local item = splitKey(k)
|
||||||
item.has_recipe, item.count, item.displayName = v:match('(%d+):(%d+):(.+)')
|
item.has_recipe, item.count, item.displayName = v:match('(%d+):(%d+):(.+)')
|
||||||
item.count = tonumber(item.count) or 0
|
item.count = tonumber(item.count) or 0
|
||||||
item.lname = item.displayName:lower()
|
item.lname = item.displayName:lower()
|
||||||
item.has_recipe = item.has_recipe == '1'
|
item.has_recipe = item.has_recipe == '1'
|
||||||
t[k] = item
|
t[k] = item
|
||||||
end
|
end
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:refresh(requestType)
|
function page:refresh(requestType)
|
||||||
context:sendRequest({ request = requestType }, 'refreshing...')
|
context:sendRequest({ request = requestType }, 'refreshing...')
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:applyFilter()
|
function page:applyFilter()
|
||||||
local function filterItems(t, filter, displayMode)
|
local function filterItems(t, filter, displayMode)
|
||||||
self.grid.sortColumn = context.state.sortColumn or 'count'
|
self.grid.sortColumn = context.state.sortColumn or 'count'
|
||||||
self.grid.inverseSort = context.state.inverseSort
|
self.grid.inverseSort = context.state.inverseSort
|
||||||
|
|
||||||
if filter then
|
if filter then
|
||||||
local r = { }
|
local r = { }
|
||||||
filter = filter:lower()
|
filter = filter:lower()
|
||||||
self.grid.sortColumn = 'score'
|
self.grid.sortColumn = 'score'
|
||||||
self.grid.inverseSort = true
|
self.grid.inverseSort = true
|
||||||
|
|
||||||
for _,v in pairs(t) do
|
for _,v in pairs(t) do
|
||||||
v.score = fuzzy(v.lname, filter)
|
v.score = fuzzy(v.lname, filter)
|
||||||
if v.score then
|
if v.score then
|
||||||
if v.count > 0 then
|
if v.count > 0 then
|
||||||
v.score = v.score + 1
|
v.score = v.score + 1
|
||||||
end
|
end
|
||||||
table.insert(r, v)
|
table.insert(r, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return r
|
return r
|
||||||
|
|
||||||
elseif displayMode > 0 then
|
elseif displayMode > 0 then
|
||||||
local r = { }
|
local r = { }
|
||||||
|
|
||||||
for _,v in pairs(t) do
|
for _,v in pairs(t) do
|
||||||
if v.count > 0 then
|
if v.count > 0 then
|
||||||
table.insert(r, v)
|
table.insert(r, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
local t = filterItems(self.items, self.filter, context.state.displayMode)
|
local t = filterItems(self.items, self.filter, context.state.displayMode)
|
||||||
self.grid:setValues(t)
|
self.grid:setValues(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
context.page = page
|
context.page = page
|
||||||
|
|
||||||
function context:setStatus(status)
|
function context:setStatus(status)
|
||||||
page.menuBar.infoBar.values = status
|
page.menuBar.infoBar.values = status
|
||||||
page.menuBar.infoBar:draw()
|
page.menuBar.infoBar:draw()
|
||||||
page:sync()
|
page:sync()
|
||||||
end
|
end
|
||||||
|
|
||||||
function context:notifySuccess(status)
|
function context:notifySuccess(status)
|
||||||
page.notification:success(status)
|
page.notification:success(status)
|
||||||
page:sync()
|
page:sync()
|
||||||
end
|
end
|
||||||
|
|
||||||
function context:notifyInfo(status)
|
function context:notifyInfo(status)
|
||||||
page.notification:info(status)
|
page.notification:info(status)
|
||||||
page:sync()
|
page:sync()
|
||||||
end
|
end
|
||||||
|
|
||||||
function context:notifyError(status)
|
function context:notifyError(status)
|
||||||
page.notification:error(status)
|
page.notification:error(status)
|
||||||
page:sync()
|
page:sync()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function processMessages(s)
|
local function processMessages(s)
|
||||||
Event.addRoutine(function()
|
Event.addRoutine(function()
|
||||||
s.co = coroutine.running()
|
s.co = coroutine.running()
|
||||||
repeat
|
repeat
|
||||||
local response = s:read()
|
local response = s:read()
|
||||||
if not response then
|
if not response then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
local h = context.responseHandlers[response.type]
|
local h = context.responseHandlers[response.type]
|
||||||
if h then
|
if h then
|
||||||
h(response)
|
h(response)
|
||||||
end
|
end
|
||||||
if response.msg then
|
if response.msg then
|
||||||
context:notifyInfo(response.msg)
|
context:notifyInfo(response.msg)
|
||||||
end
|
end
|
||||||
until not s.connected
|
until not s.connected
|
||||||
|
|
||||||
s:close()
|
s:close()
|
||||||
s = nil
|
s = nil
|
||||||
context:notifyError('disconnected ...')
|
context:notifyError('disconnected ...')
|
||||||
Sound.play('entity.villager.no')
|
Sound.play('entity.villager.no')
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function context:sendRequest(data, statusMsg)
|
function context:sendRequest(data, statusMsg)
|
||||||
if not context.state.server then
|
if not context.state.server then
|
||||||
self:notifyError('Invalid configuration')
|
self:notifyError('Invalid configuration')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local player = getPlayerName()
|
local player = getPlayerName()
|
||||||
if not player then
|
if not player then
|
||||||
self:notifyError('Missing neural or introspection')
|
self:notifyError('Missing neural or introspection')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local success
|
local success
|
||||||
sync(page, function()
|
sync(page, function()
|
||||||
local msg
|
local msg
|
||||||
for _ = 1, 2 do
|
for _ = 1, 2 do
|
||||||
if not context.socket or not context.socket.connected then
|
if not context.socket or not context.socket.connected then
|
||||||
self:notifyInfo('connecting ...')
|
self:notifyInfo('connecting ...')
|
||||||
context.socket, msg = Socket.connect(context.state.server, 4242)
|
context.socket, msg = Socket.connect(context.state.server, 4242)
|
||||||
if context.socket then
|
if context.socket then
|
||||||
context.socket:write(player)
|
context.socket:write(player)
|
||||||
local r = context.socket:read(2)
|
local r = context.socket:read(2)
|
||||||
if r and not r.msg then
|
if r and not r.msg then
|
||||||
self:notifySuccess('connected ...')
|
self:notifySuccess('connected ...')
|
||||||
processMessages(context.socket)
|
processMessages(context.socket)
|
||||||
else
|
else
|
||||||
msg = r and r.msg or 'Timed out'
|
msg = r and r.msg or 'Timed out'
|
||||||
context.socket:close()
|
context.socket:close()
|
||||||
context.socket = nil
|
context.socket = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if context.socket then
|
if context.socket then
|
||||||
if statusMsg then
|
if statusMsg then
|
||||||
self:notifyInfo(statusMsg)
|
self:notifyInfo(statusMsg)
|
||||||
end
|
end
|
||||||
if context.socket:write(data) then
|
if context.socket:write(data) then
|
||||||
success = true
|
success = true
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
context.socket:close()
|
context.socket:close()
|
||||||
context.socket = nil
|
context.socket = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:notifyError(msg or 'Failed to connect')
|
self:notifyError(msg or 'Failed to connect')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
return success
|
return success
|
||||||
end
|
end
|
||||||
|
|
||||||
function context:getState(key)
|
function context:getState(key)
|
||||||
return self.state[key]
|
return self.state[key]
|
||||||
end
|
end
|
||||||
|
|
||||||
function context:setState(key, value)
|
function context:setState(key, value)
|
||||||
self.state[key] = value
|
self.state[key] = value
|
||||||
Config.update('miloRemote', self.state)
|
Config.update('miloRemote', self.state)
|
||||||
end
|
end
|
||||||
|
|
||||||
context.responseHandlers['received'] = function(response)
|
context.responseHandlers['received'] = function(response)
|
||||||
Sound.play('entity.item.pickup')
|
Sound.play('entity.item.pickup')
|
||||||
local ritem = page.items[response.key]
|
local ritem = page.items[response.key]
|
||||||
if ritem then
|
if ritem then
|
||||||
ritem.count = response.count
|
ritem.count = response.count
|
||||||
if page.enabled then
|
if page.enabled then
|
||||||
page.grid:draw()
|
page.grid:draw()
|
||||||
page:sync()
|
page:sync()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context.responseHandlers['list'] = function(response)
|
context.responseHandlers['list'] = function(response)
|
||||||
page.items = page:expandList(response.list)
|
page.items = page:expandList(response.list)
|
||||||
page:applyFilter()
|
page:applyFilter()
|
||||||
if page.enabled then
|
if page.enabled then
|
||||||
page.grid:draw()
|
page.grid:draw()
|
||||||
page.grid:sync()
|
page.grid:sync()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context.responseHandlers['transfer'] = function(response)
|
context.responseHandlers['transfer'] = function(response)
|
||||||
if response.count > 0 then
|
if response.count > 0 then
|
||||||
Sound.play('entity.item.pickup')
|
Sound.play('entity.item.pickup')
|
||||||
local item = page.items[response.key]
|
local item = page.items[response.key]
|
||||||
if item then
|
if item then
|
||||||
item.count = response.current
|
item.count = response.current
|
||||||
if page.enabled then
|
if page.enabled then
|
||||||
page.grid:draw()
|
page.grid:draw()
|
||||||
page:sync()
|
page:sync()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if response.craft then
|
if response.craft then
|
||||||
if response.craft > 0 then
|
if response.craft > 0 then
|
||||||
context:notifyInfo(response.craft .. ' crafting ...')
|
context:notifyInfo(response.craft .. ' crafting ...')
|
||||||
elseif response.craft + response.count < response.requested then
|
elseif response.craft + response.count < response.requested then
|
||||||
if response.craft + response.count == 0 then
|
if response.craft + response.count == 0 then
|
||||||
Sound.play('entity.villager.no')
|
Sound.play('entity.villager.no')
|
||||||
end
|
end
|
||||||
context:notifyInfo((response.craft + response.count) .. ' available ...')
|
context:notifyInfo((response.craft + response.count) .. ' available ...')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function loadDirectory(dir)
|
local function loadDirectory(dir)
|
||||||
local dropdown = {
|
local dropdown = {
|
||||||
{ text = 'Setup', event = 'setup' },
|
{ text = 'Setup', event = 'setup' },
|
||||||
{ spacer = true },
|
{ spacer = true },
|
||||||
{
|
{
|
||||||
text = 'Rescan storage',
|
text = 'Rescan storage',
|
||||||
event = 'rescan',
|
event = 'rescan',
|
||||||
help = 'Rescan all inventories'
|
help = 'Rescan all inventories'
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, file in pairs(fs.list(dir)) do
|
for _, file in pairs(fs.list(dir)) do
|
||||||
local s, m = Util.run(_ENV, fs.combine(dir, file), context)
|
local s, m = Util.run(_ENV, fs.combine(dir, file), context)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G.printError('Error loading: ' .. file)
|
_G.printError('Error loading: ' .. file)
|
||||||
error(m or 'Unknown error')
|
error(m or 'Unknown error')
|
||||||
elseif s and m then
|
elseif s and m then
|
||||||
table.insert(dropdown, {
|
table.insert(dropdown, {
|
||||||
text = m.menuItem,
|
text = m.menuItem,
|
||||||
event = 'plugin',
|
event = 'plugin',
|
||||||
callback = m.callback,
|
callback = m.callback,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
page.menuBar.config:add({ dropmenu = UI.DropMenu { buttons = dropdown } })
|
page.menuBar.config:add({ dropmenu = UI.DropMenu { buttons = dropdown } })
|
||||||
end
|
end
|
||||||
|
|
||||||
local programDir = fs.getDir(shell.getRunningProgram())
|
local programDir = fs.getDir(shell.getRunningProgram())
|
||||||
@@ -528,5 +528,5 @@ UI:setPage(page)
|
|||||||
UI:pullEvents()
|
UI:pullEvents()
|
||||||
|
|
||||||
if context.socket then
|
if context.socket then
|
||||||
context.socket:close()
|
context.socket:close()
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -35,6 +35,39 @@ local function makeRecipeKey(item)
|
|||||||
return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':')
|
return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function convert(ingredient)
|
||||||
|
return type(ingredient) == 'table' and ingredient or {
|
||||||
|
key = ingredient,
|
||||||
|
count = 1,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getCraftingTool(storage, item)
|
||||||
|
local items = storage:listItems()
|
||||||
|
|
||||||
|
for _,v in pairs(items) do
|
||||||
|
if item.name == v.name and
|
||||||
|
(not item.damage or item.damage == v.damage) and
|
||||||
|
(not item.nbtHash or item.nbtHash == v.nbtHash) then
|
||||||
|
return v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return item
|
||||||
|
end
|
||||||
|
|
||||||
|
function Craft.ingedients(recipe)
|
||||||
|
local i = 0
|
||||||
|
local keys = Util.keys(recipe.ingredients)
|
||||||
|
return function()
|
||||||
|
i = i + 1
|
||||||
|
local a = keys[i]
|
||||||
|
if a then
|
||||||
|
return a, convert(recipe.ingredients[a])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function Craft.clearGrid(storage)
|
function Craft.clearGrid(storage)
|
||||||
local success = true
|
local success = true
|
||||||
local tasks = Tasks()
|
local tasks = Tasks()
|
||||||
@@ -74,8 +107,9 @@ end
|
|||||||
function Craft.sumIngredients(recipe)
|
function Craft.sumIngredients(recipe)
|
||||||
-- produces { ['minecraft:planks:0'] = 8 }
|
-- produces { ['minecraft:planks:0'] = 8 }
|
||||||
local t = { }
|
local t = { }
|
||||||
for _,item in pairs(recipe.ingredients) do
|
for _,entry in pairs(recipe.ingredients) do
|
||||||
t[item] = (t[item] or 0) + 1
|
local item = convert(entry)
|
||||||
|
t[item.key] = (t[item.key] or 0) + item.count
|
||||||
end
|
end
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
@@ -108,12 +142,13 @@ local function machineCraft(recipe, storage, machineName, request, count, item)
|
|||||||
if count > 0 then
|
if count > 0 then
|
||||||
local xferred = { }
|
local xferred = { }
|
||||||
for k,v in pairs(recipe.ingredients) do
|
for k,v in pairs(recipe.ingredients) do
|
||||||
local provided = storage:export(machine, k, count, splitKey(v))
|
local entry = convert(v)
|
||||||
|
local provided = storage:export(machine, k, count * entry.count, splitKey(entry.key))
|
||||||
xferred[k] = {
|
xferred[k] = {
|
||||||
key = v,
|
key = entry.key,
|
||||||
count = provided,
|
count = provided,
|
||||||
}
|
}
|
||||||
if provided ~= count then
|
if provided ~= count * entry.count then
|
||||||
-- take back out whatever we put in
|
-- take back out whatever we put in
|
||||||
for k2,v2 in pairs(xferred) do
|
for k2,v2 in pairs(xferred) do
|
||||||
if v2.count > 0 then
|
if v2.count > 0 then
|
||||||
@@ -143,6 +178,9 @@ local function turtleCraft(recipe, storage, request, count)
|
|||||||
|
|
||||||
for k,v in pairs(recipe.ingredients) do
|
for k,v in pairs(recipe.ingredients) do
|
||||||
local item = splitKey(v)
|
local item = splitKey(v)
|
||||||
|
if recipe.craftingTools and recipe.craftingTools[v] then
|
||||||
|
item = getCraftingTool(storage, item)
|
||||||
|
end
|
||||||
tasks:add(function()
|
tasks:add(function()
|
||||||
if storage:export(storage.turtleInventory, k, count, item) ~= count then
|
if storage:export(storage.turtleInventory, k, count, item) ~= count then
|
||||||
request.status = 'rescan needed ?'
|
request.status = 'rescan needed ?'
|
||||||
@@ -185,7 +223,8 @@ local function turtleCraft(recipe, storage, request, count)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Craft.processPending(item, storage)
|
function Craft.processPending(item, storage)
|
||||||
for key, count in pairs(item.pending) do
|
for _, key in pairs(Util.keys(item.pending)) do
|
||||||
|
local count = item.pending[key]
|
||||||
local imported = storage.activity[key]
|
local imported = storage.activity[key]
|
||||||
if imported then
|
if imported then
|
||||||
local amount = math.min(imported, count)
|
local amount = math.min(imported, count)
|
||||||
@@ -238,7 +277,6 @@ end
|
|||||||
|
|
||||||
function Craft.craftRecipeInternal(recipe, count, storage, origItem, path)
|
function Craft.craftRecipeInternal(recipe, count, storage, origItem, path)
|
||||||
local request = origItem.ingredients[recipe.result]
|
local request = origItem.ingredients[recipe.result]
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
if origItem.pending[recipe.result] then
|
if origItem.pending[recipe.result] then
|
||||||
request.status = 'processing'
|
request.status = 'processing'
|
||||||
@@ -425,23 +463,24 @@ function Craft.getCraftableAmount(inRecipe, inCount, items, missing)
|
|||||||
local canCraft = 0
|
local canCraft = 0
|
||||||
|
|
||||||
for _ = 1, count do
|
for _ = 1, count do
|
||||||
for _,item in pairs(recipe.ingredients) do
|
for _,entry in pairs(recipe.ingredients) do
|
||||||
local summedItem = summedItems[item] or Craft.getItemCount(items, item)
|
local item = convert(entry)
|
||||||
|
local summedItem = summedItems[item.key] or Craft.getItemCount(items, item.key)
|
||||||
|
|
||||||
local irecipe = findValidRecipe(item, path)
|
local irecipe = findValidRecipe(item.key, path)
|
||||||
if irecipe and summedItem <= 0 then
|
if irecipe and summedItem <= 0 then
|
||||||
local p = Util.shallowCopy(path)
|
local p = Util.shallowCopy(path)
|
||||||
p[irecipe.result] = true
|
p[irecipe.result] = true
|
||||||
summedItem = summedItem + sumItems(irecipe, summedItems, 1, p)
|
summedItem = summedItem + sumItems(irecipe, summedItems, item.count, p)
|
||||||
end
|
end
|
||||||
if summedItem <= 0 then
|
if summedItem <= 0 then
|
||||||
if missing and not irecipe then
|
if missing and not irecipe then
|
||||||
missing.name = item
|
missing.name = item.key
|
||||||
end
|
end
|
||||||
return canCraft
|
return canCraft
|
||||||
end
|
end
|
||||||
if not recipe.craftingTools or not recipe.craftingTools[item] then
|
if not recipe.craftingTools or not recipe.craftingTools[item.key] then
|
||||||
summedItems[item] = summedItem - 1
|
summedItems[item.key] = summedItem - item.count
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
canCraft = canCraft + recipe.count
|
canCraft = canCraft + recipe.count
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ local _find = string.find
|
|||||||
local _max = math.max
|
local _max = math.max
|
||||||
|
|
||||||
return function(str, pattern)
|
return function(str, pattern)
|
||||||
local start = _find(str, pattern, 1, true)
|
local start = _find(str, pattern, 1, true)
|
||||||
if start then
|
if start then
|
||||||
-- All letters before the current one are considered leading, so add them to our penalty
|
-- All letters before the current one are considered leading, so add them to our penalty
|
||||||
return SCORE_WEIGHT + _max(LEADING_LETTER_PENALTY * (start - 1), LEADING_LETTER_PENALTY_MAX)
|
return SCORE_WEIGHT + _max(LEADING_LETTER_PENALTY * (start - 1), LEADING_LETTER_PENALTY_MAX)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -224,8 +224,8 @@ function Milo:eject(item, count)
|
|||||||
total = total + amount
|
total = total + amount
|
||||||
count = count - amount
|
count = count - amount
|
||||||
|
|
||||||
--Sound.play('ui.button.click')
|
Sound.play('ui.button.click')
|
||||||
Sound.play('entity.illusion_illager.death', .3)
|
--Sound.play('entity.illusion_illager.death', .3)
|
||||||
turtle.emptyInventory()
|
turtle.emptyInventory()
|
||||||
end
|
end
|
||||||
return total
|
return total
|
||||||
@@ -273,6 +273,7 @@ function Milo:learnRecipe()
|
|||||||
local tool = Util.shallowCopy(v2)
|
local tool = Util.shallowCopy(v2)
|
||||||
if tool.maxDamage > 0 then
|
if tool.maxDamage > 0 then
|
||||||
tool.damage = '*'
|
tool.damage = '*'
|
||||||
|
v2.damage = '*'
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
|
|||||||
@@ -7,46 +7,46 @@ local os = _G.os
|
|||||||
local Adapter = class(Mini)
|
local Adapter = class(Mini)
|
||||||
|
|
||||||
function Adapter:init(args)
|
function Adapter:init(args)
|
||||||
Mini.init(self, args)
|
Mini.init(self, args)
|
||||||
|
|
||||||
self._rawList = self.list
|
self._rawList = self.list
|
||||||
|
|
||||||
function self.list()
|
function self.list()
|
||||||
-- wait for up to 1 sec until any items that have been inserted
|
-- wait for up to 1 sec until any items that have been inserted
|
||||||
-- into interface are added to the system
|
-- into interface are added to the system
|
||||||
for _ = 0, 20 do
|
for _ = 0, 20 do
|
||||||
if #self._rawList() == 0 then
|
if #self._rawList() == 0 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
os.sleep(0)
|
os.sleep(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
local list = { }
|
local list = { }
|
||||||
for _, v in pairs(self.listAvailableItems()) do
|
for _, v in pairs(self.listAvailableItems()) do
|
||||||
list[itemDB:makeKey(v)] = v
|
list[itemDB:makeKey(v)] = v
|
||||||
end
|
end
|
||||||
return list
|
return list
|
||||||
end
|
end
|
||||||
|
|
||||||
function self.getItemMeta(key)
|
function self.getItemMeta(key)
|
||||||
local item = self.findItem(itemDB:splitKey(key))
|
local item = self.findItem(itemDB:splitKey(key))
|
||||||
if item and item.getMetadata then
|
if item and item.getMetadata then
|
||||||
return item.getMetadata()
|
return item.getMetadata()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function self.pushItems(target, key, amount, slot)
|
function self.pushItems(target, key, amount, slot)
|
||||||
local item = self.findItem(itemDB:splitKey(key))
|
local item = self.findItem(itemDB:splitKey(key))
|
||||||
if item and item.export then
|
if item and item.export then
|
||||||
return item.export(target, amount, slot)
|
return item.export(target, amount, slot)
|
||||||
end
|
end
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function self.pullItems(target, key, amount, slot)
|
function self.pullItems(target, key, amount, slot)
|
||||||
_G._syslog({target, key, amount, slot })
|
_G._syslog({target, key, amount, slot })
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -7,37 +7,37 @@ local device = _G.device
|
|||||||
local Adapter = class()
|
local Adapter = class()
|
||||||
|
|
||||||
function Adapter:init(args)
|
function Adapter:init(args)
|
||||||
if args.side then
|
if args.side then
|
||||||
local inventory = device[args.side]
|
local inventory = device[args.side]
|
||||||
if inventory then
|
if inventory then
|
||||||
Util.merge(self, inventory)
|
Util.merge(self, inventory)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Adapter:listItems(throttle)
|
function Adapter:listItems(throttle)
|
||||||
local cache = { }
|
local cache = { }
|
||||||
throttle = throttle or Util.throttle()
|
throttle = throttle or Util.throttle()
|
||||||
|
|
||||||
for k,v in pairs(self.list()) do
|
for k,v in pairs(self.list()) do
|
||||||
if v.count > 0 then
|
if v.count > 0 then
|
||||||
local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')
|
local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')
|
||||||
|
|
||||||
local entry = cache[key]
|
local entry = cache[key]
|
||||||
if entry then
|
if entry then
|
||||||
entry.count = entry.count + v.count
|
entry.count = entry.count + v.count
|
||||||
else
|
else
|
||||||
cache[key] = itemDB:get(v, function() return self.getItemMeta(k) end)
|
cache[key] = itemDB:get(v, function() return self.getItemMeta(k) end)
|
||||||
end
|
end
|
||||||
throttle()
|
throttle()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: cache number of slots, free slots, used slots
|
-- TODO: cache number of slots, free slots, used slots
|
||||||
-- useful for when inserting into chests
|
-- useful for when inserting into chests
|
||||||
-- ie. insert only if chest does not have item and has free slots
|
-- ie. insert only if chest does not have item and has free slots
|
||||||
|
|
||||||
self.cache = cache
|
self.cache = cache
|
||||||
end
|
end
|
||||||
|
|
||||||
return Adapter
|
return Adapter
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -7,68 +7,68 @@ local free = { }
|
|||||||
|
|
||||||
local function createTask(fn)
|
local function createTask(fn)
|
||||||
local task = table.remove(free)
|
local task = table.remove(free)
|
||||||
if not task then
|
if not task then
|
||||||
task = {
|
task = {
|
||||||
fn = fn,
|
fn = fn,
|
||||||
co = coroutine.create(function()
|
co = coroutine.create(function()
|
||||||
local args = { }
|
local args = { }
|
||||||
while true do
|
while true do
|
||||||
pcall(task.fn, table.unpack(args))
|
pcall(task.fn, table.unpack(args))
|
||||||
task.dead = true
|
task.dead = true
|
||||||
table.insert(free, task)
|
table.insert(free, task)
|
||||||
args = { coroutine.yield() }
|
args = { coroutine.yield() }
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
task.dead = nil
|
task.dead = nil
|
||||||
task.fn = fn
|
task.fn = fn
|
||||||
end
|
end
|
||||||
return task
|
return task
|
||||||
end
|
end
|
||||||
|
|
||||||
function TaskRunner:init(args)
|
function TaskRunner:init(args)
|
||||||
self.tasks = { }
|
self.tasks = { }
|
||||||
self.errorMsg = 'Task failed: '
|
self.errorMsg = 'Task failed: '
|
||||||
|
|
||||||
for k,v in pairs(args or { }) do
|
for k,v in pairs(args or { }) do
|
||||||
self[k] = v
|
self[k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function TaskRunner:add(fn)
|
function TaskRunner:add(fn)
|
||||||
table.insert(self.tasks, createTask(fn))
|
table.insert(self.tasks, createTask(fn))
|
||||||
end
|
end
|
||||||
|
|
||||||
function TaskRunner:run()
|
function TaskRunner:run()
|
||||||
if #self.tasks > 0 then
|
if #self.tasks > 0 then
|
||||||
local event = { }
|
local event = { }
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
for n = #self.tasks, 1, -1 do
|
for n = #self.tasks, 1, -1 do
|
||||||
local task = self.tasks[n]
|
local task = self.tasks[n]
|
||||||
if task.filter == nil or task.filter == event[1] or event[1] == "terminate" then
|
if task.filter == nil or task.filter == event[1] or event[1] == "terminate" then
|
||||||
local ok, param = coroutine.resume(task.co, table.unpack(event))
|
local ok, param = coroutine.resume(task.co, table.unpack(event))
|
||||||
if not ok then
|
if not ok then
|
||||||
self:onError(param)
|
self:onError(param)
|
||||||
else
|
else
|
||||||
task.filter = param
|
task.filter = param
|
||||||
end
|
end
|
||||||
if task.dead then
|
if task.dead then
|
||||||
table.remove(self.tasks, n)
|
table.remove(self.tasks, n)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #self.tasks == 0 then
|
if #self.tasks == 0 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
event = { os.pullEventRaw() }
|
event = { os.pullEventRaw() }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function TaskRunner:onError(msg)
|
function TaskRunner:onError(msg)
|
||||||
_G._syslog(msg.errorMsg .. msg)
|
_G._syslog(msg.errorMsg .. msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
return TaskRunner
|
return TaskRunner
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ reboot
|
|||||||
|
|
||||||
Use multiple brewing stands at once to brew potions.
|
Use multiple brewing stands at once to brew potions.
|
||||||
SETUP:
|
SETUP:
|
||||||
Place an introspection module into the turtles inventory.
|
Place an introspection module into the turtles inventory.
|
||||||
Connect turtle to milo network with a wired modem.
|
Connect turtle to milo network with a wired modem.
|
||||||
Connect turtle to a second wired modem that is connected to brewing stands ONLY.
|
Connect turtle to a second wired modem that is connected to brewing stands ONLY.
|
||||||
Add as many brewing stands as needed.
|
Add as many brewing stands as needed.
|
||||||
CONFIGURATION:
|
CONFIGURATION:
|
||||||
Set turtle as a "Generic Inventory"
|
Set turtle as a "Generic Inventory"
|
||||||
export blaze powder to slot 5
|
export blaze powder to slot 5
|
||||||
import from slots 7-9
|
import from slots 7-9
|
||||||
Use this turtle for machine crafting.
|
Use this turtle for machine crafting.
|
||||||
--]]
|
--]]
|
||||||
|
|
||||||
@@ -31,23 +31,23 @@ local turtle = _G.turtle
|
|||||||
local STARTUP_FILE = 'usr/autorun/brewArray.lua'
|
local STARTUP_FILE = 'usr/autorun/brewArray.lua'
|
||||||
|
|
||||||
local function equip(side, item, rawName)
|
local function equip(side, item, rawName)
|
||||||
local equipped = peripheral.getType(side)
|
local equipped = peripheral.getType(side)
|
||||||
|
|
||||||
if equipped == item then
|
if equipped == item then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if not turtle.equip(side, rawName or item) then
|
if not turtle.equip(side, rawName or item) then
|
||||||
if not turtle.selectSlotWithQuantity(0) then
|
if not turtle.selectSlotWithQuantity(0) then
|
||||||
error('No slots available')
|
error('No slots available')
|
||||||
end
|
end
|
||||||
turtle.equip(side)
|
turtle.equip(side)
|
||||||
if not turtle.equip(side, item) then
|
if not turtle.equip(side, item) then
|
||||||
error('Unable to equip ' .. item)
|
error('Unable to equip ' .. item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
turtle.select(1)
|
turtle.select(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
equip('left', 'plethora:introspection', 'plethora:module:0')
|
equip('left', 'plethora:introspection', 'plethora:module:0')
|
||||||
@@ -55,8 +55,8 @@ local intro = device['plethora:introspection']
|
|||||||
local inv = intro.getInventory()
|
local inv = intro.getInventory()
|
||||||
|
|
||||||
if not fs.exists(STARTUP_FILE) then
|
if not fs.exists(STARTUP_FILE) then
|
||||||
Util.writeFile(STARTUP_FILE,
|
Util.writeFile(STARTUP_FILE,
|
||||||
[[os.sleep(1)
|
[[os.sleep(1)
|
||||||
shell.openForegroundTab('packages/milo/apps/brewArray.lua')]])
|
shell.openForegroundTab('packages/milo/apps/brewArray.lua')]])
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -65,27 +65,27 @@ local localName
|
|||||||
|
|
||||||
print('detecting wired modem connected to brewing stands...')
|
print('detecting wired modem connected to brewing stands...')
|
||||||
for _, dev in pairs(device) do
|
for _, dev in pairs(device) do
|
||||||
if dev.type == 'wired_modem' then
|
if dev.type == 'wired_modem' then
|
||||||
local list = dev.getNamesRemote()
|
local list = dev.getNamesRemote()
|
||||||
brew = { }
|
brew = { }
|
||||||
localName = dev.getNameLocal()
|
localName = dev.getNameLocal()
|
||||||
for _, name in pairs(list) do
|
for _, name in pairs(list) do
|
||||||
if device[name].type ~= 'minecraft:brewing_stand' then
|
if device[name].type ~= 'minecraft:brewing_stand' then
|
||||||
brew = nil
|
brew = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
table.insert(brew, device[name])
|
table.insert(brew, device[name])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if brew then
|
if brew then
|
||||||
print('Using wired modem: ' .. dev.name)
|
print('Using wired modem: ' .. dev.name)
|
||||||
print('Brewing stands: ' .. #brew)
|
print('Brewing stands: ' .. #brew)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not brew then
|
if not brew then
|
||||||
error('Turtle must be connected to a second wired_modem connected to brewing stands only')
|
error('Turtle must be connected to a second wired_modem connected to brewing stands only')
|
||||||
end
|
end
|
||||||
|
|
||||||
_G.printError([[Program must be restarted if new brewing stands are added.]])
|
_G.printError([[Program must be restarted if new brewing stands are added.]])
|
||||||
@@ -95,66 +95,66 @@ _G.printError([[Program must be restarted if new brewing stands are added.]])
|
|||||||
-- slot 5: blaze powder
|
-- slot 5: blaze powder
|
||||||
|
|
||||||
local function process(list)
|
local function process(list)
|
||||||
local active = false
|
local active = false
|
||||||
|
|
||||||
for _, brewing in ipairs(Util.shallowCopy(brew)) do
|
for _, brewing in ipairs(Util.shallowCopy(brew)) do
|
||||||
local s, m = pcall(function()-- block updates can cause errors
|
local s, m = pcall(function()-- block updates can cause errors
|
||||||
local bs = brewing.list()
|
local bs = brewing.list()
|
||||||
|
|
||||||
local cooking = bs[1] and bs[2] and bs[3] and bs[4]
|
local cooking = bs[1] and bs[2] and bs[3] and bs[4]
|
||||||
if cooking then
|
if cooking then
|
||||||
active = true
|
active = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- fuel
|
-- fuel
|
||||||
local fuel = bs[5] or { count = 0 }
|
local fuel = bs[5] or { count = 0 }
|
||||||
if fuel.count < 1 then
|
if fuel.count < 1 then
|
||||||
print('fueling ' ..brewing.name)
|
print('fueling ' ..brewing.name)
|
||||||
brewing.pullItems(localName, 5, 1, 5)
|
brewing.pullItems(localName, 5, 1, 5)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not cooking and (bs[1] or bs[2] or bs[3] or bs[4]) then
|
if not cooking and (bs[1] or bs[2] or bs[3] or bs[4]) then
|
||||||
print('pulling from : ' .. brewing.name)
|
print('pulling from : ' .. brewing.name)
|
||||||
for i = 1, 4 do
|
for i = 1, 4 do
|
||||||
brewing.pushItems(localName, i, 1, 6 + i)
|
brewing.pushItems(localName, i, 1, 6 + i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not cooking and list[1] and list[2] and list[3] and list[4] then
|
if not cooking and list[1] and list[2] and list[3] and list[4] then
|
||||||
print('brewing : ' .. brewing.name)
|
print('brewing : ' .. brewing.name)
|
||||||
for i = 1, 4 do
|
for i = 1, 4 do
|
||||||
brewing.pullItems(localName, i, 1, i)
|
brewing.pullItems(localName, i, 1, i)
|
||||||
list[i].count = list[i].count - 1
|
list[i].count = list[i].count - 1
|
||||||
if list[i].count == 0 then
|
if list[i].count == 0 then
|
||||||
list[i] = nil
|
list[i] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- push brewing stand to end of list
|
-- push brewing stand to end of list
|
||||||
Util.removeByValue(brew, brewing)
|
Util.removeByValue(brew, brewing)
|
||||||
table.insert(brew, brewing)
|
table.insert(brew, brewing)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G.printError(m)
|
_G.printError(m)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return active
|
return active
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.on('turtle_inventory', function()
|
Event.on('turtle_inventory', function()
|
||||||
while true do
|
while true do
|
||||||
if not process(inv.list()) then
|
if not process(inv.list()) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
os.sleep(3)
|
os.sleep(3)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.onInterval(5, function()
|
Event.onInterval(5, function()
|
||||||
-- for some reason, it keeps stalling ...
|
-- for some reason, it keeps stalling ...
|
||||||
os.queueEvent('turtle_inventory')
|
os.queueEvent('turtle_inventory')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
os.queueEvent('turtle_inventory')
|
os.queueEvent('turtle_inventory')
|
||||||
|
|||||||
@@ -7,22 +7,22 @@ local turtle = _G.turtle
|
|||||||
local STARTUP_FILE = 'usr/autorun/cobbleGen.lua'
|
local STARTUP_FILE = 'usr/autorun/cobbleGen.lua'
|
||||||
|
|
||||||
if not fs.exists(STARTUP_FILE) then
|
if not fs.exists(STARTUP_FILE) then
|
||||||
Util.writeFile(STARTUP_FILE,
|
Util.writeFile(STARTUP_FILE,
|
||||||
[[os.sleep(1)
|
[[os.sleep(1)
|
||||||
shell.openForegroundTab('packages/milo/apps/cobblegen')]])
|
shell.openForegroundTab('packages/milo/apps/cobblegen')]])
|
||||||
end
|
end
|
||||||
|
|
||||||
os.queueEvent('turtle_inventory')
|
os.queueEvent('turtle_inventory')
|
||||||
while true do
|
while true do
|
||||||
print('waiting')
|
print('waiting')
|
||||||
os.pullEvent('turtle_inventory')
|
os.pullEvent('turtle_inventory')
|
||||||
print('waiting for cobble')
|
print('waiting for cobble')
|
||||||
for _ = 1, 20 do
|
for _ = 1, 20 do
|
||||||
if turtle.inspectDown() then
|
if turtle.inspectDown() then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
os.sleep(.1)
|
os.sleep(.1)
|
||||||
end
|
end
|
||||||
print('digging')
|
print('digging')
|
||||||
turtle.digDown()
|
turtle.digDown()
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,48 +13,48 @@ local turtle = _G.turtle
|
|||||||
local STARTUP_FILE = 'usr/autorun/enderchest.lua'
|
local STARTUP_FILE = 'usr/autorun/enderchest.lua'
|
||||||
|
|
||||||
local enderChest = device.manipulator and
|
local enderChest = device.manipulator and
|
||||||
device.manipulator.getEnder or
|
device.manipulator.getEnder or
|
||||||
error('Must be connected to a manipulator with a bound introspection module')
|
error('Must be connected to a manipulator with a bound introspection module')
|
||||||
|
|
||||||
if not fs.exists(STARTUP_FILE) then
|
if not fs.exists(STARTUP_FILE) then
|
||||||
Util.writeFile(STARTUP_FILE,
|
Util.writeFile(STARTUP_FILE,
|
||||||
[[os.sleep(1)
|
[[os.sleep(1)
|
||||||
shell.openForegroundTab('packages/milo/apps/enderchest')]])
|
shell.openForegroundTab('packages/milo/apps/enderchest')]])
|
||||||
end
|
end
|
||||||
|
|
||||||
local directions = Util.transpose {
|
local directions = Util.transpose {
|
||||||
'north', 'south', 'east', 'west', 'up', 'down'
|
'north', 'south', 'east', 'west', 'up', 'down'
|
||||||
}
|
}
|
||||||
|
|
||||||
Event.on('turtle_inventory', function()
|
Event.on('turtle_inventory', function()
|
||||||
local s, m = pcall(function()
|
local s, m = pcall(function()
|
||||||
local direction
|
local direction
|
||||||
|
|
||||||
for _, d in pairs(enderChest().getTransferLocations()) do
|
for _, d in pairs(enderChest().getTransferLocations()) do
|
||||||
if directions[d] then
|
if directions[d] then
|
||||||
direction = d
|
direction = d
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not direction then
|
if not direction then
|
||||||
error('Unable to determine transfer direction')
|
error('Unable to determine transfer direction')
|
||||||
end
|
end
|
||||||
|
|
||||||
turtle.eachFilledSlot(function(s)
|
turtle.eachFilledSlot(function(s)
|
||||||
print('sending')
|
print('sending')
|
||||||
enderChest().pullItems(direction, s.index)
|
enderChest().pullItems(direction, s.index)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G.printError(m)
|
_G.printError(m)
|
||||||
end
|
end
|
||||||
print('idle')
|
print('idle')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.onInterval(5, function()
|
Event.onInterval(5, function()
|
||||||
-- for some reason, it keeps stalling ...
|
-- for some reason, it keeps stalling ...
|
||||||
os.queueEvent('turtle_inventory')
|
os.queueEvent('turtle_inventory')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
os.queueEvent('turtle_inventory')
|
os.queueEvent('turtle_inventory')
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
Use multiple furnaces at once to smelt items.
|
Use multiple furnaces at once to smelt items.
|
||||||
|
|
||||||
SETUP:
|
SETUP:
|
||||||
Place an introspection module into the turtles inventory.
|
Place an introspection module into the turtles inventory.
|
||||||
Connect turtle to milo network with a wired modem.
|
Connect turtle to milo network with a wired modem.
|
||||||
Connect turtle to a second wired modem that is connected to furnaces ONLY.
|
Connect turtle to a second wired modem that is connected to furnaces ONLY.
|
||||||
Add as many furnaces as needed.
|
Add as many furnaces as needed.
|
||||||
|
|
||||||
CONFIGURATION:
|
CONFIGURATION:
|
||||||
Set turtle as a "Generic Inventory"
|
Set turtle as a "Generic Inventory"
|
||||||
export coal to slot 2
|
export coal to slot 2
|
||||||
import from slot 3
|
import from slot 3
|
||||||
|
|
||||||
Use this turtle for machine crafting.
|
Use this turtle for machine crafting.
|
||||||
--]]
|
--]]
|
||||||
@@ -31,23 +31,23 @@ local FUEL_SLOT = 2
|
|||||||
local OUTPUT_SLOT = 3
|
local OUTPUT_SLOT = 3
|
||||||
|
|
||||||
local function equip(side, item, rawName)
|
local function equip(side, item, rawName)
|
||||||
local equipped = peripheral.getType(side)
|
local equipped = peripheral.getType(side)
|
||||||
|
|
||||||
if equipped == item then
|
if equipped == item then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if not turtle.equip(side, rawName or item) then
|
if not turtle.equip(side, rawName or item) then
|
||||||
if not turtle.selectSlotWithQuantity(0) then
|
if not turtle.selectSlotWithQuantity(0) then
|
||||||
error('No slots available')
|
error('No slots available')
|
||||||
end
|
end
|
||||||
turtle.equip(side)
|
turtle.equip(side)
|
||||||
if not turtle.equip(side, item) then
|
if not turtle.equip(side, item) then
|
||||||
error('Unable to equip ' .. item)
|
error('Unable to equip ' .. item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
turtle.select(1)
|
turtle.select(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
equip('left', 'plethora:introspection', 'plethora:module:0')
|
equip('left', 'plethora:introspection', 'plethora:module:0')
|
||||||
@@ -55,8 +55,8 @@ local intro = device['plethora:introspection']
|
|||||||
local inv = intro.getInventory()
|
local inv = intro.getInventory()
|
||||||
|
|
||||||
if not fs.exists(STARTUP_FILE) then
|
if not fs.exists(STARTUP_FILE) then
|
||||||
Util.writeFile(STARTUP_FILE,
|
Util.writeFile(STARTUP_FILE,
|
||||||
[[os.sleep(1)
|
[[os.sleep(1)
|
||||||
shell.openForegroundTab('packages/milo/apps/furni')]])
|
shell.openForegroundTab('packages/milo/apps/furni')]])
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -65,121 +65,121 @@ local localName
|
|||||||
|
|
||||||
print('detecting wired modem connected to furnaces...')
|
print('detecting wired modem connected to furnaces...')
|
||||||
for _, dev in pairs(device) do
|
for _, dev in pairs(device) do
|
||||||
if dev.type == 'wired_modem' and dev.getNameLocal then
|
if dev.type == 'wired_modem' and dev.getNameLocal then
|
||||||
local list = dev.getNamesRemote()
|
local list = dev.getNamesRemote()
|
||||||
furnaces = { }
|
furnaces = { }
|
||||||
localName = dev.getNameLocal()
|
localName = dev.getNameLocal()
|
||||||
for _, name in pairs(list) do
|
for _, name in pairs(list) do
|
||||||
if device[name].type ~= 'minecraft:furnace' then
|
if device[name].type ~= 'minecraft:furnace' then
|
||||||
furnaces = nil
|
furnaces = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
table.insert(furnaces, {
|
table.insert(furnaces, {
|
||||||
dev = device[name],
|
dev = device[name],
|
||||||
list = device[name].list(),
|
list = device[name].list(),
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if furnaces then
|
if furnaces then
|
||||||
print('Using wired modem: ' .. dev.name)
|
print('Using wired modem: ' .. dev.name)
|
||||||
print('Furnaces: ' .. #furnaces)
|
print('Furnaces: ' .. #furnaces)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not furnaces then
|
if not furnaces then
|
||||||
error('Turtle must be connected to a second wired_modem connected to furnaces only')
|
error('Turtle must be connected to a second wired_modem connected to furnaces only')
|
||||||
end
|
end
|
||||||
|
|
||||||
_G.printError([[Program must be restarted if new furnaces are added.]])
|
_G.printError([[Program must be restarted if new furnaces are added.]])
|
||||||
|
|
||||||
local function getSlot(furnace, slotNo)
|
local function getSlot(furnace, slotNo)
|
||||||
if not furnace.list[slotNo] then
|
if not furnace.list[slotNo] then
|
||||||
furnace.list[slotNo] = {
|
furnace.list[slotNo] = {
|
||||||
count = 0
|
count = 0
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
return furnace.list[slotNo]
|
return furnace.list[slotNo]
|
||||||
end
|
end
|
||||||
|
|
||||||
local function process(list)
|
local function process(list)
|
||||||
local inItem = list[INPUT_SLOT]
|
local inItem = list[INPUT_SLOT]
|
||||||
local inFuel = list[FUEL_SLOT]
|
local inFuel = list[FUEL_SLOT]
|
||||||
local inReturn = list[OUTPUT_SLOT] or { count = 0 }
|
local inReturn = list[OUTPUT_SLOT] or { count = 0 }
|
||||||
|
|
||||||
for _, furnace in ipairs(Util.shallowCopy(furnaces)) do
|
for _, furnace in ipairs(Util.shallowCopy(furnaces)) do
|
||||||
local s, m = pcall(function()
|
local s, m = pcall(function()
|
||||||
if furnace.list[INPUT_SLOT] and furnace.list[INPUT_SLOT].count > 0 then
|
if furnace.list[INPUT_SLOT] and furnace.list[INPUT_SLOT].count > 0 then
|
||||||
furnace.list = furnace.dev.list()
|
furnace.list = furnace.dev.list()
|
||||||
print('listing ' .. furnace.dev.name)
|
print('listing ' .. furnace.dev.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- items to cook
|
-- items to cook
|
||||||
local cooking = getSlot(furnace, INPUT_SLOT)
|
local cooking = getSlot(furnace, INPUT_SLOT)
|
||||||
if cooking.count < 64 and inItem and inItem.count > 0 then
|
if cooking.count < 64 and inItem and inItem.count > 0 then
|
||||||
if cooking.count == 0 or cooking.name == inItem.name then
|
if cooking.count == 0 or cooking.name == inItem.name then
|
||||||
print('cooking : ' .. furnace.dev.name)
|
print('cooking : ' .. furnace.dev.name)
|
||||||
local count = furnace.dev.pullItems(localName, INPUT_SLOT, SMELT_AMOUNT, INPUT_SLOT)
|
local count = furnace.dev.pullItems(localName, INPUT_SLOT, SMELT_AMOUNT, INPUT_SLOT)
|
||||||
|
|
||||||
if count > 0 then
|
if count > 0 then
|
||||||
inItem.count = inItem.count - count
|
inItem.count = inItem.count - count
|
||||||
|
|
||||||
cooking.name = inItem.name
|
cooking.name = inItem.name
|
||||||
cooking.count = cooking.count + count
|
cooking.count = cooking.count + count
|
||||||
|
|
||||||
-- push to end of queue
|
-- push to end of queue
|
||||||
Util.removeByValue(furnaces, furnace)
|
Util.removeByValue(furnaces, furnace)
|
||||||
table.insert(furnaces, furnace)
|
table.insert(furnaces, furnace)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- fuel
|
-- fuel
|
||||||
local fuel = getSlot(furnace, FUEL_SLOT)
|
local fuel = getSlot(furnace, FUEL_SLOT)
|
||||||
if fuel.count < 8 and inFuel and inFuel.count > 0 then
|
if fuel.count < 8 and inFuel and inFuel.count > 0 then
|
||||||
if fuel.count == 0 or fuel.name == inFuel.name then
|
if fuel.count == 0 or fuel.name == inFuel.name then
|
||||||
print('fueling ' .. furnace.dev.name)
|
print('fueling ' .. furnace.dev.name)
|
||||||
local count = furnace.dev.pullItems(localName, FUEL_SLOT, 8 - fuel.count, FUEL_SLOT)
|
local count = furnace.dev.pullItems(localName, FUEL_SLOT, 8 - fuel.count, FUEL_SLOT)
|
||||||
if count > 0 then
|
if count > 0 then
|
||||||
inFuel.count = inFuel.count - count
|
inFuel.count = inFuel.count - count
|
||||||
|
|
||||||
fuel.name = inFuel.name
|
fuel.name = inFuel.name
|
||||||
fuel.count = fuel.count + count
|
fuel.count = fuel.count + count
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = getSlot(furnace, OUTPUT_SLOT)
|
local result = getSlot(furnace, OUTPUT_SLOT)
|
||||||
if result.count > 0 then
|
if result.count > 0 then
|
||||||
if inReturn.count == 0 or result.name == inReturn.name then
|
if inReturn.count == 0 or result.name == inReturn.name then
|
||||||
print('pulling from : ' .. furnace.dev.name)
|
print('pulling from : ' .. furnace.dev.name)
|
||||||
local count = furnace.dev.pushItems(localName, OUTPUT_SLOT, result.count, OUTPUT_SLOT)
|
local count = furnace.dev.pushItems(localName, OUTPUT_SLOT, result.count, OUTPUT_SLOT)
|
||||||
|
|
||||||
if count > 0 then
|
if count > 0 then
|
||||||
result.count = result.count - count
|
result.count = result.count - count
|
||||||
if result.count == 0 then
|
if result.count == 0 then
|
||||||
furnace.list[OUTPUT_SLOT] = nil
|
furnace.list[OUTPUT_SLOT] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
inReturn.name = result.name
|
inReturn.name = result.name
|
||||||
inReturn.count = inReturn.count + count
|
inReturn.count = inReturn.count + count
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G.printError(m)
|
_G.printError(m)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.on('turtle_inventory', function()
|
Event.on('turtle_inventory', function()
|
||||||
process(inv.list())
|
process(inv.list())
|
||||||
print('idle')
|
print('idle')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.onInterval(3, function()
|
Event.onInterval(3, function()
|
||||||
os.queueEvent('turtle_inventory')
|
os.queueEvent('turtle_inventory')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
os.queueEvent('turtle_inventory')
|
os.queueEvent('turtle_inventory')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
--[[
|
--[[
|
||||||
For initially setting up large amounts of storage chests.
|
For initially setting up large amounts of storage chests.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local Util = require('util')
|
local Util = require('util')
|
||||||
@@ -11,17 +11,17 @@ local st = args[1] or error('Specify a storage type (ie. minecraft:chest)')
|
|||||||
|
|
||||||
local config = { }
|
local config = { }
|
||||||
peripheral.find(st, function(n)
|
peripheral.find(st, function(n)
|
||||||
config[n] = {
|
config[n] = {
|
||||||
name = n,
|
name = n,
|
||||||
category = 'storage',
|
category = 'storage',
|
||||||
mtype = 'storage',
|
mtype = 'storage',
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
print('Found ' .. Util.size(config))
|
print('Found ' .. Util.size(config))
|
||||||
|
|
||||||
if Util.size(config) == 0 then
|
if Util.size(config) == 0 then
|
||||||
error('Invalid peripheral type')
|
error('Invalid peripheral type')
|
||||||
end
|
end
|
||||||
|
|
||||||
Util.writeTable('usr/config/storageGen', config)
|
Util.writeTable('usr/config/storageGen', config)
|
||||||
|
|||||||
@@ -4,90 +4,90 @@ local UI = require('ui')
|
|||||||
local turtle = _G.turtle
|
local turtle = _G.turtle
|
||||||
|
|
||||||
local learnPage = UI.Page {
|
local learnPage = UI.Page {
|
||||||
titleBar = UI.TitleBar { title = 'Learn Recipe' },
|
titleBar = UI.TitleBar { title = 'Learn Recipe' },
|
||||||
wizard = UI.Wizard {
|
wizard = UI.Wizard {
|
||||||
y = 2, ey = -2,
|
y = 2, ey = -2,
|
||||||
pages = {
|
pages = {
|
||||||
general = UI.WizardPage {
|
general = UI.WizardPage {
|
||||||
index = 1,
|
index = 1,
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
x = 2, ex = -2, y = 2, ey = -2,
|
x = 2, ex = -2, y = 2, ey = -2,
|
||||||
disableHeader = true,
|
disableHeader = true,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Name', key = 'name'},
|
{ heading = 'Name', key = 'name'},
|
||||||
},
|
},
|
||||||
sortColumn = 'name',
|
sortColumn = 'name',
|
||||||
},
|
},
|
||||||
accelerators = {
|
accelerators = {
|
||||||
grid_select = 'nextView',
|
grid_select = 'nextView',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
notification = UI.Notification { },
|
notification = UI.Notification { },
|
||||||
}
|
}
|
||||||
|
|
||||||
local general = learnPage.wizard.pages.general
|
local general = learnPage.wizard.pages.general
|
||||||
|
|
||||||
function general:validate()
|
function general:validate()
|
||||||
Milo:setState('learnType', self.grid:getSelected().value)
|
Milo:setState('learnType', self.grid:getSelected().value)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function learnPage:enable()
|
function learnPage:enable()
|
||||||
local t = { }
|
local t = { }
|
||||||
|
|
||||||
for _, page in pairs(self.wizard.pages) do
|
for _, page in pairs(self.wizard.pages) do
|
||||||
if page.validFor then
|
if page.validFor then
|
||||||
t[page.validFor] = {
|
t[page.validFor] = {
|
||||||
name = page.validFor,
|
name = page.validFor,
|
||||||
value = page.validFor,
|
value = page.validFor,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
general.grid:setValues(t)
|
general.grid:setValues(t)
|
||||||
general.grid:setSelected('name', Milo:getState('learnType') or '')
|
general.grid:setSelected('name', Milo:getState('learnType') or '')
|
||||||
|
|
||||||
Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' })
|
Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' })
|
||||||
|
|
||||||
self:focusFirst()
|
self:focusFirst()
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function learnPage:disable()
|
function learnPage:disable()
|
||||||
Milo:resumeCrafting({ key = 'gridInUse' })
|
Milo:resumeCrafting({ key = 'gridInUse' })
|
||||||
return UI.Page.disable(self)
|
return UI.Page.disable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function learnPage.wizard:getPage(index)
|
function learnPage.wizard:getPage(index)
|
||||||
local pages = { }
|
local pages = { }
|
||||||
table.insert(pages, general)
|
table.insert(pages, general)
|
||||||
local selected = general.grid:getSelected()
|
local selected = general.grid:getSelected()
|
||||||
for _, page in pairs(self.pages) do
|
for _, page in pairs(self.pages) do
|
||||||
if page.validFor and (not selected or selected.value == page.validFor) then
|
if page.validFor and (not selected or selected.value == page.validFor) then
|
||||||
table.insert(pages, page)
|
table.insert(pages, page)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.sort(pages, function(a, b)
|
table.sort(pages, function(a, b)
|
||||||
return a.index < b.index
|
return a.index < b.index
|
||||||
end)
|
end)
|
||||||
|
|
||||||
return pages[index]
|
return pages[index]
|
||||||
end
|
end
|
||||||
|
|
||||||
function learnPage:eventHandler(event)
|
function learnPage:eventHandler(event)
|
||||||
if event.type == 'cancel' then
|
if event.type == 'cancel' then
|
||||||
turtle.emptyInventory()
|
turtle.emptyInventory()
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
|
|
||||||
elseif event.type == 'form_invalid' or event.type == 'general_error' then
|
elseif event.type == 'form_invalid' or event.type == 'general_error' then
|
||||||
self.notification:error(event.message)
|
self.notification:error(event.message)
|
||||||
self:setFocus(event.field)
|
self:setFocus(event.field)
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:addPage('learnWizard', learnPage)
|
UI:addPage('learnWizard', learnPage)
|
||||||
|
|||||||
@@ -11,390 +11,390 @@ local context = Milo:getContext()
|
|||||||
local displayMode = Milo:getState('displayMode') or 0
|
local displayMode = Milo:getState('displayMode') or 0
|
||||||
|
|
||||||
local displayModes = {
|
local displayModes = {
|
||||||
[0] = { text = 'A', help = 'Showing all items' },
|
[0] = { text = 'A', help = 'Showing all items' },
|
||||||
[1] = { text = 'I', help = 'Showing inventory items' },
|
[1] = { text = 'I', help = 'Showing inventory items' },
|
||||||
}
|
}
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
menuBar = UI.MenuBar {
|
menuBar = UI.MenuBar {
|
||||||
buttons = {
|
buttons = {
|
||||||
{ text = 'Learn', event = 'learn' },
|
{ text = 'Learn', event = 'learn' },
|
||||||
{ text = 'Craft', event = 'craft' },
|
{ text = 'Craft', event = 'craft' },
|
||||||
{ text = 'Edit', event = 'details' },
|
{ text = 'Edit', event = 'details' },
|
||||||
{ text = 'Refresh', event = 'refresh', x = -12 },
|
{ text = 'Refresh', event = 'refresh', x = -12 },
|
||||||
{
|
{
|
||||||
text = '\187',
|
text = '\187',
|
||||||
x = -3,
|
x = -3,
|
||||||
dropdown = {
|
dropdown = {
|
||||||
{ text = 'Setup', event = 'network' },
|
{ text = 'Setup', event = 'network' },
|
||||||
{ spacer = true },
|
{ spacer = true },
|
||||||
{
|
{
|
||||||
text = 'Rescan storage',
|
text = 'Rescan storage',
|
||||||
event = 'rescan',
|
event = 'rescan',
|
||||||
help = 'Rescan all inventories'
|
help = 'Rescan all inventories'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
grid = UI.Grid {
|
grid = UI.Grid {
|
||||||
y = 2, ey = -2,
|
y = 2, ey = -2,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = ' Qty', key = 'count' , width = 4, align = 'right' },
|
{ heading = ' Qty', key = 'count' , width = 4, align = 'right' },
|
||||||
{ heading = 'Name', key = 'displayName' },
|
{ heading = 'Name', key = 'displayName' },
|
||||||
{ heading = 'Min', key = 'low' , width = 4 },
|
{ heading = 'Min', key = 'low' , width = 4 },
|
||||||
{ heading = 'Max', key = 'limit' , width = 4 },
|
{ heading = 'Max', key = 'limit' , width = 4 },
|
||||||
},
|
},
|
||||||
sortColumn = Milo:getState('sortColumn') or 'count',
|
sortColumn = Milo:getState('sortColumn') or 'count',
|
||||||
inverseSort = Milo:getState('inverseSort'),
|
inverseSort = Milo:getState('inverseSort'),
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar {
|
statusBar = UI.StatusBar {
|
||||||
filter = UI.TextEntry {
|
filter = UI.TextEntry {
|
||||||
x = 1, ex = -17,
|
x = 1, ex = -17,
|
||||||
limit = 50,
|
limit = 50,
|
||||||
shadowText = 'filter',
|
shadowText = 'filter',
|
||||||
shadowTextColor = colors.gray,
|
shadowTextColor = colors.gray,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
backgroundFocusColor = colors.cyan,
|
backgroundFocusColor = colors.cyan,
|
||||||
accelerators = {
|
accelerators = {
|
||||||
[ 'enter' ] = 'eject',
|
[ 'enter' ] = 'eject',
|
||||||
[ 'up' ] = 'grid_up',
|
[ 'up' ] = 'grid_up',
|
||||||
[ 'down' ] = 'grid_down',
|
[ 'down' ] = 'grid_down',
|
||||||
[ 'control-a' ] = 'eject_all',
|
[ 'control-a' ] = 'eject_all',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
storageStatus = UI.Text {
|
storageStatus = UI.Text {
|
||||||
x = -16, ex = -9,
|
x = -16, ex = -9,
|
||||||
textColor = colors.lime,
|
textColor = colors.lime,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
value = '',
|
value = '',
|
||||||
},
|
},
|
||||||
amount = UI.TextEntry {
|
amount = UI.TextEntry {
|
||||||
x = -8, ex = -4,
|
x = -8, ex = -4,
|
||||||
limit = 3,
|
limit = 3,
|
||||||
shadowText = '1',
|
shadowText = '1',
|
||||||
shadowTextColor = colors.gray,
|
shadowTextColor = colors.gray,
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
backgroundFocusColor = colors.black,
|
backgroundFocusColor = colors.black,
|
||||||
accelerators = {
|
accelerators = {
|
||||||
[ 'enter' ] = 'eject_specified',
|
[ 'enter' ] = 'eject_specified',
|
||||||
[ 'control-a' ] = 'eject_all',
|
[ 'control-a' ] = 'eject_all',
|
||||||
},
|
},
|
||||||
help = 'Specify an amount to send',
|
help = 'Specify an amount to send',
|
||||||
},
|
},
|
||||||
display = UI.Button {
|
display = UI.Button {
|
||||||
x = -3,
|
x = -3,
|
||||||
event = 'toggle_display',
|
event = 'toggle_display',
|
||||||
value = 0,
|
value = 0,
|
||||||
text = displayModes[displayMode].text,
|
text = displayModes[displayMode].text,
|
||||||
help = displayModes[displayMode].help,
|
help = displayModes[displayMode].help,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
notification = UI.Notification {
|
notification = UI.Notification {
|
||||||
anchor = 'top',
|
anchor = 'top',
|
||||||
},
|
},
|
||||||
throttle = UI.Throttle {
|
throttle = UI.Throttle {
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
borderColor = colors.gray,
|
borderColor = colors.gray,
|
||||||
},
|
},
|
||||||
accelerators = {
|
accelerators = {
|
||||||
r = 'refresh',
|
r = 'refresh',
|
||||||
[ 'control-r' ] = 'refresh',
|
[ 'control-r' ] = 'refresh',
|
||||||
|
|
||||||
[ 'control-e' ] = 'eject',
|
[ 'control-e' ] = 'eject',
|
||||||
[ 'control-s' ] = 'eject_stack',
|
[ 'control-s' ] = 'eject_stack',
|
||||||
[ 'control-a' ] = 'eject_all',
|
[ 'control-a' ] = 'eject_all',
|
||||||
|
|
||||||
[ 'control-m' ] = 'network',
|
[ 'control-m' ] = 'network',
|
||||||
|
|
||||||
q = 'quit',
|
q = 'quit',
|
||||||
},
|
},
|
||||||
allItems = { }
|
allItems = { }
|
||||||
}
|
}
|
||||||
|
|
||||||
function page.statusBar:draw()
|
function page.statusBar:draw()
|
||||||
return UI.Window.draw(self)
|
return UI.Window.draw(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:getRowTextColor(row, selected)
|
function page.grid:getRowTextColor(row, selected)
|
||||||
if row.is_craftable then
|
if row.is_craftable then
|
||||||
return colors.yellow
|
return colors.yellow
|
||||||
end
|
end
|
||||||
if row.has_recipe then
|
if row.has_recipe then
|
||||||
return colors.cyan
|
return colors.cyan
|
||||||
end
|
end
|
||||||
return UI.Grid:getRowTextColor(row, selected)
|
return UI.Grid:getRowTextColor(row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:getDisplayValues(row)
|
function page.grid:getDisplayValues(row)
|
||||||
row = Util.shallowCopy(row)
|
row = Util.shallowCopy(row)
|
||||||
row.count = row.count > 0 and Util.toBytes(row.count)
|
row.count = row.count > 0 and Util.toBytes(row.count)
|
||||||
if row.low then
|
if row.low then
|
||||||
row.low = Util.toBytes(row.low)
|
row.low = Util.toBytes(row.low)
|
||||||
end
|
end
|
||||||
if row.limit then
|
if row.limit then
|
||||||
row.limit = Util.toBytes(row.limit)
|
row.limit = Util.toBytes(row.limit)
|
||||||
end
|
end
|
||||||
return row
|
return row
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:sortCompare(a, b)
|
function page.grid:sortCompare(a, b)
|
||||||
if self.sortColumn ~= 'displayName' then
|
if self.sortColumn ~= 'displayName' then
|
||||||
if a[self.sortColumn] == b[self.sortColumn] then
|
if a[self.sortColumn] == b[self.sortColumn] then
|
||||||
if self.inverseSort then
|
if self.inverseSort then
|
||||||
return a.displayName > b.displayName
|
return a.displayName > b.displayName
|
||||||
end
|
end
|
||||||
return a.displayName < b.displayName
|
return a.displayName < b.displayName
|
||||||
end
|
end
|
||||||
if a[self.sortColumn] == 0 then
|
if a[self.sortColumn] == 0 then
|
||||||
return self.inverseSort
|
return self.inverseSort
|
||||||
end
|
end
|
||||||
if b[self.sortColumn] == 0 then
|
if b[self.sortColumn] == 0 then
|
||||||
return not self.inverseSort
|
return not self.inverseSort
|
||||||
end
|
end
|
||||||
return a[self.sortColumn] < b[self.sortColumn]
|
return a[self.sortColumn] < b[self.sortColumn]
|
||||||
end
|
end
|
||||||
return UI.Grid.sortCompare(self, a, b)
|
return UI.Grid.sortCompare(self, a, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:eventHandler(event)
|
function page.grid:eventHandler(event)
|
||||||
if event.type == 'grid_sort' then
|
if event.type == 'grid_sort' then
|
||||||
Milo:setState('sortColumn', event.sortColumn)
|
Milo:setState('sortColumn', event.sortColumn)
|
||||||
Milo:setState('inverseSort', event.inverseSort)
|
Milo:setState('inverseSort', event.inverseSort)
|
||||||
end
|
end
|
||||||
return UI.Grid.eventHandler(self, event)
|
return UI.Grid.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eject(amount)
|
function page:eject(amount)
|
||||||
local item = self.grid:getSelected()
|
local item = self.grid:getSelected()
|
||||||
if item and amount then
|
if item and amount then
|
||||||
-- get most up-to-date item
|
-- get most up-to-date item
|
||||||
if item then
|
if item then
|
||||||
if amount == 'stack' then
|
if amount == 'stack' then
|
||||||
amount = item.maxCount or 64
|
amount = item.maxCount or 64
|
||||||
elseif amount == 'all' then
|
elseif amount == 'all' then
|
||||||
item = Milo:getItem(item)
|
item = Milo:getItem(item)
|
||||||
if item then
|
if item then
|
||||||
amount = item.count
|
amount = item.count
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if item and amount > 0 then
|
if item and amount > 0 then
|
||||||
item = Util.shallowCopy(item)
|
item = Util.shallowCopy(item)
|
||||||
self.grid.values[self.grid.sorted[self.grid.index]] = item
|
self.grid.values[self.grid.sorted[self.grid.index]] = item
|
||||||
local request = Milo:craftAndEject(item, amount)
|
local request = Milo:craftAndEject(item, amount)
|
||||||
item.count = request.current - request.count
|
item.count = request.current - request.count
|
||||||
|
|
||||||
if request.craft then
|
if request.craft then
|
||||||
if request.craft > 0 then
|
if request.craft > 0 then
|
||||||
self:notifyInfo(request.craft .. ' crafting ...')
|
self:notifyInfo(request.craft .. ' crafting ...')
|
||||||
elseif request.craft + request.count < request.requested then
|
elseif request.craft + request.count < request.requested then
|
||||||
if request.craft + request.count == 0 then
|
if request.craft + request.count == 0 then
|
||||||
Sound.play('entity.villager.no')
|
Sound.play('entity.villager.no')
|
||||||
end
|
end
|
||||||
self:notifyInfo((request.craft + request.count) .. ' available ...')
|
self:notifyInfo((request.craft + request.count) .. ' available ...')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if request.count + request.craft > 0 then
|
if request.count + request.craft > 0 then
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Sound.play('entity.villager.no')
|
Sound.play('entity.villager.no')
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'quit' then
|
if event.type == 'quit' then
|
||||||
UI:exitPullEvents()
|
UI:exitPullEvents()
|
||||||
|
|
||||||
elseif event.type == 'eject' or event.type == 'grid_select' then
|
elseif event.type == 'eject' or event.type == 'grid_select' then
|
||||||
self:eject(1)
|
self:eject(1)
|
||||||
|
|
||||||
elseif event.type == 'eject_stack' then
|
elseif event.type == 'eject_stack' then
|
||||||
self:eject('stack')
|
self:eject('stack')
|
||||||
|
|
||||||
elseif event.type == 'eject_all' then
|
elseif event.type == 'eject_all' then
|
||||||
self:eject('all')
|
self:eject('all')
|
||||||
|
|
||||||
elseif event.type == 'eject_specified' then
|
elseif event.type == 'eject_specified' then
|
||||||
if self:eject(tonumber(self.statusBar.amount.value)) then
|
if self:eject(tonumber(self.statusBar.amount.value)) then
|
||||||
self.statusBar.amount:reset()
|
self.statusBar.amount:reset()
|
||||||
self:setFocus(self.statusBar.filter)
|
self:setFocus(self.statusBar.filter)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'network' then
|
elseif event.type == 'network' then
|
||||||
UI:setPage('network')
|
UI:setPage('network')
|
||||||
|
|
||||||
elseif event.type == 'details' or event.type == 'grid_select_right' then
|
elseif event.type == 'details' or event.type == 'grid_select_right' then
|
||||||
local item = self.grid:getSelected()
|
local item = self.grid:getSelected()
|
||||||
if item then
|
if item then
|
||||||
UI:setPage('item', item)
|
UI:setPage('item', item)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'grid_up' then
|
elseif event.type == 'grid_up' then
|
||||||
self.grid:emit({ type = 'scroll_up' })
|
self.grid:emit({ type = 'scroll_up' })
|
||||||
|
|
||||||
elseif event.type == 'grid_down' then
|
elseif event.type == 'grid_down' then
|
||||||
self.grid:emit({ type = 'scroll_down' })
|
self.grid:emit({ type = 'scroll_down' })
|
||||||
|
|
||||||
elseif event.type == 'refresh' then
|
elseif event.type == 'refresh' then
|
||||||
self:refresh()
|
self:refresh()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:setFocus(self.statusBar.filter)
|
self:setFocus(self.statusBar.filter)
|
||||||
|
|
||||||
elseif event.type == 'rescan' then
|
elseif event.type == 'rescan' then
|
||||||
self:refresh(true)
|
self:refresh(true)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:setFocus(self.statusBar.filter)
|
self:setFocus(self.statusBar.filter)
|
||||||
|
|
||||||
elseif event.type == 'toggle_display' then
|
elseif event.type == 'toggle_display' then
|
||||||
displayMode = (displayMode + 1) % 2
|
displayMode = (displayMode + 1) % 2
|
||||||
Util.merge(event.button, displayModes[displayMode])
|
Util.merge(event.button, displayModes[displayMode])
|
||||||
event.button:draw()
|
event.button:draw()
|
||||||
self:applyFilter()
|
self:applyFilter()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
Milo:setState('displayMode', displayMode)
|
Milo:setState('displayMode', displayMode)
|
||||||
|
|
||||||
elseif event.type == 'learn' then
|
elseif event.type == 'learn' then
|
||||||
UI:setPage('learnWizard')
|
UI:setPage('learnWizard')
|
||||||
|
|
||||||
elseif event.type == 'craft' then
|
elseif event.type == 'craft' then
|
||||||
local item = self.grid:getSelected()
|
local item = self.grid:getSelected()
|
||||||
if item then
|
if item then
|
||||||
if Craft.findRecipe(item) then -- or item.is_craftable then
|
if Craft.findRecipe(item) then -- or item.is_craftable then
|
||||||
UI:setPage('craft', self.grid:getSelected())
|
UI:setPage('craft', self.grid:getSelected())
|
||||||
else
|
else
|
||||||
self.notification:error('No recipe defined')
|
self.notification:error('No recipe defined')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'text_change' and event.element == self.statusBar.filter then
|
elseif event.type == 'text_change' and event.element == self.statusBar.filter then
|
||||||
self.filter = event.text
|
self.filter = event.text
|
||||||
if #self.filter == 0 then
|
if #self.filter == 0 then
|
||||||
self.filter = nil
|
self.filter = nil
|
||||||
end
|
end
|
||||||
self:applyFilter()
|
self:applyFilter()
|
||||||
self.grid:setIndex(1)
|
self.grid:setIndex(1)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self.statusBar.filter:focus()
|
self.statusBar.filter:focus()
|
||||||
|
|
||||||
else
|
else
|
||||||
UI.Page.eventHandler(self, event)
|
UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:notifySuccess(status)
|
function page:notifySuccess(status)
|
||||||
self.notification:success(status)
|
self.notification:success(status)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:notifyInfo(status)
|
function page:notifyInfo(status)
|
||||||
self.notification:info(status)
|
self.notification:info(status)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:notifyError(status)
|
function page:notifyError(status)
|
||||||
self.notification:error(status)
|
self.notification:error(status)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:enable(args)
|
function page:enable(args)
|
||||||
local function updateStatus()
|
local function updateStatus()
|
||||||
self.statusBar.storageStatus.value =
|
self.statusBar.storageStatus.value =
|
||||||
context.storage:isOnline() and '' or 'offline'
|
context.storage:isOnline() and '' or 'offline'
|
||||||
self.statusBar.storageStatus.textColor =
|
self.statusBar.storageStatus.textColor =
|
||||||
context.storage:isOnline() and colors.lime or colors.red
|
context.storage:isOnline() and colors.lime or colors.red
|
||||||
end
|
end
|
||||||
updateStatus()
|
updateStatus()
|
||||||
|
|
||||||
Event.onTimeout(0, function()
|
Event.onTimeout(0, function()
|
||||||
self:refresh()
|
self:refresh()
|
||||||
self:draw()
|
self:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
|
|
||||||
self.timer = Event.onInterval(3, function()
|
self.timer = Event.onInterval(3, function()
|
||||||
for _,v in pairs(self.grid.values) do
|
for _,v in pairs(self.grid.values) do
|
||||||
local c = context.storage.cache[v.key]
|
local c = context.storage.cache[v.key]
|
||||||
v.count = c and c.count or 0
|
v.count = c and c.count or 0
|
||||||
end
|
end
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
self.handler = Event.on({ 'storage_offline', 'storage_online' }, function()
|
self.handler = Event.on({ 'storage_offline', 'storage_online' }, function()
|
||||||
updateStatus()
|
updateStatus()
|
||||||
self.statusBar.storageStatus:draw()
|
self.statusBar.storageStatus:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
if args and args.filter then
|
if args and args.filter then
|
||||||
self.filter = args.filter
|
self.filter = args.filter
|
||||||
self.statusBar.filter.value = args.filter
|
self.statusBar.filter.value = args.filter
|
||||||
end
|
end
|
||||||
|
|
||||||
if args and args.message then
|
if args and args.message then
|
||||||
self.notification:success(args.message)
|
self.notification:success(args.message)
|
||||||
end
|
end
|
||||||
|
|
||||||
self:setFocus(self.statusBar.filter)
|
self:setFocus(self.statusBar.filter)
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:disable()
|
function page:disable()
|
||||||
Event.off(self.timer)
|
Event.off(self.timer)
|
||||||
Event.off(self.handler)
|
Event.off(self.handler)
|
||||||
UI.Page.disable(self)
|
UI.Page.disable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:refresh(force)
|
function page:refresh(force)
|
||||||
local throttle = function() self.throttle:update() end
|
local throttle = function() self.throttle:update() end
|
||||||
|
|
||||||
self.throttle:enable()
|
self.throttle:enable()
|
||||||
self.allItems = Milo:mergeResources(Milo:listItems(force, throttle))
|
self.allItems = Milo:mergeResources(Milo:listItems(force, throttle))
|
||||||
self:applyFilter()
|
self:applyFilter()
|
||||||
self.throttle:disable()
|
self.throttle:disable()
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:applyFilter()
|
function page:applyFilter()
|
||||||
local function filterItems(t, filter)
|
local function filterItems(t, filter)
|
||||||
self.grid.sortColumn = Milo:getState('sortColumn') or 'count'
|
self.grid.sortColumn = Milo:getState('sortColumn') or 'count'
|
||||||
self.grid.inverseSort = Milo:getState('inverseSort')
|
self.grid.inverseSort = Milo:getState('inverseSort')
|
||||||
|
|
||||||
if filter then
|
if filter then
|
||||||
local r = { }
|
local r = { }
|
||||||
filter = filter:lower()
|
filter = filter:lower()
|
||||||
self.grid.sortColumn = 'score'
|
self.grid.sortColumn = 'score'
|
||||||
self.grid.inverseSort = true
|
self.grid.inverseSort = true
|
||||||
|
|
||||||
for _,v in pairs(t) do
|
for _,v in pairs(t) do
|
||||||
v.score = fuzzy(v.lname, filter)
|
v.score = fuzzy(v.lname, filter)
|
||||||
if v.score then
|
if v.score then
|
||||||
if v.count > 0 then
|
if v.count > 0 then
|
||||||
v.score = v.score + 1
|
v.score = v.score + 1
|
||||||
end
|
end
|
||||||
table.insert(r, v)
|
table.insert(r, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return r
|
return r
|
||||||
|
|
||||||
elseif displayMode > 0 then
|
elseif displayMode > 0 then
|
||||||
local r = { }
|
local r = { }
|
||||||
|
|
||||||
for _,v in pairs(t) do
|
for _,v in pairs(t) do
|
||||||
if v.count > 0 then
|
if v.count > 0 then
|
||||||
table.insert(r, v)
|
table.insert(r, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return r
|
return r
|
||||||
end
|
end
|
||||||
|
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
local t = filterItems(self.allItems, self.filter)
|
local t = filterItems(self.allItems, self.filter)
|
||||||
self.grid:setValues(t)
|
self.grid:setValues(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:addPage('listing', page)
|
UI:addPage('listing', page)
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
[ "9302912a2d9794a47241faefc475335b4e07a581" ] = {
|
[ "9302912a2d9794a47241faefc475335b4e07a581" ] = {
|
||||||
title = "Remote",
|
title = "Remote",
|
||||||
category = "Apps",
|
category = "Apps",
|
||||||
run = "MiloRemote",
|
run = "MiloRemote",
|
||||||
requires = "neuralInterface",
|
requires = "neuralInterface",
|
||||||
iconExt = "\0304\031f\135\129\0314\128\128\031f\130\030f\128\
|
iconExt = "\0304\031f\135\129\0314\128\128\031f\130\030f\128\
|
||||||
\031f\128\031c\159\149\0300\0317\143\0304\031c\149\030f\0314\133\
|
\031f\128\031c\159\149\0300\0317\143\0304\031c\149\030f\0314\133\
|
||||||
\031f\128\030c\0310\142\030f\031c\149\030c\0310\139\030f\031c\149\031f\128",
|
\031f\128\030c\0310\142\030f\031c\149\030c\0310\139\030f\031c\149\031f\128",
|
||||||
},
|
},
|
||||||
[ "eea426f9baef72a8fcefd091e0cec5ab94a76698" ] = {
|
[ "eea426f9baef72a8fcefd091e0cec5ab94a76698" ] = {
|
||||||
title = "Milo",
|
title = "Milo",
|
||||||
category = "Apps",
|
category = "Apps",
|
||||||
run = "MiloLocal",
|
run = "MiloLocal",
|
||||||
requires = 'advancedTurtle',
|
requires = 'advancedTurtle',
|
||||||
iconExt = "\0304\031f\135\129\0314\128\128\031f\130\030f\128\
|
iconExt = "\0304\031f\135\129\0314\128\128\031f\130\030f\128\
|
||||||
\031f\128\031c\159\149\0300\0317\143\0304\031c\149\030f\0314\133\
|
\031f\128\031c\159\149\0300\0317\143\0304\031c\149\030f\0314\133\
|
||||||
\031f\128\030c\0310\142\030f\031c\149\030c\0310\139\030f\031c\149\031f\128",
|
\031f\128\030c\0310\142\030f\031c\149\030c\0310\139\030f\031c\149\031f\128",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,239 +15,239 @@ local template =
|
|||||||
Right-clicking on the activity monitor will reset the totals.]]
|
Right-clicking on the activity monitor will reset the totals.]]
|
||||||
|
|
||||||
local wizardPage = UI.WizardPage {
|
local wizardPage = UI.WizardPage {
|
||||||
title = 'Activity Monitor',
|
title = 'Activity Monitor',
|
||||||
index = 2,
|
index = 2,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
[1] = UI.TextArea {
|
[1] = UI.TextArea {
|
||||||
x = 2, ex = -2, y = 2, ey = 6,
|
x = 2, ex = -2, y = 2, ey = 6,
|
||||||
marginRight = 0,
|
marginRight = 0,
|
||||||
value = string.format(template, Ansi.yellow, Ansi.reset),
|
value = string.format(template, Ansi.yellow, Ansi.reset),
|
||||||
},
|
},
|
||||||
form = UI.Form {
|
form = UI.Form {
|
||||||
x = 2, ex = -2, y = 7, ey = -2,
|
x = 2, ex = -2, y = 7, ey = -2,
|
||||||
manualControls = true,
|
manualControls = true,
|
||||||
[1] = UI.Chooser {
|
[1] = UI.Chooser {
|
||||||
width = 9,
|
width = 9,
|
||||||
formLabel = 'Font Size', formKey = 'textScale',
|
formLabel = 'Font Size', formKey = 'textScale',
|
||||||
nochoice = 'Small',
|
nochoice = 'Small',
|
||||||
choices = {
|
choices = {
|
||||||
{ name = 'Small', value = .5 },
|
{ name = 'Small', value = .5 },
|
||||||
{ name = 'Large', value = 1 },
|
{ name = 'Large', value = 1 },
|
||||||
},
|
},
|
||||||
help = 'Adjust text scaling',
|
help = 'Adjust text scaling',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function wizardPage:setNode(node)
|
function wizardPage:setNode(node)
|
||||||
self.form:setValues(node)
|
self.form:setValues(node)
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:validate()
|
function wizardPage:validate()
|
||||||
return self.form:save()
|
return self.form:save()
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:saveNode(node)
|
function wizardPage:saveNode(node)
|
||||||
os.queueEvent('monitor_resize', node.name)
|
os.queueEvent('monitor_resize', node.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:isValidType(node)
|
function wizardPage:isValidType(node)
|
||||||
local m = device[node.name]
|
local m = device[node.name]
|
||||||
return m and m.type == 'monitor' and {
|
return m and m.type == 'monitor' and {
|
||||||
name = 'Activity Monitor',
|
name = 'Activity Monitor',
|
||||||
value = 'activity',
|
value = 'activity',
|
||||||
category = 'display',
|
category = 'display',
|
||||||
help = 'Display storage activity'
|
help = 'Display storage activity'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:isValidFor(node)
|
function wizardPage:isValidFor(node)
|
||||||
return node.mtype == 'activity'
|
return node.mtype == 'activity'
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:getPage('nodeWizard').wizard:add({ activity = wizardPage })
|
UI:getPage('nodeWizard').wizard:add({ activity = wizardPage })
|
||||||
|
|
||||||
--[[ Display ]]--
|
--[[ Display ]]--
|
||||||
local function createPage(node)
|
local function createPage(node)
|
||||||
local monitor = UI.Device {
|
local monitor = UI.Device {
|
||||||
device = node.adapter,
|
device = node.adapter,
|
||||||
textScale = node.textScale or .5,
|
textScale = node.textScale or .5,
|
||||||
}
|
}
|
||||||
|
|
||||||
function monitor:resize()
|
function monitor:resize()
|
||||||
self.textScale = node.textScale or .5
|
self.textScale = node.textScale or .5
|
||||||
UI.Device.resize(self)
|
UI.Device.resize(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
parent = monitor,
|
parent = monitor,
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
grid = UI.Grid {
|
grid = UI.Grid {
|
||||||
ey = -3,
|
ey = -3,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Qty', key = 'count', width = 6, align = 'right' },
|
{ heading = 'Qty', key = 'count', width = 6, align = 'right' },
|
||||||
{ heading = '+/-', key = 'change', width = 6, align = 'right' },
|
{ heading = '+/-', key = 'change', width = 6, align = 'right' },
|
||||||
{ heading = 'Name', key = 'displayName' },
|
{ heading = 'Name', key = 'displayName' },
|
||||||
{ heading = 'Rate', key = 'rate', width = 6, align = 'right' },
|
{ heading = 'Rate', key = 'rate', width = 6, align = 'right' },
|
||||||
},
|
},
|
||||||
sortColumn = 'displayName',
|
sortColumn = 'displayName',
|
||||||
headerBackgroundColor = colors.black,
|
headerBackgroundColor = colors.black,
|
||||||
headerTextColor = colors.cyan,
|
headerTextColor = colors.cyan,
|
||||||
headerHeight = 2,
|
headerHeight = 2,
|
||||||
},
|
},
|
||||||
buttons = UI.Window {
|
buttons = UI.Window {
|
||||||
y = -1,
|
y = -1,
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
prevButton = UI.Button {
|
prevButton = UI.Button {
|
||||||
x = 1, width = 5,
|
x = 1, width = 5,
|
||||||
event = 'previous',
|
event = 'previous',
|
||||||
textColor = colors.cyan,
|
textColor = colors.cyan,
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
text = ' < '
|
text = ' < '
|
||||||
},
|
},
|
||||||
resetButton = UI.Button {
|
resetButton = UI.Button {
|
||||||
x = 7, ex = -7,
|
x = 7, ex = -7,
|
||||||
event = 'reset',
|
event = 'reset',
|
||||||
textColor = colors.cyan,
|
textColor = colors.cyan,
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
text = 'Reset'
|
text = 'Reset'
|
||||||
},
|
},
|
||||||
nextButton = UI.Button {
|
nextButton = UI.Button {
|
||||||
x = -5, width = 5,
|
x = -5, width = 5,
|
||||||
event = 'next',
|
event = 'next',
|
||||||
textColor = colors.cyan,
|
textColor = colors.cyan,
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
text = ' > '
|
text = ' > '
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
timestamp = os.clock(),
|
timestamp = os.clock(),
|
||||||
}
|
}
|
||||||
|
|
||||||
function page.grid:getRowTextColor(row, selected)
|
function page.grid:getRowTextColor(row, selected)
|
||||||
if row.lastCount and row.lastCount ~= row.count then
|
if row.lastCount and row.lastCount ~= row.count then
|
||||||
return row.count > row.lastCount and colors.yellow or colors.lightGray
|
return row.count > row.lastCount and colors.yellow or colors.lightGray
|
||||||
end
|
end
|
||||||
return UI.Grid:getRowTextColor(row, selected)
|
return UI.Grid:getRowTextColor(row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:getDisplayValues(row)
|
function page.grid:getDisplayValues(row)
|
||||||
row = Util.shallowCopy(row)
|
row = Util.shallowCopy(row)
|
||||||
|
|
||||||
local ind = '+'
|
local ind = '+'
|
||||||
if row.change < 0 then
|
if row.change < 0 then
|
||||||
ind = ''
|
ind = ''
|
||||||
end
|
end
|
||||||
|
|
||||||
row.change = ind .. Util.toBytes(row.change)
|
row.change = ind .. Util.toBytes(row.change)
|
||||||
row.count = Util.toBytes(row.count)
|
row.count = Util.toBytes(row.count)
|
||||||
row.rate = Util.toBytes(row.rate)
|
row.rate = Util.toBytes(row.rate)
|
||||||
|
|
||||||
return row
|
return row
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'reset' then
|
if event.type == 'reset' then
|
||||||
self:reset()
|
self:reset()
|
||||||
|
|
||||||
elseif event.type == 'next' then
|
elseif event.type == 'next' then
|
||||||
self.grid:nextPage()
|
self.grid:nextPage()
|
||||||
|
|
||||||
elseif event.type == 'previous' then
|
elseif event.type == 'previous' then
|
||||||
self.grid:previousPage()
|
self.grid:previousPage()
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.onTimeout(.1, function()
|
Event.onTimeout(.1, function()
|
||||||
self:setFocus(self.grid)
|
self:setFocus(self.grid)
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:reset()
|
function page:reset()
|
||||||
self.lastItems = nil
|
self.lastItems = nil
|
||||||
self.grid:setValues({ })
|
self.grid:setValues({ })
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:refresh()
|
function page:refresh()
|
||||||
local t = context.storage.cache
|
local t = context.storage.cache
|
||||||
|
|
||||||
if t and not self.lastItems then
|
if t and not self.lastItems then
|
||||||
self.lastItems = { }
|
self.lastItems = { }
|
||||||
for k,v in pairs(t) do
|
for k,v in pairs(t) do
|
||||||
self.lastItems[k] = {
|
self.lastItems[k] = {
|
||||||
displayName = v.displayName,
|
displayName = v.displayName,
|
||||||
initialCount = v.count,
|
initialCount = v.count,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
self.timestamp = os.clock()
|
self.timestamp = os.clock()
|
||||||
self.grid:setValues({ })
|
self.grid:setValues({ })
|
||||||
|
|
||||||
else
|
else
|
||||||
for _,v in pairs(self.lastItems) do
|
for _,v in pairs(self.lastItems) do
|
||||||
v.lastCount = v.count
|
v.lastCount = v.count
|
||||||
v.count = nil
|
v.count = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
self.elapsed = os.clock() - self.timestamp
|
self.elapsed = os.clock() - self.timestamp
|
||||||
|
|
||||||
for k,v in pairs(t) do
|
for k,v in pairs(t) do
|
||||||
local v2 = self.lastItems[k]
|
local v2 = self.lastItems[k]
|
||||||
if v2 then
|
if v2 then
|
||||||
v2.count = v.count
|
v2.count = v.count
|
||||||
else
|
else
|
||||||
self.lastItems[k] = {
|
self.lastItems[k] = {
|
||||||
displayName = v.displayName,
|
displayName = v.displayName,
|
||||||
count = v.count,
|
count = v.count,
|
||||||
initialCount = 0,
|
initialCount = 0,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local changedItems = { }
|
local changedItems = { }
|
||||||
for k,v in pairs(self.lastItems) do
|
for k,v in pairs(self.lastItems) do
|
||||||
if not v.count then
|
if not v.count then
|
||||||
v.count = 0
|
v.count = 0
|
||||||
end
|
end
|
||||||
if v.count ~= v.initialCount then
|
if v.count ~= v.initialCount then
|
||||||
v.change = v.count - v.initialCount
|
v.change = v.count - v.initialCount
|
||||||
v.rate = Util.round(60 / self.elapsed * v.change, 1)
|
v.rate = Util.round(60 / self.elapsed * v.change, 1)
|
||||||
changedItems[k] = v
|
changedItems[k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self.grid:setValues(changedItems)
|
self.grid:setValues(changedItems)
|
||||||
end
|
end
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:update()
|
function page:update()
|
||||||
page:refresh()
|
page:refresh()
|
||||||
page:sync()
|
page:sync()
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:setPage(page)
|
UI:setPage(page)
|
||||||
return page
|
return page
|
||||||
end
|
end
|
||||||
|
|
||||||
local pages = { }
|
local pages = { }
|
||||||
|
|
||||||
--[[ Task ]]--
|
--[[ Task ]]--
|
||||||
local ActivityTask = {
|
local ActivityTask = {
|
||||||
name = 'activity',
|
name = 'activity',
|
||||||
priority = 30,
|
priority = 30,
|
||||||
}
|
}
|
||||||
|
|
||||||
function ActivityTask:cycle()
|
function ActivityTask:cycle()
|
||||||
for node in context.storage:filterActive('activity') do
|
for node in context.storage:filterActive('activity') do
|
||||||
if not pages[node.name] then
|
if not pages[node.name] then
|
||||||
pages[node.name] = createPage(node)
|
pages[node.name] = createPage(node)
|
||||||
end
|
end
|
||||||
pages[node.name]:update()
|
pages[node.name]:update()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:registerTask(ActivityTask)
|
Milo:registerTask(ActivityTask)
|
||||||
|
|||||||
@@ -21,97 +21,97 @@ Backup configuration files each minecraft day.
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
local wizardPage = UI.WizardPage {
|
local wizardPage = UI.WizardPage {
|
||||||
title = 'Backup Drive',
|
title = 'Backup Drive',
|
||||||
index = 2,
|
index = 2,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
[1] = UI.TextArea {
|
[1] = UI.TextArea {
|
||||||
x = 2, ex = -2, y = 2, ey = -2,
|
x = 2, ex = -2, y = 2, ey = -2,
|
||||||
value = string.format(template, Ansi.yellow, Ansi.reset),
|
value = string.format(template, Ansi.yellow, Ansi.reset),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function wizardPage:isValidType(node)
|
function wizardPage:isValidType(node)
|
||||||
local m = device[node.name]
|
local m = device[node.name]
|
||||||
return m and m.type == 'drive' and {
|
return m and m.type == 'drive' and {
|
||||||
name = 'Backup Drive',
|
name = 'Backup Drive',
|
||||||
value = 'backup',
|
value = 'backup',
|
||||||
category = 'custom',
|
category = 'custom',
|
||||||
help = 'Backup configuration files',
|
help = 'Backup configuration files',
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:isValidFor(node)
|
function wizardPage:isValidFor(node)
|
||||||
return node.mtype == 'backup'
|
return node.mtype == 'backup'
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:getPage('nodeWizard').wizard:add({ backupDrive = wizardPage })
|
UI:getPage('nodeWizard').wizard:add({ backupDrive = wizardPage })
|
||||||
|
|
||||||
local function clearOld(dir, fname)
|
local function clearOld(dir, fname)
|
||||||
local files = { }
|
local files = { }
|
||||||
|
|
||||||
for _, file in pairs(fs.list(dir)) do
|
for _, file in pairs(fs.list(dir)) do
|
||||||
if file:match(fname) then
|
if file:match(fname) then
|
||||||
table.insert(files, file)
|
table.insert(files, file)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #files > 1 then
|
if #files > 1 then
|
||||||
table.sort(files, function(a, b)
|
table.sort(files, function(a, b)
|
||||||
return tonumber(a:match('.(%d+)')) > tonumber(b:match('.(%d+)'))
|
return tonumber(a:match('.(%d+)')) > tonumber(b:match('.(%d+)'))
|
||||||
end)
|
end)
|
||||||
while #files > 1 do
|
while #files > 1 do
|
||||||
local old = table.remove(files, #files)
|
local old = table.remove(files, #files)
|
||||||
fs.delete(fs.combine(dir, old))
|
fs.delete(fs.combine(dir, old))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function makeBackup(dir, fname)
|
local function makeBackup(dir, fname)
|
||||||
clearOld(dir, fname)
|
clearOld(dir, fname)
|
||||||
local source = fs.combine('usr/config', fname)
|
local source = fs.combine('usr/config', fname)
|
||||||
local dest = string.format('%s/%s.%d', dir, fname, os.day())
|
local dest = string.format('%s/%s.%d', dir, fname, os.day())
|
||||||
fs.copy(source, dest)
|
fs.copy(source, dest)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function backupNode(node)
|
local function backupNode(node)
|
||||||
local files = {
|
local files = {
|
||||||
'storage',
|
'storage',
|
||||||
'milo.state',
|
'milo.state',
|
||||||
'machine_crafting.db',
|
'machine_crafting.db',
|
||||||
'recipes.db',
|
'recipes.db',
|
||||||
'resources.db',
|
'resources.db',
|
||||||
}
|
}
|
||||||
local s, m = pcall(function()
|
local s, m = pcall(function()
|
||||||
if not node.adapter.isDiskPresent() then
|
if not node.adapter.isDiskPresent() then
|
||||||
_G._syslog('BACKUP error: No media present')
|
_G._syslog('BACKUP error: No media present')
|
||||||
else
|
else
|
||||||
local dir = node.adapter.getMountPath()
|
local dir = node.adapter.getMountPath()
|
||||||
for _, v in pairs(files) do
|
for _, v in pairs(files) do
|
||||||
makeBackup(dir, v)
|
makeBackup(dir, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G._syslog('BACKUP error:' .. m)
|
_G._syslog('BACKUP error:' .. m)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[ Task ]]--
|
--[[ Task ]]--
|
||||||
local BackupTask = {
|
local BackupTask = {
|
||||||
name = 'backup',
|
name = 'backup',
|
||||||
priority = 99,
|
priority = 99,
|
||||||
}
|
}
|
||||||
|
|
||||||
function BackupTask:cycle()
|
function BackupTask:cycle()
|
||||||
for node in context.storage:filterActive('backup') do
|
for node in context.storage:filterActive('backup') do
|
||||||
if not drives[node.name] then
|
if not drives[node.name] then
|
||||||
drives[node.name] = Event.onInterval(DAY, function()
|
drives[node.name] = Event.onInterval(DAY, function()
|
||||||
_G._syslog('BACKUP: started')
|
_G._syslog('BACKUP: started')
|
||||||
if node.adapter and node.adapter.online then
|
if node.adapter and node.adapter.online then
|
||||||
backupNode(node)
|
backupNode(node)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:registerTask(BackupTask)
|
Milo:registerTask(BackupTask)
|
||||||
|
|||||||
@@ -6,67 +6,67 @@ local Util = require('util')
|
|||||||
local context = Milo:getContext()
|
local context = Milo:getContext()
|
||||||
|
|
||||||
local craftTask = {
|
local craftTask = {
|
||||||
name = 'crafting',
|
name = 'crafting',
|
||||||
priority = 70,
|
priority = 70,
|
||||||
}
|
}
|
||||||
|
|
||||||
function craftTask:craft(recipe, item)
|
function craftTask:craft(recipe, item)
|
||||||
if Milo:isCraftingPaused() then
|
if Milo:isCraftingPaused() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: refactor into craft.lua
|
-- TODO: refactor into craft.lua
|
||||||
Craft.processPending(item, context.storage)
|
Craft.processPending(item, context.storage)
|
||||||
|
|
||||||
-- create a mini-list of items that are required for this recipe
|
-- create a mini-list of items that are required for this recipe
|
||||||
item.ingredients = Craft.getResourceList(
|
item.ingredients = Craft.getResourceList(
|
||||||
recipe, Milo:listItems(), item.requested - item.crafted, item.pending)
|
recipe, Milo:listItems(), item.requested - item.crafted, item.pending)
|
||||||
|
|
||||||
for k, v in pairs(item.ingredients) do
|
for k, v in pairs(item.ingredients) do
|
||||||
v.crafted = v.used
|
v.crafted = v.used
|
||||||
v.count = v.used
|
v.count = v.used
|
||||||
v.key = k
|
v.key = k
|
||||||
if v.need > 0 then
|
if v.need > 0 then
|
||||||
v.status = 'No recipe'
|
v.status = 'No recipe'
|
||||||
v.statusCode = Craft.STATUS_ERROR
|
v.statusCode = Craft.STATUS_ERROR
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
item.ingredients[recipe.result] = item
|
item.ingredients[recipe.result] = item
|
||||||
item.ingredients[recipe.result].total = item.count
|
item.ingredients[recipe.result].total = item.count
|
||||||
item.ingredients[recipe.result].crafted = item.crafted
|
item.ingredients[recipe.result].crafted = item.crafted
|
||||||
|
|
||||||
Craft.craftRecipe(recipe, item.requested - item.crafted, context.storage, item)
|
Craft.craftRecipe(recipe, item.requested - item.crafted, context.storage, item)
|
||||||
end
|
end
|
||||||
|
|
||||||
function craftTask:cycle()
|
function craftTask:cycle()
|
||||||
for _,key in pairs(Util.keys(context.craftingQueue)) do
|
for _,key in pairs(Util.keys(context.craftingQueue)) do
|
||||||
local item = context.craftingQueue[key]
|
local item = context.craftingQueue[key]
|
||||||
if item.requested - item.crafted > 0 then
|
if item.requested - item.crafted > 0 then
|
||||||
local recipe = Craft.findRecipe(key)
|
local recipe = Craft.findRecipe(key)
|
||||||
if recipe then
|
if recipe then
|
||||||
|
|
||||||
if not item.notified then
|
if not item.notified then
|
||||||
Sound.play('block.end_portal_frame.fill')
|
Sound.play('block.end_portal_frame.fill')
|
||||||
item.notified = true
|
item.notified = true
|
||||||
end
|
end
|
||||||
|
|
||||||
self:craft(recipe, item)
|
self:craft(recipe, item)
|
||||||
|
|
||||||
if item.crafted >= item.requested then
|
if item.crafted >= item.requested then
|
||||||
item.status = 'crafted'
|
item.status = 'crafted'
|
||||||
item.statusCode = Craft.STATUS_SUCCESS
|
item.statusCode = Craft.STATUS_SUCCESS
|
||||||
if item.callback then
|
if item.callback then
|
||||||
item.callback(item) -- invoke callback
|
item.callback(item) -- invoke callback
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
item.status = '(no recipe)'
|
item.status = '(no recipe)'
|
||||||
item.statusCode = Craft.STATUS_ERROR
|
item.statusCode = Craft.STATUS_ERROR
|
||||||
item.crafted = 0
|
item.crafted = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:registerTask(craftTask)
|
Milo:registerTask(craftTask)
|
||||||
@@ -7,126 +7,126 @@ local Util = require('util')
|
|||||||
local colors = _G.colors
|
local colors = _G.colors
|
||||||
|
|
||||||
local craftPage = UI.Page {
|
local craftPage = UI.Page {
|
||||||
titleBar = UI.TitleBar { },
|
titleBar = UI.TitleBar { },
|
||||||
wizard = UI.Wizard {
|
wizard = UI.Wizard {
|
||||||
y = 2, ey = -2,
|
y = 2, ey = -2,
|
||||||
pages = {
|
pages = {
|
||||||
quantity = UI.WizardPage {
|
quantity = UI.WizardPage {
|
||||||
index = 1,
|
index = 1,
|
||||||
text = UI.Text {
|
text = UI.Text {
|
||||||
x = 6, y = 3,
|
x = 6, y = 3,
|
||||||
value = 'Quantity',
|
value = 'Quantity',
|
||||||
},
|
},
|
||||||
count = UI.TextEntry {
|
count = UI.TextEntry {
|
||||||
x = 15, y = 3, width = 10,
|
x = 15, y = 3, width = 10,
|
||||||
limit = 6,
|
limit = 6,
|
||||||
value = 1,
|
value = 1,
|
||||||
},
|
},
|
||||||
ejectText = UI.Text {
|
ejectText = UI.Text {
|
||||||
x = 6, y = 4,
|
x = 6, y = 4,
|
||||||
value = 'Eject',
|
value = 'Eject',
|
||||||
},
|
},
|
||||||
eject = UI.Chooser {
|
eject = UI.Chooser {
|
||||||
x = 15, y = 4, width = 7,
|
x = 15, y = 4, width = 7,
|
||||||
value = true,
|
value = true,
|
||||||
nochoice = 'No',
|
nochoice = 'No',
|
||||||
choices = {
|
choices = {
|
||||||
{ name = 'Yes', value = true },
|
{ name = 'Yes', value = true },
|
||||||
{ name = 'No', value = false },
|
{ name = 'No', value = false },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
resources = UI.WizardPage {
|
resources = UI.WizardPage {
|
||||||
index = 2,
|
index = 2,
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = 2, ey = -2,
|
y = 2, ey = -2,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Name', key = 'displayName' },
|
{ heading = 'Name', key = 'displayName' },
|
||||||
{ heading = 'Total', key = 'total' , width = 5 },
|
{ heading = 'Total', key = 'total' , width = 5 },
|
||||||
{ heading = 'Used', key = 'used' , width = 5 },
|
{ heading = 'Used', key = 'used' , width = 5 },
|
||||||
{ heading = 'Need', key = 'need' , width = 5 },
|
{ heading = 'Need', key = 'need' , width = 5 },
|
||||||
},
|
},
|
||||||
sortColumn = 'displayName',
|
sortColumn = 'displayName',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function craftPage:enable(item)
|
function craftPage:enable(item)
|
||||||
self.item = item
|
self.item = item
|
||||||
self:focusFirst()
|
self:focusFirst()
|
||||||
self.titleBar.title = itemDB:getName(item)
|
self.titleBar.title = itemDB:getName(item)
|
||||||
-- self.wizard.pages.quantity.eject.value = true
|
-- self.wizard.pages.quantity.eject.value = true
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function craftPage.wizard.pages.resources.grid:getDisplayValues(row)
|
function craftPage.wizard.pages.resources.grid:getDisplayValues(row)
|
||||||
local function dv(v)
|
local function dv(v)
|
||||||
return v == 0 and '' or Util.toBytes(v)
|
return v == 0 and '' or Util.toBytes(v)
|
||||||
end
|
end
|
||||||
row = Util.shallowCopy(row)
|
row = Util.shallowCopy(row)
|
||||||
row.total = Util.toBytes(row.total)
|
row.total = Util.toBytes(row.total)
|
||||||
row.used = dv(row.used)
|
row.used = dv(row.used)
|
||||||
row.need = dv(row.need)
|
row.need = dv(row.need)
|
||||||
return row
|
return row
|
||||||
end
|
end
|
||||||
|
|
||||||
function craftPage.wizard.pages.resources.grid:getRowTextColor(row, selected)
|
function craftPage.wizard.pages.resources.grid:getRowTextColor(row, selected)
|
||||||
if row.need > 0 then
|
if row.need > 0 then
|
||||||
return colors.orange
|
return colors.orange
|
||||||
end
|
end
|
||||||
return UI.Grid:getRowTextColor(row, selected)
|
return UI.Grid:getRowTextColor(row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
function craftPage.wizard:eventHandler(event)
|
function craftPage.wizard:eventHandler(event)
|
||||||
if event.type == 'nextView' then
|
if event.type == 'nextView' then
|
||||||
local count = tonumber(self.pages.quantity.count.value)
|
local count = tonumber(self.pages.quantity.count.value)
|
||||||
if not count or count <= 0 then
|
if not count or count <= 0 then
|
||||||
self.pages.quantity.count.backgroundColor = colors.red
|
self.pages.quantity.count.backgroundColor = colors.red
|
||||||
self.pages.quantity.count:draw()
|
self.pages.quantity.count:draw()
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
self.pages.quantity.count.backgroundColor = colors.black
|
self.pages.quantity.count.backgroundColor = colors.black
|
||||||
end
|
end
|
||||||
return UI.Wizard.eventHandler(self, event)
|
return UI.Wizard.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
function craftPage.wizard.pages.resources:enable()
|
function craftPage.wizard.pages.resources:enable()
|
||||||
local items = Milo:listItems()
|
local items = Milo:listItems()
|
||||||
local count = tonumber(self.parent.quantity.count.value)
|
local count = tonumber(self.parent.quantity.count.value)
|
||||||
local recipe = Craft.findRecipe(craftPage.item)
|
local recipe = Craft.findRecipe(craftPage.item)
|
||||||
if recipe then
|
if recipe then
|
||||||
local ingredients = Craft.getResourceList4(recipe, items, count)
|
local ingredients = Craft.getResourceList4(recipe, items, count)
|
||||||
for _,v in pairs(ingredients) do
|
for _,v in pairs(ingredients) do
|
||||||
v.displayName = itemDB:getName(v)
|
v.displayName = itemDB:getName(v)
|
||||||
end
|
end
|
||||||
self.grid:setValues(ingredients)
|
self.grid:setValues(ingredients)
|
||||||
else
|
else
|
||||||
self.grid:setValues({ })
|
self.grid:setValues({ })
|
||||||
end
|
end
|
||||||
return UI.WizardPage.enable(self)
|
return UI.WizardPage.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function craftPage:eventHandler(event)
|
function craftPage:eventHandler(event)
|
||||||
if event.type == 'cancel' then
|
if event.type == 'cancel' then
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
|
|
||||||
elseif event.type == 'accept' then
|
elseif event.type == 'accept' then
|
||||||
local item = Util.shallowCopy(self.item)
|
local item = Util.shallowCopy(self.item)
|
||||||
item.requested = tonumber(self.wizard.pages.quantity.count.value)
|
item.requested = tonumber(self.wizard.pages.quantity.count.value)
|
||||||
item.forceCrafting = true
|
item.forceCrafting = true
|
||||||
if self.wizard.pages.quantity.eject.value then
|
if self.wizard.pages.quantity.eject.value then
|
||||||
item.callback = function(request)
|
item.callback = function(request)
|
||||||
Milo:eject(item, request.requested)
|
Milo:eject(item, request.requested)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Milo:requestCrafting(item)
|
Milo:requestCrafting(item)
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:addPage('craft', craftPage)
|
UI:addPage('craft', craftPage)
|
||||||
|
|||||||
@@ -12,27 +12,27 @@ Any items placed in this chest will be imported into storage.
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
local inputChestWizardPage = UI.WizardPage {
|
local inputChestWizardPage = UI.WizardPage {
|
||||||
title = 'Input Chest',
|
title = 'Input Chest',
|
||||||
index = 2,
|
index = 2,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
[1] = UI.TextArea {
|
[1] = UI.TextArea {
|
||||||
x = 2, ex = -2, y = 2, ey = -2,
|
x = 2, ex = -2, y = 2, ey = -2,
|
||||||
value = string.format(template, Ansi.yellow, Ansi.reset),
|
value = string.format(template, Ansi.yellow, Ansi.reset),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function inputChestWizardPage:isValidType(node)
|
function inputChestWizardPage:isValidType(node)
|
||||||
local m = device[node.name]
|
local m = device[node.name]
|
||||||
return m and m.pullItems and {
|
return m and m.pullItems and {
|
||||||
name = 'Input Chest',
|
name = 'Input Chest',
|
||||||
value = 'input',
|
value = 'input',
|
||||||
category = 'custom',
|
category = 'custom',
|
||||||
help = 'Sends all items to storage',
|
help = 'Sends all items to storage',
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function inputChestWizardPage:isValidFor(node)
|
function inputChestWizardPage:isValidFor(node)
|
||||||
return node.mtype == 'input'
|
return node.mtype == 'input'
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:getPage('nodeWizard').wizard:add({ inputChest = inputChestWizardPage })
|
UI:getPage('nodeWizard').wizard:add({ inputChest = inputChestWizardPage })
|
||||||
|
|||||||
@@ -5,57 +5,57 @@ local Util = require('util')
|
|||||||
local context = Milo:getContext()
|
local context = Milo:getContext()
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
titleBar = UI.TitleBar {
|
titleBar = UI.TitleBar {
|
||||||
title = 'Item settings',
|
title = 'Item settings',
|
||||||
previousPage = true,
|
previousPage = true,
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar { },
|
statusBar = UI.StatusBar { },
|
||||||
notification = UI.Notification { },
|
notification = UI.Notification { },
|
||||||
}
|
}
|
||||||
|
|
||||||
function page:enable(item)
|
function page:enable(item)
|
||||||
if not self.tabs then
|
if not self.tabs then
|
||||||
table.sort(context.plugins.itemTab, function(a, b) return a.index < b.index end)
|
table.sort(context.plugins.itemTab, function(a, b) return a.index < b.index end)
|
||||||
local t = Util.shallowCopy(context.plugins.itemTab)
|
local t = Util.shallowCopy(context.plugins.itemTab)
|
||||||
t.y = 2
|
t.y = 2
|
||||||
t.ey = -2
|
t.ey = -2
|
||||||
|
|
||||||
self:add({ tabs = UI.Tabs(t) })
|
self:add({ tabs = UI.Tabs(t) })
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, v in pairs(context.plugins.itemTab) do
|
for _, v in pairs(context.plugins.itemTab) do
|
||||||
if v.UIElement then
|
if v.UIElement then
|
||||||
v:setItem(item)
|
v:setItem(item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.tabs:selectTab(context.plugins.itemTab[1])
|
self.tabs:selectTab(context.plugins.itemTab[1])
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'tab_activate' then
|
if event.type == 'tab_activate' then
|
||||||
event.activated:focusFirst()
|
event.activated:focusFirst()
|
||||||
|
|
||||||
elseif event.type == 'form_invalid' then
|
elseif event.type == 'form_invalid' then
|
||||||
self.notification:error(event.message)
|
self.notification:error(event.message)
|
||||||
|
|
||||||
elseif event.type == 'focus_change' then
|
elseif event.type == 'focus_change' then
|
||||||
self.statusBar:setStatus(event.focused.help)
|
self.statusBar:setStatus(event.focused.help)
|
||||||
self.statusBar:draw()
|
self.statusBar:draw()
|
||||||
|
|
||||||
elseif event.type == 'success_message' then
|
elseif event.type == 'success_message' then
|
||||||
self.notification:success(event.message)
|
self.notification:success(event.message)
|
||||||
|
|
||||||
elseif event.type == 'info_message' then
|
elseif event.type == 'info_message' then
|
||||||
self.notification:info(event.message)
|
self.notification:info(event.message)
|
||||||
|
|
||||||
elseif event.type == 'error_message' then
|
elseif event.type == 'error_message' then
|
||||||
self.notification:error(event.message)
|
self.notification:error(event.message)
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:addPage('item', page)
|
UI:addPage('item', page)
|
||||||
|
|||||||
@@ -7,62 +7,62 @@ local colors = _G.colors
|
|||||||
local context = Milo:getContext()
|
local context = Milo:getContext()
|
||||||
|
|
||||||
local machinesTab = UI.Tab {
|
local machinesTab = UI.Tab {
|
||||||
tabTitle = 'Machine',
|
tabTitle = 'Machine',
|
||||||
index = 3,
|
index = 3,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
x = 2, ex = -2, y = 2, ey = -2,
|
x = 2, ex = -2, y = 2, ey = -2,
|
||||||
disableHeader = true,
|
disableHeader = true,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Name', key = 'displayName'},
|
{ heading = 'Name', key = 'displayName'},
|
||||||
},
|
},
|
||||||
sortColumn = 'displayName',
|
sortColumn = 'displayName',
|
||||||
help = 'Double-click to set machine',
|
help = 'Double-click to set machine',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function machinesTab:setItem(item)
|
function machinesTab:setItem(item)
|
||||||
self.item = item
|
self.item = item
|
||||||
local machine = Craft.machineLookup[self.item.key]
|
local machine = Craft.machineLookup[self.item.key]
|
||||||
local t = Util.filter(context.storage.nodes, function(node)
|
local t = Util.filter(context.storage.nodes, function(node)
|
||||||
if node.category == 'machine' or node.category == 'custom' then -- TODO: - need a setting instead (ie. canCraft)
|
if node.category == 'machine' or node.category == 'custom' then -- TODO: - need a setting instead (ie. canCraft)
|
||||||
return node.adapter and node.adapter.online and node.adapter.pushItems
|
return node.adapter and node.adapter.online and node.adapter.pushItems
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
self.grid:setValues(t)
|
self.grid:setValues(t)
|
||||||
if machine then
|
if machine then
|
||||||
self.grid:setSelected('name', machine)
|
self.grid:setSelected('name', machine)
|
||||||
end
|
end
|
||||||
self.parent:setActive(self, item.has_recipe)
|
self.parent:setActive(self, item.has_recipe)
|
||||||
end
|
end
|
||||||
|
|
||||||
function machinesTab.grid:getDisplayValues(row)
|
function machinesTab.grid:getDisplayValues(row)
|
||||||
row = Util.shallowCopy(row)
|
row = Util.shallowCopy(row)
|
||||||
row.displayName = row.displayName or row.name
|
row.displayName = row.displayName or row.name
|
||||||
return row
|
return row
|
||||||
end
|
end
|
||||||
|
|
||||||
function machinesTab.grid:getRowTextColor(row, selected)
|
function machinesTab.grid:getRowTextColor(row, selected)
|
||||||
if row.name == Craft.machineLookup[self.parent.item.key] then
|
if row.name == Craft.machineLookup[self.parent.item.key] then
|
||||||
return colors.yellow
|
return colors.yellow
|
||||||
end
|
end
|
||||||
return UI.Grid:getRowTextColor(row, selected)
|
return UI.Grid:getRowTextColor(row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
function machinesTab:eventHandler(event)
|
function machinesTab:eventHandler(event)
|
||||||
if event.type == 'grid_select' then
|
if event.type == 'grid_select' then
|
||||||
if event.selected.name == Craft.machineLookup[self.item.key] then
|
if event.selected.name == Craft.machineLookup[self.item.key] then
|
||||||
Craft.machineLookup[self.item.key] = nil
|
Craft.machineLookup[self.item.key] = nil
|
||||||
else
|
else
|
||||||
Craft.machineLookup[self.item.key] = event.selected.name
|
Craft.machineLookup[self.item.key] = event.selected.name
|
||||||
end
|
end
|
||||||
Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup)
|
Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup)
|
||||||
|
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:emit({ type = 'info_message', message = 'Saved' })
|
self:emit({ type = 'info_message', message = 'Saved' })
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return { itemTab = machinesTab }
|
return { itemTab = machinesTab }
|
||||||
|
|||||||
@@ -7,91 +7,91 @@ local Util = require('util')
|
|||||||
local context = Milo:getContext()
|
local context = Milo:getContext()
|
||||||
|
|
||||||
local manageTab = UI.Tab {
|
local manageTab = UI.Tab {
|
||||||
tabTitle = 'Manage',
|
tabTitle = 'Manage',
|
||||||
index = 1,
|
index = 1,
|
||||||
form = UI.Form {
|
form = UI.Form {
|
||||||
x = 1, ex = -1, ey = -1,
|
x = 1, ex = -1, ey = -1,
|
||||||
--manualControls = true,
|
--manualControls = true,
|
||||||
[1] = UI.TextEntry {
|
[1] = UI.TextEntry {
|
||||||
formLabel = 'Name', formKey = 'displayName', help = 'Override display name',
|
formLabel = 'Name', formKey = 'displayName', help = 'Override display name',
|
||||||
shadowText = 'Display name',
|
shadowText = 'Display name',
|
||||||
required = true,
|
required = true,
|
||||||
limit = 120,
|
limit = 120,
|
||||||
},
|
},
|
||||||
[2] = UI.TextEntry {
|
[2] = UI.TextEntry {
|
||||||
width = 7,
|
width = 7,
|
||||||
formLabel = 'Min', formKey = 'low', help = 'Craft if below min',
|
formLabel = 'Min', formKey = 'low', help = 'Craft if below min',
|
||||||
validate = 'numeric',
|
validate = 'numeric',
|
||||||
},
|
},
|
||||||
[3] = UI.TextEntry {
|
[3] = UI.TextEntry {
|
||||||
width = 7,
|
width = 7,
|
||||||
formLabel = 'Max', formKey = 'limit', help = 'Send to trash if above max',
|
formLabel = 'Max', formKey = 'limit', help = 'Send to trash if above max',
|
||||||
validate = 'numeric',
|
validate = 'numeric',
|
||||||
},
|
},
|
||||||
[4] = UI.Checkbox {
|
[4] = UI.Checkbox {
|
||||||
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage',
|
formLabel = 'Ignore Dmg', formKey = 'ignoreDamage',
|
||||||
help = 'Ignore damage of item',
|
help = 'Ignore damage of item',
|
||||||
},
|
},
|
||||||
[5] = UI.Checkbox {
|
[5] = UI.Checkbox {
|
||||||
formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash',
|
formLabel = 'Ignore NBT', formKey = 'ignoreNbtHash',
|
||||||
help = 'Ignore NBT of item',
|
help = 'Ignore NBT of item',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function manageTab:setItem(item)
|
function manageTab:setItem(item)
|
||||||
self.item = item
|
self.item = item
|
||||||
self.res = Util.shallowCopy(context.resources[item.key] or { })
|
self.res = Util.shallowCopy(context.resources[item.key] or { })
|
||||||
self.res.displayName = self.item.displayName
|
self.res.displayName = self.item.displayName
|
||||||
self.form:setValues(self.res)
|
self.form:setValues(self.res)
|
||||||
|
|
||||||
-- TODO: ignore damage should not be active if there is not a maxDamage value
|
-- TODO: ignore damage should not be active if there is not a maxDamage value
|
||||||
end
|
end
|
||||||
|
|
||||||
function manageTab:eventHandler(event)
|
function manageTab:eventHandler(event)
|
||||||
if event.type == 'form_cancel' then
|
if event.type == 'form_cancel' then
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
|
|
||||||
elseif event.type == 'form_complete' then
|
elseif event.type == 'form_complete' then
|
||||||
if self.form:save() then
|
if self.form:save() then
|
||||||
if self.res.displayName ~= self.item.displayName then
|
if self.res.displayName ~= self.item.displayName then
|
||||||
self.item.displayName = self.res.displayName
|
self.item.displayName = self.res.displayName
|
||||||
itemDB:add(self.item)
|
itemDB:add(self.item)
|
||||||
itemDB:flush()
|
itemDB:flush()
|
||||||
if context.storage.cache[self.item.key] then
|
if context.storage.cache[self.item.key] then
|
||||||
context.storage.cache[self.item.key].displayName = self.res.displayName
|
context.storage.cache[self.item.key].displayName = self.res.displayName
|
||||||
end
|
end
|
||||||
--context.storage:setDirty()
|
--context.storage:setDirty()
|
||||||
end
|
end
|
||||||
|
|
||||||
self.res.displayName = nil
|
self.res.displayName = nil
|
||||||
Map.prune(self.res, function(v)
|
Map.prune(self.res, function(v)
|
||||||
if type(v) == 'boolean' then
|
if type(v) == 'boolean' then
|
||||||
return v
|
return v
|
||||||
elseif type(v) == 'string' then
|
elseif type(v) == 'string' then
|
||||||
return #v > 0
|
return #v > 0
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local newKey = {
|
local newKey = {
|
||||||
name = self.item.name,
|
name = self.item.name,
|
||||||
damage = self.res.ignoreDamage and 0 or self.item.damage,
|
damage = self.res.ignoreDamage and 0 or self.item.damage,
|
||||||
nbtHash = not self.res.ignoreNbtHash and self.item.nbtHash or nil,
|
nbtHash = not self.res.ignoreNbtHash and self.item.nbtHash or nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
context.resources[self.item.key] = nil
|
context.resources[self.item.key] = nil
|
||||||
if not Util.empty(self.res) then
|
if not Util.empty(self.res) then
|
||||||
context.resources[itemDB:makeKey(newKey)] = self.res
|
context.resources[itemDB:makeKey(newKey)] = self.res
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:saveResources()
|
Milo:saveResources()
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
return { itemTab = manageTab }
|
return { itemTab = manageTab }
|
||||||
|
|||||||
@@ -6,85 +6,89 @@ local UI = require('ui')
|
|||||||
local colors = _G.colors
|
local colors = _G.colors
|
||||||
|
|
||||||
local recipeTab = UI.Tab {
|
local recipeTab = UI.Tab {
|
||||||
tabTitle = 'Recipe',
|
tabTitle = 'Recipe',
|
||||||
index = 2,
|
index = 2,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
x = 2, ex = -2, y = 2, ey = -4,
|
x = 2, ex = -2, y = 2, ey = -4,
|
||||||
disableHeader = true,
|
disableHeader = true,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Slot', key = 'slot', width = 2 },
|
{ heading = 'Slot', key = 'slot', width = 2 },
|
||||||
{ heading = 'Key', key = 'key' },
|
{ heading = 'Count', key = 'count', width = 2 },
|
||||||
},
|
{ heading = 'Key', key = 'key' },
|
||||||
sortColumn = 'slot',
|
},
|
||||||
},
|
sortColumn = 'slot',
|
||||||
ignoreResultNBT = UI.Button {
|
},
|
||||||
x = 2, y = -2,
|
ignoreResultNBT = UI.Button {
|
||||||
text = 'Ignore Result NBT', event = 'ignore_result_nbt',
|
x = 2, y = -2,
|
||||||
},
|
text = 'Ignore Result NBT', event = 'ignore_result_nbt',
|
||||||
ignoreNBT = UI.Button {
|
},
|
||||||
x = -13, y = -2,
|
ignoreNBT = UI.Button {
|
||||||
text = 'Ignore NBT', event = 'ignore_nbt',
|
x = -13, y = -2,
|
||||||
},
|
text = 'Ignore NBT', event = 'ignore_nbt',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function recipeTab:setItem(item)
|
function recipeTab:setItem(item)
|
||||||
self.item = item
|
self.item = item
|
||||||
self.recipe = Craft.findRecipe(self.item)
|
self.recipe = Craft.findRecipe(self.item)
|
||||||
|
|
||||||
self.parent:setActive(self, self.recipe)
|
self.parent:setActive(self, self.recipe)
|
||||||
|
|
||||||
local t = { }
|
local t = { }
|
||||||
if self.recipe then
|
if self.recipe then
|
||||||
for k, v in pairs(self.recipe.ingredients) do
|
for k, v in Craft.ingedients(self.recipe) do
|
||||||
table.insert(t, {
|
_syslog(k)
|
||||||
slot = k,
|
_syslog(v)
|
||||||
key = v,
|
table.insert(t, {
|
||||||
})
|
slot = k,
|
||||||
end
|
key = v.key,
|
||||||
local key = itemDB:splitKey(self.recipe.result)
|
count = v.count,
|
||||||
self.ignoreResultNBT.inactive = not key.nbtHash
|
})
|
||||||
end
|
end
|
||||||
self.grid:setValues(t)
|
local key = itemDB:splitKey(self.recipe.result)
|
||||||
|
self.ignoreResultNBT.inactive = not key.nbtHash
|
||||||
|
end
|
||||||
|
self.grid:setValues(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
function recipeTab:eventHandler(event)
|
function recipeTab:eventHandler(event)
|
||||||
if event.type == 'ignore_result_nbt' then
|
if event.type == 'ignore_result_nbt' then
|
||||||
-- remove old entry
|
-- remove old entry
|
||||||
Milo:updateRecipe(self.recipe.result)
|
Milo:updateRecipe(self.recipe.result)
|
||||||
|
|
||||||
local item = itemDB:splitKey(self.recipe.result)
|
local item = itemDB:splitKey(self.recipe.result)
|
||||||
item.nbtHash = nil
|
item.nbtHash = nil
|
||||||
self.recipe.result = itemDB:makeKey(item)
|
self.recipe.result = itemDB:makeKey(item)
|
||||||
|
|
||||||
-- add updated entry
|
-- add updated entry
|
||||||
Milo:updateRecipe(self.recipe.result, self.recipe)
|
Milo:updateRecipe(self.recipe.result, self.recipe)
|
||||||
|
|
||||||
self.ignoreResultNBT.inactive = true
|
self.ignoreResultNBT.inactive = true
|
||||||
self:emit({ type = 'info_message', message = 'Recipe updated' })
|
self:emit({ type = 'info_message', message = 'Recipe updated' })
|
||||||
|
|
||||||
elseif event.type == 'grid_focus_row' then
|
elseif event.type == 'grid_focus_row' then
|
||||||
local key = itemDB:splitKey(event.selected.key)
|
local key = itemDB:splitKey(event.selected.key)
|
||||||
self.ignoreNBT.inactive = not key.nbtHash
|
self.ignoreNBT.inactive = not key.nbtHash
|
||||||
self.ignoreNBT:draw()
|
self.ignoreNBT:draw()
|
||||||
|
|
||||||
elseif event.type == 'ignore_nbt' then
|
elseif event.type == 'ignore_nbt' then
|
||||||
local selected = self.grid:getSelected()
|
local selected = self.grid:getSelected()
|
||||||
local item = itemDB:splitKey(selected.key)
|
local item = itemDB:splitKey(selected.key)
|
||||||
item.nbtHash = nil
|
item.nbtHash = nil
|
||||||
selected.key = itemDB:makeKey(item)
|
selected.key = itemDB:makeKey(item)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
|
|
||||||
self.recipe.ingredients = { }
|
self.recipe.ingredients = { }
|
||||||
for _, v in pairs(self.grid.values) do
|
for _, v in pairs(self.grid.values) do
|
||||||
self.recipe.ingredients[v.slot] = v.key
|
self.recipe.ingredients[v.slot] = v.key
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:updateRecipe(self.recipe.result, self.recipe)
|
Milo:updateRecipe(self.recipe.result, self.recipe)
|
||||||
self:emit({ type = 'info_message', message = 'Recipe updated' })
|
self:emit({ type = 'info_message', message = 'Recipe updated' })
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return { itemTab = recipeTab }
|
return { itemTab = recipeTab }
|
||||||
|
|||||||
@@ -7,49 +7,49 @@ local colors = _G.colors
|
|||||||
local context = Milo:getContext()
|
local context = Milo:getContext()
|
||||||
|
|
||||||
local resetTab = UI.Tab {
|
local resetTab = UI.Tab {
|
||||||
tabTitle = 'Reset',
|
tabTitle = 'Reset',
|
||||||
index = 5,
|
index = 5,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
textArea = UI.TextArea {
|
textArea = UI.TextArea {
|
||||||
y = 2, ey = 6,
|
y = 2, ey = 6,
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
value = [[ Warning!
|
value = [[ Warning!
|
||||||
|
|
||||||
This will clear all setting,
|
This will clear all setting,
|
||||||
recipe, and machine for this item.]]
|
recipe, and machine for this item.]]
|
||||||
},
|
},
|
||||||
resetButton = UI.Button {
|
resetButton = UI.Button {
|
||||||
x = 17, y = 7,
|
x = 17, y = 7,
|
||||||
event = 'reset',
|
event = 'reset',
|
||||||
text = 'Reset',
|
text = 'Reset',
|
||||||
help = 'Clear recipe and all settings',
|
help = 'Clear recipe and all settings',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetTab:setItem(item)
|
function resetTab:setItem(item)
|
||||||
self.item = item
|
self.item = item
|
||||||
end
|
end
|
||||||
|
|
||||||
function resetTab:eventHandler(event)
|
function resetTab:eventHandler(event)
|
||||||
if event.type == 'reset' then
|
if event.type == 'reset' then
|
||||||
if context.userRecipes[self.item.key] then
|
if context.userRecipes[self.item.key] then
|
||||||
Milo:updateRecipe(self.item.key, nil)
|
Milo:updateRecipe(self.item.key, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
if context.resources[self.item.key] then
|
if context.resources[self.item.key] then
|
||||||
context.resources[self.item.key] = nil
|
context.resources[self.item.key] = nil
|
||||||
Milo:saveResources()
|
Milo:saveResources()
|
||||||
end
|
end
|
||||||
|
|
||||||
if Craft.machineLookup[self.item.key] then
|
if Craft.machineLookup[self.item.key] then
|
||||||
Craft.machineLookup[self.item.key] = nil
|
Craft.machineLookup[self.item.key] = nil
|
||||||
Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup)
|
Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup)
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return { itemTab = resetTab }
|
return { itemTab = resetTab }
|
||||||
|
|||||||
@@ -13,224 +13,224 @@ local os = _G.os
|
|||||||
|
|
||||||
--[[ Configuration Screen ]]
|
--[[ Configuration Screen ]]
|
||||||
local wizardPage = UI.WizardPage {
|
local wizardPage = UI.WizardPage {
|
||||||
title = 'Crafting Monitor',
|
title = 'Crafting Monitor',
|
||||||
index = 2,
|
index = 2,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
[1] = UI.TextArea {
|
[1] = UI.TextArea {
|
||||||
x = 2, ex = -2, y = 2, ey = 3,
|
x = 2, ex = -2, y = 2, ey = 3,
|
||||||
marginRight = 0,
|
marginRight = 0,
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
value = 'Displays the crafting progress.'
|
value = 'Displays the crafting progress.'
|
||||||
},
|
},
|
||||||
form = UI.Form {
|
form = UI.Form {
|
||||||
x = 2, ex = -2, y = 4, ey = -2,
|
x = 2, ex = -2, y = 4, ey = -2,
|
||||||
manualControls = true,
|
manualControls = true,
|
||||||
[1] = UI.Chooser {
|
[1] = UI.Chooser {
|
||||||
width = 9,
|
width = 9,
|
||||||
formLabel = 'Font Size', formKey = 'textScale',
|
formLabel = 'Font Size', formKey = 'textScale',
|
||||||
nochoice = 'Small',
|
nochoice = 'Small',
|
||||||
choices = {
|
choices = {
|
||||||
{ name = 'Small', value = .5 },
|
{ name = 'Small', value = .5 },
|
||||||
{ name = 'Large', value = 1 },
|
{ name = 'Large', value = 1 },
|
||||||
},
|
},
|
||||||
help = 'Adjust text scaling',
|
help = 'Adjust text scaling',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function wizardPage:setNode(node)
|
function wizardPage:setNode(node)
|
||||||
self.form:setValues(node)
|
self.form:setValues(node)
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:saveNode(node)
|
function wizardPage:saveNode(node)
|
||||||
os.queueEvent('monitor_resize', node.name)
|
os.queueEvent('monitor_resize', node.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:validate()
|
function wizardPage:validate()
|
||||||
return self.form:save()
|
return self.form:save()
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:isValidType(node)
|
function wizardPage:isValidType(node)
|
||||||
local m = device[node.name]
|
local m = device[node.name]
|
||||||
return m and m.type == 'monitor' and {
|
return m and m.type == 'monitor' and {
|
||||||
name = 'Crafting Monitor',
|
name = 'Crafting Monitor',
|
||||||
value = 'jobs',
|
value = 'jobs',
|
||||||
category = 'display',
|
category = 'display',
|
||||||
help = 'Display crafting progress / jobs'
|
help = 'Display crafting progress / jobs'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:isValidFor(node)
|
function wizardPage:isValidFor(node)
|
||||||
return node.mtype == 'jobs'
|
return node.mtype == 'jobs'
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:getPage('nodeWizard').wizard:add({ jobs = wizardPage })
|
UI:getPage('nodeWizard').wizard:add({ jobs = wizardPage })
|
||||||
|
|
||||||
--[[ Display ]]
|
--[[ Display ]]
|
||||||
local function createPage(node)
|
local function createPage(node)
|
||||||
local monitor = UI.Device {
|
local monitor = UI.Device {
|
||||||
device = node.adapter,
|
device = node.adapter,
|
||||||
textScale = node.textScale or .5,
|
textScale = node.textScale or .5,
|
||||||
}
|
}
|
||||||
|
|
||||||
function monitor:resize()
|
function monitor:resize()
|
||||||
self.textScale = node.textScale or .5
|
self.textScale = node.textScale or .5
|
||||||
UI.Device.resize(self)
|
UI.Device.resize(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
parent = monitor,
|
parent = monitor,
|
||||||
grid = UI.Grid {
|
grid = UI.Grid {
|
||||||
--ey = -6,
|
--ey = -6,
|
||||||
sortColumn = 'index',
|
sortColumn = 'index',
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Qty', key = 'remaining', width = 4 },
|
{ heading = 'Qty', key = 'remaining', width = 4 },
|
||||||
{ heading = 'Crafting', key = 'displayName', },
|
{ heading = 'Crafting', key = 'displayName', },
|
||||||
{ heading = 'Status', key = 'status', },
|
{ heading = 'Status', key = 'status', },
|
||||||
{ heading = 'need', key = 'need', width = 4 },
|
{ heading = 'need', key = 'need', width = 4 },
|
||||||
-- { heading = 'total', key = 'total', width = 4 },
|
-- { heading = 'total', key = 'total', width = 4 },
|
||||||
-- { heading = 'used', key = 'used', width = 4 },
|
-- { heading = 'used', key = 'used', width = 4 },
|
||||||
-- { heading = 'count', key = 'count', width = 4 },
|
-- { heading = 'count', key = 'count', width = 4 },
|
||||||
{ heading = 'crafted', key = 'crafted', width = 5 },
|
{ heading = 'crafted', key = 'crafted', width = 5 },
|
||||||
-- { heading = 'Progress', key = 'progress', width = 8 },
|
-- { heading = 'Progress', key = 'progress', width = 8 },
|
||||||
},
|
},
|
||||||
headerBackgroundColor = colors.black,
|
headerBackgroundColor = colors.black,
|
||||||
headerTextColor = colors.cyan,
|
headerTextColor = colors.cyan,
|
||||||
headerHeight = 2,
|
headerHeight = 2,
|
||||||
},
|
},
|
||||||
--[[
|
--[[
|
||||||
buttons = UI.Window {
|
buttons = UI.Window {
|
||||||
y = -5, height = 5,
|
y = -5, height = 5,
|
||||||
backgroundColor = colors.gray,
|
backgroundColor = colors.gray,
|
||||||
prevButton = UI.Button {
|
prevButton = UI.Button {
|
||||||
x = 2, y = 2, height = 3, width = 5,
|
x = 2, y = 2, height = 3, width = 5,
|
||||||
event = 'previous',
|
event = 'previous',
|
||||||
backgroundColor = colors.lightGray,
|
backgroundColor = colors.lightGray,
|
||||||
text = ' < '
|
text = ' < '
|
||||||
},
|
},
|
||||||
cancelButton = UI.Button {
|
cancelButton = UI.Button {
|
||||||
x = 8, y = 2, height = 3, ex = -8,
|
x = 8, y = 2, height = 3, ex = -8,
|
||||||
event = 'cancel_job',
|
event = 'cancel_job',
|
||||||
backgroundColor = colors.lightGray,
|
backgroundColor = colors.lightGray,
|
||||||
text = 'Cancel Job'
|
text = 'Cancel Job'
|
||||||
},
|
},
|
||||||
nextButton = UI.Button {
|
nextButton = UI.Button {
|
||||||
x = -6, y = 2, height = 3, width = 5,
|
x = -6, y = 2, height = 3, width = 5,
|
||||||
event = 'next',
|
event = 'next',
|
||||||
backgroundColor = colors.lightGray,
|
backgroundColor = colors.lightGray,
|
||||||
text = ' > '
|
text = ' > '
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]]
|
]]
|
||||||
}
|
}
|
||||||
|
|
||||||
function page:updateList(craftList)
|
function page:updateList(craftList)
|
||||||
if not Milo:isCraftingPaused() then
|
if not Milo:isCraftingPaused() then
|
||||||
local t = { }
|
local t = { }
|
||||||
for _,v in pairs(craftList) do
|
for _,v in pairs(craftList) do
|
||||||
table.insert(t, v)
|
table.insert(t, v)
|
||||||
v.index = #t
|
v.index = #t
|
||||||
for k2,v2 in pairs(v.ingredients or { }) do
|
for k2,v2 in pairs(v.ingredients or { }) do
|
||||||
if v2.key ~= v.key --[[and v2.statusCode ]] then
|
if v2.key ~= v.key --[[and v2.statusCode ]] then
|
||||||
table.insert(t, v2)
|
table.insert(t, v2)
|
||||||
if not v2.displayName then
|
if not v2.displayName then
|
||||||
v2.displayName = itemDB:getName(k2)
|
v2.displayName = itemDB:getName(k2)
|
||||||
end
|
end
|
||||||
v2.index = #t
|
v2.index = #t
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.grid:setValues(t)
|
self.grid:setValues(t)
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
self:draw()
|
self:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:getDisplayValues(row)
|
function page.grid:getDisplayValues(row)
|
||||||
row = Util.shallowCopy(row)
|
row = Util.shallowCopy(row)
|
||||||
if not row.displayName then
|
if not row.displayName then
|
||||||
row.displayName = itemDB:getName(row)
|
row.displayName = itemDB:getName(row)
|
||||||
end
|
end
|
||||||
if row.requested then
|
if row.requested then
|
||||||
row.remaining = math.max(0, row.requested - row.crafted)
|
row.remaining = math.max(0, row.requested - row.crafted)
|
||||||
else
|
else
|
||||||
row.displayName = ' ' .. row.displayName
|
row.displayName = ' ' .. row.displayName
|
||||||
end
|
end
|
||||||
--row.progress = string.format('%d/%d', row.crafted, row.count)
|
--row.progress = string.format('%d/%d', row.crafted, row.count)
|
||||||
return row
|
return row
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:getRowTextColor(row, selected)
|
function page.grid:getRowTextColor(row, selected)
|
||||||
local statusColor = {
|
local statusColor = {
|
||||||
[ Craft.STATUS_ERROR ] = colors.red,
|
[ Craft.STATUS_ERROR ] = colors.red,
|
||||||
[ Craft.STATUS_WARNING ] = colors.orange,
|
[ Craft.STATUS_WARNING ] = colors.orange,
|
||||||
[ Craft.STATUS_INFO ] = colors.yellow,
|
[ Craft.STATUS_INFO ] = colors.yellow,
|
||||||
[ Craft.STATUS_SUCCESS ] = colors.green,
|
[ Craft.STATUS_SUCCESS ] = colors.green,
|
||||||
}
|
}
|
||||||
return row.statusCode and statusColor[row.statusCode] or
|
return row.statusCode and statusColor[row.statusCode] or
|
||||||
UI.Grid:getRowTextColor(row, selected)
|
UI.Grid:getRowTextColor(row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- no sorting allowed
|
-- no sorting allowed
|
||||||
function page:setInverseSort() end
|
function page:setInverseSort() end
|
||||||
function page:setSortColumn() end
|
function page:setSortColumn() end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'cancel_job' then
|
if event.type == 'cancel_job' then
|
||||||
Sound.play('entity.villager.no', .5)
|
Sound.play('entity.villager.no', .5)
|
||||||
|
|
||||||
elseif event.type == 'next' then
|
elseif event.type == 'next' then
|
||||||
self.grid:nextPage()
|
self.grid:nextPage()
|
||||||
|
|
||||||
elseif event.type == 'previous' then
|
elseif event.type == 'previous' then
|
||||||
self.grid:previousPage()
|
self.grid:previousPage()
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.onTimeout(.1, function()
|
Event.onTimeout(.1, function()
|
||||||
self:setFocus(self.grid)
|
self:setFocus(self.grid)
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:setPage(page)
|
UI:setPage(page)
|
||||||
return page
|
return page
|
||||||
end
|
end
|
||||||
|
|
||||||
local pages = { }
|
local pages = { }
|
||||||
|
|
||||||
Event.on({ 'milo_resume', 'milo_pause' }, function(_, reason)
|
Event.on({ 'milo_resume', 'milo_pause' }, function(_, reason)
|
||||||
for node in context.storage:filterActive('jobs') do
|
for node in context.storage:filterActive('jobs') do
|
||||||
local page = pages[node.name]
|
local page = pages[node.name]
|
||||||
if page then
|
if page then
|
||||||
if reason then
|
if reason then
|
||||||
page.grid:clear()
|
page.grid:clear()
|
||||||
page.grid:centeredWrite(math.ceil(page.grid.height / 2), reason.msg)
|
page.grid:centeredWrite(math.ceil(page.grid.height / 2), reason.msg)
|
||||||
else
|
else
|
||||||
page.grid:draw()
|
page.grid:draw()
|
||||||
end
|
end
|
||||||
page:sync()
|
page:sync()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
--[[ Task ]]
|
--[[ Task ]]
|
||||||
local task = {
|
local task = {
|
||||||
name = 'job status',
|
name = 'job status',
|
||||||
priority = 80,
|
priority = 80,
|
||||||
}
|
}
|
||||||
|
|
||||||
function task:cycle()
|
function task:cycle()
|
||||||
for node in context.storage:filterActive('jobs') do
|
for node in context.storage:filterActive('jobs') do
|
||||||
if not pages[node.name] then
|
if not pages[node.name] then
|
||||||
pages[node.name] = createPage(node)
|
pages[node.name] = createPage(node)
|
||||||
end
|
end
|
||||||
pages[node.name]:updateList(context.craftingQueue)
|
pages[node.name]:updateList(context.craftingQueue)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:registerTask(task)
|
Milo:registerTask(task)
|
||||||
|
|||||||
@@ -111,7 +111,14 @@ function pages.confirmation:validate()
|
|||||||
}
|
}
|
||||||
|
|
||||||
for k,v in pairs(inventory) do
|
for k,v in pairs(inventory) do
|
||||||
recipe.ingredients[k] = itemDB:makeKey(v)
|
if v.count == 1 then
|
||||||
|
recipe.ingredients[k] = itemDB:makeKey(v)
|
||||||
|
else
|
||||||
|
recipe.ingredients[k] = {
|
||||||
|
key = itemDB:makeKey(v),
|
||||||
|
count = v.count,
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:saveMachineRecipe(recipe, result, machine.name)
|
Milo:saveMachineRecipe(recipe, result, machine.name)
|
||||||
|
|||||||
@@ -8,100 +8,100 @@ local context = Milo:getContext()
|
|||||||
local device = _G.device
|
local device = _G.device
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
titleBar = UI.TitleBar { title = 'Reassign Machine' },
|
titleBar = UI.TitleBar { title = 'Reassign Machine' },
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = 2, ey = -4,
|
y = 2, ey = -4,
|
||||||
values = context.storage.nodes,
|
values = context.storage.nodes,
|
||||||
columns = {
|
columns = {
|
||||||
{ key = 'suffix', width = 4, align = 'right' },
|
{ key = 'suffix', width = 4, align = 'right' },
|
||||||
{ heading = 'Name', key = 'displayName' },
|
{ heading = 'Name', key = 'displayName' },
|
||||||
{ heading = 'Type', key = 'mtype', width = 4 },
|
{ heading = 'Type', key = 'mtype', width = 4 },
|
||||||
{ heading = 'Pri', key = 'priority', width = 3 },
|
{ heading = 'Pri', key = 'priority', width = 3 },
|
||||||
},
|
},
|
||||||
sortColumn = 'displayName',
|
sortColumn = 'displayName',
|
||||||
help = 'Select Node',
|
help = 'Select Node',
|
||||||
},
|
},
|
||||||
accept = UI.Button {
|
accept = UI.Button {
|
||||||
x = -9, y = -2,
|
x = -9, y = -2,
|
||||||
event = 'grid_select',
|
event = 'grid_select',
|
||||||
text = 'Accept',
|
text = 'Accept',
|
||||||
},
|
},
|
||||||
cancel = UI.Button {
|
cancel = UI.Button {
|
||||||
x = -18, y = -2,
|
x = -18, y = -2,
|
||||||
event = 'cancel',
|
event = 'cancel',
|
||||||
text = 'Cancel',
|
text = 'Cancel',
|
||||||
},
|
},
|
||||||
accelerators = {
|
accelerators = {
|
||||||
grid_select = 'nextView',
|
grid_select = 'nextView',
|
||||||
},
|
},
|
||||||
notification = UI.Notification { },
|
notification = UI.Notification { },
|
||||||
}
|
}
|
||||||
|
|
||||||
function page.grid:getDisplayValues(row)
|
function page.grid:getDisplayValues(row)
|
||||||
row = Util.shallowCopy(row)
|
row = Util.shallowCopy(row)
|
||||||
local t = { row.name:match(':(.+)_(%d+)$') }
|
local t = { row.name:match(':(.+)_(%d+)$') }
|
||||||
if #t ~= 2 then
|
if #t ~= 2 then
|
||||||
t = { row.name:match('(.+)_(%d+)$') }
|
t = { row.name:match('(.+)_(%d+)$') }
|
||||||
end
|
end
|
||||||
if t and #t == 2 then
|
if t and #t == 2 then
|
||||||
row.name, row.suffix = table.unpack(t)
|
row.name, row.suffix = table.unpack(t)
|
||||||
row.name = row.name .. '_' .. row.suffix
|
row.name = row.name .. '_' .. row.suffix
|
||||||
end
|
end
|
||||||
row.displayName = row.displayName or row.name
|
row.displayName = row.displayName or row.name
|
||||||
return row
|
return row
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.grid:getRowTextColor(row, selected)
|
function page.grid:getRowTextColor(row, selected)
|
||||||
if row.mtype == 'ignore' then
|
if row.mtype == 'ignore' then
|
||||||
return colors.lightGray
|
return colors.lightGray
|
||||||
end
|
end
|
||||||
return UI.Grid:getRowTextColor(row, selected)
|
return UI.Grid:getRowTextColor(row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:applyFilter()
|
function page:applyFilter()
|
||||||
local t = Util.filter(context.storage.nodes, function(v)
|
local t = Util.filter(context.storage.nodes, function(v)
|
||||||
return v.mtype == 'ignore' and device[v.name]
|
return v.mtype == 'ignore' and device[v.name]
|
||||||
end)
|
end)
|
||||||
|
|
||||||
self.grid:setValues(t)
|
self.grid:setValues(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:enable(machine)
|
function page:enable(machine)
|
||||||
self.machine = machine
|
self.machine = machine
|
||||||
self:applyFilter()
|
self:applyFilter()
|
||||||
|
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'grid_select' then
|
if event.type == 'grid_select' then
|
||||||
local target = self.grid:getSelected()
|
local target = self.grid:getSelected()
|
||||||
if target then
|
if target then
|
||||||
local adapter = target.adapter
|
local adapter = target.adapter
|
||||||
local name = target.name
|
local name = target.name
|
||||||
Util.merge(target, self.machine)
|
Util.merge(target, self.machine)
|
||||||
target.adapter = adapter
|
target.adapter = adapter
|
||||||
target.name = name
|
target.name = name
|
||||||
|
|
||||||
context.storage.nodes[self.machine.name] = nil
|
context.storage.nodes[self.machine.name] = nil
|
||||||
context.storage:saveConfiguration()
|
context.storage:saveConfiguration()
|
||||||
|
|
||||||
for k,v in pairs(Craft.machineLookup) do
|
for k,v in pairs(Craft.machineLookup) do
|
||||||
if v == self.machine.name then
|
if v == self.machine.name then
|
||||||
Craft.machineLookup[k] = name
|
Craft.machineLookup[k] = name
|
||||||
end
|
end
|
||||||
Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup)
|
Util.writeTable(Craft.MACHINE_LOOKUP, Craft.machineLookup)
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'cancel' then
|
elseif event.type == 'cancel' then
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:addPage('machineMover', page)
|
UI:addPage('machineMover', page)
|
||||||
|
|||||||
@@ -14,29 +14,29 @@ Add all speed upgrades possible.
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
local wizardPage = UI.WizardPage {
|
local wizardPage = UI.WizardPage {
|
||||||
title = 'Mass Storage',
|
title = 'Mass Storage',
|
||||||
index = 2,
|
index = 2,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
[1] = UI.TextArea {
|
[1] = UI.TextArea {
|
||||||
x = 2, ex = -2, y = 2, ey = -2,
|
x = 2, ex = -2, y = 2, ey = -2,
|
||||||
value = string.format(template, Ansi.red, Ansi.reset),
|
value = string.format(template, Ansi.red, Ansi.reset),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function wizardPage:isValidFor(node)
|
function wizardPage:isValidFor(node)
|
||||||
if node.mtype == 'storage' then
|
if node.mtype == 'storage' then
|
||||||
local m = device[node.name]
|
local m = device[node.name]
|
||||||
return m and m.listAvailableItems
|
return m and m.listAvailableItems
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:setNode(node)
|
function wizardPage:setNode(node)
|
||||||
self.node = node
|
self.node = node
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:validate()
|
function wizardPage:validate()
|
||||||
self.node.adapterType = 'massAdapter'
|
self.node.adapterType = 'massAdapter'
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- disable until a way is found to transfer between 2 non-transferrable nodes
|
-- disable until a way is found to transfer between 2 non-transferrable nodes
|
||||||
|
|||||||
@@ -4,18 +4,18 @@ local args = { ... }
|
|||||||
local context = args[1]
|
local context = args[1]
|
||||||
|
|
||||||
local function learn()
|
local function learn()
|
||||||
context:sendRequest({
|
context:sendRequest({
|
||||||
request = 'craft',
|
request = 'craft',
|
||||||
slot = 15,
|
slot = 15,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
context.responseHandlers['craft'] = function(response)
|
context.responseHandlers['craft'] = function(response)
|
||||||
if response.success then
|
if response.success then
|
||||||
Sound.play('entity.item.pickup')
|
Sound.play('entity.item.pickup')
|
||||||
else
|
else
|
||||||
Sound.play('entity.villager.no')
|
Sound.play('entity.villager.no')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -9,36 +9,36 @@ local context = args[1]
|
|||||||
local SHIELD_SLOT = 2
|
local SHIELD_SLOT = 2
|
||||||
|
|
||||||
Event.addRoutine(function()
|
Event.addRoutine(function()
|
||||||
local lastTransfer
|
local lastTransfer
|
||||||
while true do
|
while true do
|
||||||
local sleepTime = 1.5
|
local sleepTime = 1.5
|
||||||
if lastTransfer and os.clock() - lastTransfer < 2 then
|
if lastTransfer and os.clock() - lastTransfer < 2 then
|
||||||
sleepTime = .1
|
sleepTime = .1
|
||||||
end
|
end
|
||||||
|
|
||||||
os.sleep(context.socket and sleepTime or 5)
|
os.sleep(context.socket and sleepTime or 5)
|
||||||
if context.state.deposit and context.state.server and (context.state.useShield or context.state.slot) then
|
if context.state.deposit and context.state.server and (context.state.useShield or context.state.slot) then
|
||||||
local neural = device.neuralInterface
|
local neural = device.neuralInterface
|
||||||
local inv = context.state.useShield and 'getEquipment' or 'getInventory'
|
local inv = context.state.useShield and 'getEquipment' or 'getInventory'
|
||||||
if neural and neural[inv] then
|
if neural and neural[inv] then
|
||||||
local s, m = pcall(function()
|
local s, m = pcall(function()
|
||||||
local method = neural[inv]
|
local method = neural[inv]
|
||||||
local item = method and method().list()[context.state.useShield and SHIELD_SLOT or context.state.slot]
|
local item = method and method().list()[context.state.useShield and SHIELD_SLOT or context.state.slot]
|
||||||
if item then
|
if item then
|
||||||
if context:sendRequest({
|
if context:sendRequest({
|
||||||
request = 'deposit',
|
request = 'deposit',
|
||||||
source = context.state.useShield and 'equipment' or 'inventory',
|
source = context.state.useShield and 'equipment' or 'inventory',
|
||||||
slot = context.state.useShield and SHIELD_SLOT or context.state.slot,
|
slot = context.state.useShield and SHIELD_SLOT or context.state.slot,
|
||||||
count = item.count,
|
count = item.count,
|
||||||
}) then
|
}) then
|
||||||
lastTransfer = os.clock()
|
lastTransfer = os.clock()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G._syslog(m)
|
_G._syslog(m)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -10,70 +10,70 @@ local context = args[1]
|
|||||||
local ni = peripheral.find('neuralInterface')
|
local ni = peripheral.find('neuralInterface')
|
||||||
|
|
||||||
if not context.state.depositAll then
|
if not context.state.depositAll then
|
||||||
context.state.depositAll = { }
|
context.state.depositAll = { }
|
||||||
end
|
end
|
||||||
if not context.state.depositAll.retain then
|
if not context.state.depositAll.retain then
|
||||||
context.state.depositAll.retain = { }
|
context.state.depositAll.retain = { }
|
||||||
end
|
end
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
titleBar = UI.TitleBar {
|
titleBar = UI.TitleBar {
|
||||||
backgroundColor = colors.gray,
|
backgroundColor = colors.gray,
|
||||||
title = 'Deposit full inventory',
|
title = 'Deposit full inventory',
|
||||||
previousPage = true,
|
previousPage = true,
|
||||||
},
|
},
|
||||||
items = UI.ScrollingGrid {
|
items = UI.ScrollingGrid {
|
||||||
x = 2, ex = -2, y = 2, ey = -4,
|
x = 2, ex = -2, y = 2, ey = -4,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Qty', key = 'count', width = 3 },
|
{ heading = 'Qty', key = 'count', width = 3 },
|
||||||
{ heading = 'Name', key = 'displayName', },
|
{ heading = 'Name', key = 'displayName', },
|
||||||
},
|
},
|
||||||
sortColumn = 'count',
|
sortColumn = 'count',
|
||||||
inverseSort = true
|
inverseSort = true
|
||||||
},
|
},
|
||||||
form = UI.Form {
|
form = UI.Form {
|
||||||
x = 2, ex = -2, y = -2, ey = -2,
|
x = 2, ex = -2, y = -2, ey = -2,
|
||||||
margin = 1,
|
margin = 1,
|
||||||
[1] = UI.Checkbox {
|
[1] = UI.Checkbox {
|
||||||
formLabel = 'Include hotbar', formKey = 'includeHotbar',
|
formLabel = 'Include hotbar', formKey = 'includeHotbar',
|
||||||
help = 'Also send the contents of the hotbar to Milo (excluding the neural connector)'
|
help = 'Also send the contents of the hotbar to Milo (excluding the neural connector)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
notification = UI.Notification(),
|
notification = UI.Notification(),
|
||||||
}
|
}
|
||||||
|
|
||||||
local function makeKey(item) -- group items regardless of damage
|
local function makeKey(item) -- group items regardless of damage
|
||||||
local damage = item.maxDamage == 0 and item.damage
|
local damage = item.maxDamage == 0 and item.damage
|
||||||
return itemDB:makeKey({ name = item.name, damage = damage })
|
return itemDB:makeKey({ name = item.name, damage = damage })
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:updateInventoryList()
|
function page:updateInventoryList()
|
||||||
local inv = ni.getInventory().list()
|
local inv = ni.getInventory().list()
|
||||||
local list = { }
|
local list = { }
|
||||||
|
|
||||||
for slot, item in pairs(inv) do
|
for slot, item in pairs(inv) do
|
||||||
if (context.state.depositAll.includeHotbar or slot > 9) and item.name ~= 'plethora:neuralconnector' then
|
if (context.state.depositAll.includeHotbar or slot > 9) and item.name ~= 'plethora:neuralconnector' then
|
||||||
item = itemDB:get(item, function() return ni.getInventory().getItemMeta(slot) end)
|
item = itemDB:get(item, function() return ni.getInventory().getItemMeta(slot) end)
|
||||||
local key = makeKey(item)
|
local key = makeKey(item)
|
||||||
if not list[key] then
|
if not list[key] then
|
||||||
item.displayName = item.displayName:match('(.+) %(damage:.+%)') or item.displayName
|
item.displayName = item.displayName:match('(.+) %(damage:.+%)') or item.displayName
|
||||||
list[key] = item
|
list[key] = item
|
||||||
else
|
else
|
||||||
list[key].count = list[key].count + item.count
|
list[key].count = list[key].count + item.count
|
||||||
end
|
end
|
||||||
list[key].key = key
|
list[key].key = key
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self.items:setValues(list)
|
self.items:setValues(list)
|
||||||
self.items:draw()
|
self.items:draw()
|
||||||
itemDB:flush()
|
itemDB:flush()
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:enable()
|
function page:enable()
|
||||||
self.form:setValues(context.state.depositAll)
|
self.form:setValues(context.state.depositAll)
|
||||||
self:updateInventoryList()
|
self:updateInventoryList()
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.items:getRowTextColor(row)
|
function page.items:getRowTextColor(row)
|
||||||
@@ -84,57 +84,57 @@ function page.items:getRowTextColor(row)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function page:depositAll()
|
function page:depositAll()
|
||||||
self.notification:info('Depositing all items...')
|
self.notification:info('Depositing all items...')
|
||||||
|
|
||||||
local inv = ni.getInventory().list()
|
local inv = ni.getInventory().list()
|
||||||
|
|
||||||
for slot, item in pairs(inv) do
|
for slot, item in pairs(inv) do
|
||||||
item = itemDB:get(item, function() return ni.getInventory().getItemMeta(slot) end)
|
item = itemDB:get(item, function() return ni.getInventory().getItemMeta(slot) end)
|
||||||
local key = makeKey(item)
|
local key = makeKey(item)
|
||||||
if not context.state.depositAll.retain[key] then
|
if not context.state.depositAll.retain[key] then
|
||||||
if (context.state.depositAll.includeHotbar or slot > 9) and item.name ~= 'plethora:neuralconnector' then
|
if (context.state.depositAll.includeHotbar or slot > 9) and item.name ~= 'plethora:neuralconnector' then
|
||||||
context:sendRequest({
|
context:sendRequest({
|
||||||
request = 'deposit',
|
request = 'deposit',
|
||||||
source = 'inventory',
|
source = 'inventory',
|
||||||
slot = slot,
|
slot = slot,
|
||||||
count = item.count,
|
count = item.count,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'checkbox_change' and event.element.formKey == 'includeHotbar' then
|
if event.type == 'checkbox_change' and event.element.formKey == 'includeHotbar' then
|
||||||
context.state.depositAll.includeHotbar = event.checked
|
context.state.depositAll.includeHotbar = event.checked
|
||||||
page:updateInventoryList()
|
page:updateInventoryList()
|
||||||
|
|
||||||
elseif event.type == 'grid_select' then
|
elseif event.type == 'grid_select' then
|
||||||
local key = event.selected.key
|
local key = event.selected.key
|
||||||
if context.state.depositAll.retain[key] then
|
if context.state.depositAll.retain[key] then
|
||||||
context.state.depositAll.retain[key] = nil
|
context.state.depositAll.retain[key] = nil
|
||||||
else
|
else
|
||||||
context.state.depositAll.retain[key] = true
|
context.state.depositAll.retain[key] = true
|
||||||
end
|
end
|
||||||
context:setState('depositAll', context.state.depositAll)
|
context:setState('depositAll', context.state.depositAll)
|
||||||
self.items:draw()
|
self.items:draw()
|
||||||
|
|
||||||
elseif event.type == 'form_complete' then
|
elseif event.type == 'form_complete' then
|
||||||
Config.update('miloRemote', context.state)
|
Config.update('miloRemote', context.state)
|
||||||
page:depositAll()
|
page:depositAll()
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
|
|
||||||
elseif event.type == 'form_cancel' then
|
elseif event.type == 'form_cancel' then
|
||||||
UI:setPreviousPage()
|
UI:setPreviousPage()
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
menuItem = 'Deposit all',
|
menuItem = 'Deposit all',
|
||||||
callback = function()
|
callback = function()
|
||||||
UI:setPage(page)
|
UI:setPage(page)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ local page = UI.Page {
|
|||||||
title = 'Auto-feeder',
|
title = 'Auto-feeder',
|
||||||
previousPage = true,
|
previousPage = true,
|
||||||
},
|
},
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = 2, ey = -2,
|
y = 2, ey = -2,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Name', key = 'displayName' },
|
{ heading = 'Name', key = 'displayName' },
|
||||||
},
|
},
|
||||||
sortColumn = 'displayName',
|
sortColumn = 'displayName',
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar {
|
statusBar = UI.StatusBar {
|
||||||
values = 'Double-click to toggle'
|
values = 'Double-click to toggle'
|
||||||
},
|
},
|
||||||
@@ -55,45 +55,45 @@ function page.grid:getRowTextColor(row)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function getFood(food)
|
local function getFood(food)
|
||||||
for slot,v in pairs(ni.getInventory().list()) do
|
for slot,v in pairs(ni.getInventory().list()) do
|
||||||
local key = itemDB:makeKey(v)
|
local key = itemDB:makeKey(v)
|
||||||
if key == food then
|
if key == food then
|
||||||
local item = ni.getInventory().getItem(slot)
|
local item = ni.getInventory().getItem(slot)
|
||||||
if item and item.consume then
|
if item and item.consume then
|
||||||
return item
|
return item
|
||||||
end
|
end
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'grid_select' then
|
if event.type == 'grid_select' then
|
||||||
if context.state.food == event.selected.key then
|
if context.state.food == event.selected.key then
|
||||||
context:setState('food')
|
context:setState('food')
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
elseif getFood(event.selected.key) then
|
elseif getFood(event.selected.key) then
|
||||||
context:setState('food', event.selected.key)
|
context:setState('food', event.selected.key)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
else
|
else
|
||||||
Sound.play('entity.villager.no')
|
Sound.play('entity.villager.no')
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Event.onInterval(5, function()
|
Event.onInterval(5, function()
|
||||||
local s, m = pcall(function() -- prevent errors from some mod items
|
local s, m = pcall(function() -- prevent errors from some mod items
|
||||||
if context.state.food and ni.getMetaOwner().food.hungry then
|
if context.state.food and ni.getMetaOwner().food.hungry then
|
||||||
local item = getFood(context.state.food)
|
local item = getFood(context.state.food)
|
||||||
if item then
|
if item then
|
||||||
item.consume()
|
item.consume()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
if not s and m then
|
if not s and m then
|
||||||
_G._syslog(m)
|
_G._syslog(m)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -10,78 +10,78 @@ local STARTUP_FILE = 'usr/autorun/miloRemote.lua'
|
|||||||
local context = ({ ... })[1]
|
local context = ({ ... })[1]
|
||||||
|
|
||||||
local setup = UI.SlideOut {
|
local setup = UI.SlideOut {
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
titleBar = UI.TitleBar {
|
titleBar = UI.TitleBar {
|
||||||
title = 'Remote Setup',
|
title = 'Remote Setup',
|
||||||
},
|
},
|
||||||
form = UI.Form {
|
form = UI.Form {
|
||||||
x = 2, ex = -2, y = 2, ey = -1,
|
x = 2, ex = -2, y = 2, ey = -1,
|
||||||
[1] = UI.TextEntry {
|
[1] = UI.TextEntry {
|
||||||
formLabel = 'Server', formKey = 'server',
|
formLabel = 'Server', formKey = 'server',
|
||||||
help = 'ID for the server',
|
help = 'ID for the server',
|
||||||
shadowText = 'Milo server ID',
|
shadowText = 'Milo server ID',
|
||||||
limit = 6,
|
limit = 6,
|
||||||
validate = 'numeric',
|
validate = 'numeric',
|
||||||
required = true,
|
required = true,
|
||||||
},
|
},
|
||||||
[2] = UI.TextEntry {
|
[2] = UI.TextEntry {
|
||||||
formLabel = 'Return Slot', formKey = 'slot',
|
formLabel = 'Return Slot', formKey = 'slot',
|
||||||
help = 'Use a slot for sending to storage',
|
help = 'Use a slot for sending to storage',
|
||||||
shadowText = 'Inventory slot #',
|
shadowText = 'Inventory slot #',
|
||||||
limit = 5,
|
limit = 5,
|
||||||
validate = 'numeric',
|
validate = 'numeric',
|
||||||
required = false,
|
required = false,
|
||||||
},
|
},
|
||||||
[3] = UI.Checkbox {
|
[3] = UI.Checkbox {
|
||||||
formLabel = 'Shield Slot', formKey = 'useShield',
|
formLabel = 'Shield Slot', formKey = 'useShield',
|
||||||
help = 'Or, use the shield slot for sending'
|
help = 'Or, use the shield slot for sending'
|
||||||
},
|
},
|
||||||
[4] = UI.Checkbox {
|
[4] = UI.Checkbox {
|
||||||
formLabel = 'Run on startup', formKey = 'runOnStartup',
|
formLabel = 'Run on startup', formKey = 'runOnStartup',
|
||||||
help = 'Run this program on startup'
|
help = 'Run this program on startup'
|
||||||
},
|
},
|
||||||
info = UI.TextArea {
|
info = UI.TextArea {
|
||||||
x = 1, ex = -1, y = 6, ey = -4,
|
x = 1, ex = -1, y = 6, ey = -4,
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
marginLeft = 0,
|
marginLeft = 0,
|
||||||
marginRight = 0,
|
marginRight = 0,
|
||||||
value = [[The Milo turtle must connect to a manipulator with a ]] ..
|
value = [[The Milo turtle must connect to a manipulator with a ]] ..
|
||||||
[[bound introspection module. The neural interface must ]] ..
|
[[bound introspection module. The neural interface must ]] ..
|
||||||
[[also have an introspection module.]],
|
[[also have an introspection module.]],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar {
|
statusBar = UI.StatusBar {
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup:eventHandler(event)
|
function setup:eventHandler(event)
|
||||||
if event.type == 'focus_change' then
|
if event.type == 'focus_change' then
|
||||||
self.statusBar:setStatus(event.focused.help)
|
self.statusBar:setStatus(event.focused.help)
|
||||||
|
|
||||||
elseif event.type == 'form_complete' then
|
elseif event.type == 'form_complete' then
|
||||||
Config.update('miloRemote', context.state)
|
Config.update('miloRemote', context.state)
|
||||||
self:hide()
|
self:hide()
|
||||||
context.page:refresh('list')
|
context.page:refresh('list')
|
||||||
context.page.grid:draw()
|
context.page.grid:draw()
|
||||||
context.page:setFocus(context.page.statusBar.filter)
|
context.page:setFocus(context.page.statusBar.filter)
|
||||||
|
|
||||||
if context.state.runOnStartup then
|
if context.state.runOnStartup then
|
||||||
if not fs.exists(STARTUP_FILE) then
|
if not fs.exists(STARTUP_FILE) then
|
||||||
Util.writeFile(STARTUP_FILE,
|
Util.writeFile(STARTUP_FILE,
|
||||||
[[os.sleep(1)
|
[[os.sleep(1)
|
||||||
shell.openForegroundTab('packages/milo/MiloRemote')]])
|
shell.openForegroundTab('packages/milo/MiloRemote')]])
|
||||||
end
|
end
|
||||||
elseif fs.exists(STARTUP_FILE) then
|
elseif fs.exists(STARTUP_FILE) then
|
||||||
fs.delete(STARTUP_FILE)
|
fs.delete(STARTUP_FILE)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'form_cancel' then
|
elseif event.type == 'form_cancel' then
|
||||||
self:hide()
|
self:hide()
|
||||||
context.page:setFocus(context.page.statusBar.filter)
|
context.page:setFocus(context.page.statusBar.filter)
|
||||||
end
|
end
|
||||||
|
|
||||||
return UI.SlideOut.eventHandler(self, event)
|
return UI.SlideOut.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
context.page:add({ setup = setup })
|
context.page:add({ setup = setup })
|
||||||
|
|||||||
@@ -5,42 +5,42 @@ local context = Milo:getContext()
|
|||||||
local device = _G.device
|
local device = _G.device
|
||||||
|
|
||||||
local function craftHandler(user, message, socket)
|
local function craftHandler(user, message, socket)
|
||||||
local function craft()
|
local function craft()
|
||||||
local slots = {
|
local slots = {
|
||||||
[1] = 1, [2] = 2, [3] = 3,
|
[1] = 1, [2] = 2, [3] = 3,
|
||||||
[5] = 10, [6] = 11, [7] = 12,
|
[5] = 10, [6] = 11, [7] = 12,
|
||||||
[9] = 19, [10] = 20, [11] = 21,
|
[9] = 19, [10] = 20, [11] = 21,
|
||||||
}
|
}
|
||||||
local inventory = device[user .. ':inventory']
|
local inventory = device[user .. ':inventory']
|
||||||
if inventory then
|
if inventory then
|
||||||
for k, v in pairs(slots) do
|
for k, v in pairs(slots) do
|
||||||
inventory.pushItems(context.turtleInventory.name, v + message.slot - 1, 1, k)
|
inventory.pushItems(context.turtleInventory.name, v + message.slot - 1, 1, k)
|
||||||
end
|
end
|
||||||
local recipe, msg = Milo:learnRecipe()
|
local recipe, msg = Milo:learnRecipe()
|
||||||
if recipe then
|
if recipe then
|
||||||
socket:write({
|
socket:write({
|
||||||
type = 'craft',
|
type = 'craft',
|
||||||
msg = 'Learned: ' .. itemDB:getName(recipe),
|
msg = 'Learned: ' .. itemDB:getName(recipe),
|
||||||
success = true,
|
success = true,
|
||||||
})
|
})
|
||||||
for k,v in pairs(context.turtleInventory.adapter.list()) do
|
for k,v in pairs(context.turtleInventory.adapter.list()) do
|
||||||
inventory.pullItems(context.turtleInventory.name, k, v.count)
|
inventory.pullItems(context.turtleInventory.name, k, v.count)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
socket:write({
|
socket:write({
|
||||||
type = 'craft',
|
type = 'craft',
|
||||||
msg = msg,
|
msg = msg,
|
||||||
})
|
})
|
||||||
for k, v in pairs(slots) do
|
for k, v in pairs(slots) do
|
||||||
inventory.pullItems(context.turtleInventory.name, k, 1, v + message.slot - 1)
|
inventory.pullItems(context.turtleInventory.name, k, 1, v + message.slot - 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:queueRequest({ }, craft)
|
Milo:queueRequest({ }, craft)
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
remoteHandler = { callback = craftHandler, messages = { craft = true } }
|
remoteHandler = { callback = craftHandler, messages = { craft = true } }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,39 +2,39 @@ local itemDB = require('core.itemDB')
|
|||||||
local Milo = require('milo')
|
local Milo = require('milo')
|
||||||
|
|
||||||
local ReplenishTask = {
|
local ReplenishTask = {
|
||||||
name = 'replenish',
|
name = 'replenish',
|
||||||
priority = 60,
|
priority = 60,
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReplenishTask:cycle(context)
|
function ReplenishTask:cycle(context)
|
||||||
for k,res in pairs(context.resources) do
|
for k,res in pairs(context.resources) do
|
||||||
if res.low then
|
if res.low then
|
||||||
local item = itemDB:splitKey(k)
|
local item = itemDB:splitKey(k)
|
||||||
item.key = k
|
item.key = k
|
||||||
|
|
||||||
local _, count = Milo:getMatches(item, res)
|
local _, count = Milo:getMatches(item, res)
|
||||||
|
|
||||||
if count < res.low then
|
if count < res.low then
|
||||||
local nbtHash
|
local nbtHash
|
||||||
if not res.ignoreNbtHash then
|
if not res.ignoreNbtHash then
|
||||||
nbtHash = item.nbtHash
|
nbtHash = item.nbtHash
|
||||||
end
|
end
|
||||||
Milo:requestCrafting({
|
Milo:requestCrafting({
|
||||||
name = item.name,
|
name = item.name,
|
||||||
damage = res.ignoreDamage and 0 or item.damage,
|
damage = res.ignoreDamage and 0 or item.damage,
|
||||||
nbtHash = nbtHash,
|
nbtHash = nbtHash,
|
||||||
requested = res.low - count,
|
requested = res.low - count,
|
||||||
count = count,
|
count = count,
|
||||||
replenish = true,
|
replenish = true,
|
||||||
})
|
})
|
||||||
else
|
else
|
||||||
local request = context.craftingQueue[itemDB:makeKey(item)]
|
local request = context.craftingQueue[itemDB:makeKey(item)]
|
||||||
if request and request.replenish then
|
if request and request.replenish then
|
||||||
--request.count = request.crafted
|
--request.count = request.crafted
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:registerTask(ReplenishTask)
|
Milo:registerTask(ReplenishTask)
|
||||||
|
|||||||
@@ -8,66 +8,66 @@ local context = Milo:getContext()
|
|||||||
|
|
||||||
local speakerNode = context.storage:getSingleNode('speaker')
|
local speakerNode = context.storage:getSingleNode('speaker')
|
||||||
if speakerNode then
|
if speakerNode then
|
||||||
Sound.setVolume(speakerNode.volume)
|
Sound.setVolume(speakerNode.volume)
|
||||||
end
|
end
|
||||||
|
|
||||||
local wizardPage = UI.WizardPage {
|
local wizardPage = UI.WizardPage {
|
||||||
title = 'Speaker',
|
title = 'Speaker',
|
||||||
index = 2,
|
index = 2,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
[1] = UI.Text {
|
[1] = UI.Text {
|
||||||
x = 2, y = 2,
|
x = 2, y = 2,
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
value = 'Set the volume for sound effects',
|
value = 'Set the volume for sound effects',
|
||||||
},
|
},
|
||||||
form = UI.Form {
|
form = UI.Form {
|
||||||
x = 2, ex = -2, y = 3, ey = -2,
|
x = 2, ex = -2, y = 3, ey = -2,
|
||||||
manualControls = true,
|
manualControls = true,
|
||||||
volume = UI.TextEntry {
|
volume = UI.TextEntry {
|
||||||
formLabel = 'Volume', formKey = 'volume',
|
formLabel = 'Volume', formKey = 'volume',
|
||||||
width = 5, limit = 3,
|
width = 5, limit = 3,
|
||||||
validate = 'numeric',
|
validate = 'numeric',
|
||||||
help = 'A value from 0 (mute) to 1 (loud)',
|
help = 'A value from 0 (mute) to 1 (loud)',
|
||||||
},
|
},
|
||||||
testSound = UI.Button {
|
testSound = UI.Button {
|
||||||
x = 15, y = 2,
|
x = 15, y = 2,
|
||||||
text = 'Test', event = 'test_sound',
|
text = 'Test', event = 'test_sound',
|
||||||
help = 'Test sound volume',
|
help = 'Test sound volume',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function wizardPage:setNode(node)
|
function wizardPage:setNode(node)
|
||||||
self.form:setValues(node)
|
self.form:setValues(node)
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:saveNode(node)
|
function wizardPage:saveNode(node)
|
||||||
Sound.setVolume(node.volume)
|
Sound.setVolume(node.volume)
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:validate()
|
function wizardPage:validate()
|
||||||
return self.form:save()
|
return self.form:save()
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:isValidType(node)
|
function wizardPage:isValidType(node)
|
||||||
local m = device[node.name]
|
local m = device[node.name]
|
||||||
return m and m.type == 'speaker' and {
|
return m and m.type == 'speaker' and {
|
||||||
name = 'Speaker',
|
name = 'Speaker',
|
||||||
value = 'speaker',
|
value = 'speaker',
|
||||||
category = 'custom',
|
category = 'custom',
|
||||||
help = 'Sound effects',
|
help = 'Sound effects',
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:isValidFor(node)
|
function wizardPage:isValidFor(node)
|
||||||
return node.mtype == 'speaker'
|
return node.mtype == 'speaker'
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:eventHandler(event)
|
function wizardPage:eventHandler(event)
|
||||||
if event.type == 'test_sound' then
|
if event.type == 'test_sound' then
|
||||||
local vol = tonumber(self.form.volume.value)
|
local vol = tonumber(self.form.volume.value)
|
||||||
Sound.play('entity.item.pickup', vol)
|
Sound.play('entity.item.pickup', vol)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:getPage('nodeWizard').wizard:add({ speaker = wizardPage })
|
UI:getPage('nodeWizard').wizard:add({ speaker = wizardPage })
|
||||||
|
|||||||
@@ -16,278 +16,278 @@ local template =
|
|||||||
Right-clicking on the activity monitor will reset the totals.]]
|
Right-clicking on the activity monitor will reset the totals.]]
|
||||||
|
|
||||||
local wizardPage = UI.WizardPage {
|
local wizardPage = UI.WizardPage {
|
||||||
title = 'Status Monitor',
|
title = 'Status Monitor',
|
||||||
index = 2,
|
index = 2,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
[1] = UI.TextArea {
|
[1] = UI.TextArea {
|
||||||
x = 2, ex = -2, y = 2, ey = 6,
|
x = 2, ex = -2, y = 2, ey = 6,
|
||||||
marginRight = 0,
|
marginRight = 0,
|
||||||
value = string.format(template, Ansi.yellow, Ansi.reset),
|
value = string.format(template, Ansi.yellow, Ansi.reset),
|
||||||
},
|
},
|
||||||
form = UI.Form {
|
form = UI.Form {
|
||||||
x = 2, ex = -2, y = 7, ey = -2,
|
x = 2, ex = -2, y = 7, ey = -2,
|
||||||
manualControls = true,
|
manualControls = true,
|
||||||
[1] = UI.Chooser {
|
[1] = UI.Chooser {
|
||||||
width = 9,
|
width = 9,
|
||||||
formLabel = 'Font Size', formKey = 'textScale',
|
formLabel = 'Font Size', formKey = 'textScale',
|
||||||
nochoice = 'Small',
|
nochoice = 'Small',
|
||||||
choices = {
|
choices = {
|
||||||
{ name = 'Small', value = .5 },
|
{ name = 'Small', value = .5 },
|
||||||
{ name = 'Large', value = 1 },
|
{ name = 'Large', value = 1 },
|
||||||
},
|
},
|
||||||
help = 'Adjust text scaling',
|
help = 'Adjust text scaling',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function wizardPage:setNode(node)
|
function wizardPage:setNode(node)
|
||||||
self.form:setValues(node)
|
self.form:setValues(node)
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:validate()
|
function wizardPage:validate()
|
||||||
return self.form:save()
|
return self.form:save()
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:saveNode(node)
|
function wizardPage:saveNode(node)
|
||||||
os.queueEvent('monitor_resize', node.name)
|
os.queueEvent('monitor_resize', node.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:isValidType(node)
|
function wizardPage:isValidType(node)
|
||||||
local m = device[node.name]
|
local m = device[node.name]
|
||||||
return m and m.type == 'monitor' and {
|
return m and m.type == 'monitor' and {
|
||||||
name = 'Status Monitor',
|
name = 'Status Monitor',
|
||||||
value = 'status',
|
value = 'status',
|
||||||
category = 'display',
|
category = 'display',
|
||||||
help = 'Display storage status'
|
help = 'Display storage status'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:isValidFor(node)
|
function wizardPage:isValidFor(node)
|
||||||
return node.mtype == 'status'
|
return node.mtype == 'status'
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:getPage('nodeWizard').wizard:add({ statusMonitor = wizardPage })
|
UI:getPage('nodeWizard').wizard:add({ statusMonitor = wizardPage })
|
||||||
|
|
||||||
--[[ Display ]]--
|
--[[ Display ]]--
|
||||||
local function createPage(node)
|
local function createPage(node)
|
||||||
local monitor = UI.Device {
|
local monitor = UI.Device {
|
||||||
device = node.adapter,
|
device = node.adapter,
|
||||||
textScale = node.textScale or .5,
|
textScale = node.textScale or .5,
|
||||||
}
|
}
|
||||||
|
|
||||||
function monitor:resize()
|
function monitor:resize()
|
||||||
self.textScale = node.textScale or .5
|
self.textScale = node.textScale or .5
|
||||||
UI.Device.resize(self)
|
UI.Device.resize(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
parent = monitor,
|
parent = monitor,
|
||||||
tabs = UI.Tabs {
|
tabs = UI.Tabs {
|
||||||
[1] = UI.Tab {
|
[1] = UI.Tab {
|
||||||
tabTitle = 'Overview',
|
tabTitle = 'Overview',
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
onlineLabel = UI.Text {
|
onlineLabel = UI.Text {
|
||||||
x = 2, y = 2,
|
x = 2, y = 2,
|
||||||
value = 'Storage Status',
|
value = 'Storage Status',
|
||||||
},
|
},
|
||||||
onlineText = UI.Text {
|
onlineText = UI.Text {
|
||||||
x = 18, ex = -2, y = 2,
|
x = 18, ex = -2, y = 2,
|
||||||
},
|
},
|
||||||
tpsLabel = UI.Text {
|
tpsLabel = UI.Text {
|
||||||
x = 2, y = 3,
|
x = 2, y = 3,
|
||||||
value = 'Tasks/sec',
|
value = 'Tasks/sec',
|
||||||
},
|
},
|
||||||
tpsText = UI.Text {
|
tpsText = UI.Text {
|
||||||
x = 18, ex = -2, y = 3,
|
x = 18, ex = -2, y = 3,
|
||||||
},
|
},
|
||||||
tasksLabel = UI.Text {
|
tasksLabel = UI.Text {
|
||||||
x = -18, y = 3,
|
x = -18, y = 3,
|
||||||
value = 'Proc time',
|
value = 'Proc time',
|
||||||
},
|
},
|
||||||
tasksText = UI.Text {
|
tasksText = UI.Text {
|
||||||
x = -6, ex = -2, y = 3,
|
x = -6, ex = -2, y = 3,
|
||||||
align = 'right',
|
align = 'right',
|
||||||
},
|
},
|
||||||
storageLabel = UI.Text {
|
storageLabel = UI.Text {
|
||||||
x = 2, ex = -1, y = 6,
|
x = 2, ex = -1, y = 6,
|
||||||
},
|
},
|
||||||
storage = UI.ProgressBar {
|
storage = UI.ProgressBar {
|
||||||
x = 2, ex = -2, y = 7, height = 3,
|
x = 2, ex = -2, y = 7, height = 3,
|
||||||
},
|
},
|
||||||
unlockedLabel = UI.Text {
|
unlockedLabel = UI.Text {
|
||||||
x = 2, ex = -1, y = 12,
|
x = 2, ex = -1, y = 12,
|
||||||
},
|
},
|
||||||
unlocked = UI.ProgressBar {
|
unlocked = UI.ProgressBar {
|
||||||
x = 2, ex = -2, y = 13, height = 3,
|
x = 2, ex = -2, y = 13, height = 3,
|
||||||
},
|
},
|
||||||
craftingLabel = UI.Text {
|
craftingLabel = UI.Text {
|
||||||
x = 2, ex = -1, y = 18,
|
x = 2, ex = -1, y = 18,
|
||||||
value = 'Crafting Status',
|
value = 'Crafting Status',
|
||||||
},
|
},
|
||||||
crafting = UI.ProgressBar {
|
crafting = UI.ProgressBar {
|
||||||
x = 2, ex = -2, y = 19, height = 3,
|
x = 2, ex = -2, y = 19, height = 3,
|
||||||
value = 100,
|
value = 100,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[2] = UI.Tab {
|
[2] = UI.Tab {
|
||||||
tabTitle = 'Stats',
|
tabTitle = 'Stats',
|
||||||
textArea = UI.TextArea {
|
textArea = UI.TextArea {
|
||||||
y = 3,
|
y = 3,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[3] = UI.Tab {
|
[3] = UI.Tab {
|
||||||
tabTitle = 'Storage',
|
tabTitle = 'Storage',
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = 2,
|
y = 2,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Name', key = 'name' },
|
{ heading = 'Name', key = 'name' },
|
||||||
{ heading = 'Size', key = 'size', width = 5 },
|
{ heading = 'Size', key = 'size', width = 5 },
|
||||||
{ heading = 'Used', key = 'used', width = 5 },
|
{ heading = 'Used', key = 'used', width = 5 },
|
||||||
{ heading = 'Perc', key = 'perc', width = 5 },
|
{ heading = 'Perc', key = 'perc', width = 5 },
|
||||||
-- TODO: add % to each number
|
-- TODO: add % to each number
|
||||||
},
|
},
|
||||||
sortColumn = 'name',
|
sortColumn = 'name',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[4] = UI.Tab {
|
[4] = UI.Tab {
|
||||||
tabTitle = 'Offline',
|
tabTitle = 'Offline',
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = 2,
|
y = 2,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Name', key = 'name' },
|
{ heading = 'Name', key = 'name' },
|
||||||
},
|
},
|
||||||
sortColumn = 'name',
|
sortColumn = 'name',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[5] = UI.Tab {
|
[5] = UI.Tab {
|
||||||
tabTitle = 'Activity',
|
tabTitle = 'Activity',
|
||||||
term = UI.Embedded {
|
term = UI.Embedded {
|
||||||
--visible = true,
|
--visible = true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[6] = UI.Tab {
|
[6] = UI.Tab {
|
||||||
tabTitle = 'Tasks',
|
tabTitle = 'Tasks',
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = 2,
|
y = 2,
|
||||||
values = context.tasks,
|
values = context.tasks,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Priority', key = 'priority', width = 5 },
|
{ heading = 'Priority', key = 'priority', width = 5 },
|
||||||
{ heading = 'Name', key = 'name' },
|
{ heading = 'Name', key = 'name' },
|
||||||
{ heading = 'Avg', key = 'avg', width = 7, align = 'right' },
|
{ heading = 'Avg', key = 'avg', width = 7, align = 'right' },
|
||||||
{ heading = '%', key = 'perc', width = 7, align = 'right' },
|
{ heading = '%', key = 'perc', width = 7, align = 'right' },
|
||||||
},
|
},
|
||||||
sortColumn = 'priority',
|
sortColumn = 'priority',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local overviewTab = page.tabs[1]
|
local overviewTab = page.tabs[1]
|
||||||
local statsTab = page.tabs[2]
|
local statsTab = page.tabs[2]
|
||||||
local usageTab = page.tabs[3]
|
local usageTab = page.tabs[3]
|
||||||
local stateTab = page.tabs[4]
|
local stateTab = page.tabs[4]
|
||||||
local activityTab = page.tabs[5]
|
local activityTab = page.tabs[5]
|
||||||
local taskTab = page.tabs[6]
|
local taskTab = page.tabs[6]
|
||||||
|
|
||||||
local function getStorageStats()
|
local function getStorageStats()
|
||||||
local stats = { }
|
local stats = { }
|
||||||
local totals = {
|
local totals = {
|
||||||
usedSlots = 0,
|
usedSlots = 0,
|
||||||
totalSlots = 0,
|
totalSlots = 0,
|
||||||
totalChests = 0,
|
totalChests = 0,
|
||||||
unlockedSlots = 0,
|
unlockedSlots = 0,
|
||||||
usedUnlockedSlots = 0,
|
usedUnlockedSlots = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
for n in context.storage:filterActive('storage') do
|
for n in context.storage:filterActive('storage') do
|
||||||
if n.adapter.size and n.adapter.list then
|
if n.adapter.size and n.adapter.list then
|
||||||
pcall(function()
|
pcall(function()
|
||||||
local updated = n.adapter.__lastUpdate ~= n.adapter.lastUpdate
|
local updated = n.adapter.__lastUpdate ~= n.adapter.lastUpdate
|
||||||
if updated then
|
if updated then
|
||||||
n.adapter.__used = Util.size(n.adapter.list())
|
n.adapter.__used = Util.size(n.adapter.list())
|
||||||
n.adapter.__lastUpdate = n.adapter.lastUpdate
|
n.adapter.__lastUpdate = n.adapter.lastUpdate
|
||||||
end
|
end
|
||||||
if not n.adapter.__used then
|
if not n.adapter.__used then
|
||||||
n.adapter.__used = Util.size(n.adapter.list())
|
n.adapter.__used = Util.size(n.adapter.list())
|
||||||
end
|
end
|
||||||
table.insert(stats, {
|
table.insert(stats, {
|
||||||
name = n.displayName or n.name,
|
name = n.displayName or n.name,
|
||||||
size = n.adapter.__size,
|
size = n.adapter.__size,
|
||||||
used = n.adapter.__used,
|
used = n.adapter.__used,
|
||||||
perc = math.floor(n.adapter.__used / n.adapter.__size * 100),
|
perc = math.floor(n.adapter.__used / n.adapter.__size * 100),
|
||||||
updated = updated,
|
updated = updated,
|
||||||
})
|
})
|
||||||
totals.usedSlots = totals.usedSlots + n.adapter.__used
|
totals.usedSlots = totals.usedSlots + n.adapter.__used
|
||||||
totals.totalSlots = totals.totalSlots + n.adapter.__size
|
totals.totalSlots = totals.totalSlots + n.adapter.__size
|
||||||
totals.totalChests = totals.totalChests + 1
|
totals.totalChests = totals.totalChests + 1
|
||||||
if not n.lock then
|
if not n.lock then
|
||||||
totals.unlockedSlots = totals.unlockedSlots + n.adapter.__size
|
totals.unlockedSlots = totals.unlockedSlots + n.adapter.__size
|
||||||
totals.usedUnlockedSlots = totals.usedUnlockedSlots + n.adapter.__used
|
totals.usedUnlockedSlots = totals.usedUnlockedSlots + n.adapter.__used
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stats, totals
|
return stats, totals
|
||||||
end
|
end
|
||||||
|
|
||||||
function stateTab:refresh()
|
function stateTab:refresh()
|
||||||
self.grid.values = { }
|
self.grid.values = { }
|
||||||
for _, v in pairs(context.storage.nodes) do
|
for _, v in pairs(context.storage.nodes) do
|
||||||
if v.mtype ~= 'hidden' then
|
if v.mtype ~= 'hidden' then
|
||||||
if not v.adapter or not v.adapter.online then
|
if not v.adapter or not v.adapter.online then
|
||||||
table.insert(self.grid.values, {
|
table.insert(self.grid.values, {
|
||||||
name = v.displayName or v.name
|
name = v.displayName or v.name
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
end
|
end
|
||||||
|
|
||||||
function stateTab:enable()
|
function stateTab:enable()
|
||||||
self:refresh()
|
self:refresh()
|
||||||
self.handle = Event.onInterval(5, function()
|
self.handle = Event.onInterval(5, function()
|
||||||
self:refresh()
|
self:refresh()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
UI.Tab.enable(self)
|
UI.Tab.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function stateTab:disable()
|
function stateTab:disable()
|
||||||
Event.off(self.handle)
|
Event.off(self.handle)
|
||||||
UI.Tab.disable(self)
|
UI.Tab.disable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function usageTab:refresh()
|
function usageTab:refresh()
|
||||||
self.grid:setValues(getStorageStats())
|
self.grid:setValues(getStorageStats())
|
||||||
end
|
end
|
||||||
|
|
||||||
function usageTab:enable()
|
function usageTab:enable()
|
||||||
self:refresh()
|
self:refresh()
|
||||||
self.handle = Event.onInterval(5, function()
|
self.handle = Event.onInterval(5, function()
|
||||||
self:refresh()
|
self:refresh()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
UI.Tab.enable(self)
|
UI.Tab.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function usageTab:disable()
|
function usageTab:disable()
|
||||||
Event.off(self.handle)
|
Event.off(self.handle)
|
||||||
UI.Tab.disable(self)
|
UI.Tab.disable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function usageTab.grid:getRowTextColor(row, selected)
|
function usageTab.grid:getRowTextColor(row, selected)
|
||||||
return row.updated and colors.yellow or
|
return row.updated and colors.yellow or
|
||||||
UI.Grid:getRowTextColor(row, selected)
|
UI.Grid:getRowTextColor(row, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
function statsTab.textArea:draw()
|
function statsTab.textArea:draw()
|
||||||
local _, stats = getStorageStats()
|
local _, stats = getStorageStats()
|
||||||
local totalItems, nodeCount = 0, 0
|
local totalItems, nodeCount = 0, 0
|
||||||
local formatString = [[
|
local formatString = [[
|
||||||
Storage Usage : %d%%
|
Storage Usage : %d%%
|
||||||
Slots : %d of %d used
|
Slots : %d of %d used
|
||||||
Unique Items : %d
|
Unique Items : %d
|
||||||
@@ -297,191 +297,191 @@ Nodes : %d
|
|||||||
Unlocked Slots : %d of %d (%d%%)
|
Unlocked Slots : %d of %d (%d%%)
|
||||||
]]
|
]]
|
||||||
|
|
||||||
for _,v in pairs(context.storage.nodes) do
|
for _,v in pairs(context.storage.nodes) do
|
||||||
if v.adapter and v.adapter.online then
|
if v.adapter and v.adapter.online then
|
||||||
nodeCount = nodeCount + 1
|
nodeCount = nodeCount + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _,v in pairs(context.storage.cache) do
|
for _,v in pairs(context.storage.cache) do
|
||||||
totalItems = totalItems + v.count
|
totalItems = totalItems + v.count
|
||||||
end
|
end
|
||||||
|
|
||||||
self.value = string.format(formatString,
|
self.value = string.format(formatString,
|
||||||
math.floor(stats.usedSlots / stats.totalSlots * 100),
|
math.floor(stats.usedSlots / stats.totalSlots * 100),
|
||||||
stats.usedSlots,
|
stats.usedSlots,
|
||||||
stats.totalSlots,
|
stats.totalSlots,
|
||||||
Util.size(context.storage.cache),
|
Util.size(context.storage.cache),
|
||||||
totalItems,
|
totalItems,
|
||||||
nodeCount,
|
nodeCount,
|
||||||
stats.usedUnlockedSlots,
|
stats.usedUnlockedSlots,
|
||||||
stats.unlockedSlots,
|
stats.unlockedSlots,
|
||||||
math.floor(stats.usedUnlockedSlots / stats.unlockedSlots * 100))
|
math.floor(stats.usedUnlockedSlots / stats.unlockedSlots * 100))
|
||||||
UI.TextArea.draw(self)
|
UI.TextArea.draw(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function statsTab:enable()
|
function statsTab:enable()
|
||||||
self.handle = Event.onInterval(5, function()
|
self.handle = Event.onInterval(5, function()
|
||||||
self.textArea:draw()
|
self.textArea:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
UI.Tab.enable(self)
|
UI.Tab.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function statsTab:disable()
|
function statsTab:disable()
|
||||||
Event.off(self.handle)
|
Event.off(self.handle)
|
||||||
UI.Tab.disable(self)
|
UI.Tab.disable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function taskTab.grid:getDisplayValues(row)
|
function taskTab.grid:getDisplayValues(row)
|
||||||
return {
|
return {
|
||||||
name = row.name,
|
name = row.name,
|
||||||
priority = row.priority,
|
priority = row.priority,
|
||||||
avg = Util.round(row.execTime / context.taskCounter * 1000) .. ' ms',
|
avg = Util.round(row.execTime / context.taskCounter * 1000) .. ' ms',
|
||||||
perc = Util.round(row.execTime / context.taskTimer * 100) .. '%',
|
perc = Util.round(row.execTime / context.taskTimer * 100) .. '%',
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function taskTab:refresh()
|
function taskTab:refresh()
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
end
|
end
|
||||||
|
|
||||||
function taskTab:enable()
|
function taskTab:enable()
|
||||||
self:refresh()
|
self:refresh()
|
||||||
self.handle = Event.onInterval(5, function()
|
self.handle = Event.onInterval(5, function()
|
||||||
self:refresh()
|
self:refresh()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
UI.Tab.enable(self)
|
UI.Tab.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function taskTab:disable()
|
function taskTab:disable()
|
||||||
Event.off(self.handle)
|
Event.off(self.handle)
|
||||||
UI.Tab.disable(self)
|
UI.Tab.disable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function overviewTab:draw()
|
function overviewTab:draw()
|
||||||
local _, stats = getStorageStats()
|
local _, stats = getStorageStats()
|
||||||
|
|
||||||
self.onlineText.textColor = context.storage:isOnline() and colors.green or colors.red
|
self.onlineText.textColor = context.storage:isOnline() and colors.green or colors.red
|
||||||
self.onlineText.value = context.storage:isOnline() and 'Online' or 'Offline'
|
self.onlineText.value = context.storage:isOnline() and 'Online' or 'Offline'
|
||||||
|
|
||||||
self.tpsText.value = tostring(Util.round(self.tasks / (os.clock() - self.timer), 2))
|
self.tpsText.value = tostring(Util.round(self.tasks / (os.clock() - self.timer), 2))
|
||||||
self.tasksText.value = tostring(Util.round(context.taskTimer / context.taskCounter, 2))
|
self.tasksText.value = tostring(Util.round(context.taskTimer / context.taskCounter, 2))
|
||||||
|
|
||||||
local total, crafted = 0, 0
|
local total, crafted = 0, 0
|
||||||
for _,v in pairs(context.craftingQueue) do
|
for _,v in pairs(context.craftingQueue) do
|
||||||
total = total + v.requested
|
total = total + v.requested
|
||||||
crafted = crafted + v.crafted
|
crafted = crafted + v.crafted
|
||||||
end
|
end
|
||||||
if Milo:isCraftingPaused() then
|
if Milo:isCraftingPaused() then
|
||||||
self.crafting.progressColor = colors.yellow
|
self.crafting.progressColor = colors.yellow
|
||||||
self.crafting.value = 100
|
self.crafting.value = 100
|
||||||
else
|
else
|
||||||
self.crafting.progressColor = colors.orange
|
self.crafting.progressColor = colors.orange
|
||||||
self.crafting.value = total > 0 and math.ceil(crafted / total * 100) or 0
|
self.crafting.value = total > 0 and math.ceil(crafted / total * 100) or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local percent = math.floor(stats.usedSlots / stats.totalSlots * 100)
|
local percent = math.floor(stats.usedSlots / stats.totalSlots * 100)
|
||||||
local color = colors.green
|
local color = colors.green
|
||||||
if percent > 90 then
|
if percent > 90 then
|
||||||
color = colors.red
|
color = colors.red
|
||||||
elseif percent > 75 then
|
elseif percent > 75 then
|
||||||
color = colors.yellow
|
color = colors.yellow
|
||||||
end
|
end
|
||||||
self.storage.progressColor = color
|
self.storage.progressColor = color
|
||||||
self.storage.value = percent
|
self.storage.value = percent
|
||||||
|
|
||||||
self.storageLabel.value = string.format('Total Usage: %s%% (%s of %s slots)',
|
self.storageLabel.value = string.format('Total Usage: %s%% (%s of %s slots)',
|
||||||
percent, stats.usedSlots, stats.totalSlots)
|
percent, stats.usedSlots, stats.totalSlots)
|
||||||
|
|
||||||
percent = math.floor(stats.usedUnlockedSlots / stats.unlockedSlots * 100)
|
percent = math.floor(stats.usedUnlockedSlots / stats.unlockedSlots * 100)
|
||||||
color = colors.green
|
color = colors.green
|
||||||
if percent > 90 then
|
if percent > 90 then
|
||||||
color = colors.red
|
color = colors.red
|
||||||
elseif percent > 75 then
|
elseif percent > 75 then
|
||||||
color = colors.yellow
|
color = colors.yellow
|
||||||
end
|
end
|
||||||
self.unlocked.progressColor = color
|
self.unlocked.progressColor = color
|
||||||
self.unlocked.value = percent
|
self.unlocked.value = percent
|
||||||
|
|
||||||
self.unlockedLabel.value = string.format('Unlocked Usage: %s%% (%s of %s slots)',
|
self.unlockedLabel.value = string.format('Unlocked Usage: %s%% (%s of %s slots)',
|
||||||
percent, stats.usedUnlockedSlots, stats.unlockedSlots)
|
percent, stats.usedUnlockedSlots, stats.unlockedSlots)
|
||||||
|
|
||||||
UI.Tab.draw(self)
|
UI.Tab.draw(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function overviewTab:enable()
|
function overviewTab:enable()
|
||||||
self.timer = os.clock()
|
self.timer = os.clock()
|
||||||
self.tasks = 0
|
self.tasks = 0
|
||||||
self.handle = Event.onInterval(5, function()
|
self.handle = Event.onInterval(5, function()
|
||||||
self:draw()
|
self:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
self.handle2 = Event.on({ 'milo_resume', 'milo_pause', 'storage_offline', 'storage_online' }, function()
|
self.handle2 = Event.on({ 'milo_resume', 'milo_pause', 'storage_offline', 'storage_online' }, function()
|
||||||
self:draw()
|
self:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
end)
|
end)
|
||||||
self.handle3 = Event.on('plethora_task', function()
|
self.handle3 = Event.on({ 'plethora_task', 'task_complete' }, function()
|
||||||
self.tasks = self.tasks + 1
|
self.tasks = self.tasks + 1
|
||||||
end)
|
end)
|
||||||
UI.Tab.enable(self)
|
UI.Tab.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function overviewTab:disable()
|
function overviewTab:disable()
|
||||||
Event.off(self.handle)
|
Event.off(self.handle)
|
||||||
Event.off(self.handle2)
|
Event.off(self.handle2)
|
||||||
Event.off(self.handle3)
|
Event.off(self.handle3)
|
||||||
UI.Tab.disable(self)
|
UI.Tab.disable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'tab_activate' then
|
if event.type == 'tab_activate' then
|
||||||
local state = Milo:getState('statusState') or { }
|
local state = Milo:getState('statusState') or { }
|
||||||
state[node.name] = event.activated.tabTitle
|
state[node.name] = event.activated.tabTitle
|
||||||
Milo:setState('statusState', state)
|
Milo:setState('statusState', state)
|
||||||
end
|
end
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(context.loggers, function(...)
|
table.insert(context.loggers, function(...)
|
||||||
local oterm = term.redirect(activityTab.term.win)
|
local oterm = term.redirect(activityTab.term.win)
|
||||||
activityTab.term.win.scrollBottom()
|
activityTab.term.win.scrollBottom()
|
||||||
Util.print(...)
|
Util.print(...)
|
||||||
term.redirect(oterm)
|
term.redirect(oterm)
|
||||||
if activityTab.enabled then
|
if activityTab.enabled then
|
||||||
activityTab:sync()
|
activityTab:sync()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Event.onTimeout(0, function()
|
Event.onTimeout(0, function()
|
||||||
UI:setPage(page)
|
UI:setPage(page)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- restore active tab
|
-- restore active tab
|
||||||
local tabState = Milo:getState('statusState') or { }
|
local tabState = Milo:getState('statusState') or { }
|
||||||
if tabState[node.name] then
|
if tabState[node.name] then
|
||||||
page.tabs:selectTab(Util.find(page.tabs, 'tabTitle', tabState[node.name]))
|
page.tabs:selectTab(Util.find(page.tabs, 'tabTitle', tabState[node.name]))
|
||||||
end
|
end
|
||||||
|
|
||||||
return page
|
return page
|
||||||
end
|
end
|
||||||
|
|
||||||
local pages = { }
|
local pages = { }
|
||||||
|
|
||||||
--[[ Task ]]--
|
--[[ Task ]]--
|
||||||
local task = {
|
local task = {
|
||||||
name = 'status',
|
name = 'status',
|
||||||
priority = 99,
|
priority = 99,
|
||||||
}
|
}
|
||||||
|
|
||||||
function task:cycle()
|
function task:cycle()
|
||||||
for node in context.storage:filterActive('status') do
|
for node in context.storage:filterActive('status') do
|
||||||
if not pages[node.name] then
|
if not pages[node.name] then
|
||||||
pages[node.name] = createPage(node)
|
pages[node.name] = createPage(node)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:registerTask(task)
|
Milo:registerTask(task)
|
||||||
|
|||||||
@@ -7,89 +7,89 @@ local device = _G.device
|
|||||||
|
|
||||||
--[[ Configuration Screen ]]
|
--[[ Configuration Screen ]]
|
||||||
local wizardPage = UI.WizardPage {
|
local wizardPage = UI.WizardPage {
|
||||||
title = 'Trashcan',
|
title = 'Trashcan',
|
||||||
index = 2,
|
index = 2,
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
info = UI.TextArea {
|
info = UI.TextArea {
|
||||||
x = 1, ex = -1, y = 2, ey = 4,
|
x = 1, ex = -1, y = 2, ey = 4,
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
marginLeft = 1,
|
marginLeft = 1,
|
||||||
marginRight = 1,
|
marginRight = 1,
|
||||||
value = [[ Items can be automatically dropped from this storage.]],
|
value = [[ Items can be automatically dropped from this storage.]],
|
||||||
},
|
},
|
||||||
form = UI.Form {
|
form = UI.Form {
|
||||||
x = 2, ex = -2, y = 4, ey = -2,
|
x = 2, ex = -2, y = 4, ey = -2,
|
||||||
manualControls = true,
|
manualControls = true,
|
||||||
[1] = UI.Checkbox {
|
[1] = UI.Checkbox {
|
||||||
formLabel = 'Drop', formKey = 'drop',
|
formLabel = 'Drop', formKey = 'drop',
|
||||||
help = 'Drop the items out of this inventory',
|
help = 'Drop the items out of this inventory',
|
||||||
},
|
},
|
||||||
[2] = UI.Chooser {
|
[2] = UI.Chooser {
|
||||||
width = 9,
|
width = 9,
|
||||||
formLabel = 'Direction', formKey = 'dropDirection',
|
formLabel = 'Direction', formKey = 'dropDirection',
|
||||||
nochoice = 'Down',
|
nochoice = 'Down',
|
||||||
choices = {
|
choices = {
|
||||||
{ name = 'Down', value = 'down' },
|
{ name = 'Down', value = 'down' },
|
||||||
{ name = 'Up', value = 'up' },
|
{ name = 'Up', value = 'up' },
|
||||||
{ name = 'North', value = 'north' },
|
{ name = 'North', value = 'north' },
|
||||||
{ name = 'South', value = 'south' },
|
{ name = 'South', value = 'south' },
|
||||||
{ name = 'East', value = 'east' },
|
{ name = 'East', value = 'east' },
|
||||||
{ name = 'West', value = 'west' },
|
{ name = 'West', value = 'west' },
|
||||||
},
|
},
|
||||||
help = 'Drop in a specified direction'
|
help = 'Drop in a specified direction'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function wizardPage:validate()
|
function wizardPage:validate()
|
||||||
return self.form:save()
|
return self.form:save()
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:setNode(node)
|
function wizardPage:setNode(node)
|
||||||
self.form:setValues(node)
|
self.form:setValues(node)
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:isValidType(node)
|
function wizardPage:isValidType(node)
|
||||||
local m = device[node.name]
|
local m = device[node.name]
|
||||||
return m and m.pullItems and {
|
return m and m.pullItems and {
|
||||||
name = 'Trashcan',
|
name = 'Trashcan',
|
||||||
value = 'trashcan',
|
value = 'trashcan',
|
||||||
category = 'custom',
|
category = 'custom',
|
||||||
help = 'An inventory to send unwanted items',
|
help = 'An inventory to send unwanted items',
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
function wizardPage:isValidFor(node)
|
function wizardPage:isValidFor(node)
|
||||||
return node.mtype == 'trashcan'
|
return node.mtype == 'trashcan'
|
||||||
end
|
end
|
||||||
|
|
||||||
UI:getPage('nodeWizard').wizard:add({ trashcan = wizardPage })
|
UI:getPage('nodeWizard').wizard:add({ trashcan = wizardPage })
|
||||||
|
|
||||||
--[[ TASK ]]--
|
--[[ TASK ]]--
|
||||||
local task = {
|
local task = {
|
||||||
name = 'trashcan',
|
name = 'trashcan',
|
||||||
priority = 90,
|
priority = 90,
|
||||||
}
|
}
|
||||||
|
|
||||||
local function filter(a)
|
local function filter(a)
|
||||||
return a.drop
|
return a.drop
|
||||||
end
|
end
|
||||||
|
|
||||||
function task:cycle(context)
|
function task:cycle(context)
|
||||||
local tasks = Tasks()
|
local tasks = Tasks()
|
||||||
|
|
||||||
for node in context.storage:filterActive('trashcan', filter) do
|
for node in context.storage:filterActive('trashcan', filter) do
|
||||||
pcall(function()
|
pcall(function()
|
||||||
for k in pairs(node.adapter.list()) do
|
for k in pairs(node.adapter.list()) do
|
||||||
local direction = node.dropDirection or 'down'
|
local direction = node.dropDirection or 'down'
|
||||||
tasks:add(function()
|
tasks:add(function()
|
||||||
node.adapter.drop(k, 64, direction)
|
node.adapter.drop(k, 64, direction)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
tasks:run()
|
tasks:run()
|
||||||
end
|
end
|
||||||
|
|
||||||
Milo:registerTask(task)
|
Milo:registerTask(task)
|
||||||
|
|||||||
@@ -20,160 +20,160 @@ local paused, abort
|
|||||||
local chunkIndex = 0
|
local chunkIndex = 0
|
||||||
local swarm = Swarm()
|
local swarm = Swarm()
|
||||||
local blocks = Util.transpose({
|
local blocks = Util.transpose({
|
||||||
'minecraft:chest',
|
'minecraft:chest',
|
||||||
-- 'minecraft:mob_spawner',
|
-- 'minecraft:mob_spawner',
|
||||||
'quark:crystal',
|
'quark:crystal',
|
||||||
'minecraft:mossy_cobblestone'
|
'minecraft:mossy_cobblestone'
|
||||||
})
|
})
|
||||||
local locations = { }
|
local locations = { }
|
||||||
|
|
||||||
gpt.x = gpt.x + 1
|
gpt.x = gpt.x + 1
|
||||||
|
|
||||||
local function getLocations()
|
local function getLocations()
|
||||||
local y = gpt.y - 8
|
local y = gpt.y - 8
|
||||||
while y > 5 do
|
while y > 5 do
|
||||||
table.insert(locations, y)
|
table.insert(locations, y)
|
||||||
y = y - 16
|
y = y - 16
|
||||||
end
|
end
|
||||||
if y > 0 then
|
if y > 0 then
|
||||||
table.insert(locations, 5)
|
table.insert(locations, 5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, b in pairs(scanner.scan()) do
|
for _, b in pairs(scanner.scan()) do
|
||||||
if b.name == 'computercraft:turtle_advanced' or
|
if b.name == 'computercraft:turtle_advanced' or
|
||||||
b.name == 'computercraft:turtle' then
|
b.name == 'computercraft:turtle' then
|
||||||
|
|
||||||
local v = scanner.getBlockMeta(b.x, b.y, b.z)
|
local v = scanner.getBlockMeta(b.x, b.y, b.z)
|
||||||
if v and v.computer then
|
if v and v.computer then
|
||||||
if not v.computer.isOn then
|
if not v.computer.isOn then
|
||||||
print('Powered off: ' .. v.computer.id)
|
print('Powered off: ' .. v.computer.id)
|
||||||
elseif v.turtle.fuel < 100 then
|
elseif v.turtle.fuel < 100 then
|
||||||
print('not enough fuel: ' .. v.computer.id)
|
print('not enough fuel: ' .. v.computer.id)
|
||||||
else
|
else
|
||||||
swarm:add(v.computer.id, {
|
swarm:add(v.computer.id, {
|
||||||
point = {
|
point = {
|
||||||
x = gpt.x + b.x,
|
x = gpt.x + b.x,
|
||||||
y = gpt.y + b.y,
|
y = gpt.y + b.y,
|
||||||
z = gpt.z + b.z,
|
z = gpt.z + b.z,
|
||||||
heading = Point.facings[v.state.facing].heading,
|
heading = Point.facings[v.state.facing].heading,
|
||||||
},
|
},
|
||||||
index = Util.size(swarm.pool),
|
index = Util.size(swarm.pool),
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getNextPoint(member)
|
local function getNextPoint(member)
|
||||||
local z = math.floor(chunkIndex / COLUMNS)
|
local z = math.floor(chunkIndex / COLUMNS)
|
||||||
local x = chunkIndex % COLUMNS
|
local x = chunkIndex % COLUMNS
|
||||||
|
|
||||||
chunkIndex = chunkIndex + 1
|
chunkIndex = chunkIndex + 1
|
||||||
|
|
||||||
while paused do
|
while paused do
|
||||||
if abort then
|
if abort then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
os.sleep(3)
|
os.sleep(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x = gpt.x + (x * 16),
|
x = gpt.x + (x * 16),
|
||||||
y = gpt.y + member.index,
|
y = gpt.y + member.index,
|
||||||
z = gpt.z + (z * 16)
|
z = gpt.z + (z * 16)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function run(member)
|
local function run(member)
|
||||||
local turtle = member.turtle
|
local turtle = member.turtle
|
||||||
|
|
||||||
if not turtle.has('plethora:module:2') then
|
if not turtle.has('plethora:module:2') then
|
||||||
error('missing scanner')
|
error('missing scanner')
|
||||||
end
|
end
|
||||||
turtle.reset()
|
turtle.reset()
|
||||||
turtle.set({
|
turtle.set({
|
||||||
attackPolicy = 'attack',
|
attackPolicy = 'attack',
|
||||||
digPolicy = 'turtleSafe',
|
digPolicy = 'turtleSafe',
|
||||||
movementStrategy = 'goto',
|
movementStrategy = 'goto',
|
||||||
point = member.point,
|
point = member.point,
|
||||||
})
|
})
|
||||||
turtle.select(1)
|
turtle.select(1)
|
||||||
local swapSide = turtle.isEquipped('modem') == 'right' and 'left' or 'right'
|
local swapSide = turtle.isEquipped('modem') == 'right' and 'left' or 'right'
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
local pt = getNextPoint(member)
|
local pt = getNextPoint(member)
|
||||||
if pt then
|
if pt then
|
||||||
turtle.set({ status = 'Relocating' })
|
turtle.set({ status = 'Relocating' })
|
||||||
turtle.go({ y = pt.y })
|
turtle.go({ y = pt.y })
|
||||||
local c = os.clock()
|
local c = os.clock()
|
||||||
while not turtle.go(pt) do
|
while not turtle.go(pt) do
|
||||||
if abort then
|
if abort then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
os.sleep(.5)
|
os.sleep(.5)
|
||||||
if os.clock() - c > 3 then
|
if os.clock() - c > 3 then
|
||||||
Sound.play('entity.villager.no')
|
Sound.play('entity.villager.no')
|
||||||
print('stuck: ' .. member.id)
|
print('stuck: ' .. member.id)
|
||||||
turtle.set({ status = 'Stuck' })
|
turtle.set({ status = 'Stuck' })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
turtle.set({ status = 'Boring' })
|
turtle.set({ status = 'Boring' })
|
||||||
|
|
||||||
for _, v in ipairs(locations) do
|
for _, v in ipairs(locations) do
|
||||||
if abort then
|
if abort then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
turtle.go({ y = v })
|
turtle.go({ y = v })
|
||||||
turtle.equip(swapSide, 'plethora:module:2')
|
turtle.equip(swapSide, 'plethora:module:2')
|
||||||
local found = turtle.scan(blocks)
|
local found = turtle.scan(blocks)
|
||||||
turtle.equip(swapSide, 'minecraft:diamond_pickaxe')
|
turtle.equip(swapSide, 'minecraft:diamond_pickaxe')
|
||||||
if Util.size(found) > 0 then
|
if Util.size(found) > 0 then
|
||||||
paused = true
|
paused = true
|
||||||
local _, b = next(found)
|
local _, b = next(found)
|
||||||
print(string.format('%s:%s:%s %s', b.x, b.y, b.z, b.name))
|
print(string.format('%s:%s:%s %s', b.x, b.y, b.z, b.name))
|
||||||
print('press r to continue')
|
print('press r to continue')
|
||||||
for _ = 1, 3 do
|
for _ = 1, 3 do
|
||||||
Sound.play('block.note.pling')
|
Sound.play('block.note.pling')
|
||||||
os.sleep(.3)
|
os.sleep(.3)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
turtle.go({ y = pt.y })
|
turtle.go({ y = pt.y })
|
||||||
end
|
end
|
||||||
until abort
|
until abort
|
||||||
|
|
||||||
turtle.set({ status = 'Aborting' })
|
turtle.set({ status = 'Aborting' })
|
||||||
turtle.go({ y = gpt.y + member.index })
|
turtle.go({ y = gpt.y + member.index })
|
||||||
turtle.go({ x = gpt.x, y = gpt.y + member.index, z = gpt.z })
|
turtle.go({ x = gpt.x, y = gpt.y + member.index, z = gpt.z })
|
||||||
|
|
||||||
repeat until turtle.go({ y = gpt.y })
|
repeat until turtle.go({ y = gpt.y })
|
||||||
turtle.set({ status = 'idle' })
|
turtle.set({ status = 'idle' })
|
||||||
end
|
end
|
||||||
|
|
||||||
function swarm:onRemove(member, success, message)
|
function swarm:onRemove(member, success, message)
|
||||||
if not success then
|
if not success then
|
||||||
Sound.play('entity.villager.no')
|
Sound.play('entity.villager.no')
|
||||||
print('Removed from swarm: ' .. member.id)
|
print('Removed from swarm: ' .. member.id)
|
||||||
_G.printError(message)
|
_G.printError(message)
|
||||||
end
|
end
|
||||||
|
|
||||||
print('Turtles: ' .. Util.size(self.pool))
|
print('Turtles: ' .. Util.size(self.pool))
|
||||||
if Util.size(self.pool) == 0 then
|
if Util.size(self.pool) == 0 then
|
||||||
Event.exitPullEvents()
|
Event.exitPullEvents()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
print('press a to abort, r to resume')
|
print('press a to abort, r to resume')
|
||||||
Event.on('char', function(_, k)
|
Event.on('char', function(_, k)
|
||||||
if k == 'r' then
|
if k == 'r' then
|
||||||
print('Resuming')
|
print('Resuming')
|
||||||
paused = false
|
paused = false
|
||||||
elseif k == 'a' then
|
elseif k == 'a' then
|
||||||
gpt = GPS.getPoint()
|
gpt = GPS.getPoint()
|
||||||
print('Aborting')
|
print('Aborting')
|
||||||
abort = true
|
abort = true
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
getLocations()
|
getLocations()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
[ "58ec8d6e36e346d9f42eb43935652e3e58e2c829" ] = {
|
[ "58ec8d6e36e346d9f42eb43935652e3e58e2c829" ] = {
|
||||||
title = "Mwm",
|
title = "Mwm",
|
||||||
category = "Apps",
|
category = "Apps",
|
||||||
icon = "\030f\031f \0304 \
|
icon = "\030f\031f \0304 \
|
||||||
\030f\031dshell]\0304\0314 \
|
\030f\031dshell]\0304\0314 \
|
||||||
\0304\031f ",
|
\0304\031f ",
|
||||||
iconExt = "\030 \031f\0305\031f\155\030f\128\031d\152\140\030d\031f\151\030f\128\128\0304\0314\128\
|
iconExt = "\030 \031f\0305\031f\155\030f\128\031d\152\140\030d\031f\151\030f\128\128\0304\0314\128\
|
||||||
\030 \031f\030f\0315\152\129\030d\031f\141\030f\031d\153\030d\031f\149\030f\031d\131\148\0304\0314\128\
|
\030 \031f\030f\0315\152\129\030d\031f\141\030f\031d\153\030d\031f\149\030f\031d\131\148\0304\0314\128\
|
||||||
\030 \031f\0304\031f\131\131\131\131\131\131\131\030e\0314\131",
|
\030 \031f\0304\031f\131\131\131\131\131\131\131\030e\0314\131",
|
||||||
run = "mwm.lua usr/config/mwm",
|
run = "mwm.lua usr/config/mwm",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,76 +8,76 @@ local os = _G.os
|
|||||||
local remoteId
|
local remoteId
|
||||||
local args = { ... }
|
local args = { ... }
|
||||||
if #args == 1 then
|
if #args == 1 then
|
||||||
remoteId = tonumber(args[1])
|
remoteId = tonumber(args[1])
|
||||||
else
|
else
|
||||||
print('Enter host ID')
|
print('Enter host ID')
|
||||||
remoteId = tonumber(_G.read())
|
remoteId = tonumber(_G.read())
|
||||||
end
|
end
|
||||||
|
|
||||||
if not remoteId then
|
if not remoteId then
|
||||||
error('Syntax: mirrorClient <host ID>')
|
error('Syntax: mirrorClient <host ID>')
|
||||||
end
|
end
|
||||||
|
|
||||||
local function wrapTerm(socket)
|
local function wrapTerm(socket)
|
||||||
local methods = { 'blit', 'clear', 'clearLine', 'setCursorPos', 'write',
|
local methods = { 'blit', 'clear', 'clearLine', 'setCursorPos', 'write',
|
||||||
'setTextColor', 'setTextColour', 'setBackgroundColor',
|
'setTextColor', 'setTextColour', 'setBackgroundColor',
|
||||||
'setBackgroundColour', 'scroll', 'setCursorBlink', }
|
'setBackgroundColour', 'scroll', 'setCursorBlink', }
|
||||||
|
|
||||||
socket.term = multishell.term
|
socket.term = multishell.term
|
||||||
socket.oldTerm = Util.shallowCopy(socket.term)
|
socket.oldTerm = Util.shallowCopy(socket.term)
|
||||||
|
|
||||||
for _,k in pairs(methods) do
|
for _,k in pairs(methods) do
|
||||||
socket.term[k] = function(...)
|
socket.term[k] = function(...)
|
||||||
if not socket.queue then
|
if not socket.queue then
|
||||||
socket.queue = { }
|
socket.queue = { }
|
||||||
Event.onTimeout(0, function()
|
Event.onTimeout(0, function()
|
||||||
if socket.queue then
|
if socket.queue then
|
||||||
socket:write(socket.queue)
|
socket:write(socket.queue)
|
||||||
socket.queue = nil
|
socket.queue = nil
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
table.insert(socket.queue, {
|
table.insert(socket.queue, {
|
||||||
f = k,
|
f = k,
|
||||||
args = { ... },
|
args = { ... },
|
||||||
})
|
})
|
||||||
socket.oldTerm[k](...)
|
socket.oldTerm[k](...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
print('connecting...')
|
print('connecting...')
|
||||||
local socket
|
local socket
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
socket = Socket.connect(remoteId, 5901)
|
socket = Socket.connect(remoteId, 5901)
|
||||||
if socket then
|
if socket then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
os.sleep(3)
|
os.sleep(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
print('connected')
|
print('connected')
|
||||||
|
|
||||||
wrapTerm(socket)
|
wrapTerm(socket)
|
||||||
|
|
||||||
os.queueEvent('term_resize')
|
os.queueEvent('term_resize')
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local e = Event.pullEvent()
|
local e = Event.pullEvent()
|
||||||
if e[1] == 'terminate' then
|
if e[1] == 'terminate' then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if not socket.connected then
|
if not socket.connected then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for k,v in pairs(socket.oldTerm) do
|
for k,v in pairs(socket.oldTerm) do
|
||||||
socket.term[k] = v
|
socket.term[k] = v
|
||||||
end
|
end
|
||||||
|
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,41 +7,41 @@ local term = _G.term
|
|||||||
local mon = term.current()
|
local mon = term.current()
|
||||||
local args = { ... }
|
local args = { ... }
|
||||||
if args[1] then
|
if args[1] then
|
||||||
mon = _G.device[args[1]]
|
mon = _G.device[args[1]]
|
||||||
end
|
end
|
||||||
|
|
||||||
if not mon then
|
if not mon then
|
||||||
error('Invalid monitor')
|
error('Invalid monitor')
|
||||||
end
|
end
|
||||||
|
|
||||||
mon.setBackgroundColor(colors.black)
|
mon.setBackgroundColor(colors.black)
|
||||||
mon.clear()
|
mon.clear()
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local socket = Socket.server(5901)
|
local socket = Socket.server(5901)
|
||||||
|
|
||||||
print('mirror: connection from ' .. socket.dhost)
|
print('mirror: connection from ' .. socket.dhost)
|
||||||
|
|
||||||
Event.addRoutine(function()
|
Event.addRoutine(function()
|
||||||
while true do
|
while true do
|
||||||
local data = socket:read()
|
local data = socket:read()
|
||||||
if not data then
|
if not data then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
for _,v in ipairs(data) do
|
for _,v in ipairs(data) do
|
||||||
mon[v.f](unpack(v.args))
|
mon[v.f](unpack(v.args))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
Event.pullEvent()
|
Event.pullEvent()
|
||||||
if not socket.connected then
|
if not socket.connected then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
print('connection lost')
|
print('connection lost')
|
||||||
|
|
||||||
socket:close()
|
socket:close()
|
||||||
end
|
end
|
||||||
|
|||||||
676
monitor/mwm.lua
676
monitor/mwm.lua
@@ -17,8 +17,8 @@ local term = _G.term
|
|||||||
local window = _G.window
|
local window = _G.window
|
||||||
|
|
||||||
local function syntax()
|
local function syntax()
|
||||||
printError('Syntax:')
|
printError('Syntax:')
|
||||||
error('mwm sessionName [monitor]')
|
error('mwm sessionName [monitor]')
|
||||||
end
|
end
|
||||||
|
|
||||||
local args = { ... }
|
local args = { ... }
|
||||||
@@ -33,9 +33,9 @@ local parentMon
|
|||||||
local defaultEnv = Util.shallowCopy(_ENV)
|
local defaultEnv = Util.shallowCopy(_ENV)
|
||||||
defaultEnv.multishell = multishell
|
defaultEnv.multishell = multishell
|
||||||
if args[2] then
|
if args[2] then
|
||||||
parentMon = peripheral.wrap(args[2]) or syntax()
|
parentMon = peripheral.wrap(args[2]) or syntax()
|
||||||
else
|
else
|
||||||
parentMon = peripheral.find('monitor') or syntax()
|
parentMon = peripheral.find('monitor') or syntax()
|
||||||
end
|
end
|
||||||
|
|
||||||
parentMon.setTextScale(.5)
|
parentMon.setTextScale(.5)
|
||||||
@@ -54,8 +54,8 @@ monitor.setBackgroundColor(colors.gray)
|
|||||||
monitor.clear()
|
monitor.clear()
|
||||||
|
|
||||||
local function nextUID()
|
local function nextUID()
|
||||||
UID = UID + 1
|
UID = UID + 1
|
||||||
return UID
|
return UID
|
||||||
end
|
end
|
||||||
|
|
||||||
local function xprun(env, path, ...)
|
local function xprun(env, path, ...)
|
||||||
@@ -68,471 +68,471 @@ local function xprun(env, path, ...)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function write(win, x, y, text)
|
local function write(win, x, y, text)
|
||||||
win.setCursorPos(x, y)
|
win.setCursorPos(x, y)
|
||||||
win.write(text)
|
win.write(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function redraw()
|
local function redraw()
|
||||||
--monitor.clear()
|
--monitor.clear()
|
||||||
monitor.canvas:dirty()
|
monitor.canvas:dirty()
|
||||||
--monitor.setBackgroundColor(colors.gray)
|
--monitor.setBackgroundColor(colors.gray)
|
||||||
monitor.canvas:clear(colors.gray)
|
monitor.canvas:clear(colors.gray)
|
||||||
for k, process in ipairs(processes) do
|
for k, process in ipairs(processes) do
|
||||||
process.container.canvas:dirty()
|
process.container.canvas:dirty()
|
||||||
process:focus(k == #processes)
|
process:focus(k == #processes)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getProcessAt(x, y)
|
local function getProcessAt(x, y)
|
||||||
for k = #processes, 1, -1 do
|
for k = #processes, 1, -1 do
|
||||||
local process = processes[k]
|
local process = processes[k]
|
||||||
if x >= process.x and
|
if x >= process.x and
|
||||||
y >= process.y and
|
y >= process.y and
|
||||||
x <= process.x + process.width - 1 and
|
x <= process.x + process.width - 1 and
|
||||||
y <= process.y + process.height - 1 then
|
y <= process.y + process.height - 1 then
|
||||||
return k, process
|
return k, process
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[ A runnable process ]]--
|
--[[ A runnable process ]]--
|
||||||
local Process = { }
|
local Process = { }
|
||||||
|
|
||||||
function Process:new(args)
|
function Process:new(args)
|
||||||
args.env = args.env or Util.shallowCopy(defaultEnv)
|
args.env = args.env or Util.shallowCopy(defaultEnv)
|
||||||
args.width = args.width or termDim.width
|
args.width = args.width or termDim.width
|
||||||
args.height = args.height or termDim.height
|
args.height = args.height or termDim.height
|
||||||
|
|
||||||
-- TODO: randomize start position
|
-- TODO: randomize start position
|
||||||
local self = setmetatable({
|
local self = setmetatable({
|
||||||
uid = nextUID(),
|
uid = nextUID(),
|
||||||
x = args.x or 1,
|
x = args.x or 1,
|
||||||
y = args.y or 1,
|
y = args.y or 1,
|
||||||
width = args.width + 2,
|
width = args.width + 2,
|
||||||
height = args.height + 3,
|
height = args.height + 3,
|
||||||
path = args.path,
|
path = args.path,
|
||||||
args = args.args or { },
|
args = args.args or { },
|
||||||
title = args.title or 'shell',
|
title = args.title or 'shell',
|
||||||
}, { __index = Process })
|
}, { __index = Process })
|
||||||
|
|
||||||
self:adjustDimensions()
|
self:adjustDimensions()
|
||||||
if not args.x then
|
if not args.x then
|
||||||
self.x = math.random(1, monDim.width - self.width + 1)
|
self.x = math.random(1, monDim.width - self.width + 1)
|
||||||
self.y = math.random(1, monDim.height - self.height + 1)
|
self.y = math.random(1, monDim.height - self.height + 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
self.container = Terminal.window(monitor, self.x, self.y, self.width, self.height, true)
|
self.container = Terminal.window(monitor, self.x, self.y, self.width, self.height, true)
|
||||||
self.window = window.create(self.container, 2, 3, args.width, args.height, true)
|
self.window = window.create(self.container, 2, 3, args.width, args.height, true)
|
||||||
self.terminal = self.window
|
self.terminal = self.window
|
||||||
|
|
||||||
self.container.canvas.parent = monitor.canvas
|
self.container.canvas.parent = monitor.canvas
|
||||||
table.insert(monitor.canvas.layers, 1, self.container.canvas)
|
table.insert(monitor.canvas.layers, 1, self.container.canvas)
|
||||||
self.container.canvas:setVisible(true)
|
self.container.canvas:setVisible(true)
|
||||||
|
|
||||||
--self.container.getSize = self.window.getSize
|
--self.container.getSize = self.window.getSize
|
||||||
|
|
||||||
self.co = coroutine.create(function()
|
self.co = coroutine.create(function()
|
||||||
local result, err
|
local result, err
|
||||||
|
|
||||||
if args.fn then
|
if args.fn then
|
||||||
result, err = Util.runFunction(args.env, args.fn, table.unpack(self.args))
|
result, err = Util.runFunction(args.env, args.fn, table.unpack(self.args))
|
||||||
elseif args.path then
|
elseif args.path then
|
||||||
result, err = xprun(args.env, args.path, table.unpack(self.args))
|
result, err = xprun(args.env, args.path, table.unpack(self.args))
|
||||||
end
|
end
|
||||||
|
|
||||||
if not result and err and err ~= 'Terminated' then
|
if not result and err and err ~= 'Terminated' then
|
||||||
printError('\n' .. tostring(err))
|
printError('\n' .. tostring(err))
|
||||||
os.pullEventRaw('terminate')
|
os.pullEventRaw('terminate')
|
||||||
end
|
end
|
||||||
multishell.removeProcess(self)
|
multishell.removeProcess(self)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
self:focus(false)
|
self:focus(false)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function Process:focus(focused)
|
function Process:focus(focused)
|
||||||
if focused then
|
if focused then
|
||||||
self.container.setBackgroundColor(colors.yellow)
|
self.container.setBackgroundColor(colors.yellow)
|
||||||
else
|
else
|
||||||
self.container.setBackgroundColor(colors.gray)
|
self.container.setBackgroundColor(colors.gray)
|
||||||
end
|
end
|
||||||
self.container.setTextColor(colors.black)
|
self.container.setTextColor(colors.black)
|
||||||
write(self.container, 2, 2, string.rep(' ', self.width - 2))
|
write(self.container, 2, 2, string.rep(' ', self.width - 2))
|
||||||
write(self.container, 3, 2, self.title)
|
write(self.container, 3, 2, self.title)
|
||||||
write(self.container, self.width - 2, 2, '*')
|
write(self.container, self.width - 2, 2, '*')
|
||||||
|
|
||||||
if focused then
|
if focused then
|
||||||
self.window.restoreCursor()
|
self.window.restoreCursor()
|
||||||
elseif self.showSizers then
|
elseif self.showSizers then
|
||||||
self:drawSizers(false)
|
self:drawSizers(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Process:drawSizers(showSizers)
|
function Process:drawSizers(showSizers)
|
||||||
local sizeChars = {
|
local sizeChars = {
|
||||||
'\135', '\139', '\141', '\142'
|
'\135', '\139', '\141', '\142'
|
||||||
}
|
}
|
||||||
|
|
||||||
if Util.getVersion() < 1.8 then
|
if Util.getVersion() < 1.8 then
|
||||||
sizeChars = {
|
sizeChars = {
|
||||||
'#', '#', '#', '#'
|
'#', '#', '#', '#'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
self.showSizers = showSizers
|
self.showSizers = showSizers
|
||||||
|
|
||||||
self.container.setBackgroundColor(colors.black)
|
self.container.setBackgroundColor(colors.black)
|
||||||
self.container.setTextColor(colors.white)
|
self.container.setTextColor(colors.white)
|
||||||
|
|
||||||
if self.showSizers then
|
if self.showSizers then
|
||||||
write(self.container, 1, 1, sizeChars[1])
|
write(self.container, 1, 1, sizeChars[1])
|
||||||
write(self.container, self.width, 1, sizeChars[2])
|
write(self.container, self.width, 1, sizeChars[2])
|
||||||
write(self.container, 1, self.height, sizeChars[3])
|
write(self.container, 1, self.height, sizeChars[3])
|
||||||
write(self.container, self.width, self.height, sizeChars[4])
|
write(self.container, self.width, self.height, sizeChars[4])
|
||||||
|
|
||||||
self.container.setTextColor(colors.yellow)
|
self.container.setTextColor(colors.yellow)
|
||||||
write(self.container, 1, 3, '+')
|
write(self.container, 1, 3, '+')
|
||||||
write(self.container, 1, 5, '-')
|
write(self.container, 1, 5, '-')
|
||||||
write(self.container, 3, 1, '+')
|
write(self.container, 3, 1, '+')
|
||||||
write(self.container, 5, 1, '-')
|
write(self.container, 5, 1, '-')
|
||||||
|
|
||||||
local str = string.format('%d x %d', self.width - 2, self.height - 3)
|
local str = string.format('%d x %d', self.width - 2, self.height - 3)
|
||||||
write(self.container, (self.width - #str) / 2, 1, str)
|
write(self.container, (self.width - #str) / 2, 1, str)
|
||||||
|
|
||||||
else
|
else
|
||||||
write(self.container, 1, 1, string.rep(' ', self.width))
|
write(self.container, 1, 1, string.rep(' ', self.width))
|
||||||
write(self.container, self.width, 1, ' ')
|
write(self.container, self.width, 1, ' ')
|
||||||
write(self.container, 1, self.height, ' ')
|
write(self.container, 1, self.height, ' ')
|
||||||
write(self.container, self.width, self.height, ' ')
|
write(self.container, self.width, self.height, ' ')
|
||||||
write(self.container, 1, 3, ' ')
|
write(self.container, 1, 3, ' ')
|
||||||
write(self.container, 1, 5, ' ')
|
write(self.container, 1, 5, ' ')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Process:adjustDimensions()
|
function Process:adjustDimensions()
|
||||||
self.width = math.min(self.width, monDim.width)
|
self.width = math.min(self.width, monDim.width)
|
||||||
self.height = math.min(self.height, monDim.height)
|
self.height = math.min(self.height, monDim.height)
|
||||||
|
|
||||||
self.x = math.max(1, self.x)
|
self.x = math.max(1, self.x)
|
||||||
self.y = math.max(1, self.y)
|
self.y = math.max(1, self.y)
|
||||||
self.x = math.min(self.x, monDim.width - self.width + 1)
|
self.x = math.min(self.x, monDim.width - self.width + 1)
|
||||||
self.y = math.min(self.y, monDim.height - self.height + 1)
|
self.y = math.min(self.y, monDim.height - self.height + 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Process:reposition()
|
function Process:reposition()
|
||||||
self:adjustDimensions()
|
self:adjustDimensions()
|
||||||
self.container.reposition(self.x, self.y, self.width, self.height)
|
self.container.reposition(self.x, self.y, self.width, self.height)
|
||||||
self.container.setBackgroundColor(colors.black)
|
self.container.setBackgroundColor(colors.black)
|
||||||
self.container.clear()
|
self.container.clear()
|
||||||
self.window.reposition(2, 3, self.width - 2, self.height - 3)
|
self.window.reposition(2, 3, self.width - 2, self.height - 3)
|
||||||
if self.window ~= self.terminal then
|
if self.window ~= self.terminal then
|
||||||
self.terminal.reposition(1, 1, self.width - 2, self.height - 3)
|
self.terminal.reposition(1, 1, self.width - 2, self.height - 3)
|
||||||
end
|
end
|
||||||
redraw()
|
redraw()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Process:click(x, y)
|
function Process:click(x, y)
|
||||||
if y == 2 then -- title bar
|
if y == 2 then -- title bar
|
||||||
if x == self.width - 2 then
|
if x == self.width - 2 then
|
||||||
self:resume('terminate')
|
self:resume('terminate')
|
||||||
else
|
else
|
||||||
self:drawSizers(not self.showSizers)
|
self:drawSizers(not self.showSizers)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif x == 1 or y == 1 then -- sizers
|
elseif x == 1 or y == 1 then -- sizers
|
||||||
self:resizeClick(x, y)
|
self:resizeClick(x, y)
|
||||||
|
|
||||||
elseif x > 1 and x < self.width then
|
elseif x > 1 and x < self.width then
|
||||||
if self.showSizers then
|
if self.showSizers then
|
||||||
self:drawSizers(false)
|
self:drawSizers(false)
|
||||||
end
|
end
|
||||||
self:resume('mouse_click', 1, x - 1, y - 2)
|
self:resume('mouse_click', 1, x - 1, y - 2)
|
||||||
self:resume('mouse_up', 1, x - 1, y - 2)
|
self:resume('mouse_up', 1, x - 1, y - 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Process:resizeClick(x, y)
|
function Process:resizeClick(x, y)
|
||||||
if x == 1 and y == 3 then
|
if x == 1 and y == 3 then
|
||||||
self.height = self.height + 1
|
self.height = self.height + 1
|
||||||
elseif x == 1 and y == 5 then
|
elseif x == 1 and y == 5 then
|
||||||
self.height = self.height - 1
|
self.height = self.height - 1
|
||||||
elseif x == 3 and y == 1 then
|
elseif x == 3 and y == 1 then
|
||||||
self.width = self.width + 1
|
self.width = self.width + 1
|
||||||
elseif x == 5 and y == 1 then
|
elseif x == 5 and y == 1 then
|
||||||
self.width = self.width - 1
|
self.width = self.width - 1
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self:reposition()
|
self:reposition()
|
||||||
self:resume('term_resize')
|
self:resume('term_resize')
|
||||||
self:drawSizers(true)
|
self:drawSizers(true)
|
||||||
multishell.saveSession(sessionFile)
|
multishell.saveSession(sessionFile)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Process:resume(event, ...)
|
function Process:resume(event, ...)
|
||||||
if coroutine.status(self.co) == 'dead' then
|
if coroutine.status(self.co) == 'dead' then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if not self.filter or self.filter == event or event == "terminate" then
|
if not self.filter or self.filter == event or event == "terminate" then
|
||||||
--term.redirect(self.terminal)
|
--term.redirect(self.terminal)
|
||||||
local previousTerm = term.redirect(self.terminal)
|
local previousTerm = term.redirect(self.terminal)
|
||||||
|
|
||||||
local previous = running
|
local previous = running
|
||||||
running = self -- stupid shell set title
|
running = self -- stupid shell set title
|
||||||
local ok, result = coroutine.resume(self.co, event, ...)
|
local ok, result = coroutine.resume(self.co, event, ...)
|
||||||
running = previous
|
running = previous
|
||||||
|
|
||||||
self.terminal = term.current()
|
self.terminal = term.current()
|
||||||
term.redirect(previousTerm)
|
term.redirect(previousTerm)
|
||||||
|
|
||||||
if ok then
|
if ok then
|
||||||
self.filter = result
|
self.filter = result
|
||||||
else
|
else
|
||||||
printError(result)
|
printError(result)
|
||||||
end
|
end
|
||||||
return ok, result
|
return ok, result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[ Install a multishell manager for the monitor ]]--
|
--[[ Install a multishell manager for the monitor ]]--
|
||||||
function multishell.getFocus()
|
function multishell.getFocus()
|
||||||
return processes[#processes].uid
|
return processes[#processes].uid
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.setFocus(uid)
|
function multishell.setFocus(uid)
|
||||||
local process = Util.find(processes, 'uid', uid)
|
local process = Util.find(processes, 'uid', uid)
|
||||||
|
|
||||||
if process then
|
if process then
|
||||||
local lastFocused = processes[#processes]
|
local lastFocused = processes[#processes]
|
||||||
if lastFocused ~= process then
|
if lastFocused ~= process then
|
||||||
|
|
||||||
if lastFocused then
|
if lastFocused then
|
||||||
lastFocused:focus(false)
|
lastFocused:focus(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
Util.removeByValue(processes, process)
|
Util.removeByValue(processes, process)
|
||||||
table.insert(processes, process)
|
table.insert(processes, process)
|
||||||
|
|
||||||
process.container.canvas:raise()
|
process.container.canvas:raise()
|
||||||
process:focus(true)
|
process:focus(true)
|
||||||
process.container.canvas:dirty()
|
process.container.canvas:dirty()
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.getTitle(uid)
|
function multishell.getTitle(uid)
|
||||||
local process = Util.find(processes, 'uid', uid)
|
local process = Util.find(processes, 'uid', uid)
|
||||||
if process then
|
if process then
|
||||||
return process.title
|
return process.title
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.setTitle(uid, title)
|
function multishell.setTitle(uid, title)
|
||||||
local process = Util.find(processes, 'uid', uid)
|
local process = Util.find(processes, 'uid', uid)
|
||||||
if process then
|
if process then
|
||||||
process.title = title or ''
|
process.title = title or ''
|
||||||
process:focus(process == processes[#processes])
|
process:focus(process == processes[#processes])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.getCurrent()
|
function multishell.getCurrent()
|
||||||
if running then
|
if running then
|
||||||
return running.uid
|
return running.uid
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.getCount()
|
function multishell.getCount()
|
||||||
return #processes
|
return #processes
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.getTabs()
|
function multishell.getTabs()
|
||||||
return processes
|
return processes
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.launch(env, file, ...)
|
function multishell.launch(env, file, ...)
|
||||||
return multishell.openTab({
|
return multishell.openTab({
|
||||||
path = file,
|
path = file,
|
||||||
env = env,
|
env = env,
|
||||||
title = 'shell',
|
title = 'shell',
|
||||||
args = { ... },
|
args = { ... },
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.openTab(tabInfo)
|
function multishell.openTab(tabInfo)
|
||||||
local process = Process:new(tabInfo)
|
local process = Process:new(tabInfo)
|
||||||
|
|
||||||
table.insert(processes, 1, process)
|
table.insert(processes, 1, process)
|
||||||
|
|
||||||
--local previousTerm = term.current()
|
--local previousTerm = term.current()
|
||||||
process:resume()
|
process:resume()
|
||||||
--term.redirect(previousTerm)
|
--term.redirect(previousTerm)
|
||||||
|
|
||||||
multishell.saveSession(sessionFile)
|
multishell.saveSession(sessionFile)
|
||||||
|
|
||||||
return process.uid
|
return process.uid
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.removeProcess(process)
|
function multishell.removeProcess(process)
|
||||||
Util.removeByValue(processes, process)
|
Util.removeByValue(processes, process)
|
||||||
process.container.canvas:removeLayer()
|
process.container.canvas:removeLayer()
|
||||||
|
|
||||||
multishell.saveSession(sessionFile)
|
multishell.saveSession(sessionFile)
|
||||||
redraw()
|
redraw()
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.saveSession(filename)
|
function multishell.saveSession(filename)
|
||||||
local t = { }
|
local t = { }
|
||||||
for _,process in ipairs(processes) do
|
for _,process in ipairs(processes) do
|
||||||
if process.path and not process.isShell then
|
if process.path and not process.isShell then
|
||||||
table.insert(t, {
|
table.insert(t, {
|
||||||
x = process.x,
|
x = process.x,
|
||||||
y = process.y,
|
y = process.y,
|
||||||
width = process.width - 2,
|
width = process.width - 2,
|
||||||
height = process.height - 3,
|
height = process.height - 3,
|
||||||
path = process.path,
|
path = process.path,
|
||||||
args = process.args,
|
args = process.args,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Util.writeTable(filename, t)
|
Util.writeTable(filename, t)
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.loadSession(filename)
|
function multishell.loadSession(filename)
|
||||||
local config = Util.readTable(filename)
|
local config = Util.readTable(filename)
|
||||||
if config then
|
if config then
|
||||||
for k = #config, 1, -1 do
|
for k = #config, 1, -1 do
|
||||||
multishell.openTab(config[k])
|
multishell.openTab(config[k])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.stop()
|
function multishell.stop()
|
||||||
multishell._stop = true
|
multishell._stop = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function multishell.start()
|
function multishell.start()
|
||||||
while not multishell._stop do
|
while not multishell._stop do
|
||||||
|
|
||||||
local event = { os.pullEventRaw() }
|
local event = { os.pullEventRaw() }
|
||||||
|
|
||||||
if event[1] == 'terminate' then
|
if event[1] == 'terminate' then
|
||||||
local focused = processes[#processes]
|
local focused = processes[#processes]
|
||||||
if focused.isShell then
|
if focused.isShell then
|
||||||
focused:resume('terminate')
|
focused:resume('terminate')
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event[1] == 'monitor_touch' then
|
elseif event[1] == 'monitor_touch' then
|
||||||
local x, y = event[3], event[4]
|
local x, y = event[3], event[4]
|
||||||
|
|
||||||
local key, process = getProcessAt(x, y)
|
local key, process = getProcessAt(x, y)
|
||||||
if process then
|
if process then
|
||||||
if key ~= #processes then
|
if key ~= #processes then
|
||||||
multishell.setFocus(process.uid)
|
multishell.setFocus(process.uid)
|
||||||
multishell.saveSession(sessionFile)
|
multishell.saveSession(sessionFile)
|
||||||
end
|
end
|
||||||
process:click(x - process.x + 1, y - process.y + 1)
|
process:click(x - process.x + 1, y - process.y + 1)
|
||||||
|
|
||||||
else
|
else
|
||||||
process = processes[#processes]
|
process = processes[#processes]
|
||||||
if process and process.showSizers then
|
if process and process.showSizers then
|
||||||
process.x = math.floor(x - (process.width) / 2)
|
process.x = math.floor(x - (process.width) / 2)
|
||||||
process.y = y
|
process.y = y
|
||||||
process:reposition()
|
process:reposition()
|
||||||
process:drawSizers(true)
|
process:drawSizers(true)
|
||||||
multishell.saveSession(sessionFile)
|
multishell.saveSession(sessionFile)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event[1] == 'mouse_click' or
|
elseif event[1] == 'mouse_click' or
|
||||||
event[1] == 'mouse_up' then
|
event[1] == 'mouse_up' then
|
||||||
|
|
||||||
local focused = processes[#processes]
|
local focused = processes[#processes]
|
||||||
if not focused.isShell then
|
if not focused.isShell then
|
||||||
multishell.setFocus(1) -- shell is always 1
|
multishell.setFocus(1) -- shell is always 1
|
||||||
else
|
else
|
||||||
focused:resume(unpack(event))
|
focused:resume(unpack(event))
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event[1] == 'char' or
|
elseif event[1] == 'char' or
|
||||||
event[1] == 'key' or
|
event[1] == 'key' or
|
||||||
event[1] == 'key_up' or
|
event[1] == 'key_up' or
|
||||||
event[1] == 'paste' then
|
event[1] == 'paste' then
|
||||||
|
|
||||||
local focused = processes[#processes]
|
local focused = processes[#processes]
|
||||||
if focused then
|
if focused then
|
||||||
focused:resume(unpack(event))
|
focused:resume(unpack(event))
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
for _,process in pairs(Util.shallowCopy(processes)) do
|
for _,process in pairs(Util.shallowCopy(processes)) do
|
||||||
process:resume(unpack(event))
|
process:resume(unpack(event))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
monitor.canvas:render(parentMon)
|
monitor.canvas:render(parentMon)
|
||||||
|
|
||||||
local focused = processes[#processes]
|
local focused = processes[#processes]
|
||||||
if focused then
|
if focused then
|
||||||
focused.window.restoreCursor()
|
focused.window.restoreCursor()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[ Special shell process for launching programs ]]--
|
--[[ Special shell process for launching programs ]]--
|
||||||
local function addShell()
|
local function addShell()
|
||||||
|
|
||||||
local process = setmetatable({
|
local process = setmetatable({
|
||||||
x = monDim.width,
|
x = monDim.width,
|
||||||
y = monDim.height,
|
y = monDim.height,
|
||||||
width = 1,
|
width = 1,
|
||||||
height = 1,
|
height = 1,
|
||||||
isShell = true,
|
isShell = true,
|
||||||
uid = nextUID(),
|
uid = nextUID(),
|
||||||
title = 'Terminal',
|
title = 'Terminal',
|
||||||
}, { __index = Process })
|
}, { __index = Process })
|
||||||
|
|
||||||
function process:focus(focused)
|
function process:focus(focused)
|
||||||
self.window.setVisible(focused)
|
self.window.setVisible(focused)
|
||||||
if focused then
|
if focused then
|
||||||
self.window.restoreCursor()
|
self.window.restoreCursor()
|
||||||
else
|
else
|
||||||
parentTerm.clear()
|
parentTerm.clear()
|
||||||
parentTerm.setCursorBlink(false)
|
parentTerm.setCursorBlink(false)
|
||||||
local str = 'Click screen for shell'
|
local str = 'Click screen for shell'
|
||||||
write(parentTerm,
|
write(parentTerm,
|
||||||
math.floor((termDim.width - #str) / 2),
|
math.floor((termDim.width - #str) / 2),
|
||||||
math.floor(termDim.height / 2),
|
math.floor(termDim.height / 2),
|
||||||
str)
|
str)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function process:click()
|
function process:click()
|
||||||
end
|
end
|
||||||
|
|
||||||
process.container = Terminal.window(monitor, process.x, process.y+1, process.width, process.height, true)
|
process.container = Terminal.window(monitor, process.x, process.y+1, process.width, process.height, true)
|
||||||
process.window = window.create(parentTerm, 1, 1, termDim.width, termDim.height, true)
|
process.window = window.create(parentTerm, 1, 1, termDim.width, termDim.height, true)
|
||||||
process.terminal = process.window
|
process.terminal = process.window
|
||||||
|
|
||||||
process.co = coroutine.create(function()
|
process.co = coroutine.create(function()
|
||||||
print('To run a program on the monitor, type "fg <program>"')
|
print('To run a program on the monitor, type "fg <program>"')
|
||||||
print('To quit, type "exit"')
|
print('To quit, type "exit"')
|
||||||
os.run(Util.shallowCopy(defaultEnv), shell.resolveProgram('shell'))
|
os.run(Util.shallowCopy(defaultEnv), shell.resolveProgram('shell'))
|
||||||
multishell.stop()
|
multishell.stop()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
table.insert(processes, process)
|
table.insert(processes, process)
|
||||||
process:focus(true)
|
process:focus(true)
|
||||||
|
|
||||||
local previousTerm = term.current()
|
local previousTerm = term.current()
|
||||||
process:resume()
|
process:resume()
|
||||||
term.redirect(previousTerm)
|
term.redirect(previousTerm)
|
||||||
end
|
end
|
||||||
|
|
||||||
addShell()
|
addShell()
|
||||||
|
|||||||
@@ -3,4 +3,7 @@
|
|||||||
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/neural',
|
repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/neural',
|
||||||
description = [[ Applications using various plethora modules ]],
|
description = [[ Applications using various plethora modules ]],
|
||||||
licence = 'MIT',
|
licence = 'MIT',
|
||||||
|
required = {
|
||||||
|
'core',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
local Event = require('event')
|
local Event = require('event')
|
||||||
local itemDB = require('core.itemDB')
|
local itemDB = require('core.itemDB')
|
||||||
local UI = require('ui')
|
local UI = require('ui')
|
||||||
local Util = require('util')
|
local Util = require('util')
|
||||||
|
|
||||||
local device = _G.device
|
local device = _G.device
|
||||||
local gps = _G.gps
|
local gps = _G.gps
|
||||||
|
local multishell = _ENV.multishell
|
||||||
|
|
||||||
local glasses = device['plethora:glasses']
|
local glasses = device['plethora:glasses']
|
||||||
local scanner = device['plethora:scanner'] or
|
local scanner = device['plethora:scanner'] or
|
||||||
@@ -14,11 +15,13 @@ local projecting = { }
|
|||||||
|
|
||||||
local function getPoint()
|
local function getPoint()
|
||||||
local pt = { gps.locate() }
|
local pt = { gps.locate() }
|
||||||
return {
|
if pt[1] then
|
||||||
x = pt[1],
|
return {
|
||||||
y = pt[2],
|
x = pt[1],
|
||||||
z = pt[3],
|
y = pt[2],
|
||||||
}
|
z = pt[3],
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local offset = getPoint()
|
local offset = getPoint()
|
||||||
@@ -60,7 +63,7 @@ local page = UI.Page {
|
|||||||
},
|
},
|
||||||
sortColumn = 'name',
|
sortColumn = 'name',
|
||||||
accelerators = {
|
accelerators = {
|
||||||
grid_select = 'noop',
|
grid_select = 'inspect',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -75,21 +78,25 @@ function page:scan()
|
|||||||
local entry = itemDB:get(table.concat({ b.name, b.metadata }, ':'))
|
local entry = itemDB:get(table.concat({ b.name, b.metadata }, ':'))
|
||||||
if not entry then
|
if not entry then
|
||||||
local meta = scanner.getBlockMeta(b.x, b.y, b.z)
|
local meta = scanner.getBlockMeta(b.x, b.y, b.z)
|
||||||
entry = itemDB:add({
|
if meta.name == b.name and meta.metadata == b.metadata then
|
||||||
name = meta.name,
|
entry = itemDB:add({
|
||||||
displayName = meta.displayName,
|
name = meta.name,
|
||||||
damage = meta.metadata,
|
displayName = meta.displayName,
|
||||||
})
|
damage = meta.metadata,
|
||||||
|
})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
b.key = entry.displayName
|
if entry then
|
||||||
if acc[b.key] then
|
b.key = entry.displayName
|
||||||
acc[b.key].count = acc[b.key].count + 1
|
if acc[b.key] then
|
||||||
else
|
acc[b.key].count = acc[b.key].count + 1
|
||||||
entry = Util.shallowCopy(entry)
|
else
|
||||||
entry.lname = entry.displayName:lower()
|
entry = Util.shallowCopy(entry)
|
||||||
entry.count = 1
|
entry.lname = entry.displayName:lower()
|
||||||
entry.key = b.key
|
entry.count = 1
|
||||||
acc[b.key] = entry
|
entry.key = b.key
|
||||||
|
acc[b.key] = entry
|
||||||
|
end
|
||||||
end
|
end
|
||||||
throttle()
|
throttle()
|
||||||
return acc
|
return acc
|
||||||
@@ -121,43 +128,45 @@ function page.detail:show(blocks, entry)
|
|||||||
local scanned = scanner.scan()
|
local scanned = scanner.scan()
|
||||||
local pos = getPoint()
|
local pos = getPoint()
|
||||||
|
|
||||||
blocks = Util.reduce(scanned, function(acc, b)
|
if pos and offset then
|
||||||
if b.name == t.name and b.metadata == t.damage then
|
blocks = Util.reduce(scanned, function(acc, b)
|
||||||
-- track block's world position
|
if b.name == t.name and b.metadata == t.damage then
|
||||||
b.id = table.concat({
|
-- track block's world position
|
||||||
math.floor(pos.x + b.x),
|
b.id = table.concat({
|
||||||
math.floor(pos.y + b.y),
|
math.floor(pos.x + b.x),
|
||||||
math.floor(pos.z + b.z) }, ':')
|
math.floor(pos.y + b.y),
|
||||||
acc[b.id] = b
|
math.floor(pos.z + b.z) }, ':')
|
||||||
end
|
acc[b.id] = b
|
||||||
return acc
|
|
||||||
end, { })
|
|
||||||
|
|
||||||
for _, b in pairs(blocks) do
|
|
||||||
if not projecting[b.id] then
|
|
||||||
projecting[b.id] = b
|
|
||||||
pcall(function()
|
|
||||||
b.box = canvas.addItem({
|
|
||||||
pos.x - offset.x + b.x + -(pos.x % 1) + .5,
|
|
||||||
pos.y - offset.y + b.y + -(pos.y % 1) + .5,
|
|
||||||
pos.z - offset.z + b.z + -(pos.z % 1) + .5 },
|
|
||||||
b.name, b.damage, .5)
|
|
||||||
end)
|
|
||||||
if not b.box then
|
|
||||||
b.box = canvas.addBox(
|
|
||||||
pos.x - offset.x + b.x + -(pos.x % 1) + .25,
|
|
||||||
pos.y - offset.y + b.y + -(pos.y % 1) + .25,
|
|
||||||
pos.z - offset.z + b.z + -(pos.z % 1) + .25,
|
|
||||||
.5, .5, .5)
|
|
||||||
end
|
end
|
||||||
b.box.setDepthTested(false)
|
return acc
|
||||||
end
|
end, { })
|
||||||
end
|
|
||||||
|
|
||||||
for _, b in pairs(projecting) do
|
for _, b in pairs(blocks) do
|
||||||
if not blocks[b.id] then
|
if not projecting[b.id] then
|
||||||
b.box.remove()
|
projecting[b.id] = b
|
||||||
projecting[b.id] = nil
|
pcall(function()
|
||||||
|
b.box = canvas.addItem({
|
||||||
|
pos.x - offset.x + b.x + -(pos.x % 1) + .5,
|
||||||
|
pos.y - offset.y + b.y + -(pos.y % 1) + .5,
|
||||||
|
pos.z - offset.z + b.z + -(pos.z % 1) + .5 },
|
||||||
|
b.name, b.damage, .5)
|
||||||
|
end)
|
||||||
|
if not b.box then
|
||||||
|
b.box = canvas.addBox(
|
||||||
|
pos.x - offset.x + b.x + -(pos.x % 1) + .25,
|
||||||
|
pos.y - offset.y + b.y + -(pos.y % 1) + .25,
|
||||||
|
pos.z - offset.z + b.z + -(pos.z % 1) + .25,
|
||||||
|
.5, .5, .5)
|
||||||
|
end
|
||||||
|
b.box.setDepthTested(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, b in pairs(projecting) do
|
||||||
|
if not blocks[b.id] then
|
||||||
|
b.box.remove()
|
||||||
|
projecting[b.id] = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -178,7 +187,14 @@ function page:eventHandler(event)
|
|||||||
elseif event.type == 'scan' then
|
elseif event.type == 'scan' then
|
||||||
self:scan()
|
self:scan()
|
||||||
|
|
||||||
elseif event.type == 'grid_select' and event.element == page.grid then
|
elseif event.type == 'grid_select' and event.element == self.detail.grid then
|
||||||
|
multishell.openTab({
|
||||||
|
path = 'sys/apps/Lua.lua',
|
||||||
|
args = { event.selected },
|
||||||
|
focused = true,
|
||||||
|
})
|
||||||
|
|
||||||
|
elseif event.type == 'grid_select' and event.element == self.grid then
|
||||||
self.detail:show(self.blocks, self.grid:getSelected())
|
self.detail:show(self.blocks, self.grid:getSelected())
|
||||||
|
|
||||||
elseif event.type == 'cancel' then
|
elseif event.type == 'cancel' then
|
||||||
|
|||||||
@@ -18,22 +18,13 @@ local projecting = { }
|
|||||||
local offset
|
local offset
|
||||||
local canvas = glasses and intro and glasses.canvas3d().create()
|
local canvas = glasses and intro and glasses.canvas3d().create()
|
||||||
|
|
||||||
local config = Config.load('Sensor', {
|
local config = Config.load('Sensor')
|
||||||
ignore = { }
|
|
||||||
})
|
|
||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
tabs = UI.Tabs {
|
tabs = UI.Tabs {
|
||||||
listing = UI.Tab {
|
listing = UI.Tab {
|
||||||
tabTitle = 'Listing',
|
tabTitle = 'Listing',
|
||||||
menuBar = UI.MenuBar {
|
|
||||||
buttons = {
|
|
||||||
{ text = 'Ignore', event = 'ignore' },
|
|
||||||
{ text = 'Details', event = 'detail' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = 2,
|
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Name', key = 'displayName' },
|
{ heading = 'Name', key = 'displayName' },
|
||||||
{ heading = 'X', key = 'x', width = 3, align = 'right' },
|
{ heading = 'X', key = 'x', width = 3, align = 'right' },
|
||||||
@@ -45,14 +36,7 @@ local page = UI.Page {
|
|||||||
},
|
},
|
||||||
summary = UI.Tab {
|
summary = UI.Tab {
|
||||||
tabTitle = 'Summary',
|
tabTitle = 'Summary',
|
||||||
menuBar = UI.MenuBar {
|
|
||||||
buttons = {
|
|
||||||
{ text = 'Projector', event = 'project' },
|
|
||||||
{ text = 'Ignore', event = 'ignore' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
y = 2,
|
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Name', key = 'displayName' },
|
{ heading = 'Name', key = 'displayName' },
|
||||||
{ heading = 'Count', key = 'count', width = 5, align = 'right' },
|
{ heading = 'Count', key = 'count', width = 5, align = 'right' },
|
||||||
@@ -134,13 +118,6 @@ local function project(entities)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function ignoreEntity(entity)
|
|
||||||
if entity then
|
|
||||||
config.ignore[entity.name] = true
|
|
||||||
Config.update('Sensor', config)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function detail:enable(entity)
|
function detail:enable(entity)
|
||||||
local function update()
|
local function update()
|
||||||
local t = { }
|
local t = { }
|
||||||
@@ -194,8 +171,6 @@ end
|
|||||||
function listing:enable()
|
function listing:enable()
|
||||||
self.handler = Event.onInterval(.5, function()
|
self.handler = Event.onInterval(.5, function()
|
||||||
local entities = sensor.sense()
|
local entities = sensor.sense()
|
||||||
Util.filterInplace(entities, function(e) return not config.ignore[e.name] end)
|
|
||||||
|
|
||||||
self.grid:setValues(entities)
|
self.grid:setValues(entities)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:sync()
|
self:sync()
|
||||||
@@ -209,14 +184,11 @@ function listing:disable()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function listing:eventHandler(event)
|
function listing:eventHandler(event)
|
||||||
if event.type == 'detail' or event.type == 'grid_select' then
|
if event.type == 'grid_select' then
|
||||||
local selected = self.grid:getSelected()
|
local selected = self.grid:getSelected()
|
||||||
if selected then
|
if selected then
|
||||||
UI:setPage(detail, selected)
|
UI:setPage(detail, selected)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'ignore' then
|
|
||||||
ignoreEntity(self.grid:getSelected())
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return UI.Tab.eventHandler(self, event)
|
return UI.Tab.eventHandler(self, event)
|
||||||
@@ -225,7 +197,6 @@ end
|
|||||||
function summary:enable()
|
function summary:enable()
|
||||||
self.handler = Event.onInterval(.5, function()
|
self.handler = Event.onInterval(.5, function()
|
||||||
local entities = sensor.sense()
|
local entities = sensor.sense()
|
||||||
Util.filterInplace(entities, function(e) return not config.ignore[e.name] end)
|
|
||||||
|
|
||||||
local t = { }
|
local t = { }
|
||||||
local highlight = { }
|
local highlight = { }
|
||||||
@@ -265,10 +236,7 @@ function summary.grid:getRowTextColor(row, selected)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function summary:eventHandler(event)
|
function summary:eventHandler(event)
|
||||||
if event.type == 'ignore' then
|
if event.type == 'grid_select' then
|
||||||
ignoreEntity(self.grid:getSelected())
|
|
||||||
|
|
||||||
elseif event.type == 'project' or event.type == 'grid_select' then
|
|
||||||
local selected = self.grid:getSelected()
|
local selected = self.grid:getSelected()
|
||||||
if selected then
|
if selected then
|
||||||
self.target = selected.name
|
self.target = selected.name
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
local Angle = { }
|
local Angle = { }
|
||||||
|
|
||||||
function Angle.towards(x, y, z)
|
function Angle.towards(x, y, z)
|
||||||
return math.deg(math.atan2(-x, z)), math.deg(-math.atan2(y, math.sqrt(x * x + z * z)))
|
return math.deg(math.atan2(-x, z)), math.deg(-math.atan2(y, math.sqrt(x * x + z * z)))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Angle.away(x, y, z)
|
function Angle.away(x, y, z)
|
||||||
return math.deg(math.atan2(x, -z)), 0
|
return math.deg(math.atan2(x, -z)), 0
|
||||||
end
|
end
|
||||||
|
|
||||||
return Angle
|
return Angle
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
local Mobs = { }
|
local Mobs = { }
|
||||||
|
|
||||||
local hostiles = {
|
local hostiles = {
|
||||||
BabySkeleton = true,
|
BabySkeleton = true,
|
||||||
BabyZombie = true,
|
BabyZombie = true,
|
||||||
Bat = true,
|
Bat = true,
|
||||||
Blaze = true,
|
Blaze = true,
|
||||||
CaveSpider = true,
|
CaveSpider = true,
|
||||||
Creeper = true,
|
Creeper = true,
|
||||||
Ghast = true,
|
Ghast = true,
|
||||||
Husk = true,
|
Husk = true,
|
||||||
LavaSlime = true,
|
LavaSlime = true,
|
||||||
PigZombie = true,
|
PigZombie = true,
|
||||||
Skeleton = true,
|
Skeleton = true,
|
||||||
Slime = true,
|
Slime = true,
|
||||||
Spider = true,
|
Spider = true,
|
||||||
Witch = true,
|
Witch = true,
|
||||||
WitherSkeleton = true,
|
WitherSkeleton = true,
|
||||||
Zombie = true,
|
Zombie = true,
|
||||||
ZombieVillager = true,
|
ZombieVillager = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
function Mobs.getNames()
|
function Mobs.getNames()
|
||||||
return hostiles
|
return hostiles
|
||||||
end
|
end
|
||||||
|
|
||||||
return Mobs
|
return Mobs
|
||||||
|
|||||||
@@ -7,149 +7,149 @@ local NONE = "none"
|
|||||||
local ASYNC = "async"
|
local ASYNC = "async"
|
||||||
|
|
||||||
local function call_handler(handler, params)
|
local function call_handler(handler, params)
|
||||||
if handler then
|
if handler then
|
||||||
return handler(unpack(params))
|
return handler(unpack(params))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function create_transition(name)
|
local function create_transition(name)
|
||||||
local can, to, from, params
|
local can, to, from, params
|
||||||
|
|
||||||
local function transition(self, ...)
|
local function transition(self, ...)
|
||||||
if self.asyncState == NONE then
|
if self.asyncState == NONE then
|
||||||
can, to = self:can(name)
|
can, to = self:can(name)
|
||||||
from = self.current
|
from = self.current
|
||||||
params = { self, name, from, to, ...}
|
params = { self, name, from, to, ...}
|
||||||
|
|
||||||
if not can then return false end
|
if not can then return false end
|
||||||
self.currentTransitioningEvent = name
|
self.currentTransitioningEvent = name
|
||||||
|
|
||||||
local beforeReturn = call_handler(self["onbefore" .. name], params)
|
local beforeReturn = call_handler(self["onbefore" .. name], params)
|
||||||
local leaveReturn = call_handler(self["onleave" .. from], params)
|
local leaveReturn = call_handler(self["onleave" .. from], params)
|
||||||
|
|
||||||
if beforeReturn == false or leaveReturn == false then
|
if beforeReturn == false or leaveReturn == false then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
self.asyncState = name .. "WaitingOnLeave"
|
self.asyncState = name .. "WaitingOnLeave"
|
||||||
|
|
||||||
if leaveReturn ~= ASYNC then
|
if leaveReturn ~= ASYNC then
|
||||||
transition(self, ...)
|
transition(self, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
elseif self.asyncState == name .. "WaitingOnLeave" then
|
elseif self.asyncState == name .. "WaitingOnLeave" then
|
||||||
self.current = to
|
self.current = to
|
||||||
|
|
||||||
local enterReturn = call_handler(self["onenter" .. to] or self["on" .. to], params)
|
local enterReturn = call_handler(self["onenter" .. to] or self["on" .. to], params)
|
||||||
|
|
||||||
self.asyncState = name .. "WaitingOnEnter"
|
self.asyncState = name .. "WaitingOnEnter"
|
||||||
|
|
||||||
if enterReturn ~= ASYNC then
|
if enterReturn ~= ASYNC then
|
||||||
transition(self, ...)
|
transition(self, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
elseif self.asyncState == name .. "WaitingOnEnter" then
|
elseif self.asyncState == name .. "WaitingOnEnter" then
|
||||||
call_handler(self["onafter" .. name] or self["on" .. name], params)
|
call_handler(self["onafter" .. name] or self["on" .. name], params)
|
||||||
call_handler(self["onstatechange"], params)
|
call_handler(self["onstatechange"], params)
|
||||||
self.asyncState = NONE
|
self.asyncState = NONE
|
||||||
self.currentTransitioningEvent = nil
|
self.currentTransitioningEvent = nil
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
if string.find(self.asyncState, "WaitingOnLeave") or string.find(self.asyncState, "WaitingOnEnter") then
|
if string.find(self.asyncState, "WaitingOnLeave") or string.find(self.asyncState, "WaitingOnEnter") then
|
||||||
self.asyncState = NONE
|
self.asyncState = NONE
|
||||||
transition(self, ...)
|
transition(self, ...)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self.currentTransitioningEvent = nil
|
self.currentTransitioningEvent = nil
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return transition
|
return transition
|
||||||
end
|
end
|
||||||
|
|
||||||
local function add_to_map(map, event)
|
local function add_to_map(map, event)
|
||||||
if type(event.from) == 'string' then
|
if type(event.from) == 'string' then
|
||||||
map[event.from] = event.to
|
map[event.from] = event.to
|
||||||
else
|
else
|
||||||
for _, from in ipairs(event.from) do
|
for _, from in ipairs(event.from) do
|
||||||
map[from] = event.to
|
map[from] = event.to
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function machine.create(options)
|
function machine.create(options)
|
||||||
assert(options.events)
|
assert(options.events)
|
||||||
|
|
||||||
local fsm = {}
|
local fsm = {}
|
||||||
setmetatable(fsm, machine)
|
setmetatable(fsm, machine)
|
||||||
|
|
||||||
fsm.options = options
|
fsm.options = options
|
||||||
fsm.current = options.initial or 'none'
|
fsm.current = options.initial or 'none'
|
||||||
fsm.asyncState = NONE
|
fsm.asyncState = NONE
|
||||||
fsm.events = {}
|
fsm.events = {}
|
||||||
|
|
||||||
for _, event in ipairs(options.events or {}) do
|
for _, event in ipairs(options.events or {}) do
|
||||||
local name = event.name
|
local name = event.name
|
||||||
fsm[name] = fsm[name] or create_transition(name)
|
fsm[name] = fsm[name] or create_transition(name)
|
||||||
fsm.events[name] = fsm.events[name] or { map = {} }
|
fsm.events[name] = fsm.events[name] or { map = {} }
|
||||||
add_to_map(fsm.events[name].map, event)
|
add_to_map(fsm.events[name].map, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
for name, callback in pairs(options.callbacks or {}) do
|
for name, callback in pairs(options.callbacks or {}) do
|
||||||
fsm[name] = callback
|
fsm[name] = callback
|
||||||
end
|
end
|
||||||
|
|
||||||
return fsm
|
return fsm
|
||||||
end
|
end
|
||||||
|
|
||||||
function machine:is(state)
|
function machine:is(state)
|
||||||
return self.current == state
|
return self.current == state
|
||||||
end
|
end
|
||||||
|
|
||||||
function machine:can(e)
|
function machine:can(e)
|
||||||
local event = self.events[e]
|
local event = self.events[e]
|
||||||
local to = event and event.map[self.current] or event.map['*']
|
local to = event and event.map[self.current] or event.map['*']
|
||||||
return to ~= nil, to
|
return to ~= nil, to
|
||||||
end
|
end
|
||||||
|
|
||||||
function machine:cannot(e)
|
function machine:cannot(e)
|
||||||
return not self:can(e)
|
return not self:can(e)
|
||||||
end
|
end
|
||||||
|
|
||||||
function machine:todot(filename)
|
function machine:todot(filename)
|
||||||
local dotfile = io.open(filename,'w')
|
local dotfile = io.open(filename,'w')
|
||||||
dotfile:write('digraph {\n')
|
dotfile:write('digraph {\n')
|
||||||
local transition = function(event,from,to)
|
local transition = function(event,from,to)
|
||||||
dotfile:write(string.format('%s -> %s [label=%s];\n',from,to,event))
|
dotfile:write(string.format('%s -> %s [label=%s];\n',from,to,event))
|
||||||
end
|
end
|
||||||
for _, event in pairs(self.options.events) do
|
for _, event in pairs(self.options.events) do
|
||||||
if type(event.from) == 'table' then
|
if type(event.from) == 'table' then
|
||||||
for _, from in ipairs(event.from) do
|
for _, from in ipairs(event.from) do
|
||||||
transition(event.name,from,event.to)
|
transition(event.name,from,event.to)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
transition(event.name,event.from,event.to)
|
transition(event.name,event.from,event.to)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
dotfile:write('}\n')
|
dotfile:write('}\n')
|
||||||
dotfile:close()
|
dotfile:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
function machine:transition(event)
|
function machine:transition(event)
|
||||||
if self.currentTransitioningEvent == event then
|
if self.currentTransitioningEvent == event then
|
||||||
return self[self.currentTransitioningEvent](self)
|
return self[self.currentTransitioningEvent](self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function machine:cancelTransition(event)
|
function machine:cancelTransition(event)
|
||||||
if self.currentTransitioningEvent == event then
|
if self.currentTransitioningEvent == event then
|
||||||
self.asyncState = NONE
|
self.asyncState = NONE
|
||||||
self.currentTransitioningEvent = nil
|
self.currentTransitioningEvent = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
machine.NONE = NONE
|
machine.NONE = NONE
|
||||||
|
|||||||
@@ -3,28 +3,28 @@ local GPS = require('gps')
|
|||||||
local device = _G.device
|
local device = _G.device
|
||||||
|
|
||||||
if device.neuralInterface and device.wireless_modem then
|
if device.neuralInterface and device.wireless_modem then
|
||||||
local ni = require('neural.interface')
|
local ni = require('neural.interface')
|
||||||
device.neuralInterface.goTo = function(x, y, z)
|
device.neuralInterface.goTo = function(x, y, z)
|
||||||
local pt = GPS.locate(2)
|
local pt = GPS.locate(2)
|
||||||
if pt then
|
if pt then
|
||||||
return pcall(function()
|
return pcall(function()
|
||||||
if device.neuralInterface.walk then
|
if device.neuralInterface.walk then
|
||||||
local gpt = {
|
local gpt = {
|
||||||
x = x - pt.x,
|
x = x - pt.x,
|
||||||
y = 0,
|
y = 0,
|
||||||
z = z - pt.z,
|
z = z - pt.z,
|
||||||
}
|
}
|
||||||
gpt.x = math.min(math.max(gpt.x, -15), 15)
|
gpt.x = math.min(math.max(gpt.x, -15), 15)
|
||||||
gpt.z = math.min(math.max(gpt.z, -15), 15)
|
gpt.z = math.min(math.max(gpt.z, -15), 15)
|
||||||
return device.neuralInterface.walk(gpt.x, gpt.y, gpt.z, 2)
|
return device.neuralInterface.walk(gpt.x, gpt.y, gpt.z, 2)
|
||||||
elseif ni.launch then
|
elseif ni.launch then
|
||||||
local y, p = ni.yap(pt, { x = x, y = y + 3, z = z })
|
local y, p = ni.yap(pt, { x = x, y = y + 3, z = z })
|
||||||
ni.look(y, 0)
|
ni.look(y, 0)
|
||||||
return ni.launch(y, p, 1.5)
|
return ni.launch(y, p, 1.5)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
return false, 'No GPS'
|
return false, 'No GPS'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,70 +1,70 @@
|
|||||||
local opus = {
|
local opus = {
|
||||||
'fffff00',
|
'fffff00',
|
||||||
'ffff07000',
|
'ffff07000',
|
||||||
'ff00770b00 4444',
|
'ff00770b00 4444',
|
||||||
'ff077777444444444',
|
'ff077777444444444',
|
||||||
'f07777744444444444',
|
'f07777744444444444',
|
||||||
'f0000777444444444',
|
'f0000777444444444',
|
||||||
'070000111744444',
|
'070000111744444',
|
||||||
'777770000',
|
'777770000',
|
||||||
'7777000000',
|
'7777000000',
|
||||||
'70700000000',
|
'70700000000',
|
||||||
'077000000000',
|
'077000000000',
|
||||||
}
|
}
|
||||||
|
|
||||||
local hex = {
|
local hex = {
|
||||||
['0'] = 0xF0F0F04F,
|
['0'] = 0xF0F0F04F,
|
||||||
['1'] = 0xF2B2334F,
|
['1'] = 0xF2B2334F,
|
||||||
['2'] = 0xE57FD84F,
|
['2'] = 0xE57FD84F,
|
||||||
['3'] = 0x99B2F24F,
|
['3'] = 0x99B2F24F,
|
||||||
['4'] = 0xDEDE6C4F,
|
['4'] = 0xDEDE6C4F,
|
||||||
['5'] = 0x7FCC194F,
|
['5'] = 0x7FCC194F,
|
||||||
['6'] = 0xF2B2CC4F,
|
['6'] = 0xF2B2CC4F,
|
||||||
['7'] = 0x4C4C4C4F,
|
['7'] = 0x4C4C4C4F,
|
||||||
['8'] = 0x9999994F,
|
['8'] = 0x9999994F,
|
||||||
['9'] = 0x4C99B24F,
|
['9'] = 0x4C99B24F,
|
||||||
['a'] = 0xB266E54F,
|
['a'] = 0xB266E54F,
|
||||||
['b'] = 0x3366CC4F,
|
['b'] = 0x3366CC4F,
|
||||||
['c'] = 0x7F664C4F,
|
['c'] = 0x7F664C4F,
|
||||||
['d'] = 0x57A64E4F,
|
['d'] = 0x57A64E4F,
|
||||||
['e'] = 0xCC4C4C4F,
|
['e'] = 0xCC4C4C4F,
|
||||||
-- ['f'] = 0x191919FF, -- transparent
|
-- ['f'] = 0x191919FF, -- transparent
|
||||||
}
|
}
|
||||||
|
|
||||||
local function update()
|
local function update()
|
||||||
local canvas = device['plethora:glasses'] and device['plethora:glasses'].canvas()
|
local canvas = device['plethora:glasses'] and device['plethora:glasses'].canvas()
|
||||||
if canvas then
|
if canvas then
|
||||||
local Tween = require('ui.tween')
|
local Tween = require('ui.tween')
|
||||||
|
|
||||||
canvas.clear()
|
canvas.clear()
|
||||||
local w, h = canvas.getSize()
|
local w, h = canvas.getSize()
|
||||||
local pos = { x = w / 2, y = h / 2 - 30 }
|
local pos = { x = w / 2, y = h / 2 - 30 }
|
||||||
local group = canvas.addGroup(pos)
|
local group = canvas.addGroup(pos)
|
||||||
local function drawLine(k, line)
|
local function drawLine(k, line)
|
||||||
for i = 1, #line do
|
for i = 1, #line do
|
||||||
local pix = hex[line:sub(i, i)]
|
local pix = hex[line:sub(i, i)]
|
||||||
if pix then
|
if pix then
|
||||||
group.addRectangle(i*1.5, k*2.25, 1.5, 2.25, pix)
|
group.addRectangle(i*1.5, k*2.25, 1.5, 2.25, pix)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for k,line in ipairs(opus) do
|
for k,line in ipairs(opus) do
|
||||||
drawLine(k, line)
|
drawLine(k, line)
|
||||||
end
|
end
|
||||||
os.sleep(.5)
|
os.sleep(.5)
|
||||||
local tween = Tween.new(40, pos, { x = w - 60, y = h - 30 }, 'outBounce')
|
local tween = Tween.new(40, pos, { x = w - 60, y = h - 30 }, 'outBounce')
|
||||||
repeat
|
repeat
|
||||||
local finished = tween:update(1)
|
local finished = tween:update(1)
|
||||||
os.sleep(0)
|
os.sleep(0)
|
||||||
group.setPosition(pos.x, pos.y)
|
group.setPosition(pos.x, pos.y)
|
||||||
until finished
|
until finished
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
kernel.run({
|
kernel.run({
|
||||||
title = 'opus',
|
title = 'opus',
|
||||||
env = _ENV,
|
env = _ENV,
|
||||||
hidden = true,
|
hidden = true,
|
||||||
fn = update,
|
fn = update,
|
||||||
})
|
})
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user