Advertisement
joebodo

builder2.lua

May 4th, 2014
339
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 60.13 KB | None | 0 0
  1. vos.loadApi('ui.api')
  2. vos.loadApi('tl2.api')
  3. vos.loadApi('schematic.api')
  4. vos.loadApi('profile.api')
  5. vos.loadApi('tableDB.api')
  6.  
  7. Logger.disable()
  8. if Peripheral.isPresent('wireless_modem') then
  9.   Logger.filter('rednet_message', 'event', 'ui')
  10.   Message.enableWirelessLogging()
  11. end
  12.  
  13. schematic = Schematic()
  14.  
  15. Builder = {
  16.   version = '0.05',
  17.   ccVersion = nil,
  18.   slots = { },
  19.   index = 1,
  20.   mode = 'build',
  21.   fuelItem = { id = 263, dmg = 0 },
  22.   resourceSlots = 15,
  23. }
  24.  
  25. --[[-- blockDB --]]--
  26. blockDB = TableDB({
  27.   fileName = 'block.db',
  28.   tabledef = {
  29.     autokeys = false,
  30.     columns = {
  31.       { name = 'key',     type = 'key',    length = 8 },
  32.       { name = 'id',      type = 'number', length = 5 },
  33.       { name = 'dmg',     type = 'number', length = 2 },
  34.       { name = 'name',    type = 'string', length = 35 },
  35.       { name = 'refname', type = 'string', length = 35 },
  36.     }
  37.   }
  38. })
  39.  
  40. function blockDB:load(sbDB, btDB)
  41.   if fs.exists(self.fileName) then
  42.     TableDB.load(self)
  43.   else
  44.     self:seedDB()
  45.   end
  46. end
  47.  
  48. function blockDB:seedDB()
  49.  
  50.   self:add(9, 0, 'Stationary Water')
  51.   self:add(18, 0, 'Oak Leaves')
  52.   self:add(18, 1, 'Spruce Leaves')
  53.   self:add(18, 2, 'Birch Leaves')
  54.   self:add(18, 3, 'Jungle Leaves')
  55.   self:add(26, 0, 'Bed Block')
  56.   self:add(27, 0, 'Powered Rail')
  57.   self:add(28, 0, 'Detector Rail')
  58.   self:add(29, 0, 'Sticky Piston')
  59.   self:add(30, 0, 'Web')
  60.   self:add(31, 0, 'Dead Shrub')
  61.   self:add(31, 1, 'Grass')
  62.   self:add(31, 2, 'Fern')
  63.   self:add(32, 0, 'Dead Shrub')
  64.   self:add(33, 0, 'Piston')
  65.   self:add(34, 0, 'Piston Head')
  66.   self:add(35, 0, 'White Wool')
  67.   self:add(35, 1, 'Orange Wool')
  68.   self:add(35, 2, 'Magenta Wool')
  69.   self:add(35, 3, 'Light Blue Wool')
  70.   self:add(35, 4, 'Yellow Wool')
  71.   self:add(35, 5, 'Lime Wool')
  72.   self:add(35, 6, 'Pink Wool')
  73.   self:add(35, 7, 'Gray Wool')
  74.   self:add(35, 8, 'Light Gray Wool')
  75.   self:add(35, 9, 'Cyan Wool')
  76.   self:add(35, 10, 'Purple Wool')
  77.   self:add(35, 11, 'Blue Wool')
  78.   self:add(35, 12, 'Brown Wool')
  79.   self:add(35, 13, 'Green Wool')
  80.   self:add(35, 14, 'Red Wool')
  81.   self:add(35, 15, 'Black Wool')
  82.   self:add(37, 0, 'Dandelion')
  83.   self:add(38, 0, 'Poppy')
  84.   self:add(38, 1, 'Blue Orchid')
  85.   self:add(38, 2, 'Allium')
  86.   self:add(38, 3, 'Azure Bluet')
  87.   self:add(38, 4, 'Red Tulip')
  88.   self:add(38, 5, 'Orange Tulip')
  89.   self:add(38, 6, 'White Tulip')
  90.   self:add(38, 7, 'Pink Tulip')
  91.   self:add(38, 8, 'Oxeye Daisy')
  92.   self:add(43, 0, 'Double Stone Slab')
  93.   self:add(43, 1, 'Double Sandstone Slab')
  94.   self:add(43, 2, 'Double Wooden Slab')
  95.   self:add(43, 3, 'Double Cobblestone Slab')
  96.   self:add(43, 4, 'Double Brick Slab')
  97.   self:add(43, 5, 'Double Stone Brick Slab')
  98.   self:add(43, 6, 'Double Nether Brick Slab')
  99.   self:add(43, 7, 'Double Quartz Slab')
  100.   self:add(51, 0, 'Fire')
  101.   self:add(55, 0, 'Redstone Wire')
  102.   self:add(62, 0, 'Burning Furnace')
  103.   self:add(63, 0, 'Sign Post')
  104.   self:add(64, 0, 'Wooden Door Block')
  105.   self:add(68, 0, 'Wall Sign')
  106.   self:add(71, 0, 'Iron Door Block')
  107.   self:add(75, 0, 'Redstone Torch')
  108.   self:add(76, 0, 'Redstone Torch (on)')
  109.   self:add(83, 0, 'Sugar Cane')
  110.   self:add(95, 0, 'White Stained Glass')
  111.   self:add(95, 1, 'Orange Stained Glass')
  112.   self:add(95, 2, 'Magenta Stained Glass')
  113.   self:add(95, 3, 'Light Blue Stained Glass')
  114.   self:add(95, 4, 'Yellow Stained Glass')
  115.   self:add(95, 5, 'Lime Stained Glass')
  116.   self:add(95, 6, 'Pink Stained Glass')
  117.   self:add(95, 7, 'Gray Stained Glass')
  118.   self:add(95, 8, 'Light Gray Stained Glass')
  119.   self:add(95, 9, 'Cyan Stained Glass')
  120.   self:add(95, 10, 'Purple Stained Glass')
  121.   self:add(95, 11, 'Blue Stained Glass')
  122.   self:add(95, 12, 'Brown Stained Glass')
  123.   self:add(95, 13, 'Green Stained Glass')
  124.   self:add(95, 14, 'Red Stained Glass')
  125.   self:add(95, 15, 'Black Stained Glass')
  126.   self:add(96, 0, 'Trapdoor')
  127.   self:add(117, 0, 'Brewing Stand')
  128.   self:add(118, 0, 'Cauldron')
  129.   self:add(124, 0, 'Redstone Lamp (active)')
  130.   self:add(125, 0, 'Double Oak Wood Slab')
  131.   self:add(125, 1, 'Double Spruce Wood Slab')
  132.   self:add(125, 2, 'Double Birch Wood Slab')
  133.   self:add(125, 3, 'Double Jungle Wood Slab')
  134.   self:add(125, 4, 'Double Acacia Wood Slab')
  135.   self:add(125, 5, 'Double Dark Oak Wood Slab')
  136.   self:add(126, 4, 'Acacia Wood Slab')
  137.   self:add(126, 5, 'Dark Oak Wood Slab')
  138.   self:add(130, 0, 'Ender Chest')
  139.   self:add(140, 0, 'Flower Pot')
  140.   self:add(144, 0, 'Mob Head')
  141.   self:add(160, 0, 'White Stained Glass Pane')
  142.   self:add(160, 1, 'Orange Stained Glass Pane')
  143.   self:add(160, 2, 'Magenta Stained Glass Pane')
  144.   self:add(160, 3, 'Light Blue Stained Glass Pane')
  145.   self:add(160, 4, 'Yellow Stained Glass Pane')
  146.   self:add(160, 5, 'Lime Stained Glass Pane')
  147.   self:add(160, 6, 'Pink Stained Glass Pane')
  148.   self:add(160, 7, 'Gray Stained Glass Pane')
  149.   self:add(160, 8, 'Light Gray Stained Glass Pane')
  150.   self:add(160, 9, 'Cyan Stained Glass Pane')
  151.   self:add(160, 10, 'Purple Stained Glass Pane')
  152.   self:add(160, 11, 'Blue Stained Glass Pane')
  153.   self:add(160, 12, 'Brown Stained Glass Pane')
  154.   self:add(160, 13, 'Green Stained Glass Pane')
  155.   self:add(160, 14, 'Red Stained Glass Pane')
  156.   self:add(160, 15, 'Black Stained Glass Pane')
  157.   self:add(161, 0, 'Acacia Leaves')
  158.   self:add(161, 1, 'Dark Oak Leaves')
  159.   self:add(162, 0, 'Acacia Wood')
  160.   self:add(162, 1, 'Dark Oak Wood')
  161.   self:add(163, 0, 'Acacia Wood Stairs')
  162.   self:add(164, 0, 'Dark Oak Wood Stairs')
  163.   self:add(165, 0, 'Slime Block')
  164.   self:add(167, 0, 'Iron Trapdoor')
  165.   self:add(170, 0, 'Hay Bale')
  166.   self:add(171, 0, 'White Carpet')
  167.   self:add(171, 1, 'Orange Carpet')
  168.   self:add(171, 2, 'Magenta Carpet')
  169.   self:add(171, 3, 'Light Blue Carpet')
  170.   self:add(171, 4, 'Yellow Carpet')
  171.   self:add(171, 5, 'Lime Carpet')
  172.   self:add(171, 6, 'Pink Carpet')
  173.   self:add(171, 7, 'Gray Carpet')
  174.   self:add(171, 8, 'Light Gray Carpet')
  175.   self:add(171, 9, 'Cyan Carpet')
  176.   self:add(171, 10, 'Purple Carpet')
  177.   self:add(171, 11, 'Blue Carpet')
  178.   self:add(171, 12, 'Brown Carpet')
  179.   self:add(171, 13, 'Green Carpet')
  180.   self:add(171, 14, 'Red Carpet')
  181.   self:add(171, 15, 'Black Carpet')
  182.   self:add(175, 0, 'Sunflower')
  183.   self:add(175, 1, 'Lilac')
  184.   self:add(175, 2, 'Double Tallgrass')
  185.   self:add(175, 3, 'Large Fern')
  186.   self:add(175, 4, 'Rose Bush')
  187.   self:add(175, 5, 'Peony')
  188.  
  189.   self.dirty = true
  190.   self:flush()
  191. end
  192.  
  193. function blockDB:lookup(id, dmg)
  194.   if not id or not dmg then error('blockDB:lookup: nil passed', 2) end
  195.   local key = id .. ':' .. dmg
  196.  
  197.   return self.data[key]
  198. end
  199.  
  200. function blockDB:lookupName(id, dmg)
  201.   if not id or not dmg then error('blockDB:lookupName: nil passed', 2) end
  202.   local key = id .. ':' .. dmg
  203.  
  204.   local block = self.data[key]
  205.   if block then
  206.     return block.name
  207.   end
  208. end
  209.  
  210. function blockDB:add(id, dmg, name)
  211.   local key = id .. ':' .. dmg
  212.  
  213.   TableDB.add(self, key, {
  214.     id = id,
  215.     dmg = dmg,
  216.     key = key,
  217.     name = name
  218.   })
  219. end
  220.  
  221. --[[-- SubDBClass --]]--
  222. SubDBClass = class.class(TableDB)
  223.  
  224. function SubDBClass:lookup(id, dmg)
  225.   if not id or not dmg then error('SubDBClass:lookup: nil passed', 2) end
  226.   local key = id .. ':' .. dmg
  227.   return self.data[key]
  228. end
  229.  
  230. function SubDBClass:remove(id, dmg)
  231.   local key = id .. ':' .. dmg
  232.   self.data[key] = nil
  233.   self.dirty = true
  234. end
  235.  
  236. function SubDBClass:lookupReference(refid, refdmg)
  237.   for k,v in pairs(self.data) do
  238.     if v.refid == refid and v.refdmg == refdmg then
  239.       return v
  240.     end
  241.   end
  242. end
  243.  
  244. function SubDBClass:lookupBlocksForSub(sid, sdmg)
  245.   local t = { }
  246.   for k,v in pairs(self.data) do
  247.     if v.sid == sid and v.sdmg == sdmg then
  248.       t[k] = v
  249.     end
  250.   end
  251.   return t
  252. end
  253.  
  254. function SubDBClass:lookupSubsForBlock(refid, refdmg)
  255.   local t = { }
  256.   for k,v in pairs(self.data) do
  257.     if v.refid == refid and v.refdmg == refdmg then
  258.       t[k] = v
  259.     end
  260.   end
  261.   return t
  262. end
  263.  
  264. function SubDBClass:getRealBlock(id, dmg)
  265.  
  266.   local sub = self:lookup(id, dmg)
  267.   if sub then
  268.     return { id = sub.sid, dmg = sub.sdmg, direction = sub.direction }
  269.   end
  270.  
  271.   local b = blockDB:lookup(id, dmg)
  272.   if b then
  273.     return b
  274.   end
  275.  
  276.   return { id = id, dmg = dmg }
  277. end
  278.  
  279. function SubDBClass:addSubsForBlockType(id, dmg, bt)
  280.   for _,sub in pairs(bt) do
  281.     local odmg = sub.odmg
  282.     if type(sub.odmg) == 'string' then
  283.       odmg = dmg + tonumber(string.match(odmg, '+(%d+)'))
  284.     end
  285.     self:add(
  286.       id,
  287.       odmg,
  288.       sub.sid or id,
  289.       sub.sdmg or dmg,
  290.       id,
  291.       dmg,
  292.       sub.dir)
  293.   end
  294. end
  295.  
  296. function SubDBClass:add(id, dmg, sid, sdmg, refid, refdmg, direction)
  297.   if not id or not dmg then error('SubDBClass:add: nil passed', 2) end
  298.  
  299.   local key = id .. ':' .. dmg
  300.  
  301.   if direction and #direction == 0 then
  302.     direction = nil
  303.   end
  304.  
  305.   self.data[key] = {
  306.     id = id,
  307.     dmg = dmg,
  308.     key = key,
  309.     sid = sid,
  310.     sdmg = sdmg,
  311.     skey = sid .. ':' .. sdmg,
  312.     refid = refid,
  313.     refdmg = refdmg,
  314.     direction = direction
  315.   }
  316.   self.dirty = true
  317. end
  318.  
  319. --[[-- SubDB --]]--
  320. subDB = SubDBClass({
  321.   fileName = 'sub.db',
  322.   tabledef = {
  323.     autokeys = false,
  324.     columns = {
  325.       { name = 'Key',       type = 'key',    length = 8 },
  326.       { name = 'id',        type = 'number', length = 5 },
  327.       { name = 'dmg',       type = 'number', length = 2 },
  328.       { name = 'key',       type = 'string', length = 8 },
  329.       { name = 'sid',       type = 'number', length = 5 },
  330.       { name = 'sdmg',      type = 'number', length = 2 },
  331.       { name = 'skey',      type = 'string', length = 8 },
  332.       { name = 'refid',     type = 'number', length = 5 },
  333.       { name = 'refdmg',    type = 'number', length = 2 },
  334.       { name = 'direction', type = 'string', length = 20 },
  335.     }
  336.   }
  337. })
  338.  
  339. function subDB:load(sbDB, btDB)
  340.   if fs.exists(self.fileName) then
  341.     SubDBClass.load(self)
  342.   else
  343.     self:seedDB(sbDB, btDB)
  344.   end
  345. end
  346.  
  347. function subDB:seedDB(sbDB, btDB)
  348.   for k,blockType in pairs(sbDB.data) do
  349.     local bt = btDB.data[blockType]
  350.     if not bt then
  351.       error('missing block type: ' .. blockType)
  352.     end
  353.     local id, dmg = string.match(k, '(%d+):*(%d+)')
  354.     self:addSubsForBlockType(tonumber(id), tonumber(dmg), bt)
  355.   end
  356.   self.dirty = true
  357.   self:flush()
  358. end
  359.  
  360. --[[-- StandardBlockDB --]]--
  361. standardBlockDB = TableDB({
  362.   fileName = 'standard.db',
  363.   tabledef = {
  364.     autokeys = false,
  365.     type = 'simple',
  366.     columns = {
  367.       { label = 'Key', type = 'key', length = 8 },
  368.       { label = 'Block Type', type = 'string', length = 20 }
  369.     }
  370.   }
  371. })
  372.  
  373. function standardBlockDB:load()
  374.   if fs.exists(self.fileName) then
  375.     TableDB.load(self)
  376.   else
  377.     self:seedDB()
  378.   end
  379. end
  380.  
  381. function standardBlockDB:seedDB()
  382.   self.data = {
  383.     [ '6:0'  ] = 'sapling',
  384.     [ '6:1'  ] = 'sapling',
  385.     [ '6:2'  ] = 'sapling',
  386.     [ '6:3'  ] = 'sapling',
  387.     [ '6:4'  ] = 'sapling',
  388.     [ '6:5'  ] = 'sapling',
  389.     [ '8:0'  ] = 'truncate',
  390.     [ '9:0'  ] = 'truncate',
  391.     [ '17:0' ] = 'wood',
  392.     [ '17:1' ] = 'wood',
  393.     [ '17:2' ] = 'wood',
  394.     [ '17:3' ] = 'wood',
  395.     [ '18:0' ] = 'leaves',
  396.     [ '18:1' ] = 'leaves',
  397.     [ '18:2' ] = 'leaves',
  398.     [ '18:3' ] = 'leaves',
  399.     [ '23:0' ] = 'dispenser',
  400.     [ '26:0' ] = 'bed',
  401.     [ '29:0' ] = 'piston',
  402.     [ '33:0' ] = 'piston',
  403.     [ '34:0' ] = 'air',
  404.     [ '36:0' ] = 'air',
  405.     [ '44:0' ] = 'slab',
  406.     [ '44:1' ] = 'slab',
  407.     [ '44:2' ] = 'slab',
  408.     [ '44:3' ] = 'slab',
  409.     [ '44:4' ] = 'slab',
  410.     [ '44:5' ] = 'slab',
  411.     [ '44:6' ] = 'slab',
  412.     [ '44:7' ] = 'slab',
  413.     [ '50:0' ] = 'torch',
  414.     [ '51:0' ] = 'flatten',
  415.     [ '53:0' ] = 'stairs',
  416.     [ '54:0' ] = 'chest-furnace',
  417.     [ '55:0' ] = 'flatten',
  418.     [ '61:0' ] = 'chest-furnace',
  419.     [ '62:0' ] = 'chest-furnace',
  420.     [ '63:0' ] = 'signpost',
  421.     [ '64:0' ] = 'door',
  422.     [ '65:0' ] = 'wallsign-ladder',
  423.     [ '66:0' ] = 'rail',
  424.     [ '67:0' ] = 'stairs',
  425.     [ '68:0' ] = 'wallsign-ladder',
  426.     [ '69:0' ] = 'lever',
  427.     [ '71:0' ] = 'door',
  428.     [ '75:0' ] = 'torch',
  429.     [ '76:0' ] = 'torch',
  430.     [ '77:0' ] = 'button',
  431.     [ '78:0' ] = 'flatten',
  432.     [ '81:0' ] = 'flatten',
  433.     [ '83:0' ] = 'flatten',
  434.     [ '93:0' ] = 'repeater',
  435.     [ '94:0' ] = 'repeater',
  436.     [ '96:0' ] = 'trapdoor',
  437.     [ '99:0' ] = 'flatten',
  438.     [ '100:0' ] = 'flatten',
  439.     [ '106:0' ] = 'vine',
  440.     [ '107:0' ] = 'gate',
  441.     [ '108:0' ] = 'stairs',
  442.     [ '109:0' ] = 'stairs',
  443.     [ '114:0' ] = 'stairs',
  444.     [ '118:0' ] = 'cauldron',
  445.     [ '126:0' ] = 'slab',
  446.     [ '126:1' ] = 'slab',
  447.     [ '126:2' ] = 'slab',
  448.     [ '126:3' ] = 'slab',
  449.     [ '126:4' ] = 'slab',
  450.     [ '126:5' ] = 'slab',
  451.     [ '128:0' ] = 'stairs',
  452.     [ '130:0' ] = 'chest-furnace',
  453.     [ '131:0' ] = 'tripwire',
  454.     [ '134:0' ] = 'stairs',
  455.     [ '135:0' ] = 'stairs',
  456.     [ '136:0' ] = 'stairs',
  457.     [ '140:0' ] = 'flatten',
  458.     [ '141:0' ] = 'flatten',
  459.     [ '142:0' ] = 'flatten',
  460.     [ '143:0' ] = 'button',
  461.     [ '144:0' ] = 'mobhead',
  462.     [ '145:0' ] = 'anvil',
  463.     [ '146:0' ] = 'chest-furnace',
  464.     [ '151:0' ] = 'flatten',
  465.     [ '154:0' ] = 'hopper',
  466.     [ '155:2' ] = 'quartz-pillar',
  467.     [ '156:0' ] = 'stairs',
  468.     [ '158:0' ] = 'hopper',
  469.     [ '161:0' ] = 'leaves',
  470.     [ '161:1' ] = 'leaves',
  471.     [ '162:0' ] = 'wood',
  472.     [ '162:1' ] = 'wood',
  473.     [ '163:0' ] = 'stairs',
  474.     [ '164:0' ] = 'stairs',
  475.     [ '167:0' ] = 'trapdoor',
  476.     [ '170:0'  ] = 'flatten',
  477.     [ '175:0' ] = 'largeplant',
  478.     [ '175:1' ] = 'largeplant',
  479.     [ '175:2' ] = 'largeplant',
  480.     [ '175:3' ] = 'largeplant',
  481.     [ '175:4' ] = 'largeplant',
  482.     [ '175:5' ] = 'largeplant',
  483.   }
  484.   self.dirty = true
  485.   self:flush()
  486. end
  487.  
  488. --[[-- BlockTypeDB --]]--
  489. blockTypeDB = TableDB({
  490.   fileName = 'blocktype.db',
  491.   tabledef = {
  492.     autokeys = true,
  493.     columns = {
  494.       { name = 'odmg',   type = 'number', length = 2 },
  495.       { name = 'sid',    type = 'number', length = 5 },
  496.       { name = 'sdmg',   type = 'number', length = 2 },
  497.       { name = 'dir',    type = 'string', length = 20 },
  498.     }
  499.   }
  500. })
  501.  
  502. function blockTypeDB:load()
  503.   if fs.exists(self.fileName) then
  504.     TableDB.load(self)
  505.   else
  506.     self:seedDB()
  507.   end
  508. end
  509.  
  510. function blockTypeDB:addTemp(blockType, subs)
  511.   local bt = self.data[blockType]
  512.   if not bt then
  513.     bt = { }
  514.     self.data[blockType] = bt
  515.   end
  516.   for _,sub in pairs(subs) do
  517.     table.insert(bt, {
  518.       odmg = sub[1],
  519.       sid = sub[2],
  520.       sdmg = sub[3],
  521.       dir = sub[4]
  522.     })
  523.   end
  524.   self.dirty = true
  525. end
  526.  
  527. function blockTypeDB:seedDB()
  528.  
  529.   blockTypeDB:addTemp('stairs', {
  530.     { 0, nil, 0, 'east-up' },
  531.     { 1, nil, 0, 'west-up' },
  532.     { 2, nil, 0, 'south-up' },
  533.     { 3, nil, 0, 'north-up' },
  534.     { 4, nil, 0, 'east-down' },
  535.     { 5, nil, 0, 'west-down' },
  536.     { 6, nil, 0, 'south-down' },
  537.     { 7, nil, 0, 'north-down' },
  538.   })
  539.   blockTypeDB:addTemp('gate', {
  540.     {  0, nil, 0, 'north' },
  541.     {  1, nil, 0, 'east' },
  542.     {  2, nil, 0, 'north' },
  543.     {  3, nil, 0, 'east' },
  544.     {  4, nil, 0, 'north' },
  545.     {  5, nil, 0, 'east' },
  546.     {  6, nil, 0, 'north' },
  547.     {  7, nil, 0, 'east' },
  548.   })
  549.   blockTypeDB:addTemp('anvil', {
  550.     {  0, nil, 0, 'south' },
  551.     {  1, nil, 0, 'east' },
  552.     {  2, nil, 0 },
  553.     {  3, nil, 0 },
  554.     {  4, nil, 0 },
  555.     {  5, nil, 0 },
  556.     {  6, nil, 0 },
  557.     {  7, nil, 0 },
  558.     {  8, nil, 0 },
  559.     {  9, nil, 0 },
  560.     {  10, nil, 0 },
  561.     {  11, nil, 0 },
  562.     {  12, nil, 0 },
  563.     {  13, nil, 0 },
  564.     {  14, nil, 0 },
  565.     {  15, nil, 0 },
  566.   })
  567.   blockTypeDB:addTemp('bed', {
  568.     {  0, nil, 0, 'south' },
  569.     {  1, nil, 0, 'west' },
  570.     {  2, nil, 0, 'north' },
  571.     {  3, nil, 0, 'east' },
  572.     {  4, nil, 0, 'south' },
  573.     {  5, nil, 0, 'west' },
  574.     {  6, nil, 0, 'north' },
  575.     {  7, nil, 0, 'east' },
  576.     {  8,  -1, 0 },
  577.     {  9,  -1, 0 },
  578.     { 10,  -1, 0 },
  579.     { 11,  -1, 0 },
  580.     { 12,  -1, 0 },
  581.     { 13,  -1, 0 },
  582.     { 14,  -1, 0 },
  583.     { 15,  -1, 0 },
  584.   })
  585.   blockTypeDB:addTemp('quartz-pillar', {
  586.     {  2, nil, 2 },
  587.     {  3, nil, 2, 'north-south-block' },
  588.     {  4, nil, 2, 'east-west-block' },                 -- should be east-west-block
  589.   })
  590.   blockTypeDB:addTemp('button', {
  591.     {  1, nil, 0, 'west-block' },
  592.     {  2, nil, 0, 'east-block' },
  593.     {  3, nil, 0, 'north-block' },
  594.     {  4, nil, 0, 'south-block' },
  595.   })
  596.   blockTypeDB:addTemp('cauldron', {
  597.     {  0, nil, 0 },
  598.     {  1, nil, 0 },
  599.     {  2, nil, 0 },
  600.     {  3, nil, 0 },
  601.   })
  602.   blockTypeDB:addTemp('dispenser', {
  603.     { 0, nil, 0 },
  604.     { 1, nil, 0 },
  605.     { 2, nil, 0, 'south' },
  606.     { 3, nil, 0, 'north' },
  607.     { 4, nil, 0, 'east' },
  608.     { 5, nil, 0, 'west' },
  609.     { 9, nil, 0 },
  610.   })
  611.   blockTypeDB:addTemp('hopper', {
  612.     { 0, nil, 0 },
  613.     { 1, nil, 0 },
  614.     { 2, nil, 0, 'south-block' },
  615.     { 3, nil, 0, 'north-block' },
  616.     { 4, nil, 0, 'east-block' },
  617.     { 5, nil, 0, 'west-block' },
  618.     { 9, nil, 0 },
  619.     { 10, nil, 0 },
  620.     { 11, nil, 0, 'south-block' },
  621.     { 12, nil, 0, 'north-block' },
  622.     { 13, nil, 0, 'east-block' },
  623.     { 14, nil, 0, 'west-block' },
  624.   })
  625.   blockTypeDB:addTemp('mobhead', {
  626.     { 0, nil, 0 },
  627.     { 1, nil, 0 },
  628.     { 2, nil, 0, 'south-block' },
  629.     { 3, nil, 0, 'north-block' },
  630.     { 4, nil, 0, 'west-block' },
  631.     { 5, nil, 0, 'east-block' },
  632.   })
  633.   blockTypeDB:addTemp('rail', {
  634.     { 0, nil, 0, 'south' },
  635.     { 1, nil, 0, 'east' },
  636.     { 2, nil, 0, 'east' },
  637.     { 3, nil, 0, 'east' },
  638.     { 4, nil, 0, 'south' },
  639.     { 5, nil, 0, 'south' },
  640.     { 6, nil, 0, 'east' },
  641.     { 7, nil, 0, 'south' },
  642.     { 8, nil, 0, 'east' },
  643.     { 9, nil, 0, 'south' },
  644.   })
  645.   blockTypeDB:addTemp('signpost', {
  646.     {  0, nil, 0, 'south' },
  647.     {  1, nil, 0, 'south' },
  648.     {  2, nil, 0, 'south' },
  649.     {  3, nil, 0, 'south' },
  650.     {  4, nil, 0, 'west' },
  651.     {  5, nil, 0, 'west' },
  652.     {  6, nil, 0, 'west' },
  653.     {  7, nil, 0, 'west' },
  654.     {  8, nil, 0, 'north' },
  655.     {  9, nil, 0, 'north' },
  656.     { 10, nil, 0, 'north' },
  657.     { 11, nil, 0, 'north' },
  658.     { 12, nil, 0, 'east' },
  659.     { 13, nil, 0, 'east' },
  660.     { 14, nil, 0, 'east' },
  661.     { 15, nil, 0, 'east' },
  662.   })
  663.   blockTypeDB:addTemp('vine', {
  664.     { 0, nil, 0 },
  665.     { 1, nil, 0, 'south-block' },
  666.     { 2, nil, 0, 'west-block' },
  667.     { 3, nil, 0, 'south-block' },
  668.     { 4, nil, 0, 'north-block' },
  669.     { 5, nil, 0, 'south-block' },
  670.     { 6, nil, 0, 'north-block' },
  671.     { 7, nil, 0, 'south-block' },
  672.     { 8, nil, 0, 'east-block' },
  673.     { 9, nil, 0, 'south-block' },
  674.     { 10, nil, 0, 'east-block' },
  675.     { 11, nil, 0, 'east-block' },
  676.     { 12, nil, 0, 'east-block' },
  677.     { 13, nil, 0, 'east-block' },
  678.     { 14, nil, 0, 'east-block' },
  679.     { 15, nil, 0, 'east-block' },
  680.   })
  681.   blockTypeDB:addTemp('torch', {
  682.     { 0, nil, 0 },
  683.     { 1, nil, 0, 'west-block' },
  684.     { 2, nil, 0, 'east-block' },
  685.     { 3, nil, 0, 'north-block' },
  686.     { 4, nil, 0, 'south-block' },
  687.     { 5, nil, 0 },
  688.   })
  689.   blockTypeDB:addTemp('tripwire', {
  690.     { 0, nil, 0, 'north-block' },
  691.     { 1, nil, 0, 'east-block' },
  692.     { 2, nil, 0, 'south-block' },
  693.     { 3, nil, 0, 'west-block' },
  694.   })
  695.   blockTypeDB:addTemp('trapdoor', {
  696.     { 0, nil, 0, 'south-block' },
  697.     { 1, nil, 0, 'north-block' },
  698.     { 2, nil, 0, 'east-block' },
  699.     { 3, nil, 0, 'west-block' },
  700.     { 4, nil, 0, 'south-block' },
  701.     { 5, nil, 0, 'north-block' },
  702.     { 6, nil, 0, 'east-block' },
  703.     { 7, nil, 0, 'west-block' },
  704.     { 8, nil, 0, 'south-block' },
  705.     { 9, nil, 0, 'north-block' },
  706.     { 10, nil, 0, 'east-block' },
  707.     { 11, nil, 0, 'west-block' },
  708.     { 12, nil, 0, 'south-block' },
  709.     { 13, nil, 0, 'north-block' },
  710.     { 14, nil, 0, 'east-block' },
  711.     { 15, nil, 0, 'west-block' },
  712.   })
  713.   blockTypeDB:addTemp('piston', {
  714.     { 0, nil, 0, 'down' },
  715.     { 1, nil, 0, 'up' },
  716.     { 2, nil, 0, 'south' },
  717.     { 3, nil, 0, 'north' },
  718.     { 4, nil, 0, 'east' },
  719.     { 5, nil, 0, 'west' },
  720.     { 8, nil, 0, 'down' },
  721.     { 9, nil, 0, 'up' },
  722.     { 10, nil, 0, 'south' },
  723.     { 11, nil, 0, 'north' },
  724.     { 12, nil, 0, 'east' },
  725.     { 13, nil, 0, 'west' },
  726.   })
  727.   blockTypeDB:addTemp('lever', {
  728.     { 0, nil, 0, 'up' },
  729.     { 1, nil, 0, 'west-block' },
  730.     { 2, nil, 0, 'east-block' },
  731.     { 3, nil, 0, 'north-block' },
  732.     { 4, nil, 0, 'south-block' },
  733.     { 5, nil, 0, 'north' },
  734.     { 6, nil, 0, 'west' },
  735.     { 7, nil, 0, 'up' },
  736.     { 8, nil, 0, 'up' },
  737.     { 9, nil, 0, 'west-block' },
  738.     { 10, nil, 0, 'east-block' },
  739.     { 11, nil, 0, 'north-block' },
  740.     { 12, nil, 0, 'south-block' },
  741.     { 13, nil, 0, 'north' },
  742.     { 14, nil, 0, 'west' },
  743.     { 15, nil, 0, 'up' },
  744.   })
  745.   blockTypeDB:addTemp('wallsign-ladder', {
  746.     { 0, nil, 0 },
  747.     { 2, nil, 0, 'south-block' },
  748.     { 3, nil, 0, 'north-block' },
  749.     { 4, nil, 0, 'east-block' },
  750.     { 5, nil, 0, 'west-block' },
  751.   })
  752.   blockTypeDB:addTemp('chest-furnace', {
  753.     { 0, nil, 0 },
  754.     { 2, nil, 0, 'south' },
  755.     { 3, nil, 0, 'north' },
  756.     { 4, nil, 0, 'east' },
  757.     { 5, nil, 0, 'west' },
  758.   })
  759.   blockTypeDB:addTemp('repeater', {
  760.     {  0, nil, 0, 'north' },
  761.     {  1, nil, 0, 'east' },
  762.     {  2, nil, 0, 'south' },
  763.     {  3, nil, 0, 'west' },
  764.     {  4, nil, 0, 'north' },
  765.     {  5, nil, 0, 'east' },
  766.     {  6, nil, 0, 'south' },
  767.     {  7, nil, 0, 'west' },
  768.     {  8, nil, 0, 'north' },
  769.     {  9, nil, 0, 'east' },
  770.     {  10, nil, 0, 'south' },
  771.     {  11, nil, 0, 'west' },
  772.     {  12, nil, 0, 'north' },
  773.     {  13, nil, 0, 'east' },
  774.     {  14, nil, 0, 'south' },
  775.     {  15, nil, 0, 'west' },
  776.   })
  777.   blockTypeDB:addTemp('flatten', {
  778.     {  0, nil, 0 },
  779.     {  1, nil, 0 },
  780.     {  2, nil, 0 },
  781.     {  3, nil, 0 },
  782.     {  4, nil, 0 },
  783.     {  5, nil, 0 },
  784.     {  6, nil, 0 },
  785.     {  7, nil, 0 },
  786.     {  8, nil, 0 },
  787.     {  9, nil, 0 },
  788.     {  10, nil, 0 },
  789.     {  11, nil, 0 },
  790.     {  12, nil, 0 },
  791.     {  13, nil, 0 },
  792.     {  14, nil, 0 },
  793.     {  15, nil, 0 },
  794.   })
  795.   blockTypeDB:addTemp('sapling', {
  796.     {  '+0', nil, nil },
  797.     {  '+8', nil, nil },
  798.   })
  799.   blockTypeDB:addTemp('leaves', {
  800.     {  '+0', nil, nil },
  801.     {  '+4', nil, nil },
  802.     {  '+8', nil, nil },
  803.     {  '+12', nil, nil },
  804.   })
  805.   blockTypeDB:addTemp('air', {
  806.     {  0,  -1, 0 },
  807.     {  1,  -1, 0 },
  808.     {  2,  -1, 0 },
  809.     {  3,  -1, 0 },
  810.     {  4,  -1, 0 },
  811.     {  5,  -1, 0 },
  812.     {  6,  -1, 0 },
  813.     {  7,  -1, 0 },
  814.     {  8,  -1, 0 },
  815.     {  9,  -1, 0 },
  816.     { 10,  -1, 0 },
  817.     { 11,  -1, 0 },
  818.     { 12,  -1, 0 },
  819.     { 13,  -1, 0 },
  820.     { 14,  -1, 0 },
  821.     { 15,  -1, 0 },
  822.   })
  823.   blockTypeDB:addTemp('truncate', {
  824.     {  0, nil, 0 },
  825.     {  1,  -1, 0 },
  826.     {  2,  -1, 0 },
  827.     {  3,  -1, 0 },
  828.     {  4,  -1, 0 },
  829.     {  5,  -1, 0 },
  830.     {  6,  -1, 0 },
  831.     {  7,  -1, 0 },
  832.     {  8,  -1, 0 },
  833.     {  9,  -1, 0 },
  834.     { 10,  -1, 0 },
  835.     { 11,  -1, 0 },
  836.     { 12,  -1, 0 },
  837.     { 13,  -1, 0 },
  838.     { 14,  -1, 0 },
  839.     { 15,  -1, 0 },
  840.   })
  841.   blockTypeDB:addTemp('slab', {
  842.     {  '+0', nil, nil, 'bottom' },
  843.     {  '+8', nil, nil, 'top' },
  844.   })
  845.   blockTypeDB:addTemp('largeplant', {
  846.     {  '+0', nil, nil },
  847.     {  '+8',  -1,   0 },
  848.   })
  849.   blockTypeDB:addTemp('wood', {
  850.     {  '+0',  nil, nil },
  851.     {  '+4',  nil, nil, 'east-west-block' },
  852.     {  '+8',  nil, nil, 'north-south-block' },
  853.     {  '+12', nil, nil },
  854.   })
  855.   blockTypeDB:addTemp('door', {
  856.     {  0, nil, 0, 'north-door' },
  857.     {  1, nil, 0, 'west-door' },
  858.     {  2, nil, 0, 'south-door' },
  859.     {  3, nil, 0, 'east-door' },
  860.     {  4, nil, 0, 'north-door' },
  861.     {  5, nil, 0, 'west-door' },
  862.     {  6, nil, 0, 'south-door' },
  863.     {  7, nil, 0, 'east-door' },
  864.     {  8, -1, 0 },
  865.     {  9, -1, 0 },
  866.     { 10, -1, 0 },
  867.     { 11, -1, 0 },
  868.     { 12, -1, 0 },
  869.     { 13, -1, 0 },
  870.     { 14, -1, 0 },
  871.     { 15, -1, 0 },
  872.   })
  873.   self.dirty = true
  874.   self:flush()
  875. end
  876.  
  877. --[[-- maxStackDB --]]--
  878. maxStackDB = TableDB({
  879.   fileName = 'maxstack.db',
  880.   tabledef = {
  881.     autokeys = false,
  882.     type = 'simple',
  883.     columns = {
  884.       { label = 'Key', type = 'key', length = 8 },
  885.       { label = 'Quantity', type = 'number', length = 2 }
  886.     }
  887.   }
  888. })
  889.  
  890. function maxStackDB:get(id, dmg)
  891.   return self.data[id .. ':' .. dmg] or 64
  892. end
  893.  
  894. --[[-- Builder --]]--
  895. function Builder:getBlockCounts()
  896.   local blocks = { }
  897.  
  898.   for k,b in pairs(schematic.blocks) do
  899.     if k >= self.index then
  900.       if b.id >= 0 then
  901.         local key = tostring(b.id) .. ':' .. b.dmg
  902.         local block = blocks[key]
  903.         if not block then
  904.           block ={
  905.             id = b.id,
  906.             dmg = b.dmg,
  907.             need = 0,
  908.             key = key,
  909.             qty = 0
  910.           }
  911.           blocks[key] = block
  912.         end
  913.         blocks[key].need = blocks[key].need + 1
  914.       end
  915.     end
  916.   end
  917.   return blocks
  918. end
  919.  
  920. function Builder:selectItem(id, dmg)
  921.   for k,s in ipairs(self.slots) do
  922.     if s.qty > 0 and s.id == id and s.dmg == dmg then
  923.       -- check to see if someone pulled items from inventory
  924.       -- or we passed over a hopper
  925.       if turtle.getItemCount(s.index) > 0 then
  926.         table.remove(self.slots, k)
  927.         table.insert(self.slots, 1, s)
  928.         turtle.select(s.index)
  929.         return s
  930.       end
  931.     end
  932.   end
  933. end
  934.  
  935. function Builder:getSupplyList()
  936.   for i = 1, self.resourceSlots do
  937.     self.slots[i] = {
  938.       qty = 0,
  939.       need = 0,
  940.       index = i
  941.     }
  942.   end
  943.  
  944.   local function getSlot(id, dmg)
  945.     -- find matching slot
  946.     local maxStack = maxStackDB:get(id, dmg)
  947.     for _, s in ipairs(self.slots) do
  948.       if s.id == id and s.dmg == dmg and s.need < maxStack then
  949.         return s
  950.       end
  951.     end
  952.     -- return first available slot
  953.     for _, s in ipairs(self.slots) do
  954.       if not s.id then
  955.         s.key = id .. ':' .. dmg
  956.         s.id = id
  957.         s.dmg = dmg
  958.         return s
  959.       end
  960.     end
  961.   end
  962.  
  963.   for k = self.index, #schematic.blocks do
  964.     local b = schematic.blocks[k]
  965.  
  966.     if b.id > 0 then
  967.       local slot = getSlot(b.id, b.dmg)
  968.       if not slot then
  969.         break
  970.       end
  971.       slot.need = slot.need + 1
  972.     end
  973.   end
  974.  
  975.   for _,s in pairs(self.slots) do
  976.     if s.id then
  977.       s.name = blockDB:lookupName(s.id, s.dmg)
  978.     end
  979.   end
  980. end
  981.  
  982. function Builder:substituteBlocks()
  983.   local spinner = UI.Spinner({
  984.     spinSymbols = { '' }
  985.   })
  986.  
  987.   for _,b in pairs(schematic.blocks) do
  988.     -- replace schematic block type with substitution
  989.     if b.id ~= 0 then
  990.       local sub = subDB:getRealBlock(b.id, b.dmg)
  991.       b.id = sub.id
  992.       b.dmg = sub.dmg
  993.       b.direction = sub.direction
  994.     end
  995.     spinner:spin()
  996.   end
  997. end
  998.  
  999. function Builder:dumpInventory()
  1000.   for i = 1, self.resourceSlots do
  1001.     Builder.slots[i] = { id = 0, dmg = 0, qty = 0, index = i }
  1002.     local qty = turtle.getItemCount(i)
  1003.     if qty > 0 then
  1004.       self.itemProvider:insert(i, qty)
  1005.     end
  1006.   end
  1007.   turtle.select(1)
  1008. end
  1009.  
  1010. function Builder:autocraft(supplies)
  1011.   local t = { }
  1012.   for i,s in pairs(supplies) do
  1013.     local key = s.id .. ':' .. s.dmg
  1014.     local item = t[key]
  1015.     if not item then
  1016.       item = {
  1017.         id = s.id,
  1018.         dmg = s.dmg,
  1019.         qty = 0
  1020.       }
  1021.       t[key] = item
  1022.     end
  1023.     item.qty = item.qty + (s.need-s.qty)
  1024.   end
  1025.  
  1026.   for _,item in pairs(t) do
  1027.     Builder.itemProvider:craft(item.id, item.dmg, item.qty)
  1028.   end
  1029. end
  1030.  
  1031. function Builder:getSupplies()
  1032.  
  1033.   Builder.itemProvider:refresh()
  1034.  
  1035.   local t = { }
  1036.   for k,s in ipairs(self.slots) do
  1037.     if s.need > 0 then
  1038.       local item = Builder.itemProvider:getItemInfo(s.id, s.dmg)
  1039.       if item then
  1040.         if item.name then
  1041.           s.name = item.name
  1042.         end
  1043. if s.qty ~= turtle.getItemCount(s.index) then
  1044. print('wrong!')
  1045.         --s.qty = turtle.getItemCount(s.index)
  1046. --Util.print(s)
  1047. --read()
  1048. --self:dumpInventory()
  1049. TL2.reconcileSlots(self.slots, function(qty, slot)
  1050.   self.itemProvider:insert(slot.index, qty)
  1051. end)
  1052. else
  1053.         local qty = math.min(s.need-s.qty, item.qty)
  1054.         if qty + s.qty > item.maxSize then
  1055.           maxStackDB:add({ s.id, s.dmg }, item.maxSize)
  1056.           maxStackDB:flush()
  1057. --[[
  1058. Util.print(s)
  1059. print(qty)
  1060. print(item.maxSize)
  1061. read()
  1062. --]]
  1063.           qty = item.maxSize
  1064.           s.need = qty
  1065.         end
  1066.         if qty > 0 then
  1067.           turtle.select(s.index)
  1068.           local openSlot = TL2.selectOpenSlot()
  1069. Logger.debug('requesting ' .. qty .. ' for slot ' .. s.index)
  1070.           self.itemProvider:provide(item, qty)
  1071.           if openSlot and openSlot ~= s.index then
  1072. Logger.debug('transferring to ' .. s.index)
  1073.             turtle.transferTo(s.index)
  1074.           end
  1075.           s.qty = turtle.getItemCount(s.index)
  1076.         end
  1077. end
  1078.       end
  1079.     end
  1080.     if s.qty < s.need then
  1081.       table.insert(t, s)
  1082.       local name = s.name or s.id .. ':' .. s.dmg
  1083.       Logger.log('builder', 'Need %d %s', s.need - s.qty, name)
  1084.     end
  1085.   end
  1086.  
  1087.   return t
  1088. end
  1089.  
  1090. Event.addHandler('build', function()
  1091.   Builder:build()
  1092. end)
  1093.  
  1094. function Builder:refuel()
  1095.   if turtle.getFuelLevel() < 4000 and self.fuelItem then
  1096.     Logger.log('builder', 'Refueling')
  1097.     turtle.select(1)
  1098.     self.itemProvider:provide(self.fuelItem, 64)
  1099.     turtle.refuel(64)
  1100.   end
  1101. end
  1102.  
  1103. function Builder:resupply()
  1104.  
  1105.   Logger.log('builder', 'Resupplying')
  1106.   TL2.gotoZlast(TL2.getLocation('supplies'))
  1107.  
  1108.   self:dumpInventory()
  1109.   self:refuel()
  1110.   self:getSupplyList()
  1111.  
  1112.   local supplies = self:getSupplies()
  1113.  
  1114.   if #supplies == 0 then
  1115.     os.queueEvent('build')
  1116.   else
  1117.     self:autocraft(supplies)
  1118.     Logger.log('builder', 'Waiting for supplies')
  1119.     supplyPage.grid:setTable(supplies)
  1120.     UI.pager:setPage('supply')
  1121.   end
  1122. end
  1123.  
  1124. function Builder:placeDown(slot)
  1125.   return TL2.placeDown(slot.index)
  1126. end
  1127.  
  1128. function Builder:place(slot)
  1129.   return TL2.place(slot.index)
  1130. end
  1131.  
  1132. function Builder:collectPiston(slot, tp)
  1133.  
  1134.   -- 1.6
  1135.   if self.ccVersion == 1.6 then
  1136.     return
  1137.   end
  1138.  
  1139.   if self.piston then
  1140.     if tp + 1 ~= self.piston.z then
  1141.       Logger.log('builder', 'Collecting piston')
  1142.       TL2.select(16)
  1143.       TL2.gotoZ(self.piston.z)
  1144.       self:goto(self.piston.x, self.piston.y)
  1145.       TL2.select(slot.index)
  1146.       self.piston = nil
  1147.     end
  1148.   end
  1149. end
  1150.  
  1151. function Builder:placePiston(b)
  1152.   self:goto(b.x-1, b.z)
  1153.   TL2.headTowardsX(b.x)
  1154.   TL2.place(16)
  1155.   os.sleep(.1)
  1156.   turtle.select(15)
  1157.   turtle.place()
  1158.   os.sleep(.1)
  1159.   turtle.place()
  1160.   TL2.up()
  1161.   self:goto(b.x, b.z)
  1162.   rs.setOutput('bottom', true)
  1163.   os.sleep(.25)
  1164.   rs.setOutput('bottom', false)
  1165.   turtle.select(16)
  1166.   turtle.digDown()
  1167. end
  1168.  
  1169. function Builder:movePistonTo(b)
  1170.  
  1171.   if self.ccVersion == 1.6 then
  1172.     self:placePiston(b)
  1173.     return true
  1174.   end
  1175.  
  1176.   if not self.piston or self.piston.z ~= TL2.getState().pt.z then
  1177.     self.piston = nil
  1178.  
  1179.     if turtle.getItemCount(16) > 1 then
  1180.       print('Make this piston point down')
  1181.       TL2.place(16)
  1182.     else
  1183.       print('Place piston beside me pointing down')
  1184.     end
  1185.  
  1186.     repeat
  1187.       Logger.log('builder', 'Waiting for piston')
  1188.       print('Waiting for piston')
  1189.       print('Press enter to continue')
  1190.       read()
  1191.       for i = 1, 4 do
  1192.         if turtle.detect() then
  1193.           local hi = TL2.getHeadingInfo()
  1194.           self.piston = {
  1195.             x = TL2.getState().pt.x + hi.xd,
  1196.             y = TL2.getState().pt.y + hi.yd,
  1197.             z = TL2.getState().pt.z,
  1198.           }
  1199.           break
  1200.         end
  1201.         TL2.turnRight()
  1202.       end
  1203.     until self.piston
  1204.   end
  1205.  
  1206.   while turtle.getItemCount(16) == 0 do
  1207.     Logger.log('builder', 'Waiting for a piston in inventory')
  1208.     print('Put a piston in slot 16')
  1209.     print('Press enter to continue')
  1210.     read()
  1211.   end
  1212.  
  1213.   TL2.up()
  1214.   if b.x ~= self.piston.x then
  1215.     local inc = 1
  1216.     if b.x > self.piston.x then
  1217.       inc = -1
  1218.     end
  1219.     self:goto(self.piston.x + inc, self.piston.y)
  1220.     TL2.headTowardsX(self.piston.x + inc + inc)
  1221.     rs.setOutput('bottom', true)
  1222.     while math.abs(TL2.getState().pt.x - b.x) > 1 do
  1223.       TL2.placeDown(16)
  1224.       turtle.digDown()
  1225.       TL2.goback()
  1226.     end
  1227.     if b.z ~= self.piston.y then
  1228.       rs.setOutput('bottom', false)
  1229.     end
  1230.     TL2.goback()
  1231.     rs.setOutput('bottom', false)
  1232.     self.piston.x = TL2.getState().pt.x
  1233.   end
  1234.   if b.z ~= self.piston.y then
  1235.     local inc = 1
  1236.     if b.z > self.piston.y then
  1237.       inc = -1
  1238.     end
  1239.     self:goto(self.piston.x, self.piston.y + inc)
  1240.     TL2.headTowardsY(self.piston.y + inc + inc)
  1241.     rs.setOutput('bottom', true)
  1242.     while math.abs(TL2.getState().pt.y - b.z) > 1 do
  1243.       TL2.placeDown(16)
  1244.       turtle.digDown()
  1245.       TL2.goback()
  1246.     end
  1247.     TL2.goback()
  1248.     self.piston.y = TL2.getState().pt.y
  1249.   end
  1250.   rs.setOutput('bottom', true)
  1251.   self:goto(self.piston.x, self.piston.y)
  1252.   rs.setOutput('bottom', false)
  1253.   while not turtle.detectDown() do
  1254.     print('Place piston below me pointing down')
  1255.     Logger.log('builder', 'piston missing')
  1256.     print('Press enter to continue')
  1257.     read()
  1258.     rs.setOutput('bottom', true)
  1259.     os.sleep(.25)
  1260.     rs.setOutput('bottom', false)
  1261.   end
  1262.  
  1263.   return true
  1264. end
  1265.  
  1266. function Builder:goto(x, z, y, heading)
  1267.   if not TL2.goto(x, z, y, heading) then
  1268.     Logger.log('builder', 'stuck')
  1269.     print('stuck')
  1270.     print('Press enter to continue')
  1271.     read()
  1272.   end
  1273. end
  1274.  
  1275. function Builder:placeDirectionalBlock(b, slot)
  1276.   local d = b.direction
  1277.  
  1278.   local directions = {
  1279.     [ 'north' ] = 'north',
  1280.     [ 'south' ] = 'south',
  1281.     [ 'east'  ] = 'east',
  1282.     [ 'west'  ] = 'west',
  1283.   }
  1284.   if directions[d] then
  1285.     self:goto(b.x, b.z, b.y, TL2.namedHeadings[directions[d]].heading)
  1286.     b.placed = self:placeDown(slot)
  1287.     b.warnOnly = true
  1288.   end
  1289.  
  1290.   if d == 'top' then
  1291.     self:goto(b.x, b.z, b.y+1)
  1292.     if self:placeDown(slot) then
  1293.       b.placed = self:movePistonTo(b)
  1294.     end
  1295.   end
  1296.  
  1297.   local stairDirections = {
  1298.     [ 'north-down' ] = 'north',
  1299.     [ 'south-down' ] = 'south',
  1300.     [ 'east-down'  ] = 'east',
  1301.     [ 'west-down'  ] = 'west'
  1302.   }
  1303.   if stairDirections[d] then
  1304.     self:goto(b.x, b.z, b.y+1, TL2.namedHeadings[stairDirections[d]].heading)
  1305.     if self:placeDown(slot) then
  1306.       b.placed = self:movePistonTo(b)
  1307.     end
  1308.   end
  1309.  
  1310.   local pistonDirections = {
  1311.     [ 'east-west-block'  ] = 'east',
  1312.     [ 'north-south-block' ] = 'south'
  1313.   }
  1314.   if pistonDirections[d] then
  1315.     local hi = TL2.getHeadingInfo(pistonDirections[d])
  1316.     self:goto(b.x - hi.xd, b.z - hi.yd, b.y, hi.heading)
  1317.     self:place(slot)
  1318.     if turtle.detectUp() then
  1319.       TL2.goback()
  1320.     end
  1321.     TL2.up()
  1322.     b.placed = self:movePistonTo(b)
  1323.   end
  1324.  
  1325.   local doorDirections = {
  1326.     [ 'east-door'  ] = 'south',
  1327.     [ 'south-door' ] = 'west',
  1328.     [ 'west-door'  ] = 'north',
  1329.     [ 'north-door' ] = 'east',
  1330.   }
  1331.   if doorDirections[d] then
  1332.     self:goto(b.x, b.z, b.y, TL2.namedHeadings[doorDirections[d]].heading)
  1333.     if TL2.goback() then
  1334.       if not turtle.detectDown() then
  1335.         if TL2.down() then
  1336.           if not turtle.detectDown() then
  1337.             if TL2.down() then
  1338.               b.placed = self:place(slot)
  1339.               TL2.up()
  1340.             end
  1341.           end
  1342.           TL2.up()
  1343.         end
  1344.       end
  1345.     end
  1346.   end
  1347.  
  1348.   local blockDirections = {
  1349.     [ 'north-block' ] = 'north',
  1350.     [ 'south-block' ] = 'south',
  1351.     [ 'east-block'  ] = 'east',
  1352.     [ 'west-block'  ] = 'west',
  1353.   }
  1354.   if blockDirections[d] then
  1355.     local hi = TL2.getHeadingInfo(blockDirections[d])
  1356.     self:goto(b.x - hi.xd, b.z - hi.yd, b.y-1, hi.heading)
  1357.     b.placed = self:place(slot)
  1358.   end
  1359.  
  1360.   return b.placed
  1361. end
  1362.  
  1363. function Builder:reloadSchematic()
  1364.   schematic:reload()
  1365.   self:substituteBlocks()
  1366. end
  1367.  
  1368. function Builder:logBlock(index, b)
  1369.   local bdir = ''
  1370.   if b.direction then
  1371.     bdir = b.direction
  1372.   end
  1373.   local logText = string.format('Block %d %d:%d (x:%d,z:%d:y:%d) %s',
  1374.     index, b.id, b.dmg, b.x, b.z, b.y, bdir)
  1375.   print(logText)
  1376.   Logger.log('builder', logText)
  1377.  
  1378.   if b.info then
  1379.     Logger.debug(b.info)
  1380.   end
  1381. end
  1382.  
  1383. function Builder:build()
  1384.  
  1385.   local direction = 1
  1386.   local last = #schematic.blocks
  1387.   local travelPlane = 0
  1388.  
  1389.   if self.mode == 'destroy' then
  1390.     direction = -1
  1391.     last = 1
  1392.   end
  1393.  
  1394.   UI.pager:setPage('blank')
  1395.  
  1396.   if self.mode == 'dryRun' then
  1397.     if self.index >= #schematic.blocks then
  1398.       UI.pager:setPage('start')
  1399.       return
  1400.     end
  1401.     self:resupply()
  1402.     for _,s in pairs(Builder.slots) do
  1403.       self.index = self.index + s.qty
  1404.     end
  1405.     return
  1406.   end
  1407.  
  1408.   for i = self.index, last, direction do
  1409.     self.index = i
  1410.     local b = schematic.blocks[i]
  1411.  
  1412.     if b.id > 0 then
  1413.  
  1414.       if self.mode == 'destroy' then
  1415.         self:logBlock(self.index, b)
  1416.         if b.y ~= TL2.getState().pt.z then
  1417.           TL2.gotoZ(b.y)
  1418.         end
  1419.         if not TL2.goto(b.x, b.z, b.y) then
  1420.           print('stuck')
  1421.           print('Press enter to continue')
  1422.           read()
  1423.         end
  1424.         turtle.digDown()
  1425.         if turtle.getItemCount(self.resourceSlots) > 0 then
  1426.           Logger.log('builder', 'Dropping off inventory')
  1427.           TL2.gotoLocation('supplies')
  1428.           Builder:dumpInventory()
  1429.         end
  1430.       else
  1431.         local slot = Builder:selectItem(b.id, b.dmg)
  1432.         if not slot then
  1433.           if travelPlane > TL2.getState().pt.z then
  1434.             TL2.gotoZ(travelPlane)
  1435.           end
  1436.           self:resupply()
  1437.           return
  1438.         end
  1439.         if b.y > travelPlane then
  1440.           travelPlane = b.y
  1441.           self:collectPiston(slot, travelPlane)
  1442.         end
  1443.         if travelPlane > TL2.getState().pt.z then
  1444.           TL2.gotoZ(travelPlane)
  1445.         end
  1446.  
  1447.         self:logBlock(self.index, b)
  1448.  
  1449.         if b.direction then
  1450.           self:placeDirectionalBlock(b, slot)
  1451.         else
  1452.           self:goto(b.x, b.z, b.y)
  1453.           b.placed = self:placeDown(slot)
  1454.           b.warnOnly = true
  1455.         end
  1456.  
  1457.         if b.misplaced then
  1458.           Logger.log('builder', 'Misplaced block - ' .. b.misplaced)
  1459.           print('Misplaced block - ' .. b.misplaced)
  1460.           --sleep(3)
  1461.         end
  1462.  
  1463.         if b.placed then
  1464.           slot.qty = slot.qty - 1
  1465.         else
  1466.           Logger.log('builder', 'failed to place block')
  1467.           print('failed to place block')
  1468.           if not b.warnOnly then
  1469.             print('Hit enter to continue')
  1470.             --read()
  1471.           end
  1472.         end
  1473.       end
  1474.     end
  1475.     Util.writeTable('/' .. schematic.filename .. '.progress', { index = self.index+1 })
  1476.   end
  1477.  
  1478.   TL2.gotoLocation('supplies')
  1479.   Builder:dumpInventory()
  1480.  
  1481.   for i = 1, 4 do
  1482.     TL2.turnRight()
  1483.   end
  1484.  
  1485.   Event.exitPullEvents()
  1486.   UI.term:reset()
  1487.   fs.delete(schematic.filename .. '.progress')
  1488.   Logger.log('builder', 'Finished')
  1489.   print('Finished')
  1490. end
  1491.  
  1492. --[[-- MEProvider --]]--
  1493. MEProvider = class.class()
  1494.  
  1495. function MEProvider:init(args)
  1496.   self.items = {}
  1497.   self.name = 'ME'
  1498.   self.canCraft = true
  1499. end
  1500.  
  1501. function MEProvider:isValid()
  1502.   local mep = peripheral.wrap('bottom')
  1503.   return mep and mep.getAvailableItems and mep.getAvailableItems()
  1504. end
  1505.  
  1506. function MEProvider:refresh()
  1507.   local mep = peripheral.wrap('bottom')
  1508.   if mep then
  1509.     self.items = mep.getAvailableItems()
  1510.   end
  1511.   return self.items
  1512. end
  1513.  
  1514. function MEProvider:getItemInfo(id, dmg)
  1515.  
  1516.   for key,item in pairs(self.items) do
  1517.     if item.id == id and item.dmg == dmg then
  1518.       return item
  1519.     end
  1520.   end
  1521. end
  1522.  
  1523. function MEProvider:craft(id, dmg, qty)
  1524.  
  1525.   local mep = peripheral.wrap('bottom')
  1526.   if mep then
  1527.     Logger.log('builder', 'requested crafting for: ' .. id .. ':' .. dmg .. ' qty: ' .. qty)
  1528.     mep.requestCrafting({
  1529.       id = id,
  1530.       dmg = dmg,
  1531.       qty = qty
  1532.     })
  1533.   end
  1534. end
  1535.  
  1536. function MEProvider:provide(item, qty)
  1537.   local mep = peripheral.wrap('bottom')
  1538.   if mep then
  1539.     local extractedQty = mep.extractItem({
  1540.       id = item.id,
  1541.       dmg = item.dmg,
  1542.       qty = qty
  1543.     }, 'up')
  1544.  
  1545.     if item.qty then
  1546.       item.qty = item.qty - extractedQty
  1547.     end
  1548.   end
  1549. end
  1550.  
  1551. function MEProvider:insert(slot, qty)
  1552.   local mep = peripheral.wrap('bottom')
  1553.   if mep then
  1554.     mep.insertItem(slot, qty, 'up')
  1555.   end
  1556. end
  1557.  
  1558. --[[-- ChestProvider --]]--
  1559. ChestProvider = class.class()
  1560.  
  1561. function ChestProvider:init(args)
  1562.   self.stacks = {}
  1563.   self.name = 'chest'
  1564.   self.canCraft = false
  1565. end
  1566.  
  1567. function ChestProvider:isValid()
  1568.   local chest = peripheral.wrap('bottom')
  1569.   return chest and chest.getAllStacks
  1570. end
  1571.  
  1572. function ChestProvider:refresh()
  1573.   local chest = peripheral.wrap('bottom')
  1574.   if chest then
  1575.     chest.condenseItems()
  1576.     self.stacks = chest.getAllStacks()
  1577.     local t = { }
  1578.     for _,s in ipairs(self.stacks) do
  1579.       local key = s.id .. ':' .. s.dmg
  1580.       if t[key] and t[key].qty < 64 then
  1581.         t[key].maxSize = t[key].qty
  1582.       else
  1583.         t[key] = {
  1584.           qty = s.qty
  1585.         }
  1586.       end
  1587.     end
  1588.     for _,s in ipairs(self.stacks) do
  1589.       local key = s.id .. ':' .. s.dmg
  1590.       if t[key].maxSize then
  1591.         s.maxSize = t[key].qty
  1592.       else
  1593.         s.maxSize = 64
  1594.       end
  1595.     end
  1596.   end
  1597.   return self.stacks
  1598. end
  1599.  
  1600. function ChestProvider:getItemInfo(id, dmg)
  1601.   local item = { id = id, dmg = dmg, qty = 0, maxSize = 64 }
  1602.   for _,stack in pairs(self.stacks) do
  1603.     if stack.id == id and stack.dmg == dmg then
  1604.       item.name = stack.name
  1605.       item.qty = item.qty + stack.qty
  1606.       item.maxSize = stack.maxSize
  1607.     end
  1608.   end
  1609.   return item
  1610. end
  1611.  
  1612. function ChestProvider:craft(id, dmg, qty)
  1613. end
  1614.  
  1615. function ChestProvider:provide(item, qty)
  1616.   local chest = peripheral.wrap('bottom')
  1617.   if chest then
  1618.     self.stacks = chest.getAllStacks()
  1619.     for key,stack in pairs(self.stacks) do
  1620.       if stack.id == item.id and stack.dmg == item.dmg then
  1621.         local amount = math.min(qty, stack.qty)
  1622.         chest.pushItem('up', key, amount)
  1623.         qty = qty - amount
  1624.         if qty <= 0 then
  1625.           break
  1626.         end
  1627.       end
  1628.     end
  1629.   end
  1630. end
  1631.  
  1632. function ChestProvider:insert(slot, qty)
  1633.   local chest = peripheral.wrap('bottom')
  1634.   if chest then
  1635.     turtle.select(slot)
  1636.     turtle.dropDown(qty)
  1637.   end
  1638. end
  1639.  
  1640. --[[-- blankPage --]]--
  1641. blankPage = UI.Page()
  1642. function blankPage:draw()
  1643.   self:reset()
  1644. end
  1645.  
  1646. --[[-- selectSubstitutionPage --]]--
  1647. selectSubstitutionPage = UI.Page({
  1648.   titleBar = UI.TitleBar({
  1649.     title = 'Select a substitution',
  1650.     previousPage = 'listing'
  1651.   }),
  1652.   grid = UI.ScrollingGrid({
  1653.     columns = {
  1654.       { 'OID', 'okey', 8 },
  1655.       { 'Orig', 'oname', 10 },
  1656.     },
  1657.     sortColumn = 'odmg',
  1658.     height = UI.term.height-1,
  1659.     autospace = true,
  1660.     y = 2,
  1661.   }),
  1662. })
  1663.  
  1664. function selectSubstitutionPage:enable()
  1665.  
  1666.   self.grid:adjustWidth()
  1667.   self.grid:setIndex(1)
  1668. end
  1669.  
  1670. function selectSubstitutionPage:eventHandler(event)
  1671.  
  1672.   if event.type == 'grid_select' then
  1673.     substitutionPage.origId = event.selected.id
  1674.     substitutionPage.origDmg = event.selected.dmg
  1675.     UI.pager:setPage(substitutionPage)
  1676.   elseif event.type == 'key' and event.key == 'q' then
  1677.     UI.pager:setPreviousPage()
  1678.   else
  1679.     return UI.Page.eventHandler(self, event)
  1680.   end
  1681.   return true
  1682. end
  1683.  
  1684. --[[-- substitutionPage --]]--
  1685. substitutionPage = UI.Page({
  1686.   backgroundColor = colors.gray,
  1687.   titleBar = UI.TitleBar({
  1688.     previousPage = true,
  1689.     title = 'Substitute a block'
  1690.   }),
  1691.   menuBar = UI.MenuBar({
  1692.     y = 2,
  1693.     buttons = {
  1694.       { text = 'Accept', event = 'accept', help = 'Accept' },
  1695.       { text = 'Revert', event = 'revert', help = 'Restore to original' },
  1696.       { text = 'Air', event = 'air', help = 'Air' },
  1697.     },
  1698.   }),
  1699.   inName = UI.Text({ y = 4, width = UI.term.width }),
  1700.   outName = UI.Text({ y = 5, width = UI.term.width }),
  1701.   grid = UI.ScrollingGrid({
  1702.     columns = {
  1703.       { 'Name', 'name', UI.term.width-18 },
  1704.     },
  1705.     sortColumn = 'name',
  1706.     autospace = true,
  1707.     height = UI.term.height-7,
  1708.     y = 7,
  1709.   }),
  1710.   statusBar = UI.StatusBar()
  1711. })
  1712.  
  1713. substitutionPage.menuBar:add({
  1714.   filterLabel = UI.Text({
  1715.     value = 'Search',
  1716.     x = UI.term.width-14,
  1717.     textColor = colors.black,
  1718.   }),
  1719.   filter = UI.TextEntry({
  1720.     x = UI.term.width-7,
  1721.     width = 7,
  1722.   })
  1723. })
  1724.  
  1725. function substitutionPage:draw()
  1726.  
  1727.   local inName = blockDB:lookupName(self.origId, self.origDmg) or 'Unknown'
  1728.   self.inName.value =  ' Replace ' .. inName
  1729.  
  1730.   local outName = ''
  1731.  
  1732.   for _,sub in pairs(self.subdb.data) do
  1733.     if sub.sid >= 0 then
  1734.       outName = blockDB:lookupName(sub.sid, sub.sdmg) or 'Unknown'
  1735.       break
  1736.     end
  1737.   end
  1738.  
  1739.   self.outName.value = ' With    ' .. outName
  1740.   self.grid:adjustWidth()
  1741.   UI.Page.draw(self)
  1742. end
  1743.  
  1744. function substitutionPage:enable()
  1745.   local id = self.origId
  1746.   local dmg = self.origDmg
  1747.  
  1748.   self.subdb = SubDBClass()
  1749.  
  1750.   local subs = subDB:lookupSubsForBlock(id, dmg)
  1751.   for _,sub in pairs(subs) do
  1752.     self.subdb:add(sub.id, sub.dmg, sub.sid, sub.sdmg, sub.refid, sub.refdmg, sub.direction)
  1753.   end
  1754.  
  1755.   self.allItems = Builder.itemProvider:refresh()
  1756.   self.grid.t = self.allItems
  1757.   for _,item in pairs(self.grid.t) do
  1758.     item.key = item.id .. ':' .. item.dmg
  1759.     item.lname = string.lower(item.name)
  1760.   end
  1761.  
  1762.   self.menuBar.filter.value = ''
  1763.   self.menuBar.filter.pos = 1
  1764. end
  1765.  
  1766. function substitutionPage:applySubstitute(id, dmg)
  1767.  
  1768.   self.newId = id
  1769.   self.newDmg = dmg
  1770.  
  1771.   if Util.empty(self.subdb.data) then
  1772.     self.subdb:add(self.origId, self.origDmg, id, dmg, self.origId, self.origDmg)
  1773.   else
  1774.     for _,sub in pairs(self.subdb.data) do
  1775.       if sub.sid >= 0 then
  1776.         sub.sid = id
  1777.         sub.sdmg = dmg
  1778.         sub.skey = id .. ':' .. dmg
  1779.       end
  1780.     end
  1781.   end
  1782. end
  1783.  
  1784. function substitutionPage:eventHandler(event)
  1785.  
  1786.   if event.type == 'grid_focus_row' then
  1787.     local s = string.format('ID: %d:%d  Quantity: %d',
  1788.       event.selected.id,
  1789.       event.selected.dmg,
  1790.       event.selected.qty)
  1791.     self.statusBar:setStatus(s)
  1792.  
  1793.   elseif event.type == 'grid_select' then
  1794.     if not blockDB:lookupName(event.selected.id, event.selected.dmg) then
  1795.       blockDB:add(event.selected.id, event.selected.dmg, event.selected.name)
  1796.       blockDB:flush()
  1797.     end
  1798.     self:applySubstitute(event.selected.id, event.selected.dmg)
  1799.     self:draw()
  1800.  
  1801.   elseif event.type == 'text_change' then
  1802.     local text = event.text
  1803.     if #text == 0 then
  1804.       self.grid.t = self.allItems
  1805.     else
  1806.       self.grid.t = { }
  1807.       for _,item in pairs(self.allItems) do
  1808.         if string.find(item.lname, text) then
  1809.           table.insert(self.grid.t, item)
  1810.         end
  1811.       end
  1812.     end
  1813.     self.grid:adjustWidth()
  1814.     self.grid:setIndex(1)
  1815.     self.grid:draw()
  1816.  
  1817.   elseif event.type == 'accept' then
  1818.     self.statusBar:setStatus('Saving changes...')
  1819.     self.statusBar:draw()
  1820.  
  1821.     if Util.empty(self.subdb.data) then
  1822.       subDB:remove(self.origId, self.origDmg)
  1823.     else
  1824.       for _,sub in pairs(self.subdb.data) do
  1825.         subDB:add(sub.id, sub.dmg, sub.sid, sub.sdmg, sub.refid, sub.refdmg, sub.direction)
  1826.       end
  1827.     end
  1828.     subDB:flush()
  1829.  
  1830.     Builder:reloadSchematic()
  1831.     UI.pager:setPage('listing')
  1832.  
  1833.   elseif event.type == 'air' then
  1834.     if not blockDB:lookupName(0, 0) then
  1835.       blockDB:add(0, 0, 'Air')
  1836.       blockDB:flush()
  1837.     end
  1838.     self:applySubstitute(0, 0)
  1839.     self:draw()
  1840.  
  1841.   elseif event.type == 'revert' then
  1842.     self.newId = self.origId
  1843.     self.newDmg = self.origDmg
  1844.  
  1845.     Util.clear(self.subdb.data)
  1846.  
  1847.     local st = standardBlockDB:get({ self.origId, self.origDmg })
  1848.     if st then
  1849.       local bt = blockTypeDB:get(st)
  1850.       self.subdb:addSubsForBlockType(self.origId, self.origDmg, bt)
  1851.     end
  1852.     self:draw()
  1853.  
  1854.   elseif event.type == 'cancel' then
  1855.     UI.pager:setPreviousPage()
  1856.   end
  1857.  
  1858.   return UI.Page.eventHandler(self, event)
  1859. end
  1860.  
  1861. --[[-- SupplyPage --]]--
  1862. supplyPage = UI.Page({
  1863.   titleBar = UI.TitleBar({
  1864.     title = 'Waiting for supplies',
  1865.     previousPage = 'start'
  1866.   }),
  1867.   menuBar = UI.MenuBar({
  1868.     y = 2,
  1869.     buttons = {
  1870.       --{ text = 'Refresh', event = 'refresh', help = 'Refresh inventory' },
  1871.       { text = 'Continue',    event = 'build', help = 'Continue building' },
  1872.       { text = 'Menu',        event = 'menu',  help = 'Return to main menu' },
  1873.       { text = 'Force Craft', event = 'craft', help = 'Request crafting (again)' },
  1874.     }
  1875.   }),
  1876.   grid = UI.Grid({
  1877.     columns = {
  1878.       { 'Slot', 'index', 4 },
  1879.       { 'ID', 'key', 7 },
  1880.       { 'Name', 'name', UI.term.width-20 },
  1881.       { 'Need', 'need', 4 },
  1882.       --{ 'Have', 'have', 4 },
  1883.     },
  1884.     sortColumn = 'index',
  1885.     y = 3,
  1886.     width = UI.term.width,
  1887.     height = UI.term.height - 3
  1888.   }),
  1889.   statusBar = UI.StatusBar({
  1890.     columns = {
  1891.       { 'Help', 'help', UI.term.width - 25 },
  1892.       { 'Block', 'block', 12 },
  1893.       { 'Fuel', 'fuel', 11 }
  1894.     }
  1895.   }),
  1896.   accelerators = {
  1897.     c = 'craft',
  1898.     r = 'refresh',
  1899.     b = 'build',
  1900.     m = 'menu',
  1901.   },
  1902. })
  1903.  
  1904. function supplyPage:eventHandler(event)
  1905.  
  1906.   if event.type == 'craft' then
  1907.     if Builder.itemProvider.canCraft then
  1908.       local s = self.grid:getSelected()
  1909.       Builder.itemProvider:craft(s.id, s.dmg, s.need-s.qty)
  1910.       local name = s.name or ''
  1911.       self.statusBar:timedStatus('Requested ' .. s.need-s.qty .. ' ' .. name, 3)
  1912.     else
  1913.       self.statusBar:timedStatus('Unable to craft')
  1914.     end
  1915.  
  1916.   elseif event.type == 'refresh' then
  1917.     self:refresh()
  1918.  
  1919.   elseif event.type == 'build' then
  1920.     Builder:build()
  1921.  
  1922.   elseif event.type == 'menu' then
  1923.     Builder:dumpInventory()
  1924.     --Builder.status = 'idle'
  1925.     UI.pager:setPage('start')
  1926.  
  1927.   elseif event.type == 'focus_change' then
  1928.     self.statusBar:timedStatus(event.focused.help, 3)
  1929.   end
  1930.  
  1931.   return UI.Page.eventHandler(self, event)
  1932. end
  1933.  
  1934. function supplyPage:enable()
  1935.   self.grid.index = 1
  1936.   self.statusBar:setValue('fuel',
  1937.     string.format('Fuel: %dk', math.floor(turtle.getFuelLevel() / 1024)))
  1938.   self.statusBar:setValue('block',
  1939.     string.format('Block: %d', Builder.index))
  1940.  
  1941.   Event.addNamedTimer('supplyRefresh', 12, true, function()
  1942.     if self.enabled then
  1943.       self:refresh()
  1944.       self.statusBar:timedStatus('Refreshed ', 2)
  1945.     end
  1946.   end)
  1947. end
  1948.  
  1949. function supplyPage:disable()
  1950.   Event.cancelNamedTimer('supplyRefresh')
  1951. end
  1952.  
  1953. function supplyPage:refresh()
  1954.   self.statusBar:timedStatus('Refreshed ', 3)
  1955.   local t = Builder:getSupplies()
  1956.   if #t == 0 then
  1957.     Builder:build()
  1958.   else
  1959.     self.grid:setTable(t)
  1960.     self.grid:draw()
  1961.   end
  1962. end
  1963.  
  1964. --[[-- ListingPage --]]--
  1965. listingPage = UI.Page({
  1966.   titleBar = UI.TitleBar({
  1967.     title = 'Supply List',
  1968.     previousPage = 'start'
  1969.   }),
  1970.   menuBar = UI.MenuBar({
  1971.     y = 2,
  1972.     buttons = {
  1973.       { text = 'Craft',      event = 'craft',   help = 'Request crafting' },
  1974.       { text = 'Refresh',    event = 'refresh', help = 'Refresh inventory' },
  1975.       { text = 'Toggle',     event = 'toggle',  help = 'Toggles needed blocks' },
  1976.       { text = 'Substitute', event = 'edit',    help = 'Substitute a block' }
  1977.     }
  1978.   }),
  1979.   grid = UI.ScrollingGrid({
  1980.     columns = {
  1981.       { 'ID',   'key',  7 },
  1982.       { 'Name', 'name', UI.term.width - 20 },
  1983.       { 'Need', 'need', 4 },
  1984.       { 'Have', 'qty', 4 },
  1985.     },
  1986.     sortColumn = 'key',
  1987.     y = 3,
  1988.     height = UI.term.height-3,
  1989.     help = 'Set a block type or pick a substitute block'
  1990.   }),
  1991.   accelerators = {
  1992.     q = 'menu',
  1993.     c = 'craft',
  1994.     r = 'refresh',
  1995.     t = 'toggle',
  1996.   },
  1997.   statusBar = UI.StatusBar(),
  1998.   fullList = true
  1999. })
  2000.  
  2001. function listingPage:enable()
  2002.   listingPage:refresh()
  2003. end
  2004.  
  2005. function listingPage:eventHandler(event)
  2006.  
  2007.   if event.type == 'craft' then
  2008.     if Builder.itemProvider.canCraft then
  2009.       local s = self.grid:getSelected()
  2010.       Builder.itemProvider:craft(s.id, s.dmg, s.need)
  2011.       local name = s.name
  2012.       if not name then
  2013.         name = s.key
  2014.       end
  2015.       self.statusBar:timedStatus('Requested ' .. s.need .. ' ' .. name, 3)
  2016.     else
  2017.       self.statusBar:timedStatus('Unable to craft')
  2018.     end
  2019.  
  2020.   elseif event.type == 'refresh' then
  2021.     self:refresh()
  2022.     self:draw()
  2023.     self.statusBar:timedStatus('Refreshed ', 3)
  2024.  
  2025.   elseif event.type == 'toggle' then
  2026.     self.fullList = not self.fullList
  2027.     self:refresh()
  2028.     self:draw()
  2029.  
  2030.   elseif event.type == 'menu' then
  2031.     UI.pager:setPage('start')
  2032.  
  2033.   elseif event.type == 'edit' or event.type == 'grid_select' then
  2034.     self:manageBlock(self.grid:getSelected())
  2035.  
  2036.   elseif event.type == 'focus_change' then
  2037.     if event.focused.help then
  2038.       self.statusBar:timedStatus(event.focused.help, 3)
  2039.     end
  2040.   end
  2041.  
  2042.   return UI.Page.eventHandler(self, event)
  2043. end
  2044.  
  2045. function listingPage:refresh()
  2046.  
  2047.   local supplyList = Builder:getBlockCounts()
  2048.  
  2049.   Builder.itemProvider:refresh()
  2050.  
  2051.   for _,b in pairs(supplyList) do
  2052.     if b.need > 0 then
  2053.       local item = Builder.itemProvider:getItemInfo(b.id, b.dmg)
  2054.       if item then
  2055.         local block = blockDB:lookup(b.id, b.dmg)
  2056.         if not block then
  2057.           blockDB:add(b.id, b.dmg, item.name)
  2058.         elseif not block.name and item.name then
  2059.           blockDB:add(b.id, b.dmg, item.name)
  2060.         end
  2061.         b.qty = item.qty
  2062.       end
  2063.     end
  2064.   end
  2065.   blockDB:flush()
  2066.  
  2067.   local t = {}
  2068.   for _,b in pairs(supplyList) do
  2069.     --if b.id > 0 then
  2070.       local block = blockDB:lookup(b.id, b.dmg)
  2071.       if block then
  2072.         b.name = block.name
  2073.       end
  2074.       if self.fullList or b.qty < b.need then
  2075.         table.insert(t, b)
  2076.        end
  2077.     --end
  2078.   end
  2079.   self.grid:setTable(t)
  2080. end
  2081.  
  2082. function listingPage:manageBlock(selected)
  2083.   local sid = selected.id
  2084.   local sdmg = selected.dmg
  2085.  
  2086.   local t = { }
  2087.  
  2088.   local substitutes = subDB:lookupBlocksForSub(sid, sdmg)
  2089.   for _,sub in pairs(substitutes) do
  2090.     sub = subDB:lookup(sub.refid, sub.refdmg)
  2091.     local key = sub.refid .. ':' .. sub.refdmg
  2092.     if not t[key] and sub.sid >= 0 then
  2093.       t[sub.refid .. ':' .. sub.refdmg] = {
  2094.         okey = sub.key,
  2095.         oname = blockDB:lookupName(sub.refid, sub.refdmg) or 'Unknown',
  2096.         skey = sub.sid .. ':' .. sub.sdmg,
  2097.         sname = blockDB:lookupName(sub.sid, sub.sdmg),
  2098.         rkey = sub.refid .. ':' .. sub.refdmg,
  2099.         id = sub.refid,
  2100.         dmg = sub.refdmg
  2101.       }
  2102.     end
  2103.   end
  2104.  
  2105.   if Util.empty(t) then
  2106.     substitutionPage.origId = selected.id
  2107.     substitutionPage.origDmg = selected.dmg
  2108.     UI.pager:setPage(substitutionPage)
  2109.   elseif Util.size(t) == 1 then
  2110.     local _,sub = next(t)
  2111.     substitutionPage.origId = sub.id
  2112.     substitutionPage.origDmg = sub.dmg
  2113.     UI.pager:setPage(substitutionPage)
  2114.   else
  2115.     selectSubstitutionPage.selected = selected
  2116.     selectSubstitutionPage.grid.t = t
  2117.     UI.pager:setPage(selectSubstitutionPage)
  2118.   end
  2119. end
  2120.  
  2121. --[[-- startPage --]]--
  2122. startPage = UI.Page({
  2123.   titleBar = UI.TitleBar({ title = 'Builder v' .. Builder.version }),
  2124.   window = UI.Window({
  2125.     x = UI.term.width-15,
  2126.     y = 3,
  2127.     width = 15,
  2128.     height = UI.term.height-3,
  2129.     backgroundColor = colors.gray,
  2130.     grid = UI.Grid({
  2131.       columns = {
  2132. --[[
  2133.       { 'Blocks', 'blocks', 6 },
  2134.       { 'Mode', 'mode', 7 },
  2135.       { 'Start', 'block', 6 },
  2136.       { 'Fuel', 'fuel', 5 },
  2137. --]]
  2138.         { 'Name', 'name', 6 },
  2139.         { 'Value', 'value', 7 },
  2140.       },
  2141.       disableHeader = true,
  2142.       --y = UI.term.height-1,
  2143.       x = 1,
  2144.       y = 2,
  2145.       width = 15,
  2146.       height = 9,
  2147.       --autospace = true,
  2148.       selectable = false,
  2149.       backgroundColor = colors.gray
  2150.     }),
  2151.   }),
  2152.   menu = UI.Menu({
  2153.     x = 2,
  2154.     y = 5,
  2155.     menuItems = {
  2156.       { prompt = 'Set starting level', event = 'startLevel' },
  2157.       { prompt = 'Set starting block', event = 'startBlock' },
  2158.       { prompt = 'Supply list',        event = 'assignBlocks' },
  2159.       { prompt = 'Toggle mode',        event = 'toggleMode' },
  2160.       { prompt = 'Begin',              event = 'begin' },
  2161.       { prompt = 'Quit',               event = 'quit' }
  2162.     }
  2163.   }),
  2164.   accelerators = {
  2165.     x = 'test',
  2166.     q = 'quit'
  2167.   }
  2168. })
  2169.  
  2170. function startPage:draw()
  2171.   local fuel = turtle.getFuelLevel()
  2172.   if fuel > 9999 then
  2173.     fuel = string.format('%dk', math.floor(fuel/1024))
  2174.   end
  2175.   local t = {
  2176.     { name = 'mode', value = Builder.mode },
  2177.     { name = 'start', value = Builder.index },
  2178.     { name = 'blocks', value = #schematic.blocks },
  2179.     { name = 'fuel', value = fuel },
  2180.     { name = '' },
  2181.     { name = 'length', value = schematic.length },
  2182.     { name = 'width', value = schematic.width },
  2183.     { name = 'height', value = schematic.height },
  2184.   }
  2185.  
  2186.   self.window.grid:setTable(t)
  2187.   UI.Page.draw(self)
  2188. end
  2189.  
  2190. function startPage:eventHandler(event)
  2191.  
  2192.   if event.type == 'startLevel' then
  2193.     local dialog = UI.Dialog({
  2194.       text = UI.Text({ x = 5, y = 3, value = '0 - ' .. schematic.height }),
  2195.       textEntry = UI.TextEntry({ x = 15, y = 3, '0 - 11' })
  2196.     })
  2197.  
  2198.     dialog.eventHandler = function(self, event)
  2199.       if event.type == 'accept' then
  2200.         local l = tonumber(self.textEntry.value)
  2201.         if l and l < schematic.height and l >= 0 then
  2202.           for k,v in pairs(schematic.blocks) do
  2203.             if v.y >= l then
  2204.               Builder.index = k
  2205.               Util.writeTable('/' .. schematic.filename .. '.progress',
  2206.                 { index = Builder.index })
  2207.               UI.pager:setPreviousPage()
  2208.               break
  2209.             end
  2210.           end
  2211.         else
  2212.           self.statusBar:timedStatus('Invalid Level', 3)
  2213.         end
  2214.         return true
  2215.       end
  2216.  
  2217.       return UI.Dialog.eventHandler(self, event)
  2218.     end
  2219.  
  2220.     dialog.titleBar.title = 'Enter Starting Level'
  2221.     dialog:setFocus(dialog.textEntry)
  2222.     UI.pager:setPage(dialog)
  2223.  
  2224.   elseif event.type == 'startBlock' then
  2225.     local dialog = UI.Dialog({
  2226.       text = UI.Text({ x = 5, y = 3, value = '1 - ' .. #schematic.blocks }),
  2227.       textEntry = UI.TextEntry({ x = 15, y = 3, value = tostring(Builder.index) })
  2228.     })
  2229.  
  2230.     dialog.eventHandler = function(self, event)
  2231.       if event.type == 'accept' then
  2232.         local bn = tonumber(self.textEntry.value)
  2233.         if bn and bn < #schematic.blocks and bn >= 0 then
  2234.           Builder.index = bn
  2235.           Util.writeTable('/' .. schematic.filename .. '.progress',
  2236.             { index = Builder.index })
  2237.           UI.pager:setPreviousPage()
  2238.         else
  2239.           self.statusBar:timedStatus('Invalid Block', 3)
  2240.         end
  2241.         return true
  2242.       end
  2243.  
  2244.       return UI.Dialog.eventHandler(self, event)
  2245.     end
  2246.  
  2247.     dialog.titleBar.title = 'Enter Block Number'
  2248.     dialog:setFocus(dialog.textEntry)
  2249.     UI.pager:setPage(dialog)
  2250.  
  2251.   elseif event.type == 'assignBlocks' then
  2252.     Builder:dumpInventory()
  2253.     UI.pager:setPage('listing')
  2254.  
  2255.   elseif event.type == 'toggleMode' then
  2256.     if Builder.mode == 'build' then
  2257.       if Builder.index == 1 then
  2258.         Builder.index = #schematic.blocks
  2259.       end
  2260.       Builder.mode = 'destroy'
  2261.     elseif Builder.mode == 'destroy' then
  2262.       if Builder.index == #schematic.blocks then
  2263.         Builder.index = 1
  2264.       end
  2265.       Builder.mode = 'dryRun'
  2266.     else
  2267.       Builder.mode = 'build'
  2268.     end
  2269.     self:draw()
  2270.  
  2271.   elseif event.type == 'begin' then
  2272.     UI.pager:setPage('blank')
  2273.     --Builder.status = 'building'
  2274.  
  2275.     print('Reloading schematic')
  2276.     Builder:reloadSchematic()
  2277. if Builder.mode ~= 'dryRun' then
  2278.     print('Determining block placement')
  2279.     schematic:determineBlockPlacement()
  2280.     print('Optimizing route (' .. #schematic.blocks .. ' blocks)')
  2281.     schematic:optimizeRoute()
  2282.     print('Adjusting route')
  2283.     schematic:setPlacementOrder()
  2284. end
  2285.     Builder:dumpInventory()
  2286.  
  2287.     if Builder.mode == 'destroy' then
  2288.       print('Beginning destruction')
  2289.     else
  2290.       print('Starting build')
  2291.     end
  2292.  
  2293.     Builder:build()
  2294.     Profile.display()
  2295.  
  2296.   elseif event.type == 'quit' then
  2297.     Event.exitPullEvents()
  2298.     UI.term:reset()
  2299.   end
  2300.  
  2301.   return UI.Page.eventHandler(self, event)
  2302. end
  2303.  
  2304. --[[-- startup logic --]]--
  2305. args = {...}
  2306. if #args < 1 then
  2307.   error('supply file name')
  2308. end
  2309. --if #args > 1 then
  2310.   Profile.enable()
  2311. --end
  2312.  
  2313. if os.version() == 'TurtleOS 1.5' then
  2314.   Builder.ccVersion = 1.5
  2315.   Builder.resourceSlots = 15
  2316. elseif os.version() == 'CraftOS 1.6' then
  2317.   Builder.ccVersion = 1.6
  2318.   Builder.resourceSlots = 14
  2319. else
  2320.   error('Unsupported ComputerCraft version')
  2321. end
  2322.  
  2323. Builder.itemProvider = ChestProvider()
  2324. if not Builder.itemProvider:isValid() then
  2325.   Builder.itemProvider = MEProvider()
  2326.   if not Builder.itemProvider:isValid() then
  2327.     error('No provider below turtle')
  2328.   end
  2329. end
  2330.  
  2331. blockDB:load()
  2332. standardBlockDB:load()
  2333. blockTypeDB:load()
  2334. maxStackDB:load()
  2335. subDB:load(standardBlockDB, blockTypeDB)
  2336.  
  2337. print('Loading ' .. args[1])
  2338. schematic:load(args[1])
  2339. print('Substituting blocks')
  2340. Builder:substituteBlocks()
  2341.  
  2342. local progress = Util.readTable(schematic.filename .. '.progress')
  2343. if progress then
  2344.   Builder.index = progress.index
  2345.   if Builder.index > #schematic.blocks then
  2346.     Builder.index = 1
  2347.   end
  2348. end
  2349.  
  2350. UI.pager:setPages({
  2351.   listing = listingPage,
  2352.   start = startPage,
  2353.   supply = supplyPage,
  2354.   blank = blankPage
  2355. })
  2356.  
  2357. UI.pager:setPage('start')
  2358. TL2.setPolicy(TL2.policies.digAttack)
  2359. TL2.getState().pt.x = -1
  2360. TL2.getState().pt.y = -1
  2361. TL2.saveLocation('supplies')
  2362.  
  2363. Event.pullEvents()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement