Advertisement
BombBloke

HoloBuilder (RandomPeripherals)

Aug 9th, 2015
850
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 41.52 KB | None | 0 0
  1. -- +--------------------------------------------------------+
  2. -- |                                                        |
  3. -- |                      WorldPorter                       |
  4. -- |                   (Hologram  Alpha)                    |
  5. -- +--------------------------------------------------------+
  6.  
  7. local version = "Version 0.1.0"
  8.  
  9. -- By Jeffrey Alexander, aka Bomb Bloke.
  10. -- Displays WorldPorter build files as RandomPeripherals holograms.
  11. -- http://www.computercraft.info/forums2/index.php?/topic/20785-mc-1710cc-174-randomperipherals
  12.  
  13. ---------------------------------------------
  14. ------------Variable Declarations------------
  15. ---------------------------------------------
  16.  
  17. local holo = peripheral.find("hologram_projector")
  18.  
  19. local xSize, ySize, myEvent = term.getSize()
  20. local curMenu, subMenu, startDir, cursor = 1, 1, shell.resolve("."), {{">>  ","  <<"},{"> > "," < <"},{" >> "," << "},{"> > "," < <"}}
  21. local labelText, labelBack, inputText, inputBack, buttonText, buttonBack = colours.blue, colours.lightGrey, colours.purple, colours.grey, colours.lightGrey, colours.grey
  22.  
  23. -- Doors need special handling:
  24. local doorBlocks = {
  25.     "minecraft:wooden_door","minecraft:iron_door"
  26. }
  27.  
  28. -- These may need solid blocks horizontally besides them in order to be placed:
  29. local dependantBlocks = {
  30.     "minecraft:torch","minecraft:stone_button","minecraft:wooden_button","minecraft:trapdoor",
  31.     "minecraft:tripwire_hook","minecraft:redstone_torch","minecraft:lever","minecraft:portal",
  32.     "minecraft:wall_sign","minecraft:end_portal","minecraft:ladder","minecraft:vine","minecraft:bed"
  33. }
  34.  
  35. -- These are either liquids, or need to be placed on liquids:
  36. local fluidBlocks = {
  37.     "minecraft:water","minecraft:flowing_water","minecraft:lava","minecraft:flowing_lava",
  38.     "minecraft:waterlily"
  39. }
  40.  
  41. -- Translations that must always be performed:
  42. local forcedTranslation = {
  43.     ["minecraft:portal"] = {{1,0},{2,0}}
  44. }
  45.  
  46. -- Used to rotate the blocks in concern (via metadata):
  47. local rotationTranslation = {
  48.     -- MineCraft 1.7.10:
  49.     ["minecraft:torch"] = {{1,3,2,4}},
  50.     ["minecraft:redstone_torch"] = {{1,3,2,4}},
  51.     ["minecraft:unlit_redstone_torch"] = {{1,3,2,4}},
  52.     ["minecraft:oak_stairs"] = {{3,0,2,1},{7,4,6,5}},
  53.     ["minecraft:stone_stairs"] = {{3,0,2,1},{7,4,6,5}},
  54.     ["minecraft:stone_brick_stairs"] = {{3,0,2,1},{7,4,6,5}},
  55.     ["minecraft:brick_stairs"] = {{3,0,2,1},{7,4,6,5}},
  56.     ["minecraft:sandstone_stairs"] = {{3,0,2,1},{7,4,6,5}},
  57.     ["minecraft:nether_brick_stairs"] = {{3,0,2,1},{7,4,6,5}},
  58.     ["minecraft:spruce_stairs"] = {{3,0,2,1},{7,4,6,5}},
  59.     ["minecraft:birch_stairs"] = {{3,0,2,1},{7,4,6,5}},
  60.     ["minecraft:jungle_stairs"] = {{3,0,2,1},{7,4,6,5}},
  61.     ["minecraft:quartz_stairs"] = {{3,0,2,1},{7,4,6,5}},
  62.     ["minecraft:acacia_stairs"] = {{3,0,2,1},{7,4,6,5}},
  63.     ["minecraft:dark_oak_stairs"] = {{3,0,2,1},{7,4,6,5}},
  64.     ["minecraft:hay_block"] = {{8,4}},
  65.     ["minecraft:log"] = {{8,4},{9,5},{10,6},{11,7}},
  66.     ["minecraft:log2"] = {{8,4},{9,5}},
  67.     ["minecraft:cocoa"] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}},
  68.     ["minecraft:pumpkin"] = {{0,1,2,3}},
  69.     ["minecraft:lit_pumpkin"] = {{0,1,2,3}},
  70.     ["minecraft:quartz_block"] = {{3,4}},
  71.     ["minecraft:chest"] = {{3,4,2,5}},
  72.     ["minecraft:trapped_chest"] = {{3,4,2,5}},
  73.     ["minecraft:ender_chest"] = {{3,4,2,5}},
  74.     ["minecraft:furnace"] = {{3,4,2,5}},
  75.     ["minecraft:lit_furnace"] = {{3,4,2,5}},
  76.     ["minecraft:ladder"] = {{3,4,2,5}},
  77.     ["minecraft:vine"] = {{4,8,1,2},{3,6,12,9},{5,10},{11,7,14,13}},
  78.     ["minecraft:anvil"] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}},
  79.     ["minecraft:standing_sign"] = {{0,4,8,12},{1,5,9,13},{2,6,10,14},{3,7,11,15}},
  80.     ["minecraft:wall_sign"] = {{3,4,2,5}},
  81.     ["minecraft:bed"] = {{2,3,0,1},{10,11,8,9}},
  82.     ["minecraft:skull"] = {{3,4,2,5}},
  83.     ["minecraft:dispenser"] = {{3,4,2,5}},
  84.     ["minecraft:dropper"] = {{3,4,2,5}},
  85.     ["minecraft:hopper"] = {{3,4,2,5}},
  86.     ["minecraft:piston"] = {{3,4,2,5},{11,12,10,13}},
  87.     ["minecraft:sticky_piston"] = {{3,4,2,5},{11,12,10,13}},
  88.     ["minecraft:piston_head"] = {{3,4,2,5},{11,12,10,13}},
  89.     ["minecraft:lever"] = {{6,5},{13,14},{3,2,4,1},{11,10,12,9},{0,7},{15,8}},
  90.     ["minecraft:stone_button"] = {{1,3,2,4},{11,10,12,9}},
  91.     ["minecraft:wooden_button"] = {{1,3,2,4},{11,10,12,9}},
  92.     ["minecraft:trapdoor"] = {{0,3,1,2},{5,6,4,7},{9,10,8,11},{13,14,12,15}},
  93.     ["minecraft:fence_gate"] = {{3,0,1,2},{7,4,5,6}},
  94.     ["minecraft:tripwire_hook"] = {{0,1,2,3},{7,4,5,6},{15,12,13,14}},
  95.     ["minecraft:wooden_door"] = {{3,0,1,2},{7,4,5,6}},
  96.     ["minecraft:iron_door"] = {{3,0,1,2},{7,4,5,6}},
  97.     ["minecraft:unpowered_repeater"] = {{0,1,2,3},{4,5,6,7},{8,9,10,11},{12,13,14,15}},
  98.     ["minecraft:powered_repeater"] = {{0,1,2,3},{4,5,6,7},{8,9,10,11},{12,13,14,15}},
  99.     ["minecraft:unpowered_comparator"] = {{0,1,2,3},{4,5,6,7},{8,9,10,11},{12,13,14,15}},
  100.     ["minecraft:rail"] = {{0,1},{4,2,5,3},{8,9,6,7}},
  101.     ["minecraft:golden_rail"] = {{0,1},{4,2,5,3},{8,9},{12,10,13,11}},
  102.     ["minecraft:detector_rail"] = {{0,1},{4,2,5,3},{8,9},{12,10,13,11}},
  103.     ["minecraft:activator_rail"] = {{0,1},{4,2,5,3},{8,9},{12,10,13,11}},
  104.     ["minecraft:red_mushroom_block"] = {{1,3,9,7},{2,6,8,4}},
  105.     ["minecraft:brown_mushroom_block"] = {{1,3,9,7},{2,6,8,4}},
  106.     ["minecraft:end_portal_frame"] = {{0,1,2,3},{4,5,6,7}},
  107.  
  108.     -- ComputerCraft 1.72:
  109.     ["ComputerCraft:CC-Computer"] = {{3,4,2,5},{11,12,10,13}},
  110.     ["ComputerCraft:CC-Cable"] = {{3,4,2,5},{10,8,11,9}},
  111.     ["ComputerCraft:CC-Peripheral"] = {{3,4,2,5},{6,9,7,8}},
  112.     ["ComputerCraft:command_computer"] = {{3,4,2,5}},
  113.  
  114.     -- MoarPeripherals 1.52:
  115.     ["moarperipherals:blockKeyboardMac"] = {{3,4,2,5}},
  116.     ["moarperipherals:blockKeyboardPc"] = {{3,4,2,5}},
  117.    
  118.     -- MoarPeripherals 1.53:
  119.     ["MoarPeripherals:blockKeyboardMac"] = {{3,4,2,5}},
  120.     ["MoarPeripherals:blockKeyboardPc"] = {{3,4,2,5}}
  121. }
  122.  
  123. local menu = {
  124.     {"What Do?",
  125.     {
  126.         --{["button"] = {["text"] = "Scan", ["x"] = 21, ["y"] = 8, ["length"] = 10}},
  127.         {["button"] = {["text"] = "Build", ["x"] = 21, ["y"] = 11, ["length"] = 10}},
  128.         {["button"] = {["text"] = "Quit", ["x"] = 21, ["y"] = 14, ["length"] = 10}}
  129.     }},
  130.    
  131.     {
  132.         {"Enter Bounding Co-ordinates",
  133.         {
  134.             {["label"] = {["x"] = 11, ["y"] =  7, ["text"] = "X1"}, ["textbox"] = true, ["x"] = 15, ["y"] =  7, ["length"] = 7, ["number"] = true},
  135.             {["label"] = {["x"] = 11, ["y"] = 10, ["text"] = "Y1"}, ["textbox"] = true, ["x"] = 15, ["y"] = 10, ["length"] = 7, ["number"] = true, ["min"] = 0, ["max"] = 255},
  136.             {["label"] = {["x"] = 11, ["y"] = 13, ["text"] = "Z1"}, ["textbox"] = true, ["x"] = 15, ["y"] = 13, ["length"] = 7, ["number"] = true},
  137.             {["label"] = {["x"] = 31, ["y"] =  7, ["text"] = "X2"}, ["textbox"] = true, ["x"] = 35, ["y"] =  7, ["length"] = 7, ["number"] = true},
  138.             {["label"] = {["x"] = 31, ["y"] = 10, ["text"] = "Y2"}, ["textbox"] = true, ["x"] = 35, ["y"] = 10, ["length"] = 7, ["number"] = true, ["min"] = 0, ["max"] = 255},
  139.             {["label"] = {["x"] = 31, ["y"] = 13, ["text"] = "Z2"}, ["textbox"] = true, ["x"] = 35, ["y"] = 13, ["length"] = 7, ["number"] = true},
  140.             {["button"] = {["text"] = "Ok", ["x"] = xSize - 7, ["y"] = ySize - 4, ["length"] = 6}},
  141.             {["button"] = {["text"] = "Back", ["x"] = 2, ["y"] = ySize - 4, ["length"] = 6, ["ignoreFilters"] = true}}
  142.         }},
  143.        
  144.         {"Set Output",
  145.         {
  146.             {["label"] = {["x"] = 11, ["y"] =  7, ["text"] = "Filename"}, ["textbox"] = true, ["x"] = 21, ["y"] =  7, ["length"] = 12, ["filter"] = "\\/:*?\"<>"},
  147.             {["button"] = {["text"] = "Ok", ["x"] = xSize - 7, ["y"] = ySize - 4, ["length"] = 6}},
  148.             {["button"] = {["text"] = "Back", ["x"] = 2, ["y"] = ySize - 4, ["length"] = 6, ["ignoreFilters"] = true}}
  149.         }},
  150.        
  151.         {"File exists; overwrite?",
  152.         {
  153.             {["button"] = {["text"] = "Ok", ["x"] = xSize - 7, ["y"] = ySize - 4, ["length"] = 6}},
  154.             {["button"] = {["text"] = "Back", ["x"] = 2, ["y"] = ySize - 4, ["length"] = 6, ["ignoreFilters"] = true}}
  155.         }}
  156.     },
  157.    
  158.     {
  159.         {"Choose A File"},
  160.        
  161.         {"Enter Destination",
  162.         {
  163.             {["label"] = {["x"] = 11, ["y"] =  7, ["text"] = "X"}, ["textbox"] = true, ["x"] = 14, ["y"] =  7, ["length"] = 7, ["number"] = true},
  164.             {["label"] = {["x"] = 11, ["y"] = 10, ["text"] = "Y"}, ["textbox"] = true, ["x"] = 14, ["y"] = 10, ["length"] = 7, ["number"] = true, ["min"] = 0, ["max"] = 255},
  165.             {["label"] = {["x"] = 11, ["y"] = 13, ["text"] = "Z"}, ["textbox"] = true, ["x"] = 14, ["y"] = 13, ["length"] = 7, ["number"] = true},
  166.             {["label"] = {["x"] = 28, ["y"] =  7, ["text"] = "Rotation 0"},   ["radiobox"] = true, ["x"] = 41, ["y"] =  7, ["set"] = "rotate"},
  167.             {["label"] = {["x"] = 28, ["y"] =  9, ["text"] = "Rotation 90"},  ["radiobox"] = true, ["x"] = 41, ["y"] =  9, ["set"] = "rotate"},
  168.             {["label"] = {["x"] = 28, ["y"] = 11, ["text"] = "Rotation 180"}, ["radiobox"] = true, ["x"] = 41, ["y"] = 11, ["set"] = "rotate"},
  169.             {["label"] = {["x"] = 28, ["y"] = 13, ["text"] = "Rotation 270"}, ["radiobox"] = true, ["x"] = 41, ["y"] = 13, ["set"] = "rotate"},
  170.             {["label"] = {["x"] = 15, ["y"] = 15, ["text"] = "Pre-clear build zone?"}, ["checkbox"] = true, ["x"] = 37, ["y"] = 15},
  171.             {["button"] = {["text"] = "Ok", ["x"] = xSize - 7, ["y"] = ySize - 4, ["length"] = 6}},
  172.             {["button"] = {["text"] = "Back", ["x"] = 2, ["y"] = ySize - 4, ["length"] = 6, ["ignoreFilters"] = true}}
  173.         }},
  174.        
  175.         {"Set Direction",
  176.         {
  177.             {["label"] = {["x"] = 11, ["y"] =  7, ["text"] = "North West"}, ["radiobox"] = true, ["x"] = 23, ["y"] =  7, ["set"] = "horizontalDir"},
  178.             {["label"] = {["x"] = 11, ["y"] =  9, ["text"] = "North East"}, ["radiobox"] = true, ["x"] = 23, ["y"] =  9, ["set"] = "horizontalDir"},
  179.             {["label"] = {["x"] = 11, ["y"] = 11, ["text"] = "South East"}, ["radiobox"] = true, ["x"] = 23, ["y"] = 11, ["set"] = "horizontalDir"},
  180.             {["label"] = {["x"] = 11, ["y"] = 13, ["text"] = "South West"}, ["radiobox"] = true, ["x"] = 23, ["y"] = 13, ["set"] = "horizontalDir"},
  181.             {["label"] = {["x"] = 31, ["y"] =  7, ["text"] = "Upwards"},    ["radiobox"] = true, ["x"] = 41, ["y"] =  7, ["set"] = "verticalDir"},
  182.             {["label"] = {["x"] = 31, ["y"] =  9, ["text"] = "Downwards"},  ["radiobox"] = true, ["x"] = 41, ["y"] =  9, ["set"] = "verticalDir"},
  183.             {["button"] = {["text"] = "Ok", ["x"] = xSize - 7, ["y"] = ySize - 4, ["length"] = 6}},
  184.             {["button"] = {["text"] = "Back", ["x"] = 2, ["y"] = ySize - 4, ["length"] = 6, ["ignoreFilters"] = true}}
  185.         }}
  186.     },
  187.    
  188.     {
  189.         {3, 7, "Low NW:", 1, 1, 1}, {3, 9, "Low NE:", 2, 1, 1}, {3, 11, "Low SW:", 1, 1, 2}, {3, 13, "Low SE:", 2, 1, 2},
  190.         {28, 7, "Top NW:", 1, 2, 1}, {28, 9, "Top NE:", 2, 2, 1}, {28, 11, "Top SW:", 1, 2, 2}, {28, 13, "Top SE:", 2, 2, 2}
  191.     }
  192. }
  193.  
  194. ---------------------------------------------
  195. ------------    Misc Functions   ------------
  196. ---------------------------------------------
  197.  
  198. local function clear(textCol, backCol)
  199.     if textCol then term.setTextColour(textCol) end
  200.     if backCol then term.setBackgroundColour(backCol) end
  201.     for i = 4, ySize - 3 do
  202.         term.setCursorPos(1, i)
  203.         term.clearLine()
  204.     end
  205. end
  206.  
  207. -- Returns whether a click was performed at a given location.
  208. -- If one parameter is passed, it checks to see if y is [1].
  209. -- If two parameters are passed, it checks to see if x is [1] and y is [2].
  210. -- If three parameters are passed, it checks to see if x is between [1]/[2] (non-inclusive) and y is [3].
  211. -- If four paramaters are passed, it checks to see if x is between [1]/[2] and y is between [3]/[4] (non-inclusive).
  212. local function clickedAt(...)
  213.     if myEvent[1] ~= "mouse_click" then return false end
  214.     if #arg == 1 then return (arg[1] == myEvent[4])
  215.     elseif #arg == 2 then return (myEvent[3] == arg[1] and myEvent[4] == arg[2])
  216.     elseif #arg == 3 then return (myEvent[3] > arg[1] and myEvent[3] < arg[2] and myEvent[4] == arg[3])
  217.     else return (myEvent[3] > arg[1] and myEvent[3] < arg[2] and myEvent[4] > arg[3] and myEvent[4] < arg[4]) end
  218. end
  219.  
  220. -- Returns whether one of a given set of keys was pressed.
  221. local function pressedKey(...)
  222.     if myEvent[1] ~= "key" then return false end
  223.     for i=1,#arg do if arg[i] == myEvent[2] then return true end end
  224.     return false
  225. end
  226.  
  227. ---------------------------------------------
  228. ------------  Scanning Function  ------------
  229. ---------------------------------------------
  230.  
  231. local function doScan(filename, xstart, ystart, zstart, xmax, ymax, zmax)
  232.     clear(colours.black, colours.lightGrey)
  233.     term.setCursorPos(22, 5)
  234.     term.write("Scanning")
  235.     term.setBackgroundColour(colours.grey)
  236.     term.setCursorPos(4, 11)
  237.     term.write(string.rep(" ", 45))
  238.     term.setTextColour(colours.grey)
  239.     term.setBackgroundColour(colours.lightGrey)
  240.    
  241.     local x, y, z, scan, fileOut, counter, commonBlocks, last, amount, myTimer = 1, 1, 1, {}, fs.open(shell.resolve(filename), "wb"), 0, {}
  242.    
  243.     local function writeString(text)
  244.         for i = 1, #text do fileOut.write(text:byte(i)) end
  245.     end
  246.    
  247.     local function writeInt(num)
  248.         fileOut.write(bit.band(num, 255))
  249.         fileOut.write(bit.brshift(num, 8))
  250.     end
  251.    
  252.     writeString("BLK")
  253.     fileOut.write(0)  -- File version.
  254.     fileOut.write(commands.getBlockInfo(commands.getBlockPosition()).metadata)  -- Facing of scanning computer.
  255.     fileOut.write(ystart)
  256.     writeInt(xmax)
  257.     fileOut.write(ymax)
  258.     writeInt(zmax)
  259.    
  260.     local scanfuncs = {function()
  261.         local countmax, lastcount = xmax * ymax * zmax, 0
  262.  
  263.         repeat
  264.             myTimer = os.startTimer(60)
  265.             repeat event, id = os.pullEvent("timer") until id == myTimer
  266.            
  267.             term.setBackgroundColour(colours.yellow)
  268.             term.setCursorPos(4, 11)
  269.             term.write(string.rep(" ", math.floor(counter / countmax * 45)))
  270.             term.setBackgroundColour(colours.grey)
  271.             term.write(string.rep(" ", 45 - math.floor(counter / countmax * 45)))
  272.            
  273.             term.setBackgroundColour(colours.lightGrey)
  274.             term.setCursorPos(4, 7)
  275.             term.clearLine()
  276.             term.write(tostring(counter) .. " / " .. tostring(countmax) .. " blocks" .. (counter < countmax and ", ~" .. tostring(counter - lastcount) .. " per minute." or "."))
  277.             term.setCursorPos(4, 9)
  278.             term.clearLine()
  279.             term.write(counter < countmax and tostring(math.floor(counter / countmax * 100)) .. "%, about " .. tostring(math.ceil((countmax - counter) / (counter - lastcount))) .. " minutes remaining." or "100%")
  280.            
  281.             lastcount = counter
  282.         until counter == countmax
  283.     end}
  284.    
  285.     local function checkPos()
  286.         while y < ymax + 1 do
  287.             local myX, myY, myZ = x, y, z
  288.  
  289.             counter = counter + 1
  290.             x = x + 1
  291.             if x > xmax then
  292.                 z = z + 1
  293.                 x = 1
  294.                 if z > zmax then
  295.                     y = y + 1
  296.                     z = 1
  297.                 end
  298.             end
  299.  
  300.             if not scan[myY] then scan[myY] = {} end
  301.             if not scan[myY][myZ] then scan[myY][myZ] = {} end
  302.             scan[myY][myZ][myX] = commands.getBlockInfo(myX + xstart - 1, myY + ystart - 1, myZ + zstart - 1)
  303.         end
  304.  
  305.         os.queueEvent("timer", myTimer)
  306.     end
  307.  
  308.     for i = 2, maxSeekerFuncs + 1 do scanfuncs[i] = checkPos end
  309.  
  310.     local function doWrite()
  311.         if type(commonBlocks[last]) == "table" then
  312.             if #commonBlocks > 255 then writeInt(0) else fileOut.write(0) end
  313.             writeString(commonBlocks[last][1])
  314.             fileOut.write(0)
  315.             fileOut.write(commonBlocks[last][2])
  316.             commonBlocks[last] = true
  317.         elseif #commonBlocks > 255 then writeInt(last) else fileOut.write(last) end
  318.  
  319.         if amount > 255 then
  320.             fileOut.write(0)
  321.             writeInt(amount - 1)
  322.         else fileOut.write(amount) end
  323.     end
  324.  
  325.     scanfuncs[#scanfuncs + 1] = function()
  326.         for y = 1, ymax do for z = 1, zmax do for x = 1, xmax do
  327.             while not (scan[y] and scan[y][z] and scan[y][z][x]) do os.pullEvent("task_complete") end
  328.  
  329.             local next = scan[y][z][x]
  330.             scan[y][z][x] = nil
  331.            
  332.             if not (commonBlocks[next.name] and commonBlocks[next.name][next.metadata]) then
  333.                 commonBlocks[#commonBlocks + 1] = {next.name, next.metadata}
  334.                 if not commonBlocks[next.name] then commonBlocks[next.name] = {} end
  335.                 commonBlocks[next.name][next.metadata] = #commonBlocks
  336.             end
  337.  
  338.             if commonBlocks[next.name][next.metadata] == last then
  339.                 amount = amount + 1
  340.             else
  341.                 if last then doWrite() end
  342.                 last = commonBlocks[next.name][next.metadata]
  343.                 amount = 1
  344.             end
  345.         end scan[y][z] = nil end scan[y] = nil end
  346.     end
  347.    
  348.     parallel.waitForAll(unpack(scanfuncs))
  349.     doWrite()
  350.     fileOut.close()
  351.    
  352.     term.setCursorPos(4, 13)
  353.     term.write("Completed write to \"" .. fs.getName(filename) .. "\".")   
  354.     term.setCursorPos(4, 15)
  355.     term.write("Press any key to continue...")
  356.     os.pullEvent("key")
  357. end
  358.  
  359. ---------------------------------------------
  360. ------------  Building Function  ------------
  361. ---------------------------------------------
  362.  
  363. local function doBuild(filename, x1, y1, z1, rotate, preclear)
  364.     clear(colours.black, colours.lightGrey)
  365.     term.setCursorPos(22, 5)
  366.     term.write("Building")
  367.     term.setBackgroundColour(colours.grey)
  368.     term.setCursorPos(4, 11)
  369.     term.write(string.rep(" ", 45))
  370.     term.setTextColour(colours.grey)
  371.     term.setBackgroundColour(colours.lightGrey)
  372.    
  373.     local fileIn, myTimer = fs.open(filename,"rb")
  374.     if not fileIn then error(filename.." does not exist or cannot be opened.") end
  375.    
  376.     local function readString()
  377.         local result, val = {}
  378.        
  379.         while true do
  380.             val = fileIn.read()
  381.             if val == 0 then return table.concat(result) end
  382.             result[#result + 1] = string.char(val)
  383.         end
  384.     end
  385.    
  386.     local function readInt()
  387.         local result = fileIn.read()
  388.         return result + bit.blshift(fileIn.read(), 8)
  389.     end
  390.    
  391.     do
  392.         local header = 0
  393.         for i = 1, 3 do header = header + fileIn.read() end
  394.         if header ~= 217 then error(filename.." is not a valid WorldPorter data file.") end
  395.     end
  396.    
  397.     fileIn.read()  -- File version.
  398.     local facing = fileIn.read()
  399.     local originalY = fileIn.read()
  400.     local xmax = readInt()
  401.     local ymax = fileIn.read()
  402.     local zmax = readInt()
  403.     local curCommands, counter = 0, 0
  404.    
  405.     local holoTable = {}
  406.        
  407.     parallel.waitForAny(
  408.         function()
  409.             local countmax, lastcount = xmax * ymax * zmax, 0
  410.             if preclear then countmax = countmax * 2 end
  411.  
  412.             repeat
  413.                 myTimer = os.startTimer(60)
  414.                 repeat local event, id = os.pullEvent("timer") until id == myTimer
  415.  
  416.                 term.setBackgroundColour(colours.yellow)
  417.                 term.setCursorPos(4, 11)
  418.                 term.write(string.rep(" ", math.floor(counter / countmax * 45)))
  419.                 term.setBackgroundColour(colours.grey)
  420.                 term.write(string.rep(" ", 45 - math.floor(counter / countmax * 45)))
  421.  
  422.                 term.setBackgroundColour(colours.lightGrey)
  423.                 term.setCursorPos(4, 7)
  424.                 term.clearLine()
  425.                 term.write(tostring(counter) .. " / " .. tostring(countmax) .. " blocks" .. (counter < countmax and ", ~" .. tostring(counter - lastcount) .. " per minute." or "."))
  426.                 term.setCursorPos(4, 9)
  427.                 term.clearLine()
  428.                 term.write(counter < countmax and tostring(math.floor(counter / countmax * 100)) .. "%, about " .. tostring(math.ceil((countmax - counter) / (counter - lastcount))) .. " minutes remaining." or "100%")
  429.  
  430.                 lastcount = counter
  431.             until counter == countmax
  432.         end,
  433.  
  434.         function()
  435.             local record, commonBlocks, secondPass, thirdPass, fourthPass, doorCache, toCoordsString = {nil, nil, 0}, {}, {}, {}, {}, {}
  436.            
  437.             toCoordsString = function(x, y, z) return tostring(x).." "..tostring(y).." "..tostring(z) end
  438.            
  439.             local function doCommand(doThis)
  440.                 if doThis[1] > 0 and doThis[1] < 9 and doThis[2] > 0 and doThis[2] < 9 and doThis[3] > 0 and doThis[3] < 9 then
  441.                     holoTable[#holoTable + 1] = {["x"] = doThis[1] - 1, ["y"] = doThis[2] - 1, ["z"] = doThis[3] - 1, ["name"] = doThis[4], ["meta"] = doThis[5]}
  442.                 end
  443.                 counter = counter + 1
  444.             end
  445.            
  446.             if preclear then for y = ymax, 1, -1 do for z = 1, zmax do for x = 1, xmax do doCommand("setblock " .. toCoordsString(x, y, z) .. " minecraft:air 0") end end end end
  447.            
  448.             for y = 1, ymax do for z = 1, zmax do for x = 1, xmax do
  449.                 if record[3] == 0 then
  450.                     local block
  451.                     if #commonBlocks > 255 then block = readInt() else block = fileIn.read() end
  452.  
  453.                     if block == 0 then
  454.                         block = readString()
  455.                         commonBlocks[#commonBlocks + 1] = {block, fileIn.read()}
  456.                         block = #commonBlocks
  457.                     end
  458.  
  459.                     local amount = fileIn.read()
  460.                     record = {commonBlocks[block][1], commonBlocks[block][2], amount == 0 and readInt() or amount - 1}
  461.                     if forcedTranslation[record[1]] and forcedTranslation[record[1]][record[2]] then record[2] = forcedTranslation[record[1]][record[2]] end
  462.                     --if rotationTranslation[record[1]] and rotationTranslation[record[1]][record[2]] then for i = 0, rotate - 1 do record[2] = rotationTranslation[record[1]][record[2]] end end
  463.                 else record[3] = record[3] - 1 end
  464.  
  465.                 local doThis = {x, y, z, record[1], record[2]}
  466.                 if preclear and record[1] == "minecraft:air" then
  467.                     counter = counter + 1
  468.                 elseif doorBlocks[record[1]] then
  469.                     if record[2] > 7 then
  470.                         secondPass[#secondPass + 1] = doorCache[toCoordsString(x, y, z)]
  471.                         secondPass[#secondPass + 1] = doThis
  472.                         doorCache[toCoordsString(x, y, z)] = nil
  473.                     else doorCache[toCoordsString(x, y + 1, z)] = doThis end
  474.                 elseif dependantBlocks[record[1]] then
  475.                     thirdPass[#thirdPass + 1] = doThis
  476.                 elseif fluidBlocks[record[1]] then
  477.                     fourthPass[#fourthPass + 1] = doThis
  478.                 else doCommand(doThis) end
  479.             end end end
  480.  
  481.             fileIn.close()
  482.  
  483.             for i = 1, #secondPass do doCommand(secondPass[i]) end
  484.             for i = 1, #thirdPass do doCommand(thirdPass[i]) end
  485.             for i = 1, #fourthPass do doCommand(fourthPass[i]) end
  486.            
  487.             os.queueEvent("timer", myTimer)
  488.             while true do os.pullEvent("stopResumingMe") end
  489.         end
  490.     )
  491.    
  492.     holo.clear("minecraft:air", 0)
  493.     holo.draw(holoTable)
  494.    
  495.     term.setCursorPos(4, 13)
  496.     term.write("Completed build of \"" .. fs.getName(filename) .. "\".")   
  497.     term.setCursorPos(4, 15)
  498.     term.write("Press any key to continue...")
  499.     os.pullEvent("key")
  500. end
  501.  
  502. ---------------------------------------------
  503. ------------    GUI Functions    ------------
  504. ---------------------------------------------
  505.  
  506. local function fileBrowser()
  507.     local bump = math.floor((xSize - 49) / 2) + 1
  508.  
  509.     while true do
  510.         local displayList, position, lastPosition, animationTimer, curCount, gapTimer, lastProgress = {}, 1, 0, os.startTimer(0), 1
  511.         if #shell.resolve(".") > 0 then displayList[1] = ".." end
  512.  
  513.         do
  514.             local fullList = fs.list(shell.resolve("."))
  515.             table.sort(fullList, function (a, b) return string.lower(a) < string.lower(b) end)
  516.             for i = 1, #fullList do if fs.isDir(shell.resolve(fullList[i])) then displayList[#displayList + 1] = fullList[i] end end
  517.             for i = 1, #fullList do if fullList[i]:sub(#fullList[i] - 3):lower() == ".blk" then displayList[#displayList + 1] = fs.getName(fullList[i]) end end
  518.         end
  519.  
  520.         while true do
  521.             myEvent = {os.pullEvent()}
  522.  
  523.             -- Track animations (bouncing cursor + scrolling marquee).
  524.             if myEvent[1] == "timer" and myEvent[2] == animationTimer then
  525.                 curCount = curCount == 4 and 1 or (curCount + 1)
  526.                 animationTimer = os.startTimer(0.5)
  527.                 myEvent[1] = "cabbage"
  528.  
  529.             -- Bail.
  530.             elseif pressedKey(keys.backspace) or (myEvent[1] == "mouse_click" and myEvent[2] == 2) then
  531.                 return nil
  532.  
  533.             -- Move down the list.
  534.             elseif pressedKey(keys.down, keys.s) or (myEvent[1] == "mouse_scroll" and myEvent[2] == 1) then
  535.                 position = position == #displayList and 1 or (position + 1)
  536.  
  537.             -- Move up the list.
  538.             elseif pressedKey(keys.up, keys.w) or (myEvent[1] == "mouse_scroll" and myEvent[2] == -1) then
  539.                 position = position == 1 and #displayList or (position - 1)
  540.  
  541.             -- Select something.
  542.             elseif pressedKey(keys.enter, keys.space) or clickedAt(math.floor(ySize / 2) + 1) then
  543.                 if fs.isDir(shell.resolve(displayList[position])) then
  544.                     shell.setDir(shell.resolve(displayList[position]))
  545.                     break
  546.                 else return shell.resolve(displayList[position]) end
  547.            
  548.             -- User clicked somewhere on the file list; move that entry to the currently-selected position.
  549.             elseif clickedAt(0, xSize + 1, 3, ySize - 2) then
  550.                 position = position + myEvent[4] - math.floor(ySize / 2) - 1
  551.                 position = position > #displayList and #displayList or position
  552.                 position = position < 1 and 1 or position
  553.             end
  554.  
  555.             -- Update other screen stuff.
  556.             if myEvent[1] ~= "timer" then
  557.                 -- File list.
  558.                 term.setBackgroundColour(colours.black)
  559.                 for y = position == lastPosition and (math.floor(ySize / 2) + 1) or 4, position == lastPosition and (math.floor(ySize / 2) + 1) or (ySize - 3) do
  560.                     local thisLine = y + position - math.floor(ySize / 2) - 1
  561.  
  562.                     if displayList[thisLine] then
  563.                         local thisString = displayList[thisLine]
  564.                         thisString = fs.isDir(shell.resolve(thisString)) and "["..thisString.."]" or thisString:sub(1, #thisString-4)
  565.  
  566.                         if thisLine == position then
  567.                             term.setCursorPos(math.floor((xSize - #thisString - 8) / 2) + 1, y)
  568.                             term.clearLine()
  569.                             term.setTextColour(colours.red)
  570.                             term.write(cursor[curCount][1])
  571.                             term.setTextColour(colours.orange)
  572.                             term.write(thisString)
  573.                             term.setTextColour(colours.red)
  574.                             term.write(cursor[curCount][2])
  575.                         else
  576.                             term.setCursorPos(math.floor((xSize - #thisString) / 2) + 1, y)
  577.                             term.clearLine()
  578.  
  579.                             if y == 4 or y == ySize - 3 then term.setTextColour(colours.black)
  580.                             elseif y == 5 or y == ySize - 4 then term.setTextColour(colours.grey)
  581.                             elseif y == 6 or y == ySize - 5 then term.setTextColour(colours.lightGrey)
  582.                             else term.setTextColour(colours.white) end
  583.  
  584.                             term.write(thisString)
  585.                         end
  586.                     else
  587.                         term.setCursorPos(1, y)
  588.                         term.clearLine()
  589.                     end
  590.                 end
  591.  
  592.                 lastPosition = position
  593.             end
  594.         end
  595.     end
  596. end
  597.  
  598. local function form(...)
  599.     local radio = {}
  600.    
  601.     for index, element in ipairs(arg) do
  602.         if element.button then
  603.             term.setTextColour(buttonText)
  604.             term.setBackgroundColour(buttonBack)
  605.             term.setCursorPos(element.button.x, element.button.y)
  606.             term.write(string.rep(" ", math.ceil((element.button.length - #element.button.text) / 2))..element.button.text..string.rep(" ", math.floor((element.button.length - #element.button.text) / 2)))
  607.         else
  608.             term.setTextColour(labelText)
  609.             term.setBackgroundColour(labelBack)
  610.             term.setCursorPos(element.label.x, element.label.y)
  611.             element.label.text = tostring(element.label.text)
  612.             term.write(element.label.text .. (element.textbox and ":" or ""))
  613.             term.setBackgroundColour(inputBack)
  614.             term.setCursorPos(element.x, element.y)
  615.            
  616.             term.setTextColour(inputText)
  617.             if element.textbox then
  618.                 element.content = element.content and tostring(element.content) or ""
  619.                 element.curX = #element.content + 1
  620.                 element.scroll = math.max(1, #element.content - element.length + 2)
  621.                 term.write(element.content:sub(element.scroll, element.scroll + element.length - 1) .. string.rep(" ", math.max(0, element.length - #element.content + element.scroll - 1)))
  622.             elseif element.radiobox then
  623.                 if radio[element.set] then
  624.                     radio[element.set][#radio[element.set] + 1] = index
  625.                     term.write(" ")
  626.                 else
  627.                     radio[element.set] = {index, ["set"] = 1}
  628.                     term.write("O")
  629.                 end
  630.             elseif element.checkbox then
  631.                 if type(element.content) == "nil" then element.content = false end
  632.                 term.write(element.content and "X" or " ")
  633.             end
  634.         end
  635.     end
  636.    
  637.     term.setTextColour(inputText)
  638.    
  639.     local curElement = arg[1]
  640.     if curElement.textbox then
  641.         term.setCursorPos(curElement.x + curElement.curX - curElement.scroll, curElement.y)
  642.         term.setCursorBlink(true)
  643.     elseif curElement.button then
  644.         term.setCursorBlink(false)
  645.         term.setTextColour(buttonText)
  646.         term.setBackgroundColour(buttonBack)
  647.         term.setCursorPos(curElement.button.x, curElement.button.y)
  648.         term.write("["..string.rep(" ", math.ceil((curElement.button.length - #curElement.button.text) / 2) - 1)..curElement.button.text..string.rep(" ", math.floor((curElement.button.length - #curElement.button.text) / 2) - 1).."]")
  649.     else
  650.         term.setCursorPos(curElement.x, curElement.y)
  651.         term.setCursorBlink(true)
  652.     end
  653.    
  654.     while true do
  655.         local myEvent, oldElement, finished = {os.pullEvent()}, curElement, false
  656.        
  657.         if (myEvent[1] == "key" and (myEvent[2] == keys.tab or myEvent[2] == keys.down)) or (myEvent[1] == "mouse_scroll" and myEvent[2] == 1) then
  658.             for i = 1, #arg do if arg[i] == curElement then
  659.                 curElement = arg[i == #arg and 1 or i + 1]
  660.                 break
  661.             end end
  662.         elseif (myEvent[1] == "key" and myEvent[2] == keys.up) or (myEvent[1] == "mouse_scroll" and myEvent[2] == -1) then
  663.             for i = 1, #arg do if arg[i] == curElement then
  664.                 curElement = arg[i == 1 and #arg or i - 1]
  665.                 break
  666.             end end
  667.         elseif myEvent[1] == "mouse_click" and myEvent[2] == 1 then
  668.             for index, element in ipairs(arg) do
  669.                 if element.textbox and myEvent[3] >= element.x and myEvent[3] < element.x + element.length and myEvent[4] == element.y then
  670.                     curElement = element
  671.                     curElement.curX = math.max(1, math.min(myEvent[3] - curElement.x + curElement.scroll, #curElement.content + 1))
  672.                     term.setCursorPos(curElement.x + curElement.curX - curElement.scroll, curElement.y)
  673.                     break
  674.                 elseif element.radiobox and myEvent[3] == element.x and myEvent[4] == element.y then
  675.                     for i = 1, #radio[element.set] do
  676.                         term.setCursorPos(arg[radio[element.set][i]].x, arg[radio[element.set][i]].y)
  677.                         if radio[element.set][i] == index then
  678.                             radio[element.set].set = i
  679.                             term.write("O")
  680.                         else term.write(" ") end
  681.                     end
  682.                    
  683.                     curElement = element
  684.                     term.setCursorPos(curElement.x, curElement.y)
  685.                     break
  686.                 elseif element.checkbox and myEvent[3] == element.x and myEvent[4] == element.y then
  687.                     curElement = element
  688.                     element.content = not element.content
  689.                     term.setCursorPos(curElement.x, curElement.y)
  690.                     term.write(element.content and "X" or " ")
  691.                     term.setCursorPos(curElement.x, curElement.y)
  692.                     break
  693.                 elseif element.button and myEvent[3] >= element.button.x and myEvent[3] < element.button.x + element.button.length and myEvent[4] == element.button.y then
  694.                     finished = true
  695.                     curElement = element
  696.                     break
  697.                 end
  698.             end
  699.         elseif myEvent[1] == "paste" and curElement.textbox then
  700.             for i = 1, #myEvent[2] do os.queueEvent("char", myEvent[2]:sub(i, i)) end      
  701.         elseif myEvent[1] == "char" and curElement.textbox then
  702.             curElement.content = curElement.content:sub(1, curElement.curX - 1) .. myEvent[2] .. curElement.content:sub(curElement.curX, #curElement.content)
  703.             curElement.curX = curElement.curX + 1
  704.             if curElement.curX - curElement.scroll + 1 > curElement.length then curElement.scroll = curElement.scroll + 1 end
  705.  
  706.             term.setCursorPos(curElement.x, curElement.y)
  707.             term.write(curElement.content:sub(curElement.scroll, curElement.scroll + curElement.length - 1))
  708.             term.setCursorPos(curElement.x + curElement.curX - curElement.scroll, curElement.y)
  709.         elseif myEvent[1] == "key" then
  710.             if myEvent[2] == keys.enter then
  711.                 for i = 1, #arg do
  712.                     if curElement.button then break end
  713.                     if arg[i] == curElement then curElement = arg[i == #arg and 1 or i + 1] end
  714.                 end
  715.                 finished = true
  716.             elseif curElement.textbox then
  717.                 if myEvent[2] == keys.left and curElement.curX > 1 then
  718.                     curElement.curX = curElement.curX - 1
  719.                     if curElement.curX < curElement.scroll then curElement.scroll = curElement.curX end
  720.                 elseif myEvent[2] == keys.right and curElement.curX < #curElement.content + 1 then
  721.                     curElement.curX = curElement.curX + 1
  722.                     if curElement.curX > curElement.scroll + curElement.length - 1 then curElement.scroll = curElement.scroll + 1 end
  723.                 elseif myEvent[2] == keys.backspace and curElement.curX > 1 then
  724.                     curElement.content = curElement.content:sub(1, curElement.curX - 2) .. curElement.content:sub(curElement.curX, #curElement.content)
  725.                     curElement.curX = curElement.curX - 1
  726.                     if curElement.curX < curElement.scroll then curElement.scroll = curElement.curX end
  727.                 elseif myEvent[2] == keys.delete and curElement.curX <= #curElement.content then
  728.                     curElement.content = curElement.content:sub(1, curElement.curX - 1) .. curElement.content:sub(curElement.curX + 1, #curElement.content)
  729.                 end
  730.  
  731.                 term.setCursorPos(curElement.x, curElement.y)
  732.                 term.write(curElement.content:sub(curElement.scroll, curElement.scroll + curElement.length - 1) .. string.rep(" ", math.max(0, curElement.length - #curElement.content + curElement.scroll - 1)))
  733.                 term.setCursorPos(curElement.x + curElement.curX - curElement.scroll, curElement.y)
  734.             elseif curElement.radiobox and myEvent[2] == keys.space and arg[radio[curElement.set][radio[curElement.set].set]] ~= curElement then
  735.                 local set = radio[curElement.set]
  736.                 for i = 1, #set do
  737.                     term.setCursorPos(arg[set[i]].x, arg[set[i]].y)
  738.                     if arg[set[i]] == curElement then
  739.                         set.set = i
  740.                         term.write("O")
  741.                     else term.write(" ") end
  742.                 end
  743.                 term.setCursorPos(curElement.x, curElement.y)
  744.             elseif curElement.checkbox and myEvent[2] == keys.space then
  745.                 curElement.content = not curElement.content
  746.                 term.write(curElement.content and "X" or " ")
  747.                 term.setCursorPos(curElement.x, curElement.y)
  748.             elseif curElement.button and myEvent[2] == keys.space then
  749.                 finished = true
  750.                 os.pullEvent("char")
  751.             end
  752.         end
  753.        
  754.         if oldElement ~= curElement then
  755.             if oldElement.button then
  756.                 term.setTextColour(buttonText)
  757.                 term.setBackgroundColour(buttonBack)
  758.                 term.setCursorPos(oldElement.button.x, oldElement.button.y)
  759.                 term.write(string.rep(" ", math.ceil((oldElement.button.length - #oldElement.button.text) / 2))..oldElement.button.text..string.rep(" ", math.floor((oldElement.button.length - #oldElement.button.text) / 2)))
  760.             end
  761.            
  762.             if curElement.button then
  763.                 term.setCursorBlink(false)
  764.                 term.setTextColour(buttonText)
  765.                 term.setBackgroundColour(buttonBack)
  766.                 term.setCursorPos(curElement.button.x, curElement.button.y)
  767.                 term.write("["..string.rep(" ", math.ceil((curElement.button.length - #curElement.button.text) / 2) - 1)..curElement.button.text..string.rep(" ", math.floor((curElement.button.length - #curElement.button.text) / 2) - 1).."]")
  768.             else
  769.                 term.setTextColour(inputText)
  770.                 term.setBackgroundColour(inputBack)
  771.                
  772.                 if curElement.textbox then
  773.                     term.setCursorPos(curElement.x + curElement.curX - curElement.scroll, curElement.y)
  774.                 else
  775.                     term.setCursorPos(curElement.x, curElement.y)
  776.                 end
  777.                
  778.                 term.setCursorBlink(true)
  779.             end
  780.         end
  781.        
  782.         if finished then
  783.             local results, flash = {curElement.button.text}, {}
  784.            
  785.             for index, element in ipairs(arg) do
  786.                 if element.textbox then
  787.                     if element.number then
  788.                         results[element.label.text] = tonumber(element.content)
  789.                         if results[element.label.text] then
  790.                             if element.min and results[element.label.text] < element.min then
  791.                                 results[element.label.text] = nil
  792.                             elseif element.max and results[element.label.text] > element.max then
  793.                                 results[element.label.text] = nil
  794.                             end
  795.                         end
  796.                     elseif element.filter then
  797.                         local ok = true
  798.                         for i = 1, #element.filter do if element.content:find(element.filter:sub(i,i)) then
  799.                             ok = false
  800.                             break
  801.                         end end
  802.                         if ok and #element.content > 0 then results[element.label.text] = element.content end
  803.                     else results[element.label.text] = element.content end
  804.  
  805.                     if not results[element.label.text] then
  806.                         flash[#flash + 1] = index
  807.                         results[element.label.text] = element.content
  808.                     end
  809.                 elseif element.checkbox then
  810.                     results[element.label.text] = element.content
  811.                 end
  812.             end
  813.            
  814.             for key, element in pairs(radio) do results[key] = element.set end
  815.  
  816.             if #flash > 0 and not curElement.button.ignoreFilters then
  817.                 term.setTextColour(colours.brown)
  818.                 term.setBackgroundColour(colors.red)
  819.                
  820.                 for i = 1, #flash do
  821.                     local element = arg[flash[i]]
  822.                     term.setCursorPos(element.x, element.y)
  823.                     term.write(element.content:sub(element.scroll, element.scroll + element.length - 1) .. string.rep(" ", math.max(0, element.length - #element.content + element.scroll - 1)))
  824.                 end
  825.                
  826.                 sleep(0.25)
  827.                
  828.                 term.setTextColour(inputText)
  829.                 term.setBackgroundColour(inputBack)
  830.                
  831.                 for i = 1, #flash do
  832.                     local element = arg[flash[i]]
  833.                     term.setCursorPos(element.x, element.y)
  834.                     term.write(element.content:sub(element.scroll, element.scroll + element.length - 1) .. string.rep(" ", math.max(0, element.length - #element.content + element.scroll - 1)))
  835.                 end
  836.             else return results end
  837.         end
  838.     end
  839. end
  840.  
  841. ---------------------------------------------
  842. ------------         Init        ------------
  843. ---------------------------------------------
  844.  
  845. do
  846.     local temp = {}
  847.     for i = 1, #dependantBlocks do temp[dependantBlocks[i]] = i end
  848.     dependantBlocks = temp
  849.    
  850.     temp = {}
  851.     for i = 1, #doorBlocks do temp[doorBlocks[i]] = i end
  852.     doorBlocks = temp
  853.    
  854.     temp = {}
  855.     for i = 1, #fluidBlocks do temp[fluidBlocks[i]] = i end
  856.     fluidBlocks = temp
  857. end
  858.  
  859. for key, value in pairs(rotationTranslation) do
  860.     local temp = {}
  861.     for i = 1, #value do
  862.         for j = 1, #value[i] - 1 do temp[value[i][j]] = value[i][j + 1] end
  863.         temp[value[i][#value[i]]] = value[i][1]
  864.     end
  865.     rotationTranslation[key] = temp
  866. end
  867.  
  868. for key, value in pairs(forcedTranslation) do
  869.     local temp = {}
  870.     for i = 1, #value do temp[value[i][1]] = value[i][2] end
  871.     forcedTranslation[key] = temp
  872. end
  873.  
  874. term.setTextColour(colours.black)
  875. term.setBackgroundColour(colours.yellow)
  876.  
  877. for j = 0, 1 do for i = 1, 3 do
  878.     term.setCursorPos(1, i + (j * (ySize - 3)))
  879.     term.clearLine()
  880. end end
  881.  
  882. term.setCursorPos(5, 2)
  883. term.write("WorldPorter")
  884. term.setCursorPos(2, ySize - 1)
  885. --term.write("This system is facing "..({"unknown", "north", "south", "west", "east"})[commands.getBlockInfo(commands.getBlockPosition()).metadata]..".")
  886.  
  887. paintutils.drawPixel(1, 1, colours.blue)
  888. paintutils.drawPixel(2, 1, colours.green)
  889. paintutils.drawPixel(3, 1, colours.yellow)
  890. paintutils.drawPixel(1, 2, colours.grey)
  891. paintutils.drawPixel(2, 2, colours.white)
  892. paintutils.drawPixel(3, 2, colours.lightBlue)
  893. paintutils.drawPixel(1, 3, colours.orange)
  894. paintutils.drawPixel(2, 3, colours.lime)
  895. paintutils.drawPixel(3, 3, colours.red)
  896.  
  897. ---------------------------------------------
  898. ------------  Main Program Loop  ------------
  899. ---------------------------------------------
  900.  
  901. while true do
  902.     local menuInfo = menu[curMenu]
  903.     if curMenu > 1 then menuInfo = menuInfo[subMenu] end
  904.    
  905.     if curMenu == 3 and subMenu == 1 then
  906.         menu[3][1] = {fileBrowser()}
  907.         if menu[3][1][1] then
  908.             local fileIn = fs.open(menu[3][1][1],"rb")
  909.             for i = 1, 3 do fileIn.read() end
  910.             if fileIn.read() > 0 then error("Requires a later version of WorldPorter; suggest updating with shell commands:\nrm worldporter\npastebin get pXjdQDf6 worldporter") end
  911.             fileIn.close()
  912.            
  913.             doBuild(menu[3][1][1])
  914.             curMenu, subMenu = 1, 1
  915.         else curMenu = 1 end
  916.     elseif curMenu == 2 and subMenu == 3 then
  917.         if fs.exists(shell.resolve(menu[2][2][3].Filename)) then
  918.             term.setTextColour(labelText)
  919.             term.setBackgroundColour(labelBack)
  920.             term.setCursorPos(11, 9)
  921.             term.write(menuInfo[1])
  922.             subMenu = form(unpack(menuInfo[2]))[1] == "Ok" and 4 or 2
  923.         else subMenu = 4 end
  924.     elseif subMenu == 4 then
  925.         local bounds, func, params = {{},{},{}}
  926.         if curMenu == 2 then
  927.             bounds[1][1] = menu[2][1][3].X1
  928.             bounds[1][2] = menu[2][1][3].X2
  929.             bounds[2][1] = menu[2][1][3].Y1
  930.             bounds[2][2] = menu[2][1][3].Y2
  931.             bounds[3][1] = menu[2][1][3].Z1
  932.             bounds[3][2] = menu[2][1][3].Z2
  933.             table.sort(bounds[1])
  934.             table.sort(bounds[2])
  935.             table.sort(bounds[3])
  936.            
  937.             func = doScan
  938.             params = {menu[2][2][3].Filename, bounds[1][1], bounds[2][1], bounds[3][1], bounds[1][2] - bounds[1][1] + 1, bounds[2][2] - bounds[2][1] + 1, bounds[3][2] - bounds[3][1] + 1}
  939.         else
  940.             bounds[1][1] = menu[3][2][3].X
  941.             bounds[1][2] = menu[3][2][3].X + (((menu[3][2][3].rotate == 1 or menu[3][2][3].rotate == 3) and menu[3][1][3] or menu[3][1][5]) - 1) * ((menu[3][3][3].horizontalDir == 2 or menu[3][3][3].horizontalDir == 3) and 1 or -1)
  942.             bounds[2][1] = menu[3][2][3].Y
  943.             bounds[2][2] = menu[3][2][3].Y + (menu[3][1][4] - 1) * (menu[3][3][3].verticalDir == 1 and 1 or -1)
  944.             bounds[3][1] = menu[3][2][3].Z
  945.             bounds[3][2] = menu[3][2][3].Z + (((menu[3][2][3].rotate == 1 or menu[3][2][3].rotate == 3) and menu[3][1][5] or menu[3][1][3]) - 1) * (menu[3][3][3].horizontalDir > 2 and 1 or -1)
  946.             table.sort(bounds[1])
  947.             table.sort(bounds[2])
  948.             table.sort(bounds[3])
  949.            
  950.             func = doBuild
  951.             params = {menu[3][1][1], bounds[1][1] - 1, bounds[2][1] - 1, bounds[3][1] - 1, menu[3][2][3].rotate - 1, menu[3][2][3]["Pre-clear build zone?"]}
  952.         end
  953.        
  954.         clear(colours.black, colours.lightGrey)
  955.         term.setCursorPos(13, 5)
  956.         term.write("Proceed With These Bounds?")
  957.        
  958.         for _, value in pairs(menu[4]) do
  959.             term.setTextColour(labelText)
  960.             term.setCursorPos(value[1], value[2])
  961.             term.write(value[3])
  962.            
  963.             local pos = tostring(bounds[1][value[4]])..","..tostring(bounds[2][value[5]])..","..tostring(bounds[3][value[6]])
  964.             term.setTextColour(colours.grey)
  965.             term.setCursorPos(value[1] - #pos + 22, value[2])
  966.             term.write(pos)
  967.         end
  968.        
  969.         if form(unpack(menu[2][3][2]))[1] == "Ok" then
  970.             func(unpack(params))
  971.             curMenu, subMenu = 1, 1
  972.         else subMenu = curMenu == 2 and 2 or 3 end
  973.     else
  974.         clear(colours.black, colours.lightGrey)
  975.        
  976.         term.setCursorPos((xSize - #menuInfo[1]) / 2 + 1, 5)
  977.         term.write(menuInfo[1])
  978.        
  979.         menuInfo[3] = form(unpack(menuInfo[2]))
  980.         for i = 1, #menuInfo[2] do if menuInfo[2].textbox then menuInfo[2].content = menuInfo[3][menuInfo[2].label.text] end end
  981.        
  982.         if curMenu == 1 then
  983.             if menuInfo[3][1] == "Scan" then
  984.                 curMenu = 2
  985.             elseif menuInfo[3][1] == "Build" then
  986.                 curMenu = 3
  987.             elseif menuInfo[3][1] == "Quit" then
  988.                 term.setTextColour(colours.white)
  989.                 term.setBackgroundColour(colours.black)
  990.                 term.clear()
  991.                 term.setCursorPos(1, 1)
  992.                 print("Thanks for using WorldPorter!\n")
  993.                 shell.setDir(startDir)
  994.                 error()
  995.             end
  996.         else
  997.             if menuInfo[3][1] == "Ok" then
  998.                 if curMenu == 2 and subMenu == 2 and menuInfo[3].Filename:sub(-4):lower() ~= ".blk" then menuInfo[3].Filename = menuInfo[3].Filename .. ".blk" end
  999.                 for key, value in pairs(menuInfo[3]) do if type(value) == number then menuInfo[3][key] = math.floor(value) end end
  1000.                 subMenu = subMenu + 1
  1001.             else
  1002.                 subMenu = subMenu - 1
  1003.                 if subMenu < 1 or (curMenu == 3 and subMenu < 2) then
  1004.                     subMenu = 1
  1005.                     curMenu = 1
  1006.                 end
  1007.             end
  1008.         end
  1009.     end
  1010. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement