Advertisement
BombBloke

WorldPorter (ComputerCraft)

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