spaces->tab, equipper improvements, supertreefarm rewrite, follow improvements, sensor cleanup, milo multiple items allowed in recipes, remote canvas access

This commit is contained in:
kepler155c@gmail.com
2019-06-18 15:23:20 -04:00
parent 3b9b509429
commit 045b32884f
162 changed files with 20448 additions and 20286 deletions

View File

@@ -142,7 +142,7 @@ 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)

View File

@@ -15,14 +15,20 @@ local gpt = GPS.getPoint() or error('GPS not found')
local pts, blocks local pts, blocks
local page = UI.Page { local page = UI.Page {
menuBar = UI.MenuBar {
buttons = {
{ text = 'Range', event = 'range' },
{ text = 'Stop', event = 'stop' },
},
mode = UI.Chooser { mode = UI.Chooser {
x = 13, y = -1, x = -16,
choices = { choices = {
{ name = 'No breaking', value = 'digNone' }, { name = 'No breaking', value = 'digNone' },
{ name = 'Destructive', value = 'turtleSafe' }, { name = 'Destructive', value = 'turtleSafe' },
}, },
value = 'digNone', value = 'digNone',
}, },
},
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
y = 2, ey = -2, y = 2, ey = -2,
columns = { columns = {
@@ -34,6 +40,32 @@ local page = UI.Page {
sortColumn = 'distance', sortColumn = 'distance',
autospace = true, 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)
@@ -80,7 +112,7 @@ 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',
}) })
@@ -140,6 +172,31 @@ function page:eventHandler(event)
member.snmp:write({ type = 'scriptEx', args = script }) member.snmp:write({ type = 'scriptEx', args = script })
end end
elseif event.type == 'stop' then
for id in pairs(swarm.pool) do
swarm:remove(id)
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 else
return UI.Page.eventHandler(self, event) return UI.Page.eventHandler(self, event)
end end

45
common/canvasClient.lua Normal file
View 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

32
core/apis/proxy.lua Normal file
View 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

View File

@@ -1,7 +1,7 @@
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)

View File

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

View File

@@ -17,7 +17,7 @@ local FUEL = Util.transpose {
'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'] =
@@ -105,9 +105,12 @@ local function scan()
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
return b.metadata == crops[b.name].mature
end
end) end)
local harvestCount = 0 local harvestCount = 0
@@ -186,7 +189,7 @@ local function harvest(blocks)
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')
@@ -218,7 +221,7 @@ local function harvest(blocks)
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()

View File

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

View File

@@ -30,7 +30,7 @@ local ANIMALS = {
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')
@@ -49,9 +49,15 @@ local function getAnimalCount()
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
babies = babies + 1
else
grown = grown + 1
end
return not entity.isChild
end
end end
end) end)
@@ -69,7 +75,7 @@ local function butcher()
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

View File

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

View File

@@ -10,14 +10,16 @@ local turtle = _G.turtle
local STARTUP_FILE = 'usr/autorun/superTreefarm.lua' local STARTUP_FILE = 'usr/autorun/superTreefarm.lua'
local FUEL_BASE = 0 local FUEL_DIRE = 10
local FUEL_DIRE = FUEL_BASE + 10 local FUEL_GOOD = 1000
local FUEL_GOOD = FUEL_BASE + 2000
local MIN_CHARCOAL = 24 local MIN_CHARCOAL = 24
local MIN_SAPLINGS = 32 local MIN_SAPLINGS = 32
local MAX_SAPLINGS = 48 local MAX_SAPLINGS = 48
local RADIUS_X = 2
local RADIUS_Z = 3
local GRID = { local GRID = {
TL = { x = 8, y = 0, z = -7 }, TL = { x = 8, y = 0, z = -7 },
TR = { x = 8, y = 0, z = 8 }, TR = { x = 8, y = 0, z = 8 },
@@ -27,88 +29,30 @@ local GRID = {
local HOME_PT = { x = 0, y = 0, z = 0, heading = 0 } local HOME_PT = { x = 0, y = 0, z = 0, heading = 0 }
local DIG_BLACKLIST = {
[ 'minecraft:furnace' ] = true,
[ 'minecraft:lit_furnace' ] = true,
[ 'minecraft:chest' ] = true,
}
local APPLE = 'minecraft:apple:0'
local CHARCOAL = 'minecraft:coal:1' local CHARCOAL = 'minecraft:coal:1'
local CHEST = 'minecraft:chest:0' local CHEST = 'minecraft:chest:0'
local COBBLESTONE = 'minecraft:cobblestone:0'
local CRAFTING_TABLE = 'minecraft:crafting_table:0'
local PICKAXE = 'minecraft:diamond_pickaxe'
local DIRT = 'minecraft:dirt:0' local DIRT = 'minecraft:dirt:0'
local PICKAXE = 'minecraft:diamond_pickaxe'
local FURNACE = 'minecraft:furnace:0' local FURNACE = 'minecraft:furnace:0'
local MODEM = 'computercraft:peripheral:1'
local LOG = 'minecraft:log' local LOG = 'minecraft:log'
local LOG2 = 'minecraft:log2' local LOG2 = 'minecraft:log2'
local OAK_LOG = 'minecraft:log:0'
local OAK_PLANK = 'minecraft:planks:0'
local OAK_SAPLING = 'minecraft:sapling:0' local OAK_SAPLING = 'minecraft:sapling:0'
local SAPLING = 'minecraft:sapling' local SAPLING = 'minecraft:sapling'
local SCANNER = 'plethora:module:2' local SCANNER = 'plethora:module:2'
local SENSOR = 'plethora:module:3' local SENSOR = 'plethora:module:3'
local STICK = 'minecraft:stick:0'
local STONE = 'minecraft:stone:0'
local TORCH = 'minecraft:torch:0'
local ALL_SAPLINGS = { } local retain = Util.transpose {
PICKAXE,
local state = Util.readTable('usr/config/superTreefarm') or { CHARCOAL,
trees = { SAPLING,
{ x = 1, y = 0, z = 0 } SCANNER,
} SENSOR,
} }
local state = Util.readTable('usr/config/superTreefarm') or { }
local clock = os.clock() local clock = os.clock()
local function equip(side, item, rawName)
-- is it already equipped on the correct side?
local equipped = peripheral.getType(side)
if equipped == item then
return true
end
-- is it equipped on the opposite side?
-- will not work for non-peripheral items :(
local osides = { left = 'right', right = 'left' }
if peripheral.getType(osides[side]) == item then
if not turtle.selectSlotWithQuantity(0) then
error('No slots available')
end
turtle.equip(osides[side])
elseif not turtle.has(rawName or item) then
-- don't have the item - unequip that side to see if it's the correct item
if not turtle.selectSlotWithQuantity(0) then
error('No slots available')
end
turtle.equip(side)
end
-- TODO: if the non-peripheral item was equipped on the other side, then this will not work
if not turtle.has(rawName or item) then
error('Missing ' .. (rawName or item))
end
if not turtle.equip(side, rawName or item) then
error('Unable to equip ' .. (rawName or item))
end
turtle.select(1)
end
local function inspect(fn)
local s, item = fn()
if s and item then
return item.name .. ':' .. item.metadata
end
return 'minecraft:air:0'
end
local function setState(key, value) local function setState(key, value)
state[key] = value state[key] = value
Util.writeTable('usr/config/superTreefarm', state) Util.writeTable('usr/config/superTreefarm', state)
@@ -117,129 +61,17 @@ end
local function refuel() local function refuel()
if turtle.getFuelLevel() < FUEL_GOOD then if turtle.getFuelLevel() < FUEL_GOOD then
local charcoal = turtle.getItemCount(CHARCOAL) local charcoal = turtle.getItemCount(CHARCOAL)
if charcoal > 1 then --if charcoal > 1 then
turtle.refuel(CHARCOAL, math.min(charcoal - 1, MIN_CHARCOAL / 2)) turtle.refuel(CHARCOAL, math.min(charcoal, MIN_CHARCOAL / 2))
print('fuel: ' .. turtle.getFuelLevel()) print('fuel: ' .. turtle.getFuelLevel())
--end
end end
end
return true
end
local function safePlaceBlock(item)
if turtle.placeUp(item) then
return true
end
local s, m = turtle.inspectUp()
if s and not DIG_BLACKLIST[m.name] then
turtle.digUp()
return turtle.placeUp(item)
end
turtle.forward()
return turtle.placeUp(item)
end
local function craftItem(item, qty)
local success, msg
if safePlaceBlock(CHEST) then
os.sleep(.2) -- needed for minecraft 1.12
Util.print('Crafting %d %s', (qty or 1), item)
success, msg = turtle.craftItem(item, qty or 1, {
side = 'top',
direction = 'down',
})
repeat until not turtle.suckUp()
if not success then
print(msg)
end
turtle.digUp()
end
return success
end
local function emptyFurnace()
if state.cooking then
print('Emptying furnace')
turtle.suckDownAt(state.furnace)
turtle.suckForwardAt(state.furnace)
turtle.suckUpAt(state.furnace)
setState('cooking')
end
end
local function cook(item, count, result, fuel, fuelCount)
emptyFurnace()
setState('cooking', true)
fuel = fuel or CHARCOAL
fuelCount = fuelCount or math.ceil(count / 8)
Util.print('Making %d %s', count, result)
turtle.dropForwardAt(state.furnace, fuel, fuelCount)
turtle.dropDownAt(state.furnace, item, count)
count = count + turtle.getItemCount(result)
turtle.select(1)
turtle.pathfind(Point.below(state.furnace))
local lastSuck = os.clock()
repeat
os.sleep(1)
if turtle.suckUp() then
lastSuck = os.clock()
end
if os.clock() - lastSuck > 10 then
-- sponge bug
Util.print('Timed out waiting for furnace')
return
end
until turtle.getItemCount(result) >= count
setState('cooking')
end
local function makeSingleCharcoal()
local slots = turtle.getSummedInventory()
if not state.furnace or
slots[CHARCOAL] or
not slots[OAK_LOG] or
slots[OAK_LOG].count < 2 then
return true
end
turtle.faceAgainst(state.furnace)
if craftItem(OAK_PLANK) then
cook(OAK_LOG, 1, CHARCOAL, OAK_PLANK, 1)
turtle.refuel(OAK_PLANK)
end
return true return true
end end
local function makeCharcoal() local function makeCharcoal()
local slots = turtle.getSummedInventory() local slots = turtle.getSummedInventory()
if not state.furnace or
not slots[CHARCOAL] or
slots[CHARCOAL].count >= MIN_CHARCOAL then
return true
end
local function getLogSlot() local function getLogSlot()
local maxslot = { count = 0 } local maxslot = { count = 0 }
for k,slot in pairs(slots) do for k,slot in pairs(slots) do
@@ -252,126 +84,104 @@ local function makeCharcoal()
return maxslot return maxslot
end end
repeat if turtle.pathfind(Point.above(state.furnace)) then
pcall(function()
local f = peripheral.wrap('bottom')
local inv = f.list()
if inv[3] and
(not slots[CHARCOAL] or
slots[CHARCOAL].count < MIN_CHARCOAL) then
f.pushItems('up', 3, 24)
end
if turtle.has(CHARCOAL) and turtle.getFuelLevel() > 100 then
local count = inv[2] and inv[2].count or 0
if count < 8 then
f.pullItems('up', turtle.getSlot(CHARCOAL).index, 8-count, 2)
end
else
slots = turtle.getSummedInventory() slots = turtle.getSummedInventory()
local charcoal = slots[CHARCOAL].count
local slot = getLogSlot(slots) local slot = getLogSlot(slots)
if slot.count > 0 then
local s = turtle.getSlot(slot.key)
f.pullItems('up', s.index, 1, 2)
end
end
if slot.count < 8 then local count = inv[1] and inv[1].count or 0
if count < 32 then
for key, slot in pairs(turtle.getSummedInventory()) do
if string.match(key, 'minecraft:log') then
if turtle.dropDown(key, 32-count) then
count = count + slot.count
if count >= 32 then
break break
end end
end
local toCook = math.min(charcoal, math.floor(slot.count / 8)) end
toCook = math.min(toCook, math.floor((MIN_CHARCOAL + 8 - charcoal) / 8)) end
toCook = toCook * 8 end
end)
cook(slot.key, toCook, CHARCOAL) end
until charcoal + toCook >= MIN_CHARCOAL
return true return true
end end
local function getCobblestone(count)
local slots = turtle.getSummedInventory()
if not slots[COBBLESTONE] or slots[COBBLESTONE].count < count then
print('Collecting cobblestone')
slots[COBBLESTONE] = true
slots[DIRT] = true
local pt = Point.copy(GRID.BR)
pt.x = GRID.BR.x + 2
pt.z = GRID.BR.z - 2
turtle.pathfind(pt)
repeat
turtle.select(1)
turtle.digDown()
turtle.down()
for _ = 1, 4 do
if inspect(turtle.inspect) == STONE then
turtle.dig()
end
turtle.turnRight()
end
for item in pairs(turtle.getSummedInventory()) do
if not slots[item] then
turtle.drop(item)
end
end
until turtle.getItemCount(COBBLESTONE) >= count
turtle.go(pt)
turtle.placeDown(DIRT)
turtle.drop(DIRT)
end
end
local function createFurnace() local function createFurnace()
if not state.furnace then if not state.furnace then
if turtle.getFuelLevel() < FUEL_BASE + 100 then
return true -- try again later
end
print('Adding a furnace')
if not turtle.has(FURNACE) then if not turtle.has(FURNACE) then
getCobblestone(8) error('Turtle must have a furnace')
end end
if turtle.has(FURNACE) or craftItem(FURNACE) then print('Adding a furnace')
turtle.drop(COBBLESTONE) local pt = Point.below(HOME_PT)
local furnacePt = { x = GRID.BL.x + 1, y = 1, z = GRID.BL.z + 1 } if not turtle.placeDownAt(pt, FURNACE) then
turtle.placeAt(furnacePt, FURNACE) error('Error placing furnace')
setState('furnace', furnacePt)
end end
setState('furnace', pt)
end end
turtle.addWorldBlock(state.furnace)
end end
local function createChests() local function createChests()
if state.chest then if not state.chest and turtle.getFuelLevel() > 1 then
return if not turtle.has(CHEST) then
error('Turtle must have a chest')
end end
if turtle.getFuelLevel() > FUEL_GOOD and
turtle.canCraft(CHEST, 4, turtle.getSummedInventory()) then
print('Adding storage') print('Adding storage')
if turtle.has(CHEST, 2) or craftItem(CHEST, 2) then
local pt = Point.copy(GRID.BL) local pt = Point.below(HOME_PT)
pt.x = pt.x + 1 pt.x = pt.x - 1
pt.y = pt.y - 1
pt.z = pt.z + 1 if not turtle.placeDownAt(pt, CHEST) then
error('Error placing chest')
turtle.digDownAt(pt)
turtle.placeDown(CHEST)
pt.z = pt.z + 1
turtle.digDownAt(pt)
turtle.placeDown(CHEST)
setState('chest', Util.shallowCopy(pt))
turtle.drop(DIRT)
turtle.refuel(OAK_PLANK)
end end
setState('chest', pt)
turtle.dropDown(DIRT)
end end
return true return true
end end
local function dropOffItems() local function getSaplings()
local slots = turtle.getSummedInventory()
local saplings = { }
if state.chest then for _, slot in pairs(slots) do
if slot.name == SAPLING then
table.insert(saplings, slot)
end
end
if #saplings == 0 then
table.insert(saplings, { name = OAK_SAPLING, count = 0 })
end
return saplings
end
local function dropOffItems()
local slots = turtle.getSummedInventory() local slots = turtle.getSummedInventory()
if state.chest and if state.chest and
@@ -382,24 +192,20 @@ local function dropOffItems()
print('Storing logs') print('Storing logs')
turtle.pathfind(Point.above(state.chest)) turtle.pathfind(Point.above(state.chest))
turtle.dropDown(LOG)
turtle.dropDown(LOG2)
for _, sapling in pairs(ALL_SAPLINGS) do for k,v in pairs(turtle.getInventory()) do
if sapling.count > MAX_SAPLINGS then if v.count > 0 and not retain[v.name] and not retain[v.key] then
turtle.dropDown(sapling.key, sapling.count - MAX_SAPLINGS) turtle.select(k)
turtle.dropDown()
end end
end end
turtle.dropDown(APPLE)
end
end end
return true return true
end end
local function eatSaplings() local function eatSaplings()
Util.each(ALL_SAPLINGS, function(sapling) Util.each(getSaplings(), function(sapling)
if sapling.count > MAX_SAPLINGS then if sapling.count > MAX_SAPLINGS then
turtle.refuel(sapling.key, sapling.count - MAX_SAPLINGS) turtle.refuel(sapling.key, sapling.count - MAX_SAPLINGS)
end end
@@ -407,60 +213,11 @@ local function eatSaplings()
return true return true
end end
local function placeTorches()
if state.torches then
return
end
local slots = turtle.getSummedInventory()
if turtle.getFuelLevel() > 100 and
slots[CHARCOAL] and
slots[CHARCOAL].count >= MIN_CHARCOAL and
turtle.canCraft(TORCH, 4, slots) then
print('Placing torches')
if turtle.has(TORCH, 4) or craftItem(TORCH, 4) then
local pts = { }
for x = -4, 4, 8 do
for z = -4, 4, 8 do
table.insert(pts, { x = x, y = 0, z = z })
end
end
turtle.addWorldBlocks(pts)
Point.eachClosest(turtle.point, pts, function(pt)
turtle.placeDownAt(pt, TORCH)
end)
turtle.refuel(STICK)
turtle.refuel(OAK_PLANK)
setState('torches', pts)
end
end
return true
end
local function countSaplings()
local slots = turtle.getSummedInventory()
ALL_SAPLINGS = { }
for _, slot in pairs(slots) do
if slot.name == SAPLING then
table.insert(ALL_SAPLINGS, slot)
end
end
if #ALL_SAPLINGS == 0 then
table.insert(ALL_SAPLINGS, { name = OAK_SAPLING, count = 0 })
end
return true
end
local function randomSapling() local function randomSapling()
local sapling = ALL_SAPLINGS[math.random(1, #ALL_SAPLINGS)] local saplings = getSaplings()
local sapling = saplings[math.random(1, #saplings)]
if sapling.count > 0 then if sapling.count > 0 then
sapling.count = sapling.count - 1
return sapling.key return sapling.key
end end
end end
@@ -470,10 +227,10 @@ local function makeKey(b)
end end
local function findDroppedSaplings() local function findDroppedSaplings()
Equipper.equipLeft(SENSOR, 'plethora:sensor') local sensor = Equipper.equipLeft('plethora:sensor')
local raw = peripheral.call('left', 'sense') local raw = sensor.sense()
local sensed = Util.reduce(raw, function(acc, b) return Util.reduce(raw, function(acc, b)
Point.rotate(b, state.home.heading) Point.rotate(b, state.home.heading)
b.x = Util.round(b.x) + turtle.point.x b.x = Util.round(b.x) + turtle.point.x
b.y = math.ceil(b.y) + turtle.point.y b.y = math.ceil(b.y) + turtle.point.y
@@ -484,15 +241,13 @@ local function findDroppedSaplings()
end end
return acc return acc
end, { }) end, { })
return sensed
end end
local function scan(pt, filter, blocks) local function scan(pt, filter, blocks)
turtle.pathfind(pt) turtle.pathfind(pt)
Equipper.equipLeft(SCANNER, 'plethora:scanner') local scanner = Equipper.equipLeft('plethora:scanner')
local raw = peripheral.call('left', 'scan') local raw = scanner.scan()
return Util.reduce(raw, function(acc, b) return Util.reduce(raw, function(acc, b)
if b.y >= 0 then if b.y >= 0 then
@@ -509,82 +264,96 @@ local function scan(pt, filter, blocks)
end end
local function getPlantLocations(blocks) local function getPlantLocations(blocks)
countSaplings() for _,sapling in pairs(state.trees) do
Util.each(state.trees, function(sapling)
local key = makeKey(sapling) local key = makeKey(sapling)
local b = blocks[key] local b = blocks[key]
if b then if b then
if b.name == SAPLING then if b.name == SAPLING then
blocks[key] = nil blocks[key] = nil
else else
b.plant = randomSapling() b.plant = true
end
return
end end
elseif turtle.getFuelLevel() > 100 or sapling.x == 1 and sapling.z == 0 then
b = Util.shallowCopy(sapling) b = Util.shallowCopy(sapling)
b.plant = randomSapling() b.plant = true
if b.plant then
blocks[key] = b blocks[key] = b
end end
end) end
end
local function desperateRefuel()
local fuels = { CHARCOAL, LOG, LOG2 }
if turtle.getFuelLevel() < FUEL_DIRE then
while true do
for _, fuel in pairs(fuels) do
if turtle.has(fuel) then
turtle.refuel(fuel, 1)
print('fuel: ' .. turtle.getFuelLevel())
turtle.select(1)
break
end
end
if turtle.getFuelLevel() > 0 then
break
end
print('Out of fuel')
print('Add logs or charcoal to turtle')
os.pullEvent('turtle_inventory')
end
end
end end
local function fellTrees(blocks) local function fellTrees(blocks)
local function desperateRefuel(min) turtle.select(1)
if turtle.getFuelLevel() < min then
local logs = turtle.getItemCount(OAK_LOG)
if logs > 0 then
if craftItem(OAK_PLANK, math.min(8, logs * 4)) then
turtle.refuel(OAK_PLANK)
print('fuel: ' .. turtle.getFuelLevel())
end
end
end
end
turtle.setMoveCallback(function() desperateRefuel(FUEL_DIRE) end)
desperateRefuel(FUEL_DIRE)
if turtle.point.y == 0 then
if #state.trees == 1 and turtle.getFuelLevel() == 0 then
turtle.dig()
end
turtle.up()
end
for pt in Point.iterateClosest(turtle.point, blocks) do for pt in Point.iterateClosest(turtle.point, blocks) do
-- initial tree
if turtle.getFuelLevel() == 0 then
if not turtle.digAt(pt) then
break
end
desperateRefuel()
end
if pt.y == 0 then if pt.y == 0 then
if pt.sapling then if pt.sapling then
repeat until not turtle.suckDownAt(pt) repeat until not turtle.suckDownAt(pt)
else elseif pt.plant then
local s = randomSapling()
if pt.name and pt.name ~= SAPLING then
turtle.digDownAt(pt) turtle.digDownAt(pt)
if pt.plant then end
turtle.placeDown(pt.plant) if s then
turtle.placeDownAt(pt, s)
turtle.select(1) turtle.select(1)
end end
end end
else else
turtle.digAt(pt) turtle.digAt(pt)
end end
os.queueEvent('canvas', {
type = 'canvas_remove',
data = { pt },
})
end end
desperateRefuel(FUEL_BASE + 100) turtle.pathfind(HOME_PT)
turtle.clearMoveCallback()
return true return true
end end
local function fell() local function fell()
local function filter(b) local function filter(b)
return b.y >= 0 and (b.name == LOG or b.name == LOG2 or b.name == SAPLING) return b.name == LOG or b.name == LOG2 or b.name == SAPLING
end end
local fuel = turtle.getFuelLevel() local fuel = turtle.getFuelLevel()
local sensed = { } local sensed = { }
-- determine if we need saplings -- determine if we need saplings
if not Util.every(ALL_SAPLINGS, function(sapling) if not Util.every(getSaplings(), function(sapling)
return sapling.count >= MIN_SAPLINGS return sapling.count >= MIN_SAPLINGS
end) then end) then
sensed = findDroppedSaplings() sensed = findDroppedSaplings()
@@ -592,7 +361,6 @@ local function fell()
-- low scan -- low scan
local blocks = scan(HOME_PT, filter) local blocks = scan(HOME_PT, filter)
local pt = Util.shallowCopy(HOME_PT) local pt = Util.shallowCopy(HOME_PT)
while Util.any(blocks, function(b) return b.y > pt.y + 6 end) do while Util.any(blocks, function(b) return b.y > pt.y + 6 end) do
-- tree might be above low scan range, do a scan higher up -- tree might be above low scan range, do a scan higher up
@@ -607,6 +375,11 @@ local function fell()
getPlantLocations(blocks) getPlantLocations(blocks)
Equipper.equipLeft(PICKAXE) Equipper.equipLeft(PICKAXE)
os.queueEvent('canvas', {
type = 'canvas_update',
data = blocks,
})
if not Util.empty(blocks) then if not Util.empty(blocks) then
print('Chopping') print('Chopping')
@@ -618,49 +391,27 @@ local function fell()
return true return true
end end
local function moreTrees() local function setTrees()
if #state.trees > 1 then if not state.trees then
return
end
if not state.chest or turtle.getItemCount(OAK_SAPLING) < 2 then
return true
end
print('Adding more trees')
local singleTree = state.trees[1]
state.trees = { } state.trees = { }
for x = -2, 2, 1 do for x = -RADIUS_X, RADIUS_X, 1 do
for z = -2, 2, 1 do for z = -RADIUS_Z, RADIUS_Z, 1 do
if x ~= 0 or z ~= 0 then if z ~= 0 or x > 0 then
local tree = { x = x, y = 0, z = z } local tree = { x = x, y = 0, z = z }
table.insert(state.trees, tree) table.insert(state.trees, tree)
turtle.addWorldBlock(tree)
end end
end end
end end
turtle.digAt(singleTree)
setState('trees', state.trees) setState('trees', state.trees)
countSaplings()
Point.eachClosest(turtle.point, state.trees, function(pt)
local sapling = randomSapling()
if sapling then
turtle.placeDownAt(pt, sapling)
end end
end)
end end
local function findHome() local function findHome()
local pt = GPS.getPoint(2) or error('GPS not found') local pt = GPS.getPoint(2) or error('GPS not found')
Equipper.equipLeft(SCANNER, 'plethora:scanner') local scanner = Equipper.equipLeft('plethora:scanner')
local facing = peripheral.call('left', 'getBlockMeta', 0, 0, 0).state.facing local facing = scanner.getBlockMeta(0, 0, 0).state.facing
pt.heading = Point.facings[facing].heading pt.heading = Point.facings[facing].heading
Equipper.equipLeft(PICKAXE) Equipper.equipLeft(PICKAXE)
@@ -670,11 +421,14 @@ local function findHome()
end end
-- convert to relative coordinates -- convert to relative coordinates
turtle.setPoint({ turtle.set({
point = {
x = pt.x - state.home.x, x = pt.x - state.home.x,
y = pt.y - state.home.y, y = pt.y - state.home.y,
z = pt.z - state.home.z, z = pt.z - state.home.z,
heading = pt.heading, heading = pt.heading,
},
reference = state.home,
}) })
Point.rotate(turtle.point, state.home.heading) Point.rotate(turtle.point, state.home.heading)
@@ -689,12 +443,6 @@ local function findHome()
ey = 32, ey = 32,
ez = GRID.BR.z, ez = GRID.BR.z,
}) })
turtle.setPersistent(true)
turtle.addWorldBlocks(state.trees)
if state.torches and type(state.torches) == 'table' then
turtle.addWorldBlocks(state.trees)
end
end end
local function returnHome() local function returnHome()
@@ -716,19 +464,24 @@ local function updateClock()
return true return true
end end
local function setWorldBlocks()
turtle.setPersistent(true)
turtle.addWorldBlocks(state.trees)
return true
end
local function startupCheck() local function startupCheck()
Equipper.equipRight(MODEM, 'modem') Equipper.equipModem('right')
Equipper.equipLeft(PICKAXE) Equipper.equipLeft(PICKAXE)
local slots = turtle.getSummedInventory() local slots = turtle.getSummedInventory()
if not slots[CHEST] or not slots[CRAFTING_TABLE] or not slots[SCANNER] or not slots[SENSOR] then if not slots[SCANNER] or not slots[SENSOR] then
error([[ printError([[
Required: Required:
* chest
* crafting table
* block scanner * block scanner
* entity sensor]]) * entity sensor]])
error('Missing required item')
end end
if not fs.exists(STARTUP_FILE) then if not fs.exists(STARTUP_FILE) then
@@ -740,18 +493,15 @@ shell.openForegroundTab('superTreefarm.lua')]])
end end
local tasks = { local tasks = {
{ desc = 'Setting trees', fn = setTrees },
{ desc = 'Startup check', fn = startupCheck }, { desc = 'Startup check', fn = startupCheck },
{ desc = 'Finding home', fn = findHome }, { desc = 'Finding home', fn = findHome },
{ desc = 'Set world blocks', fn = setWorldBlocks },
{ desc = 'Creating furnace', fn = createFurnace }, { desc = 'Creating furnace', fn = createFurnace },
{ desc = 'Creating chest', fn = createChests },
{ desc = 'Counting saplings', fn = countSaplings },
{ desc = 'Adding trees', fn = moreTrees },
{ desc = 'Emptying furnace', fn = emptyFurnace },
{ desc = 'Chopping', fn = fell }, { desc = 'Chopping', fn = fell },
{ desc = 'Creating chest', fn = createChests },
{ desc = 'Snacking', fn = eatSaplings }, { desc = 'Snacking', fn = eatSaplings },
{ desc = 'Making charcoal', fn = makeSingleCharcoal },
{ desc = 'Making charcoal', fn = makeCharcoal }, { desc = 'Making charcoal', fn = makeCharcoal },
--{ desc = 'Placing torches', fn = placeTorches },
{ desc = 'Refueling', fn = refuel }, { desc = 'Refueling', fn = refuel },
{ desc = 'Dropping off items', fn = dropOffItems }, { desc = 'Dropping off items', fn = dropOffItems },
{ desc = 'Condensing', fn = turtle.condense }, { desc = 'Condensing', fn = turtle.condense },
@@ -759,15 +509,14 @@ local tasks = {
{ desc = 'Sleeping', fn = updateClock }, { desc = 'Sleeping', fn = updateClock },
} }
local s, m = turtle.run(function() turtle.reset()
turtle.reset() turtle.set({
require('farms.crafting')
--turtle.addFeatures('core.crafting')
turtle.set({
attackPolicy = 'attack', attackPolicy = 'attack',
digPolicy = 'dig', digPolicy = 'dig',
}) moveCallback = desperateRefuel,
})
local s, m = pcall(function()
while not turtle.isAborted() do while not turtle.isAborted() do
print('fuel: ' .. turtle.getFuelLevel()) print('fuel: ' .. turtle.getFuelLevel())
for _,task in ipairs(Util.shallowCopy(tasks)) do for _,task in ipairs(Util.shallowCopy(tasks)) do
@@ -781,6 +530,8 @@ local s, m = turtle.run(function()
end end
end) end)
if not s then turtle.reset()
error(m or 'Failed')
if not s and m then
error(m)
end end

View File

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

View File

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

View File

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

View File

@@ -14,6 +14,7 @@ local recipeTab = UI.Tab {
disableHeader = true, disableHeader = true,
columns = { columns = {
{ heading = 'Slot', key = 'slot', width = 2 }, { heading = 'Slot', key = 'slot', width = 2 },
{ heading = 'Count', key = 'count', width = 2 },
{ heading = 'Key', key = 'key' }, { heading = 'Key', key = 'key' },
}, },
sortColumn = 'slot', sortColumn = 'slot',
@@ -36,10 +37,13 @@ function recipeTab:setItem(item)
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
_syslog(k)
_syslog(v)
table.insert(t, { table.insert(t, {
slot = k, slot = k,
key = v, key = v.key,
count = v.count,
}) })
end end
local key = itemDB:splitKey(self.recipe.result) local key = itemDB:splitKey(self.recipe.result)

View File

@@ -111,7 +111,14 @@ function pages.confirmation:validate()
} }
for k,v in pairs(inventory) do for k,v in pairs(inventory) do
if v.count == 1 then
recipe.ingredients[k] = itemDB:makeKey(v) 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)

View File

@@ -423,7 +423,7 @@ Unlocked Slots : %d of %d (%d%%)
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)

View File

@@ -317,7 +317,7 @@ local function collectDrops(suckAction)
end end
local function scan() local function scan()
local scanner = Equipper.equipLeft('plethora:module:2', 'plethora:scanner') local scanner = Equipper.equipLeft('plethora:scanner')
local blocks = scanner.scan() local blocks = scanner.scan()
Equipper.equipLeft('minecraft:diamond_pickaxe') Equipper.equipLeft('minecraft:diamond_pickaxe')
local throttle = Util.throttle() local throttle = Util.throttle()
@@ -491,7 +491,7 @@ end
-- in plethora code, we can override initialize with a scanner version -- in plethora code, we can override initialize with a scanner version
turtle.initialize = function() turtle.initialize = function()
Equipper.equipRight('computercraft:advanced_modem', 'modem') Equipper.equipModem('right')
Equipper.equipLeft('minecraft:diamond_pickaxe') Equipper.equipLeft('minecraft:diamond_pickaxe')
local function verify(item) local function verify(item)
@@ -507,7 +507,7 @@ turtle.initialize = function()
--os.sleep(5) --os.sleep(5)
local pt = GPS.getPoint(2) or error('GPS not found') local pt = GPS.getPoint(2) or error('GPS not found')
local scanner = Equipper.equipLeft('plethora:module:2', 'plethora:scanner') local scanner = Equipper.equipLeft('plethora:scanner')
local facing = scanner.getBlockMeta(0, 0, 0).state.facing local facing = scanner.getBlockMeta(0, 0, 0).state.facing
pt.heading = Point.facings[facing].heading pt.heading = Point.facings[facing].heading
turtle.setPoint(pt, true) turtle.setPoint(pt, true)

View File

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

View File

@@ -5,6 +5,7 @@ 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() }
if pt[1] then
return { return {
x = pt[1], x = pt[1],
y = pt[2], y = pt[2],
z = pt[3], 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,12 +78,15 @@ 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)
if meta.name == b.name and meta.metadata == b.metadata then
entry = itemDB:add({ entry = itemDB:add({
name = meta.name, name = meta.name,
displayName = meta.displayName, displayName = meta.displayName,
damage = meta.metadata, damage = meta.metadata,
}) })
end end
end
if entry then
b.key = entry.displayName b.key = entry.displayName
if acc[b.key] then if acc[b.key] then
acc[b.key].count = acc[b.key].count + 1 acc[b.key].count = acc[b.key].count + 1
@@ -91,6 +97,7 @@ function page:scan()
entry.key = b.key entry.key = b.key
acc[b.key] = entry acc[b.key] = entry
end end
end
throttle() throttle()
return acc return acc
end, end,
@@ -121,6 +128,7 @@ function page.detail:show(blocks, entry)
local scanned = scanner.scan() local scanned = scanner.scan()
local pos = getPoint() local pos = getPoint()
if pos and offset then
blocks = Util.reduce(scanned, function(acc, b) blocks = Util.reduce(scanned, function(acc, b)
if b.name == t.name and b.metadata == t.damage then if b.name == t.name and b.metadata == t.damage then
-- track block's world position -- track block's world position
@@ -161,6 +169,7 @@ function page.detail:show(blocks, entry)
end end
end end
end end
end
end) end)
end end
return UI.SlideOut.show(self) return UI.SlideOut.show(self)
@@ -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

View File

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

189
neural/canvasServer.lua Normal file
View File

@@ -0,0 +1,189 @@
local neural = require('neural.interface')
local device = _G.device
local gps = _G.gps
local os = _G.os
local parallel = _G.parallel
neural.assertModules({
'plethora:glasses',
'plethora:introspection',
'plethora:sensor',
})
local function getPoint()
local pt = { gps.locate() }
if pt[1] then
return {
x = pt[1],
y = pt[2],
z = pt[3],
}
end
end
local projecting = { }
local offset = getPoint() or error('GPS not found')
local canvas = neural.canvas3d().create({
-(offset.x % 1),
-(offset.y % 1),
-(offset.z % 1)
})
local function update(scanned)
for _, b in pairs(scanned) do
if not projecting[b.id] then
local box
local x = b.x - math.floor(offset.x)
local y = b.y - math.floor(offset.y)
local z = b.z - math.floor(offset.z)
-- items are centered at the mid-point of the cube
-- boxes are aligned to the top corner - sigh
if b.path then
box = canvas.addBox(x + .4, y + .4, z + .4, .2, .2, .2, 0xa0902080)
elseif b.name then
pcall(function()
box = canvas.addItem({ x + .5, y + .5, z + .5 }, b.name, b.damage or b.metadata, .5)
end)
end
if not box then
box = canvas.addBox(x, y, z, 1, 1, 1, 0x8080ff30)
end
if box then
box.setDepthTested(false)
end
projecting[b.id] = box
end
end
for id, box in pairs(projecting) do
if not scanned[id] then
box.remove()
projecting[id] = nil
end
end
end
local scanned = { }
local dirty
local function processMessage(msg)
if msg.type == 'canvas_clear' then
scanned = { }
projecting = { }
canvas.clear()
elseif msg.type == 'canvas_remove' then
for _, v in pairs(msg.data) do
v.id = table.concat({ v.x, v.y, v.z }, ':')
scanned[v.id] = nil
end
dirty = true
elseif msg.type == 'canvas_update' then
scanned = { }
for _, v in pairs(msg.data) do
v.id = table.concat({ v.x, v.y, v.z }, ':')
scanned[v.id] = v
end
dirty = true
elseif msg.type == 'canvas_barrier' then
for _, v in pairs(msg.data) do
v.id = table.concat({ v.x, v.y, v.z }, ':')
if projecting[v.id] then
projecting[v.id].remove()
projecting[v.id] = nil
end
v.name = 'minecraft:barrier'
v.damage = 0
scanned[v.id] = v
end
dirty = true
elseif msg.type == 'canvas_path' then
for k, v in pairs(scanned or { }) do
if v.path then
scanned[k] = nil
end
end
for _, v in pairs(msg.data) do
v.path = true
v.id = table.concat({ v.x, v.y, v.z }, ':')
scanned[v.id] = v
end
dirty = true
end
end
local function recenter()
while true do
os.sleep(3)
local pos = getPoint()
if pos then
if math.abs(pos.x - offset.x) +
math.abs(pos.y - offset.y) +
math.abs(pos.z - offset.z) > 64 then
for _, box in pairs(projecting) do
box.remove()
end
projecting = { }
offset = pos
canvas.recenter({
-(offset.x % 1),
-(offset.y % 1),
-(offset.z % 1)
})
update(scanned)
end
end
end
end
local function queueListener()
while true do
local _, msg = os.pullEvent('canvas_message')
processMessage(msg)
end
end
local function modemListener()
device.wireless_modem.open(3773)
while true do
local _, _, dport, _, msg = os.pullEvent('modem_message')
if dport == 3773 and type(msg) == 'table' then
processMessage(msg)
end
end
end
local s, m = pcall(function()
parallel.waitForAny(
queueListener,
modemListener,
recenter,
function()
while true do
if dirty then
dirty = false
update(scanned)
end
os.sleep(1)
end
end
)
end)
canvas.clear()
device.wireless_modem.close(3773)
if not s and m then
_G.printError(m)
end

View File

@@ -1,7 +1,7 @@
local GPS = require('gps') local GPS = require('gps')
local Util = require('util') local Util = require('util')
local Point = require('point') local Point = require('point')
local Proxy = require('proxy') local Proxy = require('core.proxy')
local os = _G.os local os = _G.os

View File

@@ -29,6 +29,9 @@ elseif not modules.scan then
showRequirements('Scanner module') showRequirements('Scanner module')
end end
-- size of displayed block
local BLOCK_SIZE = .5
local function getPoint() local function getPoint()
local pt = { gps.locate() } local pt = { gps.locate() }
if pt[1] then if pt[1] then
@@ -54,7 +57,10 @@ local targets = {
} }
local projecting = { } local projecting = { }
local offset = getPoint() or showRequirements('GPS') local offset = getPoint() or showRequirements('GPS')
local canvas = modules.canvas3d().create() local canvas = modules.canvas3d().create({
-(offset.x % 1) + .5,
-(offset.y % 1) + .5,
-(offset.z % 1) + .5 })
local function update() local function update()
while true do while true do
@@ -71,7 +77,10 @@ local function update()
end end
projecting = { } projecting = { }
offset = pos offset = pos
canvas.recenter() canvas.recenter({
-(offset.x % 1) + .5,
-(offset.y % 1) + .5,
-(offset.z % 1) + .5 })
end end
local blocks = { } local blocks = { }
@@ -90,20 +99,18 @@ local function update()
if not projecting[b.id] then if not projecting[b.id] then
projecting[b.id] = b projecting[b.id] = b
local target = targets[b.name] local target = targets[b.name]
local x = b.x - math.floor(offset.x) + math.floor(pos.x)
local y = b.y - math.floor(offset.y) + math.floor(pos.y)
local z = b.z - math.floor(offset.z) + math.floor(pos.z)
--[[ --[[
b.box = canvas.addFrame({ b.box = canvas.addFrame({ x, y, z })
pos.x - offset.x + b.x + -(pos.x % 1),
pos.y - offset.y + b.y + -(pos.y % 1),
pos.z - offset.z + b.z + -(pos.z % 1),
})
b.box.setDepthTested(false) b.box.setDepthTested(false)
b.box.addItem({ .25, .25 }, target[1], target[2], 2) b.box.addItem({ .25, .25 }, target[1], target[2], 2)
--]] --]]
b.box = canvas.addItem({
pos.x - offset.x + b.x + -(pos.x % 1) + .5, b.box = canvas.addItem({ x, y, z }, target[1], target[2], BLOCK_SIZE)
pos.y - offset.y + b.y + -(pos.y % 1) + .5,
pos.z - offset.z + b.z + -(pos.z % 1) + .5,
}, target[1], target[2], .5)
b.box.setDepthTested(false) b.box.setDepthTested(false)
end end
end end

View File

@@ -1,10 +1,22 @@
local Config = require('config')
local peripheral = _G.peripheral local peripheral = _G.peripheral
local turtle = _G.turtle local turtle = _G.turtle
local Equipper = { } local Equipper = { }
local equipmentList = Config.load('equipment', {
[ 'plethora:scanner' ] = 'plethora:module:2',
[ 'plethora:sensor' ] = 'plethora:module:3',
[ 'plethora:laser' ] = 'plethora:module:1',
[ 'plethora:introspection' ] = 'plethora:module:0',
[ 'plethora:kinetic' ] = 'plethora:module:4',
[ 'advanced_modem' ] = 'computercraft:advanced_modem:0',
[ 'standard_modem' ] = 'computercraft:peripheral:1',
})
local SCANNER_EQUIPPED = 'plethora:scanner' local SCANNER_EQUIPPED = 'plethora:scanner'
local SCANNER_INV = 'plethora:module:2' local SCANNER_INV = equipmentList[SCANNER_EQUIPPED] or 'unknown'
local reversed = { local reversed = {
left = 'right', left = 'right',
@@ -68,7 +80,9 @@ function Equipper.unequip(side)
error('No slots available') error('No slots available')
end end
turtle.equip(side) turtle.equip(side)
if Equipper.equipped then
Equipper.equipped[side] = nil Equipper.equipped[side] = nil
end
return turtle.getItemDetail(slot) return turtle.getItemDetail(slot)
end end
@@ -81,37 +95,53 @@ function Equipper.isEquipped(name)
Equipper.equipped.right == name and 'right' Equipper.equipped.right == name and 'right'
end end
function Equipper.equip(side, invName, equippedName) -- so convoluted - needs it's own function
function Equipper.equipModem(side)
if peripheral.getType(side) ~= 'modem' then
if peripheral.getType(reversed[side]) then
Equipper.unequip(reversed[side])
end
if turtle.has(equipmentList['advanced_modem']) then
return Equipper.equip(side, equipmentList['advanced_modem'])
end
if turtle.has(equipmentList['standard_modem']) then
return Equipper.equip(side, equipmentList['standard_modem'])
end
error('Missing modem')
end
end
function Equipper.equip(side, item)
if not Equipper.equipped then if not Equipper.equipped then
getEquipped() getEquipped()
end end
-- is it already equipped ? -- is it already equipped ?
if matches(Equipper.equipped[side], equippedName or invName) then if matches(Equipper.equipped[side], item) then
return peripheral.getType(side) and peripheral.wrap(side) return peripheral.getType(side) and peripheral.wrap(side)
end end
-- is it equipped on other side ? -- is it equipped on other side ?
if matches(Equipper.equipped[reversed[side]], equippedName or invName) then if matches(Equipper.equipped[reversed[side]], item) then
Equipper.unequip(reversed[side]) Equipper.unequip(reversed[side])
end end
local s, m = turtle.equip(side, invName) local s, m = turtle.equip(side, equipmentList[item] or item)
if not s then if not s then
error(string.format('Unable to equip %s\n%s', (equippedName or invName), m)) error(string.format('Unable to equip %s\n%s', item, m))
end end
Equipper.equipped[side] = peripheral.getType(side) or invName Equipper.equipped[side] = peripheral.getType(side) or item
return peripheral.getType(side) and peripheral.wrap(side) return peripheral.getType(side) and peripheral.wrap(side)
end end
function Equipper.equipLeft(invName, equippedName) function Equipper.equipLeft(item)
return Equipper.equip('left', invName, equippedName) return Equipper.equip('left', item)
end end
function Equipper.equipRight(invName, equippedName) function Equipper.equipRight(item)
return Equipper.equip('right', invName, equippedName) return Equipper.equip('right', item)
end end
return Equipper return Equipper

View File

@@ -15,7 +15,7 @@ if not turtle.has('minecraft:bucket') then
end end
local swapSide = peripheral.getType('right') == 'modem' and 'left' or 'right' local swapSide = peripheral.getType('right') == 'modem' and 'left' or 'right'
local scanner = Equipper.equip(swapSide, 'plethora:module:2', 'plethora:scanner') local scanner = Equipper.equip(swapSide, 'plethora:scanner')
if not turtle.select('minecraft:bucket') then if not turtle.select('minecraft:bucket') then
error('bucket required') error('bucket required')