VirtualParkourStore

Virtual Parkour V1.2

Jul 23rd, 2015
313
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 91.77 KB | None | 0 0
  1. local parkourVersion=1.2;
  2. local updateURL="http://pastebin.com/raw.php?i=rKXPu5LU";
  3.  
  4. if not commands then
  5.     print("This program only runs on a Command Computer(creative only)");
  6.     error();
  7. end
  8. local pos={commands.getBlockPosition()};
  9. local selectionPos={pos[1],pos[2],pos[3],4};
  10. local forceMonitorSelection=false;
  11. local canStayInCreative=false;
  12. local allowPlayerUnlocking=true;
  13. local confiscateItems=true;
  14. if fs.exists("parkour_config") then
  15.     local h=fs.open("parkour_config","r");
  16.     local lineNum=0;
  17.     while true do
  18.         local line=h.readLine();
  19.         if line==nil then break; end
  20.         lineNum=lineNum+1;
  21.         local equal=line:find("=");
  22.         if equal then
  23.             local param=line:sub(1,equal-1);
  24.             local value=line:sub(equal+1);
  25.             if param=="player_selection_x" then
  26.                 selectionPos[1]=tonumber(value);
  27.                 if selectionPos[1]==nil then
  28.                     h.close();
  29.                     printError("Config line #"..lineNum.." player_selection_x value is invalid");
  30.                     error();
  31.                 end
  32.                 selectionPos[1]=math.floor(selectionPos[1]);
  33.             elseif param=="player_selection_y" then
  34.                 selectionPos[2]=tonumber(value);
  35.                 if selectionPos[2]==nil then
  36.                     h.close();
  37.                     printError("Config line #"..lineNum.." player_selection_y value is invalid");
  38.                     error();
  39.                 end
  40.                 selectionPos[2]=math.floor(selectionPos[2]);
  41.             elseif param=="player_selection_z" then
  42.                 selectionPos[3]=tonumber(value);
  43.                 if selectionPos[3]==nil then
  44.                     h.close();
  45.                     printError("Config line #"..lineNum.." player_selection_z value is invalid");
  46.                     error();
  47.                 end
  48.                 selectionPos[3]=math.floor(selectionPos[3]);
  49.             elseif param=="player_selection_shift_x" then
  50.                 local dist=tonumber(value);
  51.                 if dist==nil then
  52.                     h.close();
  53.                     printError("Config line #"..lineNum.." player_selection_shift_x value is invalid");
  54.                     error();
  55.                 end
  56.                 selectionPos[1]=math.ceil(selectionPos[1]+dist);
  57.             elseif param=="player_selection_shift_y" then
  58.                 local dist=tonumber(value);
  59.                 if dist==nil then
  60.                     h.close();
  61.                     printError("Config line #"..lineNum.." player_selection_shift_y value is invalid");
  62.                     error();
  63.                 end
  64.                 selectionPos[2]=math.ceil(selectionPos[2]+dist);
  65.             elseif param=="player_selection_shift_z" then
  66.                 local dist=tonumber(value);
  67.                 if dist==nil then
  68.                     h.close();
  69.                     printError("Config line #"..lineNum.." player_selection_shift_z value is invalid");
  70.                     error();
  71.                 end
  72.                 selectionPos[3]=math.ceil(selectionPos[3]+dist);
  73.             elseif param=="player_selection_range" then
  74.                 selectionPos[4]=tonumber(value);
  75.                 if selectionPos[4]==nil then
  76.                     h.close();
  77.                     printError("Config line #"..lineNum.." player_selection_range value is invalid");
  78.                     error();
  79.                 end
  80.                 selectionPos[4]=math.ceil(selectionPos[4]);
  81.             elseif param=="always_allow_monitor_selection" then
  82.                 if value=="true" then
  83.                     forceMonitorSelection=true;
  84.                 elseif value=="false" then
  85.                     forceMonitorSelection=false;
  86.                 else
  87.                     h.close();
  88.                     printError("Config line #"..lineNum.." always_allow_monitor_selection value is invalid, it can only be true or false, not \""..value.."\"");
  89.                     error();
  90.                 end
  91.             elseif param=="can_stay_in_creative" then
  92.                 if value=="true" then
  93.                     canStayInCreative=true;
  94.                 elseif value=="false" then
  95.                     canStayInCreative=false;
  96.                 else
  97.                     h.close();
  98.                     printError("Config line #"..lineNum.." can_stay_in_creative value is invalid, it can only be true or false, not \""..value.."\"");
  99.                     error();
  100.                 end
  101.             elseif param=="allow_player_unlocking" then
  102.                 if value=="true" then
  103.                     allowPlayerUnlocking=true;
  104.                 elseif value=="false" then
  105.                     allowPlayerUnlocking=false;
  106.                 else
  107.                     h.close();
  108.                     printError("Config line #"..lineNum.." allow_player_unlocking value is invalid, it can only be true or false, not \""..value.."\"");
  109.                     error();
  110.                 end
  111.             elseif param=="confiscate_items" then
  112.                 if value=="true" then
  113.                     confiscateItems=true;
  114.                 elseif value=="false" then
  115.                     confiscateItems=false;
  116.                 else
  117.                     h.close();
  118.                     printError("Config line #"..lineNum.." confiscate_items value is invalid, it can only be true or false, not \""..value.."\"");
  119.                     error();
  120.                 end
  121.             elseif line~="" and line:sub(1,1)~="#" then
  122.                 h.close();
  123.                 printError("Invalid config line #"..lineNum..": Invalid parameter \""..param.."\"");
  124.                 error();
  125.             end
  126.         else
  127.             if line~="" and line:sub(1,1)~="#" then
  128.                 h.close();
  129.                 printError("Invalid config line #"..lineNum..": No = found");
  130.                 error();
  131.             end
  132.         end
  133.     end
  134.     h.close();
  135. end
  136.  
  137. local structures={
  138.     empty=function(what,s)
  139.         if what=="place_marker" then
  140.             return true;
  141.         elseif what=="build" then
  142.             return {
  143.                 blocks={},
  144.                 areaCheck={},
  145.                 exit={0,0,0},
  146.                 next="x+",
  147.             };
  148.         end
  149.     end,
  150. };
  151. local structNames={};   --For structure debugging
  152. function structNamePresent(name)
  153.     local isItThere=false;
  154.     for i,s in ipairs(structNames) do
  155.         if s==name then isItThere=true;break; end
  156.     end
  157.     return isItThere;
  158. end
  159.  
  160. local checkpointStride=100;
  161. local structsetPool={};
  162. local final;    --The last structure, relative point to place new structures
  163. function transform(b)
  164.     if final.next=="x-" then
  165.         b[1]=-b[1];
  166.         b[3]=-b[3];
  167.     elseif final.next=="z+" then
  168.         local tx=b[1];
  169.         local tz=b[3];
  170.         b[1]=-tz;
  171.         b[3]=tx;
  172.     elseif final.next=="z-" then
  173.         local tx=b[1];
  174.         local tz=b[3];
  175.         b[1]=tz;
  176.         b[3]=-tx;
  177.     end
  178.     b[1]=final.exit[1]+b[1];
  179.     b[2]=final.exit[2]+b[2];
  180.     b[3]=final.exit[3]+b[3];
  181.     return b;
  182. end
  183. function dirToNumber(f)
  184.     if f=="x+" then return 0;
  185.     elseif f=="z+" then return 1;
  186.     elseif f=="x-" then return 2;
  187.     elseif f=="z-" then return 3;
  188.     else error("Invalid direction "..tostring(f),2); end
  189. end
  190. function numberToDir(n)
  191.     n=n%4;
  192.     if n==0 then return "x+";
  193.     elseif n==1 then return "z+";
  194.     elseif n==2 then return "x-";
  195.     elseif n==3 then return "z-";
  196.     else error("Invalid number "..tostring(n),2); end
  197. end
  198. -------------------------------------------------------------WARNING-------------------------------------------------------------------
  199. --Some TileEntities have a problem with setblock, and they simply ignore the metadata in setblock commands.
  200. --There is no method (that I know) to make them work. I included them here in case it gets fixed.
  201. --The metadatas given are correct, it just doesnt set with setblock
  202. --This is why bedrock cage has the chest sometimes pointing wrongly
  203. --Blocks that dont work: dispenser, dropper, furnace, lit_furnace, chest, and trapped_chest
  204. --TileEntities that work: hopper, anvil, wall_sign, unpowered_comparator and powered_comparator(Deprecated block. Could not find if it's a TileEntity or not)
  205. --All other blocks work
  206. function getMetaFor(block,dir)
  207.     if block=="torch" or block=="redstone_torch" then
  208.         if dir=="x+" then return 1;
  209.         elseif dir=="x-" then return 2;
  210.         elseif dir=="z+" then return 3;
  211.         elseif dir=="z-" then return 4;
  212.         elseif dir=="y+" then return 5;
  213.         else error("Invalid direction "..tostring(dir),2); end
  214.     elseif block=="piston" or block=="sticky_piston" or block=="piston_head" or block=="dispenser" or block=="dropper" or block=="hopper" then
  215.         if dir=="y-" then return 0;
  216.         elseif dir=="y+" and block~="hopper" then return 1;
  217.         elseif dir=="z-" then return 2;
  218.         elseif dir=="z+" then return 3;
  219.         elseif dir=="x-" then return 4;
  220.         elseif dir=="x+" then return 5;
  221.         else error("Invalid direction "..tostring(dir),2); end
  222.     --elseif block=="stairs" then
  223.     elseif block=="oak_stairs" or block=="brick_stairs" or block=="stone_brick_stairs" or block=="nether_brick_stairs" or block=="spruce_stairs"
  224.         or block=="birch_stairs" or block=="jungle_stairs" or block=="quartz_stairs" or block=="stone_stairs" or block=="sandstone_stairs"
  225.         or block=="acacia_stairs" or block=="dark_oak_stairs" or --[[And for future versions,]]block=="red_sandstone_stairs" then
  226.         if dir=="x+" then return 0;
  227.         elseif dir=="x-" then return 1;
  228.         elseif dir=="z+" then return 2;
  229.         elseif dir=="z-" then return 3;
  230.         else error("Invalid direction "..tostring(dir),2); end
  231.     elseif block=="rail" or block=="golden_rail" or block=="detector_rail" or block=="anvil" then
  232.         if dir=="z+" or dir=="z-" then return 0;
  233.         elseif dir=="x+" or dir=="x-" then return 1;
  234.         else error("Invalid direction "..tostring(dir),2); end
  235.     elseif block=="ladder" or block=="furnace" or block=="lit_furnace" or block=="chest" or block=="trapped_chest" or block=="wall_sign" then
  236.         if dir=="z-" then return 2;
  237.         elseif dir=="z+" then return 3;
  238.         elseif dir=="x-" then return 4;
  239.         elseif dir=="x+" then return 5;
  240.         else error("Invalid direction "..tostring(dir),2); end
  241.     elseif block=="lever" then
  242.         if dir=="y-" then return 0;
  243.         elseif dir=="x+" then return 1;
  244.         elseif dir=="x-" then return 2;
  245.         elseif dir=="z+" then return 3;
  246.         elseif dir=="z-" then return 4;
  247.         elseif dir=="y+" then return 5;
  248.         else error("Invalid direction "..tostring(dir),2); end
  249.     elseif block=="stone_button" or block=="wooden_button" then
  250.         if dir=="x+" then return 1;
  251.         elseif dir=="x-" then return 2;
  252.         elseif dir=="z+" then return 3;
  253.         elseif dir=="z-" then return 4;
  254.         else error("Invalid direction "..tostring(dir),2); end
  255.     elseif block=="pumpkin" or block=="lit_pumpkin" or block=="fence_gate" or block=="end_portal_frame" or block=="tripwire_hook" then
  256.         if dir=="z+" then return 0;
  257.         elseif dir=="x-" then return 1;
  258.         elseif dir=="z-" then return 2;
  259.         elseif dir=="x+" then return 3;
  260.         else error("Invalid direction "..tostring(dir),2); end
  261.     elseif block=="unpowered_repeater" or block=="powered_repeater" or block=="unpowered_comparator" or block=="powered_comparator" then
  262.         if dir=="z-" then return 0;
  263.         elseif dir=="x+" then return 1;
  264.         elseif dir=="z+" then return 2;
  265.         elseif dir=="x-" then return 3;
  266.         else error("Invalid direction "..tostring(dir),2); end
  267.     else return 0; end
  268. end
  269. function autoMeta(b,dir)
  270.     local name=b[4];
  271.     if name:sub(1,10)=="minecraft:" then
  272.         name=name:sub(11);
  273.     end
  274.     if dir==nil then
  275.         dir=b[5];
  276.     end
  277.     if type(dir)=="number" then
  278.         dir=numberToDir(dir);
  279.     end
  280.     --print("Getting meta for block "..tostring(name)..", Direction "..tostring(dir));
  281.     local ok,meta=pcall(getMetaFor,name,dir);
  282.     if ok then
  283.         b[5]=meta;
  284.         --print("Meta is "..tostring(meta));
  285.     else
  286.         error(meta,2);
  287.     end
  288. end
  289. local playerCount;
  290. local sidebarMode;
  291. local fromMonitor;
  292. local monitor;
  293. local monitorName;
  294. local realMode;
  295. local checkpointGoal;
  296. local cycleSidebar;
  297. local quitPlayer;
  298.  
  299.  
  300. --Make sure some stuff is accessible to structures
  301. _G.dirToNumber=dirToNumber;
  302. _G.numberToDir=numberToDir;
  303. _G.final=function()return final;end;
  304. _G.structures=function()return structures;end;
  305. _G.structNames=function()return structNames;end;
  306. _G.structNamePresent=structNamePresent;
  307. _G.checkpointStride=function()return checkpointStride;end;
  308. _G.parkourVersion=parkourVersion;
  309. _G.playerCount=function()return playerCount;end;
  310. _G.getMetaFor=getMetaFor;
  311. _G.sidebarMode=function()return sidebarMode;end;
  312. _G.fromMonitor=function()return fromMonitor;end;
  313. _G.getMonitor=function()return monitor;end;
  314. _G.monitorName=function()return monitorName;end;
  315. _G.parkourGamemode=function()return realMode;end;
  316. _G.checkpointGoal=function()
  317.     if realMode==1 then error("Attempt to get checkpoint goal in freeplay");
  318.     else return checkpointGoal; end
  319. end
  320. _G.cycleSidebar=cycleSidebar;
  321.  
  322. structures.heightWarp=function(what)
  323.     if what=="build" then
  324.         local dist=175-final.exit[2];
  325.         function teleport(s)
  326.             commands.async.tp(s.playerData.selector,s.blocks[5][1],s.blocks[5][2]+1,s.blocks[5][3]);
  327.         end
  328.         return {
  329.             blocks={
  330.                 {1,0,0,"minecraft:quartz_block"},
  331.                 {2,0,0,"minecraft:quartz_block"},
  332.                 {3,0,0,"minecraft:quartz_block"},
  333.                 {4,0,0,"minecraft:quartz_block",2},
  334.                
  335.                 {4,dist,0,"minecraft:quartz_block",2},
  336.             },
  337.             areaCheck={
  338.                 {0,0,0,4},
  339.                 {3,0,0,4},
  340.                 {4,1,0,1,teleport},
  341.                 {4,dist,0,4},
  342.             },
  343.             exit={5,dist,0},
  344.             next="x+",
  345.         };
  346.     end
  347. end
  348. local reallyStartParkour;
  349. structures.first=function(what)
  350.     if what=="build" then
  351.         --[[local b={};
  352.         for i=31,35 do
  353.             b[#b+1]={0,i,0,"minecraft:stone"};
  354.         end
  355.         return {
  356.             blocks=b,
  357.             areaCheck={{0,35,0,10}},
  358.             exit={0,35,0},
  359.             next="x+",
  360.         };]]--
  361.         function doTick(firstS)
  362.             if not confiscateItems then
  363.                 if not firstS.allPlayers.alreadyGone then
  364.                     local s=firstS;
  365.                     local allInit=true;
  366.                     for i,pd in ipairs(s.allPlayers) do
  367.                         if not pd.initialized then allInit=false;break; end
  368.                     end
  369.                     if allInit then
  370.                         s.allPlayers.alreadyGone=true;
  371.                         for i,pd in ipairs(s.allPlayers) do
  372.                             local tf=pd.current[1].exit;
  373.                             commands.async.clear(pd.selector);
  374.                             commands.tp(pd.selector,tf[1],tf[2]+1,tf[3]);
  375.                             sleep(0.1);
  376.                             commands.async.give(pd.selector.." ComputerCraft:pocketComputer 1 1 {display:{Name:Parkour Manager},computerID:"..os.computerID().."}");
  377.                         end
  378.                         reallyStartParkour();
  379.                     end
  380.                 end
  381.             elseif firstS.playerData.nowQuitting then
  382.                 local s=firstS.playerData.previous;
  383.                 if s.playerData.readyToGo==false then
  384.                     s.playerData.readyToGo=nil;
  385.                     s.playerData.initialTime=s.playerData.time;
  386.                     commands.async.tellraw(s.playerData.selector.." \"You can get your items now. After that press the button.\"");
  387.                     commands.async.tellraw(s.playerData.selector.." [\"You have \",{text:\"30 seconds\",bold:true},\" before the button is automatically pushed, and all items in the chest will be \",{text:gone,color:red},\".\"]");
  388.                     for i=2,11 do
  389.                         commands.async.setblock(unpack(s.blocks[i]));
  390.                     end
  391.                     s.playerData.time=0;
  392.                     s.playerData.finished=false;
  393.                     cycleSidebar(2);
  394.                 end
  395.                 if not s.playerData.readyToGo and commands.getBlockInfo(s.blocks[3][1],s.blocks[3][2],s.blocks[3][3]).name=="minecraft:stonebrick" then
  396.                     s.playerData.readyToGo=true;
  397.                     commands.async.tellraw(s.playerData.selector.." {text:\"Goodbye.\",color:green}");
  398.                     s.playerData.finished=true;
  399.                     commands.async.tp(s.playerData.selector.." ~ ~4 ~");
  400.                     --commands.async.setblock(s.blocks[10][1],s.blocks[10][2],s.blocks[10][3],"minecraft:lava");
  401.                     --commands.async.setblock(s.blocks[11][1],s.blocks[11][2],s.blocks[11][3],"minecraft:lava");
  402.                     for i=2,13 do
  403.                         commands.async.setblock(s.blocks[i][1],s.blocks[i][2],s.blocks[i][3],"minecraft:lava");
  404.                     end
  405.                     sleep(2);
  406.                     quitPlayer(s.playerID);
  407.                     local allReady=true;
  408.                     for i,pd in ipairs(s.allPlayers) do
  409.                         if not pd.readyToGo then allReady=false;break; end
  410.                     end
  411.                     if allReady then
  412.                         return true;
  413.                     end
  414.                 end
  415.                 if not s.playerData.readyToGo then
  416.                     if s.playerData.time>=30 then
  417.                         commands.async.tellraw(s.playerData.selector.." \"Your 30 seconds are up, your button will be pushed automatically.\"");
  418.                         local b;
  419.                         b=s.blocks[2];
  420.                         commands.async.setblock(b[1],b[2],b[3],b[4],bit.bor(b[5],8));
  421.                         b=s.blocks[3];
  422.                         commands.async.setblock(b[1],b[2],b[3],"minecraft:stonebrick",3);
  423.                         commands.async.playSound("random.click "..s.playerData.selector);
  424.                     end
  425.                 end
  426.             else
  427.                 local s=firstS.playerData.previous;
  428.                 if not s.playerData.readyToGo and commands.getBlockInfo(s.blocks[3][1],s.blocks[3][2],s.blocks[3][3]).name=="minecraft:stonebrick" then
  429.                     s.playerData.readyToGo=true;
  430.                     local allReady=true;
  431.                     for i,pd in ipairs(s.allPlayers) do
  432.                         if i==s.playerID then commands.async.tellraw(pd.selector.." \"You are ready to start.\"");
  433.                         else commands.async.tellraw(pd.selector.." \"Player #"..s.playerID.." is ready to start.\""); end
  434.                         if not pd.readyToGo then allReady=false; end
  435.                     end
  436.                     if allReady then
  437.                         for i,pd in ipairs(s.allPlayers) do
  438.                             --pd.readyToGo=nil;
  439.                             local tf=pd.current[1];
  440.                             commands.async.clear(pd.selector);  --Clear now so theres no cheating
  441.                             commands.tp(pd.selector,tf.exit[1],tf.exit[2]+1,tf.exit[3]);
  442.                             sleep(0.1);
  443.                             commands.async.give(pd.selector.." ComputerCraft:pocketComputer 1 1 {display:{Name:Parkour Manager},computerID:"..os.computerID().."}");
  444.                             pd.time=0;
  445.                         end
  446.                         reallyStartParkour();
  447.                     end
  448.                 end
  449.                 if not s.playerData.readyToGo then
  450.                     if s.playerData.time>=30 then
  451.                         commands.async.tellraw(s.playerData.selector.." \"Your 30 seconds are up, all buttons will be pushed automatically.\"");
  452.                         local b;
  453.                         b=s.blocks[2];
  454.                         commands.async.setblock(b[1],b[2],b[3],b[4],bit.bor(b[5],8));
  455.                         b=s.blocks[3];
  456.                         commands.async.setblock(b[1],b[2],b[3],"minecraft:stonebrick",3);
  457.                         commands.async.playSound("random.click "..s.playerData.selector);
  458.                     end
  459.                 end
  460.             end
  461.         end
  462.         return {
  463.             blocks={},
  464.             areaCheck={{-4,0,0,4},{0,0,0,10,doTick}},
  465.             exit={0,0,0},
  466.             next="x+",
  467.         };
  468.     end
  469. end
  470. structures.itemDeposit=function(what,s)
  471.     if what=="checkpoint" then
  472.         commands.async.tp(
  473.         s.playerData
  474.         .selector,
  475.         s.blocks[1][1],
  476.         s.blocks[1][2],
  477.         s.blocks[1][3]);
  478.         return true;
  479.     elseif what=="build" then
  480.         local bb={
  481.             --Extras
  482.             {0,36,0,reference=true},
  483.             {0,36,-1,"minecraft:stone_button","x-",autoMeta=true},
  484.             {1,36,0,"minecraft:command_block",0,"replace"},
  485.             {1,36,-1},
  486.             {0,38,0,"minecraft:lit_pumpkin"},
  487.             {0,38,-1,"minecraft:lit_pumpkin"},
  488.             {0,37,0,"minecraft:air"},
  489.             {0,37,-1,"minecraft:air"},
  490.             {0,36,0,"minecraft:air"},
  491.             {1,38,0,"minecraft:air"},
  492.             {1,38,-1,"minecraft:air"},
  493.             {1,37,0,"minecraft:chest"},--,"x-",autoMeta=true},  Sadly chest orientation does not work due to a minecraft bug.
  494.             {1,37,-1,"minecraft:chest"},--,"x-",autoMeta=true}, See getMetaFor() definition for more info
  495.            
  496.             --Floor
  497.             {-1,35,-2},{-1,35,-1},{-1,35,0},{-1,35,1},{0,35,-2},{0,35,-1},{0,35,0},{0,35,1},
  498.             {1,35,-2},{1,35,-1},{1,35,0},{1,35,1},{2,35,-2},{2,35,-1},{2,35,0},{2,35,1},
  499.            
  500.             --1st Wall
  501.             {-1,36,-2},{-1,36,-1},{-1,36,0},{-1,36,1},{0,36,-2},{0,36,1},
  502.             {1,36,-2},{1,36,1},{2,36,-2},{2,36,-1},{2,36,0},{2,36,1},
  503.            
  504.             --2nd Wall
  505.             {-1,37,-2},{-1,37,-1},{-1,37,0},{-1,37,1},{0,37,-2},{0,37,1},
  506.             {1,37,-2},{1,37,1},{2,37,-2},{2,37,-1},{2,37,0},{2,37,1},
  507.            
  508.             --3rd Wall
  509.             {-1,38,-2},{-1,38,-1},{-1,38,0},{-1,38,1},{0,38,-2},{0,38,1},
  510.             {1,38,-2},{1,38,1},{2,38,-2},{2,38,-1},{2,38,0},{2,38,1},
  511.            
  512.             --Roof
  513.             {-1,39,-2},{-1,39,-1},{-1,39,0},{-1,39,1},{0,39,-2},{0,39,-1},{0,39,0},{0,39,1},
  514.             {1,39,-2},{1,39,-1},{1,39,0},{1,39,1},{2,39,-2},{2,39,-1},{2,39,0},{2,39,1},
  515.         };
  516.         for i,b in ipairs(bb) do
  517.             if not b[4] then b[4]="minecraft:bedrock"; end
  518.         end
  519.         return {
  520.             blocks=bb,
  521.             areaCheck={{1,35,0,8}},
  522.             exit={4,35,0},
  523.             next="x+",
  524.         };
  525.     elseif what=="post_transform" then
  526.         local b=s.blocks[3];
  527.         b[7]="{Command:\"setblock "..b[1].." "..b[2].." "..b[3].." minecraft:stonebrick 3\"}";
  528.     end
  529. end
  530.  
  531. function buildCheckpoint(struct)
  532.     if not struct.func("place_marker",struct) then
  533.         if struct.exit[1]~=pos[1] or struct.exit[2]~=pos[2] or struct.exit[3]~=pos[3] then
  534.             commands.async.setblock(struct.exit[1],struct.exit[2],struct.exit[3],"minecraft:obsidian 0 replace");
  535.         end
  536.     end
  537. end
  538. function buildStructure(struct)
  539.     for i,b in ipairs(struct.blocks) do
  540.         if not b.disableAutobuild and not b.reference then
  541.             commands.async.setblock(unpack(b));
  542.             if i%200==0 then sleep(0.05); end
  543.         end
  544.     end
  545. end
  546. function deleteCheckpoint(struct)
  547.     if not struct.func("remove_marker",struct) then
  548.         if struct.exit[1]~=pos[1] or struct.exit[2]~=pos[2] or struct.exit[3]~=pos[3] then
  549.             commands.async.setblock(struct.exit[1],struct.exit[2],struct.exit[3],"minecraft:air 0 replace");
  550.         end
  551.     end
  552. end
  553. function deleteStructure(struct)
  554.     for i,b in ipairs(struct.blocks) do
  555.         if not b.reference then
  556.             commands.async.setblock(b[1],b[2],b[3],"minecraft:air 0 replace");
  557.             if i%200==0 then sleep(0.05); end
  558.         end
  559.     end
  560. end
  561. function genNewStructset(s)
  562.     local structset={};
  563.    
  564.     local sLength=0;
  565.     function genSingleStruct(space)
  566.         local struct;
  567.         if s then struct=s("build",final.nextAngle);struct.func=s; end
  568.         while not struct do
  569.             local candidates={};
  570.             local probabilities={};
  571.             local totalWeight=0;
  572.             for i,sf in ipairs(structures) do
  573.                 local str=sf("build",final.nextAngle);
  574.                 if (str.length or checkpointStride)<=space then
  575.                     str.func=sf;
  576.                     candidates[#candidates+1]=str;
  577.                     probabilities[#probabilities+1]=sf("weight") or 1;
  578.                     totalWeight=totalWeight+probabilities[#probabilities];
  579.                 end
  580.             end
  581.             if totalWeight>0 then
  582.                 local r=math.random()*totalWeight;
  583.                 local currentProb=0;
  584.                 for i,str in ipairs(candidates) do
  585.                     currentProb=currentProb+probabilities[i];
  586.                     if r<=currentProb then
  587.                         struct=str;
  588.                         break;
  589.                     end
  590.                 end
  591.             end
  592.             space=space+20;
  593.         end
  594.        
  595.         --Transform direction
  596.         struct.nextAngle=final.nextAngle or 0;
  597.         if type(struct.next)=="number" then
  598.             local absAngle=struct.next%(2*math.pi);
  599.             if      absAngle<0.25*math.pi then  struct.next="x+";struct.nextAngle=absAngle;
  600.             elseif  absAngle<0.75*math.pi then  struct.next="z+";struct.nextAngle=absAngle-0.5*math.pi;
  601.             elseif  absAngle<1.25*math.pi then  struct.next="x-";struct.nextAngle=absAngle-math.pi;
  602.             elseif  absAngle<1.75*math.pi then  struct.next="z-";struct.nextAngle=absAngle-1.5*math.pi;
  603.             else                                struct.next="x+";struct.nextAngle=absAngle-2*math.pi;
  604.             end
  605.         end
  606.         struct.next=numberToDir(dirToNumber(final.next)+dirToNumber(struct.next));
  607.         --Transform blocks
  608.         for i,b in ipairs(struct.blocks) do
  609.             b=transform(b);
  610.             if b.autoMeta then
  611.                 if type(b[5])=="string" then b[5]=dirToNumber(b[5]); end
  612.                 b[5]=dirToNumber(final.next)+b[5];
  613.                 autoMeta(b);
  614.             end
  615.         end
  616.         --Transform exit
  617.         struct.exit=transform(struct.exit);
  618.         --Transform areaCheck
  619.         local unorganizedAC=struct.areaCheck;
  620.         local cpac=false;
  621.         for i,b in ipairs(unorganizedAC) do
  622.             if b[1]==0 and b[2]==0 and b[3]==0 then cpac=true; end
  623.             unorganizedAC[i]=transform(b);
  624.             unorganizedAC[i][4]=math.ceil(unorganizedAC[i][4]);
  625.         end
  626.         --Make sure there's an areaCheck for the checkpoint, but don't add duplicates
  627.         if not cpac then unorganizedAC[#unorganizedAC+1]=transform({0,0,0,5}); end
  628.         struct.areaCheck={};
  629.         for i,area in ipairs(unorganizedAC) do
  630.             if type(area[5])=="function" then
  631.                 struct.areaCheck[#struct.areaCheck+1]=area;
  632.             end
  633.         end
  634.         for i,area in ipairs(unorganizedAC) do
  635.             if type(area[5])~="function" then
  636.                 struct.areaCheck[#struct.areaCheck+1]=area;
  637.             end
  638.         end
  639.        
  640.         final=struct;
  641.         return struct;
  642.     end
  643.     while sLength<checkpointStride do
  644.         local struct=genSingleStruct(checkpointStride-sLength);
  645.         structset[#structset+1]=struct;
  646.         sLength=sLength+(struct.length or checkpointStride);
  647.     end
  648.    
  649.     structsetPool[#structsetPool+1]=structset;
  650. end
  651. function getNextStructset(pid)
  652.     local pd=playerData[pid];
  653.     --Get our raw structset data from pool
  654.     pd.currentStructset=pd.currentStructset+1;
  655.     --If there is no next structset, generate it.
  656.     if structsetPool[pd.currentStructset]==nil then
  657.         if realMode==1 or pd.checkpoints<checkpointGoal then
  658.             local adjustHeight=final.exit[2]>230 or final.exit[2]<30;
  659.             if not adjustHeight then
  660.                 for i=1,playerCount do
  661.                     if not adjustHeight then
  662.                         adjustHeight=commands.getBlockInfo(final.exit[1]+pd.xOffset,final.exit[2]-20,final.exit[3]).name~="minecraft:air";
  663.                         if not adjustHeight then
  664.                             adjustHeight=commands.getBlockInfo(final.exit[1]+pd.xOffset,final.exit[2]-15,final.exit[3]).name~="minecraft:air";
  665.                             if not adjustHeight then
  666.                                 adjustHeight=commands.getBlockInfo(final.exit[1]+pd.xOffset,final.exit[2]-10,final.exit[3]).name~="minecraft:air";
  667.                             end
  668.                         end
  669.                     end
  670.                 end
  671.             end
  672.             if adjustHeight then
  673.                 genNewStructset(structures.heightWarp);
  674.             else
  675.                 genNewStructset();
  676.             end
  677.         else
  678.             genNewStructset(structures.empty);
  679.         end
  680.     end
  681.     local rawStructset=structsetPool[pd.currentStructset];
  682.     --Do a semi-deep copy, and offset any X that needs to be offsetted.
  683.     local structset={};
  684.     for i,rawStruct in ipairs(rawStructset) do
  685.         function deepCopy(orig,dest,lvl)
  686.             for name,value in pairs(orig) do
  687.                 if type(value)=="table" and not value.global then
  688.                     dest[name]={};
  689.                     deepCopy(value,dest[name],lvl+1);
  690.                     if lvl==1 then
  691.                         if name=="blocks" then
  692.                             for j,b in ipairs(dest.blocks) do
  693.                                 b[1]=b[1]+pd.xOffset;
  694.                             end
  695.                         elseif name=="areaCheck" then
  696.                             for j,ac in ipairs(dest.areaCheck) do
  697.                                 ac[1]=ac[1]+pd.xOffset;
  698.                             end
  699.                         elseif name=="exit" then
  700.                             dest.exit[1]=dest.exit[1]+pd.xOffset;
  701.                         end
  702.                     end
  703.                 else
  704.                     dest[name]=value;
  705.                 end
  706.             end
  707.         end
  708.         structset[i]={};
  709.         deepCopy(rawStruct,structset[i],1);
  710.         structset[i].playerData=pd;
  711.         structset[i].playerID=pid;
  712.         structset[i].allPlayers=playerData;
  713.         structset[i].func("post_transform",structset[i]);
  714.     end
  715.    
  716.     if pd.currentStructset>3 then
  717.         deleteStructure(pd.previous);
  718.         deleteCheckpoint(pd.previous);
  719.     else
  720.         pd.itemDeposit=pd.previous;
  721.     end
  722.     for i,struct in ipairs(pd.current) do
  723.         if i~=#pd.current then
  724.             deleteStructure(struct);
  725.         end
  726.     end
  727.     pd.previous=pd.current[#pd.current];
  728.     pd.current=structset;
  729.     for i,struct in ipairs(pd.current) do
  730.         buildStructure(struct);
  731.         if i==#pd.current then
  732.             buildCheckpoint(struct);
  733.         end
  734.     end
  735. end
  736.  
  737. local localStructs={};
  738. function superLoop()
  739.     local sWidth;
  740.     local sHeight;
  741.     sWidth,sHeight=term.getSize();
  742.     local onlineStructs={};
  743.     local onlineError=false;
  744.     local scroll=0;
  745.     local mScroll=0;
  746.     local debuggingStructs=false;
  747.     local updateData=false;
  748.     local canStart=false;
  749.     local canStartChange=false;
  750.     local shallQuit=false;
  751.     monitor=false;
  752.     monitorName="";
  753.     fromMonitor=false;
  754.     for _,name in ipairs(peripheral.getNames()) do
  755.         if peripheral.getType(name)=="monitor" then
  756.             local m=peripheral.wrap(name);
  757.             if m.isColor() then
  758.                 m.setTextScale(1);
  759.                 local w,h=m.getSize();
  760.                 if w>=18 and h>=12 then
  761.                     monitor=m;
  762.                     monitorName=name;
  763.                     break;
  764.                 end
  765.             end
  766.         end
  767.     end
  768.     function updateLocalStructs()
  769.         if not fs.isDir("structures") then
  770.             fs.delete("structures");
  771.             fs.makeDir("structures");
  772.         end
  773.         local files=fs.list("structures");
  774.         local oldSelections={};
  775.         for _,ls in ipairs(localStructs) do
  776.             local name=ls[1];
  777.             if ls[4] then
  778.                 name=name.."."..ls[4];
  779.             end
  780.             oldSelections[name]=ls[3];
  781.         end
  782.         localStructs={};
  783.         for i,filename in ipairs(files) do
  784.             if not fs.isDir("structures/"..filename) then
  785.                 local struct,err=loadfile("structures/"..filename);
  786.                
  787.                 local ver;
  788.                 local dot=filename:find("%.");
  789.                 local name=filename;
  790.                 if dot then
  791.                     ver=tonumber(filename:sub(dot+1));
  792.                     name=name:sub(1,dot-1);
  793.                 end
  794.                 if struct then
  795.                     setfenv(struct,_G);
  796.                     local selected=oldSelections[filename];
  797.                     if selected==nil then selected=true; end
  798.                     localStructs[#localStructs+1]={name,struct,selected,ver};
  799.                 else
  800.                     localStructs[#localStructs+1]={name,err,false,ver};
  801.                 end
  802.             end
  803.         end
  804.     end
  805.     function beautifulName(n)
  806.         n=string.upper(n:sub(1,1))..n:sub(2);
  807.         while true do
  808.             local underscore=n:find("_");
  809.             if not underscore then break; end
  810.             n=(n:sub(1,underscore-1)).." "..string.upper(n:sub(underscore+1,underscore+1))..(n:sub(underscore+2));
  811.         end
  812.         return n;
  813.     end
  814.     function renderStructList()
  815.         term.setBackgroundColor(colors.orange);
  816.         term.clear();
  817.         term.setTextColor(colors.black);
  818.         for i=1,sHeight-3 do
  819.             term.setBackgroundColor(colors.white);
  820.             local ls=localStructs[i+scroll];
  821.             if ls then
  822.                 if ls[3] then term.setBackgroundColor(colors.green); end
  823.             end
  824.             term.setCursorPos(2,i+1);
  825.             term.write(string.rep(" ",sWidth-2));
  826.             if ls then
  827.                 term.setCursorPos(2,i+1);
  828.                 if type(ls[2])=="string" then
  829.                     term.setTextColor(colors.red);
  830.                     term.write("Error in "..beautifulName(ls[1]));
  831.                     term.setTextColor(colors.black);
  832.                 else
  833.                     term.write(beautifulName(ls[1]));
  834.                     if ls[4] then
  835.                         local v;
  836.                         if math.floor(ls[4])==ls[4] then
  837.                             v=ls[4]..".0";
  838.                         else
  839.                             v=tostring(ls[4]);
  840.                         end
  841.                         term.setCursorPos(sWidth-3-#v,i+1);
  842.                         term.write(v);
  843.                     end
  844.                 end
  845.                 term.setBackgroundColor(colors.red);
  846.                 term.setCursorPos(sWidth-2,i+1);
  847.                 term.write(" -");
  848.             end
  849.         end
  850.         term.setBackgroundColor(colors.blue);
  851.         term.setCursorPos(2,sHeight-1);
  852.         term.write(string.rep(" ",sWidth-2));
  853.         term.setCursorPos(2,sHeight-1);
  854.         term.write("Get structures");
  855.         term.setCursorPos(sWidth-9,sHeight-1);
  856.         term.setBackgroundColor(colors.lightGray);
  857.         term.setTextColor(colors.white);
  858.         term.write("< >");
  859.         term.setCursorPos(sWidth-8,sHeight-1);
  860.         term.setTextColor(colors.gray);
  861.         if checkpointStride==60 then
  862.             term.setBackgroundColor(colors.green);
  863.             term.write("E");
  864.         elseif checkpointStride==100 then
  865.             term.setBackgroundColor(colors.yellow);
  866.             term.write("N");
  867.         elseif checkpointStride==150 then
  868.             term.setBackgroundColor(colors.red);
  869.             term.write("H");
  870.         else
  871.             term.setBackgroundColor(colors.blue);
  872.             term.write("?");
  873.         end
  874.         term.setCursorPos(sWidth-5,sHeight-1);
  875.         local any=false;
  876.         if canStart then
  877.             for i,ls in ipairs(localStructs) do
  878.                 if ls[3] then
  879.                     any=true;
  880.                     break;
  881.                 end
  882.             end
  883.         end
  884.         if any then term.setBackgroundColor(colors.green); term.setTextColor(colors.white);
  885.         else term.setBackgroundColor(colors.gray);term.setTextColor(colors.black); end
  886.         term.write("Start");
  887.         term.setCursorPos(1,1);
  888.         term.setTextColor(colors.white);
  889.         term.setBackgroundColor(colors.red);
  890.         term.write("X");
  891.         renderMonitor();
  892.     end
  893.     function renderMonitor()
  894.         if not monitor then return; end
  895.         monitor.setTextScale(0.5);
  896.         local mWidth;
  897.         local mHeight;
  898.         mWidth,mHeight=monitor.getSize();
  899.         monitor.setBackgroundColor(colors.orange);
  900.         monitor.clear();
  901.         monitor.setTextColor(colors.black);
  902.         for i=1,mHeight-3 do
  903.             monitor.setBackgroundColor(colors.white);
  904.             local ls=localStructs[i+mScroll];
  905.             if ls then
  906.                 if ls[3] then monitor.setBackgroundColor(colors.green); end
  907.             end
  908.             monitor.setCursorPos(2,i+1);
  909.             monitor.write(string.rep(" ",mWidth-2));
  910.             local doScroll=#localStructs-(mHeight-3)>0;
  911.             if ls then
  912.                 monitor.setCursorPos(2,i+1);
  913.                 if type(ls[2])=="string" then
  914.                     monitor.setTextColor(colors.red);
  915.                     monitor.write("Error in "..beautifulName(ls[1]));
  916.                     monitor.setTextColor(colors.black);
  917.                 else
  918.                     monitor.write(beautifulName(ls[1]));
  919.                     if ls[4] then
  920.                         local v;
  921.                         if math.floor(ls[4])==ls[4] then
  922.                             v=ls[4]..".0";
  923.                         else
  924.                             v=tostring(ls[4]);
  925.                         end
  926.                         if doScroll then monitor.setCursorPos(mWidth-2-#v,i+1);
  927.                         else monitor.setCursorPos(mWidth-#v,i+1); end
  928.                         monitor.write(v);
  929.                     end
  930.                 end
  931.             end
  932.             if doScroll then
  933.                 monitor.setBackgroundColor(colors.lightGray);
  934.                 monitor.setCursorPos(mWidth-2,i+1);
  935.                 if i==1 then monitor.write("/\\");
  936.                 elseif i==mHeight-3 then monitor.write("\\/");
  937.                 else monitor.write("  "); end
  938.             end
  939.         end
  940.         term.setBackgroundColor(colors.blue);
  941.         term.setCursorPos(2,mHeight-1);
  942.         term.write(string.rep(" ",mWidth-2));
  943.         monitor.setCursorPos(mWidth-16,mHeight-1);
  944.         monitor.setBackgroundColor(colors.lightGray);
  945.         monitor.setTextColor(colors.white);
  946.         monitor.write("<-      ->");
  947.         monitor.setCursorPos(mWidth-14,mHeight-1);
  948.         monitor.setTextColor(colors.gray);
  949.         if checkpointStride==60 then
  950.             monitor.setBackgroundColor(colors.green);
  951.             monitor.write(" Easy ");
  952.         elseif checkpointStride==100 then
  953.             monitor.setBackgroundColor(colors.yellow);
  954.             monitor.write("Normal");
  955.         elseif checkpointStride==150 then
  956.             monitor.setBackgroundColor(colors.red);
  957.             monitor.write(" Hard ");
  958.         else
  959.             monitor.setBackgroundColor(colors.blue);
  960.             monitor.write("?");
  961.         end
  962.         monitor.setCursorPos(mWidth-5,mHeight-1);
  963.         local any=false;
  964.         if canStart then
  965.             for i,ls in ipairs(localStructs) do
  966.                 if ls[3] then
  967.                     any=true;
  968.                     break;
  969.                 end
  970.             end
  971.         end
  972.         if any then monitor.setBackgroundColor(colors.green); monitor.setTextColor(colors.white);
  973.         else monitor.setBackgroundColor(colors.gray);monitor.setTextColor(colors.black); end
  974.         monitor.write("Start");
  975.     end
  976.     function updateOnlineStructs()
  977.         if not http then
  978.             onlineError="HTTP disabled";
  979.             renderStore();
  980.             return;
  981.         end
  982.         onlineError="Connecting...";
  983.         renderStore();
  984.         local onlineList=http.get("http://pastebin.com/raw.php?i=TWzu3GRU");
  985.         if onlineList then
  986.             onlineStructs={};
  987.             local debugAmount=tonumber(onlineList.readLine());
  988.             if not debugAmount then debugAmount=0; end
  989.             while true do
  990.                 local name=onlineList.readLine();
  991.                 local url=onlineList.readLine();
  992.                 local author=onlineList.readLine();
  993.                 local desc=onlineList.readLine();
  994.                 local ver=tonumber(onlineList.readLine() or true);
  995.                 onlineList.readLine();
  996.                 if name==nil or url==nil or author==nil or desc==nil then break; end
  997.                 local new=true;
  998.                 local update=false;
  999.                 for i,l in ipairs(localStructs) do
  1000.                     if l[1]==name then
  1001.                         if ver then
  1002.                             if not l[4] then
  1003.                                 update=true;
  1004.                             elseif ver>l[4] then
  1005.                                 update=true;
  1006.                             end
  1007.                         end
  1008.                         new=false;
  1009.                         break;
  1010.                     end
  1011.                 end
  1012.                 if debugAmount<=0 or debuggingStructs then
  1013.                     onlineStructs[#onlineStructs+1]={name,url,false,debugAmount>0,author,desc,new,ver,update};
  1014.                 end
  1015.                 debugAmount=debugAmount-1;
  1016.             end
  1017.             onlineList.close();
  1018.             if #onlineStructs==0 then
  1019.                 onlineError="No online structures left";
  1020.             else
  1021.                 onlineError=false;
  1022.             end
  1023.         else
  1024.             onlineError="Error connecting to store";
  1025.         end
  1026.         renderStore();
  1027.     end
  1028.     function renderStore()
  1029.         term.setBackgroundColor(colors.blue);
  1030.         term.clear();
  1031.         term.setBackgroundColor(colors.white);
  1032.         term.setTextColor(colors.black);
  1033.         for i=1,sHeight-2 do
  1034.             term.setBackgroundColor(colors.white);
  1035.             local ls=onlineStructs[i+scroll];
  1036.             if ls and not onlineError then
  1037.                 if ls[3] then
  1038.                     term.setBackgroundColor(colors.cyan);
  1039.                 elseif ls[4] then
  1040.                     term.setBackgroundColor(colors.yellow);
  1041.                 end
  1042.             end
  1043.             term.setCursorPos(2,i+1);
  1044.             term.write(string.rep(" ",sWidth-2));
  1045.             if ls and not onlineError then
  1046.                 term.setCursorPos(2,i+1);
  1047.                 if not ls[7] then
  1048.                     if ls[9] then term.setTextColor(colors.black);
  1049.                     else term.setTextColor(colors.lightGray); end
  1050.                 end
  1051.                 term.write(beautifulName(ls[1]));
  1052.                 if not ls[7] then
  1053.                     local msg;
  1054.                     if ls[9] then msg="Update";
  1055.                     else msg="Installed"; end
  1056.                     term.setCursorPos(sWidth-#msg,i+1);
  1057.                     term.write(msg);
  1058.                 end
  1059.                 term.setTextColor(colors.black);
  1060.                 if ls[3] then
  1061.                     term.setCursorPos(sWidth-11,i+1);
  1062.                     term.write("Downloading");
  1063.                 end
  1064.             end
  1065.         end
  1066.         if onlineError then
  1067.             term.setCursorPos(2,2);
  1068.             term.write(tostring(onlineError));
  1069.         end
  1070.         term.setCursorPos(1,1);
  1071.         term.setBackgroundColor(colors.blue);
  1072.         term.setTextColor(colors.white);
  1073.         term.write("<");
  1074.     end
  1075.     function downloadStruct(i)
  1076.         local ls=onlineStructs[i];
  1077.         ls[3]=true;
  1078.         renderStore();
  1079.         local structRaw=false;
  1080.         if http then structRaw=http.get("http://pastebin.com/raw.php?i="..textutils.urlEncode(ls[2])); end
  1081.         if structRaw then
  1082.             local files=fs.list("structures/");
  1083.             for _,file in ipairs(files) do
  1084.                 if file:sub(1,#ls[1])==ls[1] then
  1085.                     local nextChar=file:sub(#ls[1]+1,#ls[1]+1);
  1086.                     if nextChar=="." or nextChar=="" then
  1087.                         fs.delete("structures/"..file);
  1088.                     end
  1089.                 end
  1090.             end
  1091.             local filename="structures/"..ls[1];
  1092.             if ls[8] then
  1093.                 filename=filename.."."..ls[8];
  1094.             end
  1095.             local handle=fs.open(filename,"w");
  1096.             handle.write(structRaw.readAll());
  1097.             handle.close();
  1098.             structRaw.close();
  1099.             ls[3]=false;
  1100.             updateLocalStructs();
  1101.             updateOnlineStructs();
  1102.         else
  1103.             ls[3]=false;
  1104.             renderStore();
  1105.         end
  1106.     end
  1107.     function scrollOStructs(n)
  1108.         local old=scroll;
  1109.         scroll=scroll+n;
  1110.         if scroll>#onlineStructs-(sHeight-2) then scroll=#onlineStructs-(sHeight-2); end
  1111.         if scroll<0 then scroll=0; end
  1112.         if scroll~=old then renderStore(); end
  1113.     end
  1114.     function scrollLStructs(n)
  1115.         local old=scroll;
  1116.         scroll=scroll+n;
  1117.         if scroll>#localStructs-(sHeight-3) then scroll=#localStructs-(sHeight-3); end
  1118.         if scroll<0 then scroll=0; end
  1119.         if scroll~=old then renderStructList(); end
  1120.     end
  1121.     function scrollMLStructs(n)
  1122.         if not monitor then return; end
  1123.         local mWidth,mHeight=monitor.getSize();
  1124.         local old=mScroll;
  1125.         mScroll=mScroll+n;
  1126.         if mScroll>#localStructs-(mHeight-3) then mScroll=#localStructs-(mHeight-3); end
  1127.         if mScroll<0 then mScroll=0; end
  1128.         if mScroll~=old then renderMonitor(); end
  1129.     end
  1130.     function downloadUpdate()
  1131.         renderStructList();
  1132.         term.setBackgroundColor(colors.yellow);
  1133.         term.setTextColor(colors.black);
  1134.         local my=math.ceil(sHeight/2);
  1135.         local mx=math.ceil(sWidth/2);
  1136.         for i=1,3 do
  1137.             term.setCursorPos(mx-20,my-2+i);
  1138.             term.write(string.rep(" ",41));
  1139.         end
  1140.         local msg="Update Available";
  1141.         term.setCursorPos(mx-#msg/2,my-1);
  1142.         term.write(msg);
  1143.         msg="An update is available from "..parkourVersion.." to "..updateData[1];
  1144.         term.setCursorPos(mx-#msg/2,my);
  1145.         term.write(msg);
  1146.         msg="Do you wish to update? (Y/N)";
  1147.         term.setCursorPos(mx-#msg/2,my+1);
  1148.         term.write(msg);
  1149.         local yes;
  1150.         while true do
  1151.             local ev={os.pullEvent()};
  1152.             if ev[1]=="key" then
  1153.                 if ev[2]==keys.y then yes=true;break;
  1154.                 elseif ev[2]==keys.n then yes=false;break; end
  1155.             end
  1156.         end
  1157.         if yes then
  1158.             term.setBackgroundColor(colors.black);
  1159.             term.setTextColor(colors.orange);
  1160.             term.clear();
  1161.             term.setCursorPos(1,1);
  1162.             print("Downloading V"..updateData[1]);
  1163.             local newest=http.get("http://pastebin.com/raw.php?i="..updateData[2]);
  1164.             if newest then
  1165.                 local handle=fs.open(shell.getRunningProgram(),"w");
  1166.                 handle.write(newest.readAll());
  1167.                 handle.close();
  1168.                 newest.close();
  1169.                 print("V"..updateData[1].." downloaded. Running new version...");
  1170.                 sleep(1);
  1171.                 shell.run(shell.getRunningProgram());
  1172.                 shallQuit=true;
  1173.                 error();
  1174.             else
  1175.                 print("Error downloading V"..updateData[1]);
  1176.                 print("Using current V"..parkourVersion);
  1177.                 sleep(1);
  1178.             end
  1179.         end
  1180.         updateData=false;
  1181.         canStart=true;
  1182.         renderStructList();
  1183.     end
  1184.     function mainGUI()
  1185.         function startParkour()
  1186.             local any=false;
  1187.             if canStart then
  1188.                 for i,ls in ipairs(localStructs) do
  1189.                     if ls[3] then
  1190.                         any=true;
  1191.                         break;
  1192.                     end
  1193.                 end
  1194.             end
  1195.             if any then
  1196.                 for i,_ in ipairs(structures) do
  1197.                     structures[i]=nil;
  1198.                 end
  1199.                 for i,ls in ipairs(localStructs) do
  1200.                     if ls[3] then
  1201.                         structNames[#structures+1]=ls[1];
  1202.                         structures[#structures+1]=ls[2];
  1203.                     end
  1204.                 end
  1205.                 term.setBackgroundColor(colors.black);
  1206.                 term.setTextColor(colors.white);
  1207.                 term.clear();
  1208.                 term.setCursorPos(1,1);
  1209.                 if monitor then
  1210.                     monitor.setBackgroundColor(colors.black);
  1211.                     monitor.setTextColor(colors.white);
  1212.                     monitor.clear();
  1213.                     monitor.setCursorPos(1,1);
  1214.                 end
  1215.                 return true;
  1216.             end
  1217.             return false;
  1218.         end
  1219.         function changeDifficulty(dir)
  1220.             if dir=="up" then
  1221.                 if checkpointStride==100 then checkpointStride=150;renderStructList();
  1222.                 elseif checkpointStride==60 then checkpointStride=100;renderStructList();
  1223.                 end
  1224.             elseif dir=="down" then
  1225.                 if checkpointStride==100 then checkpointStride=60;renderStructList();
  1226.                 elseif checkpointStride==150 then checkpointStride=100;renderStructList();
  1227.                 end
  1228.             end
  1229.         end
  1230.         updateLocalStructs();
  1231.         renderStructList();
  1232.         while true do
  1233.             if updateData then
  1234.                 downloadUpdate();
  1235.             end
  1236.             if canStartChange then
  1237.                 canStartChange=false;
  1238.                 renderStructList();
  1239.             end
  1240.             local ev={os.pullEvent()};
  1241.             if updateData then
  1242.                 downloadUpdate();
  1243.             end
  1244.             if canStartChange then
  1245.                 canStartChange=false;
  1246.                 renderStructList();
  1247.             end
  1248.             if ev[1]=="mouse_click" then
  1249.                 if ev[3]>1 and ev[3]<sWidth and ev[4]>1 and ev[4]<sHeight then
  1250.                     if ev[4]==sHeight-1 then
  1251.                         if ev[3]>=sWidth-5 then
  1252.                             --Start
  1253.                             fromMonitor=false;
  1254.                             if startParkour() then break; end
  1255.                         elseif ev[3]>=sWidth-9 and ev[3]<sWidth-6 then
  1256.                             if ev[3]==sWidth-9 then
  1257.                                 changeDifficulty("down");
  1258.                             elseif ev[3]==sWidth-7 then
  1259.                                 changeDifficulty("up");
  1260.                             end
  1261.                         else
  1262.                             --Store
  1263.                             scroll=0;
  1264.                             updateOnlineStructs();
  1265.                             while true do
  1266.                                 local ev={os.pullEvent()};
  1267.                                 if ev[1]=="mouse_click" then
  1268.                                     if ev[3]==1 and ev[4]==1 then break;
  1269.                                     elseif ev[3]>1 and ev[3]<sWidth and ev[4]>1 and ev[4]<sHeight then
  1270.                                         local ls=onlineStructs[ev[4]-1+scroll];
  1271.                                         if ls and (ls[7] or ls[9]) then
  1272.                                             term.setBackgroundColor(colors.cyan);
  1273.                                             term.setTextColor(colors.black);
  1274.                                             local my=math.ceil(sHeight/2);
  1275.                                             local mx=math.ceil(sWidth/2);
  1276.                                             for y=my-4,my+4 do
  1277.                                                 term.setCursorPos(mx-20,y);
  1278.                                                 term.write(string.rep(" ",41));
  1279.                                             end
  1280.                                             local msg="Download "..beautifulName(ls[1]).."?";
  1281.                                             term.setCursorPos(mx-#msg/2,my-4);
  1282.                                             term.write(msg);
  1283.                                             term.setCursorPos(mx-19,my-3);
  1284.                                             term.write("by "..ls[5]:sub(1,37));
  1285.                                             if ls[8] then
  1286.                                                 if math.floor(ls[8])==ls[8] then
  1287.                                                     msg=ls[8]..".0";
  1288.                                                 else
  1289.                                                     msg=tostring(ls[8]);
  1290.                                                 end
  1291.                                                 term.setCursorPos(mx+20-#msg,my-3);
  1292.                                                 term.write("V"..msg);
  1293.                                             end
  1294.                                             function wordWrap(str)
  1295.                                                 function trim(s)
  1296.                                                     while s:sub(1,1)==" " do
  1297.                                                         s=s:sub(2);
  1298.                                                     end
  1299.                                                     while s:sub(#s,#s)==" " do
  1300.                                                         s=s:sub(1,#s-1);
  1301.                                                     end
  1302.                                                     return s;
  1303.                                                 end
  1304.                                                 str=trim(str);
  1305.                                                 if #str<=41 then
  1306.                                                     return str;
  1307.                                                 else
  1308.                                                     local index=41;
  1309.                                                     for i=41,1,-1 do
  1310.                                                         if str:sub(i,i)==" " then
  1311.                                                             index=i;
  1312.                                                             break;
  1313.                                                         end
  1314.                                                     end
  1315.                                                     return trim(str:sub(1,index)).."\n"..wordWrap(str:sub(index+1));
  1316.                                                 end
  1317.                                             end
  1318.                                             if ls[6]:sub(1,16)=="#external-desc: " then
  1319.                                                 if http then
  1320.                                                     term.setCursorPos(mx-20,my-2);
  1321.                                                     term.write("Loading...");
  1322.                                                     local desc=http.get("http://pastebin.com/raw.php?i="..textutils.urlEncode(ls[6]:sub(17)));
  1323.                                                     if desc then
  1324.                                                         msg=desc.readAll();
  1325.                                                         desc.close();
  1326.                                                     else
  1327.                                                         msg="Error getting external description in "..tostring(ls[6]:sub(17));
  1328.                                                     end
  1329.                                                     term.setCursorPos(mx-20,my-2);
  1330.                                                     term.write(string.rep(" ",10));
  1331.                                                 else
  1332.                                                     msg="Can't get description, HTTP disabled";
  1333.                                                 end
  1334.                                             else
  1335.                                                 msg=ls[6];
  1336.                                             end
  1337.                                             msg=wordWrap(msg);
  1338.                                             local lastLine=1;
  1339.                                             for i=0,5 do
  1340.                                                 local newline=msg:find("\n",lastLine);
  1341.                                                 local line;
  1342.                                                 if newline then
  1343.                                                     line=msg:sub(lastLine,newline-1);
  1344.                                                     lastLine=newline+1;
  1345.                                                 else
  1346.                                                     if lastLine<=#msg then
  1347.                                                         line=msg:sub(lastLine);
  1348.                                                         lastLine=#msg+1;
  1349.                                                     else
  1350.                                                         line="";
  1351.                                                     end
  1352.                                                 end
  1353.                                                 term.setCursorPos(mx-20,my-2+i);
  1354.                                                 term.write(line);
  1355.                                             end
  1356.                                             msg="[Y]Download    [N]Cancel";
  1357.                                             term.setCursorPos(mx-#msg/2,my+4);
  1358.                                             term.write(msg);
  1359.                                             while true do
  1360.                                                 local evi={os.pullEvent()};
  1361.                                                 if evi[1]=="key" then
  1362.                                                     if evi[2]==keys.y then
  1363.                                                         downloadStruct(ev[4]-1+scroll);
  1364.                                                         break;
  1365.                                                     elseif evi[2]==keys.n then
  1366.                                                         renderStore();
  1367.                                                         break;
  1368.                                                     end
  1369.                                                 end
  1370.                                             end
  1371.                                         end
  1372.                                     end
  1373.                                 elseif ev[1]=="mouse_scroll" then
  1374.                                     scrollOStructs(ev[2]);
  1375.                                 elseif ev[1]=="key" then
  1376.                                     if ev[2]==keys.up then scrollOStructs(-1);
  1377.                                     elseif ev[2]==keys.down then scrollOStructs(1);
  1378.                                     elseif ev[2]==keys.d then
  1379.                                         if not debuggingStructs then
  1380.                                             debuggingStructs=true;
  1381.                                             updateOnlineStructs();
  1382.                                         end
  1383.                                     end
  1384.                                 end
  1385.                             end
  1386.                             scroll=0;
  1387.                             renderStructList();
  1388.                         end
  1389.                     else
  1390.                         local ls=localStructs[ev[4]-1+scroll];
  1391.                         if ls then
  1392.                             if ev[3]>sWidth-3 then
  1393.                                 local msg="Are you sure you want to delete? (Y/N)";
  1394.                                 term.setBackgroundColor(colors.white);
  1395.                                 term.setTextColor(colors.red);
  1396.                                 term.setCursorPos(math.ceil(sWidth/2-#msg/2),math.ceil(sHeight/2));
  1397.                                 term.write(msg);
  1398.                                 while true do
  1399.                                     local ev={os.pullEvent()};
  1400.                                     if ev[1]=="key" then
  1401.                                         if ev[2]==keys.y then
  1402.                                             local filename="structures/"..ls[1];
  1403.                                             if ls[4] then
  1404.                                                 filename=filename.."."..ls[4];
  1405.                                             end
  1406.                                             fs.delete(filename);
  1407.                                             updateLocalStructs();
  1408.                                             break;
  1409.                                         elseif ev[2]==keys.n then
  1410.                                             break;
  1411.                                         end
  1412.                                     end
  1413.                                 end
  1414.                                 renderStructList();
  1415.                             else
  1416.                                 if type(ls[2])=="function" then
  1417.                                     ls[3]=not ls[3];
  1418.                                     renderStructList();
  1419.                                 end
  1420.                             end
  1421.                         end
  1422.                     end
  1423.                 elseif ev[3]==1 and ev[4]==1 then
  1424.                     term.setBackgroundColor(colors.black);
  1425.                     term.setTextColor(colors.white);
  1426.                     term.clear();
  1427.                     term.setCursorPos(1,1);
  1428.                     if monitor then
  1429.                         monitor.setBackgroundColor(colors.black);
  1430.                         monitor.clear();
  1431.                     end
  1432.                     shallQuit=true;
  1433.                     error();
  1434.                 end
  1435.             elseif ev[1]=="mouse_scroll" then
  1436.                 scrollLStructs(ev[2]);
  1437.             elseif ev[1]=="monitor_touch" and ev[2]==monitorName then
  1438.                 local mWidth;
  1439.                 local mHeight;
  1440.                 mWidth,mHeight=monitor.getSize();
  1441.                 if ev[4]>1 and ev[4]<mHeight-1 then
  1442.                     if ev[3]<mWidth and ev[3]>1 then
  1443.                         if (ev[3]==mWidth-1 or ev[3]==mWidth-2) and #localStructs-(mHeight-3)>0 then
  1444.                             if ev[4]==2 or ev[4]==3 then
  1445.                                 scrollMLStructs(-1);
  1446.                             elseif ev[4]==mHeight-2 or ev[4]==mHeight-3 then
  1447.                                 scrollMLStructs(1);
  1448.                             end
  1449.                         else
  1450.                             local ls=localStructs[ev[4]-1+mScroll];
  1451.                             if ls then
  1452.                                 if type(ls[2])=="function" then
  1453.                                     ls[3]=not ls[3];
  1454.                                     renderStructList();
  1455.                                 end
  1456.                             end
  1457.                         end
  1458.                     end
  1459.                 elseif ev[4]==mHeight-1 then
  1460.                     if ev[3]>=mWidth-5 then
  1461.                         --Start
  1462.                         fromMonitor=true;
  1463.                         if startParkour() then break; end
  1464.                     elseif ev[3]==mWidth-16 or ev[3]==mWidth-15 then
  1465.                         changeDifficulty("down");
  1466.                     elseif ev[3]==mWidth-7 or ev[3]==mWidth-8 then
  1467.                         changeDifficulty("up");
  1468.                     end
  1469.                 end
  1470.             elseif ev[1]=="monitor_resize" and ev[2]==monitorName then
  1471.                 renderMonitor();
  1472.             elseif ev[1]=="key" then
  1473.                 if ev[2]==keys.up then scrollLStructs(-1);
  1474.                 elseif ev[2]==keys.down then scrollLStructs(1);
  1475.                 end
  1476.             end
  1477.         end
  1478.     end
  1479.     function updateAsync()
  1480.         local tempCanStart=true;
  1481.         if http then
  1482.             local last=http.get(updateURL);
  1483.             if last then
  1484.                 local ver=tonumber(last.readLine());
  1485.                 if type(ver)=="number" then
  1486.                     if ver>parkourVersion then
  1487.                         local newurl=last.readLine();
  1488.                         if type(newurl)=="string" then
  1489.                             tempCanStart=false;
  1490.                             updateData={ver,newurl};
  1491.                             os.queueEvent("update_available");
  1492.                         end
  1493.                     end
  1494.                 end
  1495.                 last.close();
  1496.             end
  1497.         end
  1498.         canStart=tempCanStart;
  1499.         canStartChange=true;
  1500.         os.queueEvent("canStart_changed");
  1501.         while true do os.pullEvent(); end
  1502.     end
  1503.     function limitUpdateTime()
  1504.         sleep(2);
  1505.         canStart=true;
  1506.         canStartChange=true;
  1507.         os.queueEvent("canStart_changed");
  1508.         while true do os.pullEvent(); end
  1509.     end
  1510.     parallel.waitForAny(mainGUI,updateAsync,limitUpdateTime);
  1511.     structsetPool={};
  1512.     final={exit={pos[1],pos[2],pos[3]},next=numberToDir(math.random(0,1)*2+1)};
  1513.     if shallQuit then
  1514.         error();
  1515.     end
  1516.     function monPrint(str)
  1517.         local mx1,my1=monitor.getCursorPos();
  1518.         monitor.write(str);
  1519.         monitor.setCursorPos(1,my1+1);
  1520.     end
  1521.     --Print compilation errors, if any
  1522.     local compErrors=false;
  1523.     for i,struct in ipairs(localStructs) do
  1524.         if type(struct[2])=="string" then
  1525.             printError("Non-LUA file in structures directory: "..struct[1]);
  1526.             printError(struct[2]);
  1527.             compErrors=true;
  1528.         end
  1529.     end
  1530.     if compErrors then
  1531.         print("Read the errors, then press a key");
  1532.         os.pullEvent("key");
  1533.     end
  1534.     --Test Structures for errors
  1535.     term.write("Testing Structures... ");
  1536.     local tx,ty=term.getCursorPos();
  1537.     print();
  1538.     local mx;
  1539.     local my;
  1540.     if fromMonitor then
  1541.         monitor.write("Testing Structures... ");
  1542.         mx,my=monitor.getCursorPos();
  1543.         monitor.setCursorPos(1,my+1);
  1544.     end
  1545.     local warnings=0;
  1546.     function structError(i,msg)
  1547.         printError("Error in structure ["..structNames[i].."]");
  1548.         printError(tostring(msg));
  1549.         if fromMonitor then
  1550.             monPrint("Error in "..structNames[i]);
  1551.             monPrint("Try deactivating the structure");
  1552.             monPrint("And telling the structure maker");
  1553.         end
  1554.         error();
  1555.     end
  1556.     function structWarning(i,msg)
  1557.         warnings=warnings+1;
  1558.         printError("Warning in structure ["..structNames[i].."]");
  1559.         printError(tostring(msg));
  1560.     end
  1561.     for i,func in ipairs(structures) do
  1562.         local ok,struct=pcall(func,"build",0);
  1563.         if not ok then
  1564.             structError(i,struct);
  1565.         end
  1566.         if type(struct)~="table" then
  1567.             structError(i,"Function must return a structure table");
  1568.         end
  1569.         --Check struct.blocks
  1570.         if type(struct.blocks)~="table" then
  1571.             structError(i,"StructureTable must contain a \"blocks\" subtable");
  1572.         elseif #struct.blocks==0 then
  1573.             structError(i,"StructureTable.blocks is empty");
  1574.         end
  1575.         for j,b in ipairs(struct.blocks) do
  1576.             if type(b)~="table" then
  1577.                 structError(i,"StructureTable.blocks entry #"..j.." is not a table");
  1578.             end
  1579.             if type(b[1])~="number" then
  1580.                 structError(i,"StructureTable.blocks entry #"..j.."'s first element(X) is not a number: "..tostring(b[1]));
  1581.             elseif type(b[2])~="number" then
  1582.                 structError(i,"StructureTable.blocks entry #"..j.."'s second element(Y) is not a number: "..tostring(b[2]));
  1583.             elseif type(b[3])~="number" then
  1584.                 structError(i,"StructureTable.blocks entry #"..j.."'s third element(Z) is not a number: "..tostring(b[3]));
  1585.             elseif b.autoMeta then
  1586.                 if type(b[5])~="number" and b[5]~="x+" and b[5]~="z+" and b[5]~="x-" and b[5]~="z-" then
  1587.                     structError(i,"StructureTable.blocks entry #"..j.."'s fifth element (Orientation) is an invalid orientation for autoMeta: "..tostring(b[5]));
  1588.                 end
  1589.             end
  1590.         end
  1591.         --Check struct.areaCheck
  1592.         if type(struct.areaCheck)~="table" then
  1593.             structError(i,"StructureTable must contain an \"areaCheck\" subtable");
  1594.         elseif #struct.areaCheck==0 then
  1595.             structWarning(i,"StructureTable.areaCheck is empty, players may wander freely");
  1596.         end
  1597.         for j,b in ipairs(struct.areaCheck) do
  1598.             if type(b)~="table" then
  1599.                 structError(i,"StructureTable.areaCheck entry #"..j.." is not a table");
  1600.             end
  1601.             if type(b[1])~="number" then
  1602.                 structError(i,"StructureTable.areaCheck entry #"..j.."'s first element(X) is not a number");
  1603.             elseif type(b[2])~="number" then
  1604.                 structError(i,"StructureTable.areaCheck entry #"..j.."'s second element(Y) is not a number");
  1605.             elseif type(b[3])~="number" then
  1606.                 structError(i,"StructureTable.areaCheck entry #"..j.."'s third element(Z) is not a number");
  1607.             elseif type(b[4])~="number" then
  1608.                 structError(i,"StructureTable.areaCheck entry #"..j.."'s fourth element(Range) is not a number");
  1609.             end
  1610.         end
  1611.         --Check struct.exit
  1612.         if type(struct.exit)~="table" then
  1613.             structError(i,"StructureTable.exit is not a table");
  1614.         end
  1615.         if type(struct.exit[1])~="number" then
  1616.             structError(i,"StructureTable.exit first element(X) is not a number");
  1617.         elseif type(struct.exit[2])~="number" then
  1618.             structError(i,"StructureTable.exit second element(Y) is not a number");
  1619.         elseif type(struct.exit[3])~="number" then
  1620.             structError(i,"StructureTable.exit third element(Z) is not a number");
  1621.         end
  1622.         --Check struct.next
  1623.         if type(struct.next)=="string" then
  1624.             if struct.next~="x+" and struct.next~="z+" and struct.next~="x-" and struct.next~="z-" then
  1625.                 structError(i,"StructureTable.next must be x+, z+, x- or z-, not "..struct.next);
  1626.             end
  1627.         elseif type(struct.next)=="number" then
  1628.             if struct.next==math.huge or struct.next==-math.huge or struct.next~=struct.next then
  1629.                 structError(i,"StructureTable.next is not a valid number");
  1630.             end
  1631.         else
  1632.             structError(i,"StructureTable.next is neither a string or a number");
  1633.         end
  1634.        
  1635.         --Check "weight" func
  1636.         local ok,t=pcall(func,"weight");
  1637.         if not ok then
  1638.             structError(i,t);
  1639.         end
  1640.         if t~=nil and type(t)~="number" then
  1641.             if type(t)=="number" then
  1642.                 if t<0 or t==math.huge or t~=t then
  1643.                     structError(i,"Structure call with \"weight\" argument must return a positive valid number");
  1644.                 end
  1645.             else
  1646.                 structError(i,"Structure call with \"weight\" argument must return a number or nil, not "..tostring(t));
  1647.             end
  1648.         end
  1649.     end
  1650.     if warnings==0 then
  1651.         sleep(0.2);
  1652.         local tx1,ty1=term.getCursorPos();
  1653.         term.setCursorPos(tx,ty);
  1654.         term.write("No errors");
  1655.         term.setCursorPos(tx1,ty1);
  1656.         if fromMonitor then
  1657.             local mx1,my1=monitor.getCursorPos();
  1658.             monitor.setCursorPos(mx,my);
  1659.             monitor.write("No errors");
  1660.             monitor.setCursorPos(mx1,my1);
  1661.         end
  1662.         sleep(0.3);
  1663.     else
  1664.         print("Finished testing, found "..warnings.." warnings");
  1665.         sleep(2);
  1666.     end
  1667.    
  1668.     playerCount=-1;
  1669.     local playersLocked=false;
  1670.     local selectedMode=0;
  1671.     realMode=1;
  1672.     checkpointGoal=5;
  1673.     function renderSelection()
  1674.         term.setBackgroundColor(colors.lime);
  1675.         term.clear();
  1676.         for i=1,sHeight-2 do
  1677.             if i==1 then term.setBackgroundColor(colors.green);
  1678.             elseif playersLocked and i==6 then term.setBackgroundColor(colors.lime);
  1679.             elseif playersLocked and i==7 then term.setBackgroundColor(colors.green);
  1680.             else term.setBackgroundColor(colors.white); end
  1681.             term.setCursorPos(2,i+1);
  1682.             term.write(string.rep(" ",sWidth-2));
  1683.             if i==1 then
  1684.                 term.setTextColor(colors.white);
  1685.                 local msg="Player Selection";
  1686.                 term.setCursorPos(math.ceil(sWidth/2-#msg/2),i+1);
  1687.                 term.write(msg);
  1688.             elseif i==2 then
  1689.                 term.setTextColor(colors.lightGray);
  1690.                 term.setCursorPos(2,i+1);
  1691.                 if selectionPos[4]==-1 then term.write("Players are selected from the whole world");
  1692.                 else term.write("Players are selected from a "..(selectionPos[4]*2).."-diameter sphere"); end
  1693.             elseif i==3 then
  1694.                 term.setTextColor(colors.lightGray);
  1695.                 term.setCursorPos(2,i+1);
  1696.                 term.write("15 players max, chosen by proximity");
  1697.             elseif not confiscateItems and i==4 then
  1698.                 term.setTextColor(colors.red);
  1699.                 term.setCursorPos(2,i+1);
  1700.                 term.write("WARNING: All your items will be destroyed!");
  1701.             elseif i==5 then
  1702.                 local msg;
  1703.                 if playerCount==-1 then
  1704.                     msg="Searching for players...";
  1705.                 elseif playerCount==0 then
  1706.                     msg="No players found";
  1707.                 elseif playerCount==1 then
  1708.                     msg="1 player found";
  1709.                 else
  1710.                     msg=playerCount.." players found";
  1711.                 end
  1712.                 term.setTextColor(colors.black);
  1713.                 term.setCursorPos(2,i+1);
  1714.                 term.write(msg);
  1715.             end
  1716.         end
  1717.         if playersLocked then
  1718.             local msg;
  1719.             if playerCount==1 then msg=" Singleplayer ";
  1720.             else msg=" Multiplayer "; end
  1721.             term.setCursorPos(sWidth/2-#msg/2,8);
  1722.             term.setBackgroundColor(colors.green);
  1723.             term.setTextColor(colors.white);
  1724.             term.write(msg);
  1725.             if playerCount==1 then
  1726.                 msg="Freeplay";
  1727.                 if selectedMode==1 then term.setBackgroundColor(colors.green);
  1728.                 else term.setBackgroundColor(colors.lightGray); end
  1729.                 term.setCursorPos(3,10);
  1730.                 term.write(string.rep(" ",sWidth-4));
  1731.                 term.setCursorPos(sWidth/2-#msg/2,10);
  1732.                 term.write(msg);
  1733.             else
  1734.                 msg="Race";
  1735.                 if selectedMode==1 then term.setBackgroundColor(colors.green);
  1736.                 else term.setBackgroundColor(colors.lightGray); end
  1737.                 term.setCursorPos(3,10);
  1738.                 term.write(string.rep(" ",sWidth-4));
  1739.                 term.setCursorPos(sWidth/2-#msg/2,10);
  1740.                 term.write(msg);
  1741.                 msg="Skill";
  1742.                 if selectedMode==2 then term.setBackgroundColor(colors.green);
  1743.                 else term.setBackgroundColor(colors.lightGray); end
  1744.                 term.setCursorPos(3,11);
  1745.                 term.write(string.rep(" ",sWidth-4));
  1746.                 term.setCursorPos(sWidth/2-#msg/2,11);
  1747.                 term.write(msg);
  1748.                 msg="Freeplay";
  1749.                 if selectedMode==3 then term.setBackgroundColor(colors.green);
  1750.                 else term.setBackgroundColor(colors.lightGray); end
  1751.                 term.setCursorPos(3,12);
  1752.                 term.write(string.rep(" ",sWidth-4));
  1753.                 term.setCursorPos(sWidth/2-#msg/2,12);
  1754.                 term.write(msg);
  1755.                 if selectedMode==1 or selectedMode==2 then
  1756.                     local goalX=math.floor(sWidth/2)-13;
  1757.                     term.setCursorPos(goalX+1,14);
  1758.                     term.setBackgroundColor(colors.white);
  1759.                     term.write("   ");
  1760.                     msg=tostring(checkpointGoal);
  1761.                     term.setCursorPos(goalX+4-#msg,14);
  1762.                     term.setTextColor(colors.black);
  1763.                     term.write(msg);
  1764.                     term.setTextColor(colors.lightGray);
  1765.                     term.setCursorPos(goalX+6,14);
  1766.                     term.write("Checkpoints to finish");
  1767.                     term.setBackgroundColor(colors.gray);
  1768.                     term.setTextColor(colors.white);
  1769.                     term.setCursorPos(goalX,14);
  1770.                     term.write("<");
  1771.                     term.setCursorPos(goalX+4,14);
  1772.                     term.write(">");
  1773.                 end
  1774.             end
  1775.             term.setCursorPos(sWidth-5,sHeight-1);
  1776.             if selectedMode==0 then term.setBackgroundColor(colors.gray);term.setTextColor(colors.black);
  1777.             else term.setBackgroundColor(colors.green);term.setTextColor(colors.white); end
  1778.             term.write("Start");
  1779.         end
  1780.         term.setCursorPos(2,sHeight-1);
  1781.         if playersLocked then term.setBackgroundColor(colors.green);term.setTextColor(colors.white);
  1782.         elseif playerCount>0 then term.setBackgroundColor(colors.green);term.setTextColor(colors.white);
  1783.         else term.setBackgroundColor(colors.gray);term.setTextColor(colors.black); end
  1784.         if playersLocked then term.write("Unlock Players");
  1785.         else term.write(" Lock Players "); end
  1786.         term.setCursorPos(1,1);
  1787.         term.setBackgroundColor(colors.lime);
  1788.         term.setTextColor(colors.white);
  1789.         term.write("<");
  1790.         renderMonitorSelection();
  1791.     end
  1792.     function renderMonitorSelection()
  1793.         if not monitor then return; end
  1794.         monitor.setTextScale(0.5);
  1795.         local mWidth,mHeight=monitor.getSize();
  1796.         monitor.setBackgroundColor(colors.lime);
  1797.         monitor.clear();
  1798.         for i=1,mHeight-2 do
  1799.             if i==1 then monitor.setBackgroundColor(colors.green);
  1800.             elseif playersLocked and i==6 then monitor.setBackgroundColor(colors.lime);
  1801.             elseif playersLocked and i==7 then monitor.setBackgroundColor(colors.green);
  1802.             else monitor.setBackgroundColor(colors.white); end
  1803.             monitor.setCursorPos(2,i+1);
  1804.             monitor.write(string.rep(" ",mWidth-2));
  1805.             if i==1 then
  1806.                 monitor.setTextColor(colors.white);
  1807.                 local msg="Player Selection";
  1808.                 monitor.setCursorPos(math.ceil(mWidth/2-#msg/2),i+1);
  1809.                 monitor.write(msg);
  1810.             elseif i==2 then
  1811.                 monitor.setTextColor(colors.lightGray);
  1812.                 monitor.setCursorPos(2,i+1);
  1813.                 monitor.write("Player selection area:");
  1814.             elseif i==3 then
  1815.                 monitor.setTextColor(colors.lightGray);
  1816.                 monitor.setCursorPos(2,i+1);
  1817.                 if selectionPos[4]==-1 then monitor.write("Whole world");
  1818.                 else monitor.write((selectionPos[4]*2).."-diameter sphere"); end
  1819.             elseif i==4 then
  1820.                 monitor.setTextColor(colors.lightGray);
  1821.                 monitor.setCursorPos(2,i+1);
  1822.                 if confiscateItems then monitor.write("15 players max");
  1823.                 else monitor.setTextColor(colors.red);monitor.write("WARNING: Will destroy your items!"); end
  1824.             elseif i==5 then
  1825.                 local msg;
  1826.                 if playerCount==-1 then
  1827.                     msg="Searching for players...";
  1828.                 elseif playerCount==0 then
  1829.                     msg="No players found";
  1830.                 elseif playerCount==1 then
  1831.                     msg="1 player found";
  1832.                 else
  1833.                     msg=playerCount.." players found";
  1834.                 end
  1835.                 monitor.setTextColor(colors.black);
  1836.                 monitor.setCursorPos(2,i+1);
  1837.                 monitor.write(msg);
  1838.             end
  1839.         end
  1840.         if playersLocked then
  1841.             local msg;
  1842.             if playerCount==1 then msg=" Singleplayer ";
  1843.             else msg=" Multiplayer "; end
  1844.             monitor.setCursorPos(mWidth/2-#msg/2,8);
  1845.             monitor.setBackgroundColor(colors.green);
  1846.             monitor.setTextColor(colors.white);
  1847.             monitor.write(msg);
  1848.             if playerCount==1 then
  1849.                 msg="Freeplay";
  1850.                 if selectedMode==1 then monitor.setBackgroundColor(colors.green);
  1851.                 else monitor.setBackgroundColor(colors.lightGray); end
  1852.                 monitor.setCursorPos(3,10);
  1853.                 monitor.write(string.rep(" ",mWidth-4));
  1854.                 monitor.setCursorPos(mWidth/2-#msg/2,10);
  1855.                 monitor.write(msg);
  1856.             else
  1857.                 msg="Race";
  1858.                 if selectedMode==1 then monitor.setBackgroundColor(colors.green);
  1859.                 else monitor.setBackgroundColor(colors.lightGray); end
  1860.                 monitor.setCursorPos(3,10);
  1861.                 monitor.write(string.rep(" ",mWidth-4));
  1862.                 monitor.setCursorPos(mWidth/2-#msg/2,10);
  1863.                 monitor.write(msg);
  1864.                 msg="Skill";
  1865.                 if selectedMode==2 then monitor.setBackgroundColor(colors.green);
  1866.                 else monitor.setBackgroundColor(colors.lightGray); end
  1867.                 monitor.setCursorPos(3,11);
  1868.                 monitor.write(string.rep(" ",mWidth-4));
  1869.                 monitor.setCursorPos(mWidth/2-#msg/2,11);
  1870.                 monitor.write(msg);
  1871.                 msg="Freeplay";
  1872.                 if selectedMode==3 then monitor.setBackgroundColor(colors.green);
  1873.                 else monitor.setBackgroundColor(colors.lightGray); end
  1874.                 monitor.setCursorPos(3,12);
  1875.                 monitor.write(string.rep(" ",mWidth-4));
  1876.                 monitor.setCursorPos(mWidth/2-#msg/2,12);
  1877.                 monitor.write(msg);
  1878.                 if selectedMode==1 or selectedMode==2 then
  1879.                     local goalX=math.floor(mWidth/2)-14;
  1880.                     monitor.setCursorPos(goalX+2,14);
  1881.                     monitor.setBackgroundColor(colors.white);
  1882.                     monitor.write("   ");
  1883.                     msg=tostring(checkpointGoal);
  1884.                     monitor.setCursorPos(goalX+5-#msg,14);
  1885.                     monitor.setTextColor(colors.black);
  1886.                     monitor.write(msg);
  1887.                     monitor.setTextColor(colors.lightGray);
  1888.                     monitor.setCursorPos(goalX+8,14);
  1889.                     monitor.write("Checkpoints to finish");
  1890.                     monitor.setBackgroundColor(colors.gray);
  1891.                     if fromMonitor or forceMonitorSelection then monitor.setTextColor(colors.white);
  1892.                     else monitor.setTextColor(colors.black); end
  1893.                     monitor.setCursorPos(goalX,14);
  1894.                     monitor.write("<-");
  1895.                     monitor.setCursorPos(goalX+5,14);
  1896.                     monitor.write("->");
  1897.                 end
  1898.             end
  1899.             monitor.setCursorPos(mWidth-5,mHeight-1);
  1900.             if (not fromMonitor and not forceMonitorSelection) or selectedMode==0 then monitor.setBackgroundColor(colors.gray);monitor.setTextColor(colors.black);
  1901.             else monitor.setBackgroundColor(colors.green);monitor.setTextColor(colors.white); end
  1902.             monitor.write("Start");
  1903.         end
  1904.         monitor.setCursorPos(2,mHeight-1);
  1905.         if playersLocked then
  1906.             if allowPlayerUnlocking then
  1907.                 if fromMonitor or forceMonitorSelection then
  1908.                     monitor.setBackgroundColor(colors.green);
  1909.                     monitor.setTextColor(colors.white);
  1910.                 else
  1911.                     monitor.setBackgroundColor(colors.gray);
  1912.                     monitor.setTextColor(colors.black);
  1913.                 end
  1914.             else
  1915.                 monitor.setBackgroundColor(colors.white);
  1916.                 monitor.setTextColor(colors.lightGray);
  1917.             end
  1918.         else
  1919.             if fromMonitor or forceMonitorSelection then
  1920.                 monitor.setBackgroundColor(colors.green);
  1921.                 monitor.setTextColor(colors.white);
  1922.             else
  1923.                 monitor.setBackgroundColor(colors.gray);
  1924.                 monitor.setTextColor(colors.black);
  1925.             end
  1926.         end
  1927.         if playersLocked then
  1928.             if allowPlayerUnlocking then monitor.write("Unlock Players");
  1929.             else monitor.write("Players Locked"); end
  1930.         else monitor.write(" Lock Players "); end
  1931.         monitor.setCursorPos(1,1);
  1932.         monitor.setBackgroundColor(colors.lime);
  1933.         if fromMonitor or forceMonitorSelection then monitor.setTextColor(colors.white);
  1934.         else monitor.setTextColor(colors.gray); end
  1935.         monitor.write("<");
  1936.     end
  1937.     function lockPlayers()
  1938.         for i=1,playerCount do
  1939.             commands.async.scoreboard("players add @p[x="..selectionPos[1]..",y="..selectionPos[2]..",z="..selectionPos[3]..",c="..i.."] parkour_id 1");
  1940.         end
  1941.         playersLocked=true;
  1942.     end
  1943.     function unlockPlayers()
  1944.         commands.scoreboard("objectives remove parkour_id");
  1945.         commands.async.scoreboard("objectives add parkour_id dummy");
  1946.         playersLocked=false;
  1947.         selectedMode=0;
  1948.         realMode=1;
  1949.     end
  1950.    
  1951.     renderSelection();
  1952.     --Computers cant get redstone output out of their commands.
  1953.     --So better build a commandblock contraption and get the redstone level
  1954.     unlockPlayers();
  1955.     --15 is no artificial limit, comparator's max output is 15
  1956.     if selectionPos[4]==-1 then
  1957.         commands.async.setblock(pos[1],255,pos[3],"minecraft:command_block",0,"replace",
  1958.             "{Command:\"testfor @p[c=15]\"}");
  1959.     else
  1960.         commands.async.setblock(pos[1],255,pos[3],"minecraft:command_block",0,"replace",
  1961.             "{Command:\"testfor @p[c=15,x="..selectionPos[1]..",y="..selectionPos[2]..",z="..selectionPos[3]..",r="..selectionPos[4].."]\"}");
  1962.     end
  1963.     commands.async.setblock(pos[1]+1,254,pos[3],"minecraft:stone");
  1964.     commands.async.setblock(pos[1]+1,255,pos[3],"minecraft:unpowered_comparator",1);
  1965.     commands.async.setblock(pos[1]+2,254,pos[3],"minecraft:stone");
  1966.     commands.async.setblock(pos[1]+2,255,pos[3],"minecraft:redstone_wire");
  1967.     sleep(0.1);
  1968.     local why=0;
  1969.     parallel.waitForAny(function()
  1970.         while true do
  1971.             if playersLocked then
  1972.                 os.pullEvent();
  1973.             else
  1974.                 commands.setblock(pos[1],254,pos[3],"minecraft:air");
  1975.                 commands.setblock(pos[1],254,pos[3],"minecraft:redstone_block");
  1976.                 sleep(0.2);
  1977.                 playerCount=commands.getBlockInfo(pos[1]+2,255,pos[3]).metadata;
  1978.                 renderSelection();
  1979.             end
  1980.         end
  1981.     end,function()
  1982.         while true do
  1983.             local ev={os.pullEvent()};
  1984.             if ev[1]=="mouse_click" then
  1985.                 if ev[3]==1 and ev[4]==1 then
  1986.                     why=1;
  1987.                     return;
  1988.                 elseif playersLocked then
  1989.                     if playerCount==1 then
  1990.                         if ev[3]>2 and ev[3]<sWidth-1 then
  1991.                             if ev[4]==10 then
  1992.                                 selectedMode=1;
  1993.                                 realMode=1;
  1994.                                 renderSelection();
  1995.                             end
  1996.                         end
  1997.                     else
  1998.                         if ev[3]>2 and ev[3]<sWidth-1 then
  1999.                             if ev[4]==10 then
  2000.                                 selectedMode=1;
  2001.                                 realMode=2;
  2002.                                 renderSelection();
  2003.                             elseif ev[4]==11 then
  2004.                                 selectedMode=2;
  2005.                                 realMode=3;
  2006.                                 renderSelection();
  2007.                             elseif ev[4]==12 then
  2008.                                 selectedMode=3;
  2009.                                 realMode=1;
  2010.                                 renderSelection();
  2011.                             end
  2012.                         end
  2013.                         if ev[4]==14 and (selectedMode==1 or selectedMode==2) then
  2014.                             local goalX=math.floor(sWidth/2)-13;
  2015.                             if ev[3]==goalX and checkpointGoal>1 then
  2016.                                 checkpointGoal=checkpointGoal-1;
  2017.                                 renderSelection();
  2018.                             elseif ev[3]==goalX+4 and checkpointGoal<999 then
  2019.                                 checkpointGoal=checkpointGoal+1;
  2020.                                 renderSelection();
  2021.                             end
  2022.                         end
  2023.                     end
  2024.                     if selectedMode~=0 and ev[4]==sHeight-1 and ev[3]>=sWidth-5 and ev[3]<sWidth then
  2025.                         why=2;
  2026.                         fromMonitor=false;
  2027.                         return;
  2028.                     end
  2029.                     if ev[4]==sHeight-1 and ev[3]>=2 and ev[3]<=15 and playerCount>0 then
  2030.                         unlockPlayers();
  2031.                         renderSelection();
  2032.                     end
  2033.                 else
  2034.                     if ev[4]==sHeight-1 and ev[3]>=2 and ev[3]<=15 and playerCount>0 then
  2035.                         lockPlayers();
  2036.                         renderSelection();
  2037.                     end
  2038.                 end
  2039.             elseif (fromMonitor or forceMonitorSelection) and ev[1]=="monitor_touch" and ev[2]==monitorName then
  2040.                 local mWidth,mHeight=monitor.getSize();
  2041.                 if ev[3]==1 and ev[4]==1 then
  2042.                     why=1;
  2043.                     return;
  2044.                 elseif playersLocked then
  2045.                     if playerCount==1 then
  2046.                         if ev[3]>2 and ev[3]<mWidth-1 then
  2047.                             if ev[4]==10 then
  2048.                                 selectedMode=1;
  2049.                                 realMode=1;
  2050.                                 renderSelection();
  2051.                             end
  2052.                         end
  2053.                     else
  2054.                         if ev[3]>2 and ev[3]<mWidth-1 then
  2055.                             if ev[4]==10 then
  2056.                                 selectedMode=1;
  2057.                                 realMode=2;
  2058.                                 renderSelection();
  2059.                             elseif ev[4]==11 then
  2060.                                 selectedMode=2;
  2061.                                 realMode=3;
  2062.                                 renderSelection();
  2063.                             elseif ev[4]==12 then
  2064.                                 selectedMode=3;
  2065.                                 realMode=1;
  2066.                                 renderSelection();
  2067.                             end
  2068.                         end
  2069.                         if ev[4]==14 and (selectedMode==1 or selectedMode==2) then
  2070.                             local goalX=math.floor(mWidth/2)-14;
  2071.                             if (ev[3]==goalX or ev[3]==goalX+1) and checkpointGoal>1 then
  2072.                                 checkpointGoal=checkpointGoal-1;
  2073.                                 renderSelection();
  2074.                             elseif (ev[3]==goalX+5 or ev[3]==goalX+6) and checkpointGoal<999 then
  2075.                                 checkpointGoal=checkpointGoal+1;
  2076.                                 renderSelection();
  2077.                             end
  2078.                         end
  2079.                     end
  2080.                     if selectedMode~=0 and ev[4]==mHeight-1 and ev[3]>=mWidth-5 and ev[3]<mWidth then
  2081.                         why=2;
  2082.                         fromMonitor=true;
  2083.                         return;
  2084.                     end
  2085.                     if allowPlayerUnlocking and ev[4]==mHeight-1 and ev[3]>=2 and ev[3]<=15 and playerCount>0 then
  2086.                         unlockPlayers();
  2087.                         renderSelection();
  2088.                     end
  2089.                 else
  2090.                     if ev[4]==mHeight-1 and ev[3]>=2 and ev[3]<=15 and playerCount>0 then
  2091.                         lockPlayers();
  2092.                         renderSelection();
  2093.                     end
  2094.                 end
  2095.             end
  2096.         end
  2097.     end);
  2098.     commands.async.setblock(pos[1],255,pos[3],"minecraft:air");
  2099.     commands.async.setblock(pos[1]+1,255,pos[3],"minecraft:air");
  2100.     commands.async.setblock(pos[1]+1,254,pos[3],"minecraft:air");
  2101.     commands.async.setblock(pos[1]+2,255,pos[3],"minecraft:air");
  2102.     commands.async.setblock(pos[1]+2,254,pos[3],"minecraft:air");
  2103.     commands.async.setblock(pos[1],254,pos[3],"minecraft:air");
  2104.     if why==1 then
  2105.         commands.async.scoreboard("objectives remove parkour_id");
  2106.         return;
  2107.     end
  2108.     term.setBackgroundColor(colors.black);
  2109.     term.setTextColor(colors.white);
  2110.     term.clear();
  2111.     term.setCursorPos(1,1);
  2112.     if monitor then
  2113.         monitor.setBackgroundColor(colors.black);
  2114.         monitor.setTextColor(colors.white);
  2115.         monitor.clear();
  2116.         monitor.setCursorPos(1,1);
  2117.     end
  2118.     function selectorFor(id)
  2119.         return "@p[score_parkour_id_min="..id..",score_parkour_id="..id;
  2120.     end
  2121.     playerData={};
  2122.     for id=1,playerCount do
  2123.         playerData[id]={
  2124.             deaths=0,
  2125.             checkpoints=-1,
  2126.             time=0,
  2127.         };
  2128.         --commands.async.tellraw(selectorFor(id).."] {text:\"You are player #"..id.."\"}");
  2129.     end
  2130.    
  2131.     sidebarMode=0;
  2132.     function updateScoreboard(pid)
  2133.         if sidebarMode==0 then commands.async.scoreboard("players set "..selectorFor(pid).."] parkour_deaths "..playerData[pid].deaths);
  2134.         elseif sidebarMode==1 then commands.async.scoreboard("players set "..selectorFor(pid).."] parkour_checks "..playerData[pid].checkpoints);
  2135.         elseif sidebarMode==2 then
  2136.             local btime="";
  2137.             local msg;
  2138.             local rtime=playerData[pid].time;
  2139.             local ttime=rtime;
  2140.             if rtime>=3600 then
  2141.                 btime=btime..tostring(math.floor(ttime/3600));
  2142.                 ttime=ttime%3600;
  2143.             end
  2144.             if rtime>=60 then
  2145.                 msg=tostring(math.floor(ttime/60));
  2146.                 if #msg==1 then msg="0"..msg; end
  2147.                 btime=btime..msg;
  2148.                 ttime=ttime%60;
  2149.             end
  2150.             msg=tostring(math.floor(ttime));
  2151.             if #msg==1 then msg="0"..msg; end
  2152.             btime=btime..msg;
  2153.             commands.async.scoreboard("players set "..selectorFor(pid).."] parkour_time "..btime);
  2154.         --elseif sidebarMode==3 then --It doesnt matter, IDs aren't updated
  2155.         end
  2156.     end
  2157.     cycleSidebar=function(o)
  2158.         if o then sidebarMode=o;
  2159.         else sidebarMode=(sidebarMode+1)%4; end
  2160.         for i=1,playerCount do
  2161.             updateScoreboard(i);
  2162.         end
  2163.         if sidebarMode==0 then      commands.async.scoreboard("objectives setdisplay sidebar parkour_deaths");
  2164.         elseif sidebarMode==1 then  commands.async.scoreboard("objectives setdisplay sidebar parkour_checks");
  2165.         elseif sidebarMode==2 then  commands.async.scoreboard("objectives setdisplay sidebar parkour_time");
  2166.         elseif sidebarMode==3 then  commands.async.scoreboard("objectives setdisplay sidebar parkour_number");
  2167.         end
  2168.     end
  2169.     commands.async.scoreboard("objectives add parkour_deaths dummy Deaths");
  2170.     commands.async.scoreboard("objectives add parkour_checks dummy Checkpoints Reached");
  2171.     commands.async.scoreboard("objectives add parkour_time dummy Time");
  2172.     commands.async.scoreboard("objectives add parkour_number dummy Player Number");
  2173.     for i=1,playerCount do
  2174.         commands.async.scoreboard("players set "..selectorFor(i).."] parkour_number "..i);
  2175.     end
  2176.     cycleSidebar(3);
  2177.    
  2178.     local turnOnCBO=false;
  2179.     if (({commands.gamerule("commandBlockOutput")})[2][1]):find("true") then
  2180.         if fromMonitor then
  2181.             commands.gamerule("commandBlockOutput false");
  2182.             print("Command Block Output turned off for the session.");
  2183.             turnOnCBO=true;
  2184.             monitor.write("Turned off CommandBlockOutput for this time");
  2185.             local _,y=monitor.getCursorPos();
  2186.             monitor.setCursorPos(1,y+1);
  2187.         else
  2188.             print("Command Block Output is on");
  2189.             print("Do you wish to turn it off for the session? (Y/N)");
  2190.             while true do
  2191.                 local ev={os.pullEvent()};
  2192.                 if ev[1]=="char" then
  2193.                     if ev[2]=="y" then
  2194.                         commands.gamerule("commandBlockOutput false");
  2195.                         print("Command Block Output turned off for the session.");
  2196.                         turnOnCBO=true;
  2197.                         break;
  2198.                     elseif ev[2]=="n" then
  2199.                         print("Command Block Output left on.");
  2200.                         break;
  2201.                     end
  2202.                 end
  2203.             end
  2204.         end
  2205.     end
  2206.  
  2207.     local allAdventure=true;
  2208.     for j=1,playerCount do
  2209.         local pd=playerData[j];
  2210.         local selector=selectorFor(j);
  2211.         pd.oldMode=-1;
  2212.         for i=0,3 do
  2213.             if commands.testfor(selector..",m="..i.."]") then pd.oldMode=i;break; end
  2214.         end
  2215.         if pd.oldMode~=2 then allAdventure=false; end
  2216.     end
  2217.     if not allAdventure then
  2218.         if not canStayInCreative or fromMonitor then
  2219.             for i=1,playerCount do
  2220.                 commands.async.gamemode("2 "..selectorFor(i).."]");
  2221.             end
  2222.             monitor.write("Put all players in adventure mode");
  2223.             local _,y=monitor.getCursorPos();
  2224.             monitor.setCursorPos(1,y+1);
  2225.             print("Put all players in adventure mode for the session");
  2226.         else
  2227.             print("Some players are not in adventure mode");
  2228.             print("Do you wish them to be? (Y/N)");
  2229.             while true do
  2230.                 local ev={os.pullEvent()};
  2231.                 if ev[1]=="char" then
  2232.                     if ev[2]=="y" then
  2233.                         for i=1,playerCount do
  2234.                             commands.async.gamemode("2 "..selectorFor(i).."]");
  2235.                         end
  2236.                         print("Put them in adventure mode for the session");
  2237.                         break;
  2238.                     elseif ev[2]=="n" then
  2239.                         print("Playing in their respective gamemodes");
  2240.                         for i=1,playerCount do
  2241.                             playerData[i].oldMode=nil;
  2242.                         end
  2243.                         break;
  2244.                     end
  2245.                 end
  2246.             end
  2247.         end
  2248.     end
  2249.  
  2250.     print("Running parkour with "..#structures.." structures");
  2251.     if monitor then
  2252.         monitor.write("Running parkour with "..#structures.." structures");
  2253.         local _,y=monitor.getCursorPos();
  2254.         monitor.setCursorPos(1,y+1);
  2255.     end
  2256.  
  2257.     if fs.exists("/startup") then
  2258.         fs.delete("/startupbackup.donteverusethisname.idequalsvirtualparkour");
  2259.         fs.copy("/startup","/startupbackup.donteverusethisname.idequalsvirtualparkour");
  2260.     end
  2261.     local handle=fs.open("/startup","w");
  2262.     handle.writeLine("if not pocket then");
  2263.     handle.writeLine(" fs.delete(\"/startup\");");
  2264.     handle.writeLine(" pcall(function() fs.move(\"/startupbackup.donteverusethisname.idequalsvirtualparkour\",\"/startup\") end);");
  2265.     handle.writeLine(" os.reboot();");
  2266.     handle.writeLine("end");
  2267.     handle.writeLine("term.setBackgroundColor(colors.red);");
  2268.     handle.writeLine("term.setTextColor(colors.white);");
  2269.     handle.writeLine("term.clear();");
  2270.     handle.writeLine("term.setCursorPos(2,5);");
  2271.     handle.writeLine("term.write(\"[X] key to quit parkour\");");
  2272.     handle.writeLine("term.setCursorPos(2,6);");
  2273.     handle.writeLine("term.write(\"[C] key to cycle stats\");");
  2274.     handle.writeLine("while true do");
  2275.     handle.writeLine(" local ev={os.pullEventRaw()};");
  2276.     handle.writeLine(" if ev[1]==\"key\" then");
  2277.     handle.writeLine("  if ev[2]==keys.x then");
  2278.     handle.writeLine("   fs.open(\"/stopvirtualparkournow.idequalsvirtualparkour\",\"w\").close();");
  2279.     handle.writeLine("   os.shutdown();");
  2280.     handle.writeLine("  elseif ev[2]==keys.c then");
  2281.     handle.writeLine("   fs.open(\"/cyclethroughtheobjectives.idequalsvirtualparkour\",\"w\").close();")
  2282.     handle.writeLine("  end");
  2283.     handle.writeLine(" end");
  2284.     handle.writeLine("end");
  2285.     handle.close();
  2286.     --for i=1,playerCount do
  2287.     --  commands.give(selectorFor(i).."] ComputerCraft:pocketComputer 1 1 {display:{Name:Parkour Manager},computerID:"..os.computerID().."}");
  2288.     --end
  2289.     --sleep(0.1);
  2290.  
  2291.     reallyStartParkour=function()
  2292.         if realMode==2 then cycleSidebar(1);
  2293.         else cycleSidebar(0); end
  2294.         for i=1,playerCount do
  2295.             local selector=selectorFor(i);
  2296.             commands.async.tellraw(selector.."] {text:\"Parkour Started\",color:gray}");
  2297.             if realMode==1 then
  2298.                 commands.async.tellraw(selector.."] {text:\"In freeplay you can jump freely through the parkour\",color:green}");
  2299.                 --commands.async.tellraw(selector.."] {text:\"Also check your brand new Parkour Manager\",color:gray}");
  2300.             elseif realMode==2 then
  2301.                 commands.async.tellraw(selector.."] {text:\"Race to the end of this parkour, quickly!\",color:green}");
  2302.                 --commands.async.tellraw(selector.."] {text:\"You can use your brand new Parkour Manager to quit the parkour\",color:gray}");
  2303.             elseif realMode==3 then
  2304.                 commands.async.tellraw(selector.."] {text:\"You must get to the finish dying as little as possible\",color:green}");
  2305.                 --commands.async.tellraw(selector.."] {text:\"You can use your brand new Parkour Manager to check your stats\",color:gray}");
  2306.             end
  2307.         end
  2308.     end
  2309.     if confiscateItems then
  2310.         for i=1,playerCount do
  2311.             commands.async.tellraw(selectorFor(i).."] {text:\"IMPORTANT:\",color:red}");
  2312.             commands.async.tellraw(selectorFor(i).."] [{text:\"Deposit \"},{text:\"ALL\",bold:true},{text:\" your items in the chest, then click the button.\"}]");
  2313.             commands.async.tellraw(selectorFor(i).."] [{text:\"Any item in your inventory after this will be \"},{text:\"destroyed\",color:red},\".\"]");
  2314.             commands.async.tellraw(selectorFor(i).."] \"After 30 seconds the button will be pushed automatically.\"");
  2315.         end
  2316.     end
  2317.    
  2318.     local baseOffset=math.random(-5,5)-math.floor(playerCount/2)*40;
  2319.     local takingItems=false;
  2320.     function quitPlayer(id)
  2321.         local pd=playerData[id];
  2322.         for i,struct in ipairs(pd.current) do
  2323.             pcall(struct.func,"death",struct);
  2324.         end
  2325.         deleteStructure(pd.previous);
  2326.         deleteCheckpoint(pd.previous);
  2327.         for i,struct in ipairs(pd.current) do
  2328.             deleteStructure(struct);
  2329.             if i==#pd.current then
  2330.                 deleteCheckpoint(struct);
  2331.             end
  2332.         end
  2333.         local selector=selectorFor(id);
  2334.         commands.tp(selector.."]",pos[1],pos[2]+1,pos[3]);
  2335.         if pd.oldMode then
  2336.             commands.async.gamemode(pd.oldMode,selector.."]");
  2337.         end
  2338.         commands.async.effect(selector.."] clear");
  2339.     end
  2340.     function reallyQuitParkour(dontQuitPlayers)
  2341.         fs.delete("/startup");
  2342.         pcall(function() fs.move("/startupbackup.donteverusethisname.idequalsvirtualparkour","/startup") end);
  2343.        
  2344.         --Double negation FTW!
  2345.         if not dontQuitPlayers then
  2346.             for id=1,playerCount do
  2347.                 local ok,err=pcall(quitPlayer,id);
  2348.                 if not ok then
  2349.                     print("Error quitting player #"..id..":");
  2350.                     printError(tostring(err));
  2351.                     print("But continuing on. Anyways, this isn't the first, right?");
  2352.                 end
  2353.             end
  2354.         end
  2355.         commands.async.scoreboard("objectives setdisplay sidebar");
  2356.         commands.async.scoreboard("objectives remove parkour_deaths");
  2357.         commands.async.scoreboard("objectives remove parkour_checks");
  2358.         commands.async.scoreboard("objectives remove parkour_time");
  2359.         commands.async.scoreboard("objectives remove parkour_number");
  2360.         commands.async.scoreboard("objectives remove parkour_id");
  2361.         if turnOnCBO then
  2362.             commands.gamerule("commandBlockOutput true");
  2363.         end
  2364.         print("Quitted Parkour");
  2365.     end
  2366.     function startQuittingParkour()
  2367.         for id=1,playerCount do
  2368.             local pd=playerData[id];
  2369.             for i,struct in ipairs(pd.current) do
  2370.                 pcall(struct.func,"death",struct);
  2371.             end
  2372.             if pd.currentStructset>2 then
  2373.                 deleteStructure(pd.previous);
  2374.                 deleteCheckpoint(pd.previous);
  2375.             end
  2376.             for i,struct in ipairs(pd.current) do
  2377.                 deleteStructure(struct);
  2378.                 if i==#pd.current then
  2379.                     deleteCheckpoint(struct);
  2380.                 end
  2381.             end
  2382.             local selector=selectorFor(id);
  2383.             commands.async.clear(selector.."]");
  2384.             pd.currentStructset=1;
  2385.             pd.current={pd.itemDeposit};
  2386.             getNextStructset(id);
  2387.             pd.readyToGo=false;
  2388.             pd.nowQuitting=true;
  2389.             if confiscateItems then pd.previous.func("checkpoint",pd.previous); end
  2390.         end
  2391.         if confiscateItems then
  2392.             takingItems=true;
  2393.         else
  2394.             reallyQuitParkour();
  2395.         end
  2396.         return not confiscateItems;
  2397.     end
  2398.    
  2399.    
  2400.     local shouldQuitNow=false;
  2401.     function mainLoop()
  2402.         genNewStructset(structures.itemDeposit);
  2403.         genNewStructset(structures.first);
  2404.         local parkourID=0;
  2405.         while true do
  2406.             parkourID=parkourID+1;
  2407.             if parkourID>playerCount then
  2408.                 if not takingItems then
  2409.                     local allF=true;
  2410.                     for i=1,playerCount do
  2411.                         if not playerData[i].finished then allF=false;break; end
  2412.                     end
  2413.                     if allF then error("All players have finished"); end
  2414.                 end
  2415.                 parkourID=1;
  2416.             end
  2417.             local pd=playerData[parkourID];
  2418.             if pd.initialized then
  2419.                 if fs.exists("/stopvirtualparkournow.idequalsvirtualparkour") or shouldQuitNow then
  2420.                     shouldQuitNow=false;
  2421.                     fs.delete("/stopvirtualparkournow.idequalsvirtualparkour");
  2422.                     if startQuittingParkour() then break; end
  2423.                 elseif fs.exists("/cyclethroughtheobjectives.idequalsvirtualparkour") then
  2424.                     fs.delete("/cyclethroughtheobjectives.idequalsvirtualparkour");
  2425.                     cycleSidebar();
  2426.                 end
  2427.             else
  2428.                 pd.xOffset=baseOffset+(parkourID-1)*40;
  2429.                 pd.selector=selectorFor(parkourID).."]";
  2430.                 pd.incompleteSelector=selectorFor(parkourID)..",";
  2431.                 pd.previous={blocks={},exit=pos,func=structures.first};
  2432.                 pd.currentIndex=1;
  2433.                 pd.current={{blocks={},exit=pos,func=structures.first,next="z+"}};
  2434.                 pd.currentStructset=0;
  2435.                 getNextStructset(parkourID);
  2436.                 getNextStructset(parkourID);
  2437.                 if not pd.previous.func("checkpoint",pd.previous) then
  2438.                     commands.tp(pd.selector,pd.previous.exit[1],pd.previous.exit[2]+1,pd.previous.exit[3]);
  2439.                 end
  2440.                
  2441.                 pd.resistanceCounter=1000000;
  2442.                 pd.wasOnCheckpoint=false;
  2443.                 pd.initialized=true;
  2444.             end
  2445.            
  2446.            
  2447.             if takingItems then
  2448.                 if pd.current[1].areaCheck[1][5](pd.current[1],1) then
  2449.                     break;
  2450.                 end
  2451.                 sleep(0.1);
  2452.             elseif pd.finished then
  2453.                 local s=pd.previous;
  2454.                 local ret=s.func("check_exit",s);
  2455.                 if (type(ret)=="boolean" and not ret)
  2456.                 or (type(ret)~="boolean" and not commands.testfor(pd.incompleteSelector.."x="..s.exit[1]..",y="..s.exit[2]..",z="..s.exit[3]..",r=1]")) then
  2457.                     if not s.func("checkpoint",s) then
  2458.                         commands.async.tp(pd.selector,s.exit[1],s.exit[2]+1,s.exit[3]);
  2459.                     end
  2460.                 end
  2461.             else
  2462.                 --Check if player is inside any of the structure's area, else TP to the previous exit
  2463.                 local out=#pd.current[pd.currentIndex].areaCheck>0;
  2464.                 function checkStructure(struct)
  2465.                     for i,area in ipairs(struct.areaCheck) do
  2466.                         if commands.testfor(pd.incompleteSelector.."x="..area[1]..",y="..area[2]..",z="..area[3]..",r="..area[4].."]") then
  2467.                             out=false;
  2468.                             if type(area[5])=="function" then
  2469.                                 area[5](struct,i);
  2470.                             else
  2471.                                 break;  --Since non-functional detectors are (reorganized by genNewStructset) after functional ones, we can quit safely
  2472.                             end
  2473.                         end
  2474.                         if i%500==0 then sleep(0.05); end
  2475.                     end
  2476.                 end
  2477.                 --First check current structure
  2478.                 checkStructure(pd.current[pd.currentIndex]);
  2479.                 if out then
  2480.                     --If it isn't there, check following structures
  2481.                     for i=pd.currentIndex+1,#pd.current do
  2482.                         checkStructure(pd.current[i]);
  2483.                         if not out then
  2484.                             pd.currentIndex=i;
  2485.                             break;
  2486.                         end
  2487.                     end
  2488.                     if out then
  2489.                         --And if it still isn't found, check structures before
  2490.                         for i=1,pd.currentIndex-1 do
  2491.                             checkStructure(pd.current[i]);
  2492.                             if not out then
  2493.                                 pd.currentIndex=i;
  2494.                                 break;
  2495.                             end
  2496.                         end
  2497.                     end
  2498.                 end
  2499.                 if out then
  2500.                     if not pd.previous.func("checkpoint",pd.previous) then
  2501.                         commands.async.tp(pd.selector,pd.previous.exit[1],(pd.previous.exit[2]+1),pd.previous.exit[3]);
  2502.                     end
  2503.                     commands.async.tellraw(pd.selector.." {text:\"You died.\",color:red}");
  2504.                     pd.deaths=pd.deaths+1;
  2505.                     updateScoreboard(parkourID);
  2506.                     for i=1,#pd.current do
  2507.                         pd.current[i].func("death",pd.current[i]);
  2508.                     end
  2509.                     pd.currentIndex=1;
  2510.                 end
  2511.                 --Testfor current structure's exit
  2512.                 if pd.currentIndex<#pd.current then
  2513.                     local ret=pd.current[pd.currentIndex].func("check_exit",pd.current[pd.currentIndex]);
  2514.                     if type(ret)=="boolean" then
  2515.                         if ret then
  2516.                             pd.currentIndex=pd.currentIndex+1;
  2517.                         end
  2518.                     else
  2519.                         if commands.testfor(pd.incompleteSelector.."x="..pd.current[pd.currentIndex].exit[1]..",y="..(pd.current[pd.currentIndex].exit[2]+1)..",z="..pd.current[pd.currentIndex].exit[3]..",r=1]") then
  2520.                             pd.currentIndex=pd.currentIndex+1;
  2521.                         end
  2522.                     end
  2523.                 end
  2524.                 --Testfor current structset's exit
  2525.                 local ret=pd.current[#pd.current].func("check_exit",pd.current[#pd.current]);
  2526.                 if (type(ret)=="boolean" and ret)
  2527.                 or (type(ret)~="boolean" and commands.testfor(pd.incompleteSelector.."x="..pd.current[#pd.current].exit[1]..",y="..(pd.current[#pd.current].exit[2]+1)..",z="..pd.current[#pd.current].exit[3]..",r=1]")) then
  2528.                     if pd.wasOnCheckpoint then
  2529.                         pd.checkpoints=pd.checkpoints+1;
  2530.                         updateScoreboard(parkourID);
  2531.                         getNextStructset(parkourID);
  2532.                         pd.currentIndex=1;
  2533.                         if realMode==1 or pd.checkpoints<checkpointGoal then
  2534.                             if pd.currentStructset>3 then commands.async.tellraw(pd.selector.." {text:\"Checkpoint!\",color:yellow}"); end
  2535.                         else
  2536.                             pd.finished=true;
  2537.                             local finishCount=0;
  2538.                             for i=1,playerCount do
  2539.                                 if playerData[i].finished then finishCount=finishCount+1; end
  2540.                             end
  2541.                             if realMode==2 then
  2542.                                 for i=1,playerCount do
  2543.                                     if i==parkourID then
  2544.                                         local color="white";
  2545.                                         if finishCount==1 then playerData.winner=parkourID;color="green";
  2546.                                         elseif finishCount==2 then color="yellow";
  2547.                                         elseif finishCount==3 then color="gold";
  2548.                                         end
  2549.                                         commands.async.tellraw(pd.selector.." {text:\"You are #"..finishCount.." in the race\",color:"..color.."}");
  2550.                                     else
  2551.                                         commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..parkourID.." is #"..finishCount.." in the race\"}");
  2552.                                     end
  2553.                                 end
  2554.                             elseif realMode==3 then
  2555.                                 for i=1,playerCount do
  2556.                                     if i==parkourID then
  2557.                                         commands.async.tellraw(pd.selector.." {text:\"You've got to the end dying "..pd.deaths.." times\"}");
  2558.                                     else
  2559.                                         commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..parkourID.." got to the end dying "..pd.deaths.." times\"}");
  2560.                                     end
  2561.                                 end
  2562.                             end
  2563.                             if finishCount>=playerCount then
  2564.                                 if realMode==2 then
  2565.                                     for i=1,playerCount do
  2566.                                         commands.async.tellraw(playerData[i].selector..
  2567.                                         " {text:\"The race has ended, player #"..playerData.winner.." has won in "..playerData[playerData.winner].time.." seconds\"}");
  2568.                                     end
  2569.                                     sleep(2);
  2570.                                     if startQuittingParkour() then break; end
  2571.                                 elseif realMode==3 then
  2572.                                     local posFound=0;
  2573.                                     local positions={};
  2574.                                     local lastBest=-1;
  2575.                                     local lastPos=1;
  2576.                                     local lastRealPos=1;
  2577.                                     while posFound<playerCount do
  2578.                                         local ld=-1;
  2579.                                         local nextID=0;
  2580.                                         for i,opd in ipairs(playerData) do
  2581.                                             if not opd.checkedPos and opd.deaths>=lastBest and (opd.deaths<ld or ld==-1) then
  2582.                                                 nextID=i;
  2583.                                                 ld=opd.deaths;
  2584.                                             end
  2585.                                         end
  2586.                                         if ld>lastBest then lastRealPos=lastPos; end
  2587.                                         lastPos=lastPos+1;
  2588.                                         posFound=posFound+1;
  2589.                                         lastBest=ld;
  2590.                                         positions[#positions+1]={lastRealPos,nextID};
  2591.                                         playerData[nextID].checkedPos=true;
  2592.                                     end
  2593.                                     for i=1,playerCount do
  2594.                                         commands.async.tellraw(playerData[i].selector.." {text:\"The game has ended, positions are:\"}");
  2595.                                         for id,pos in ipairs(positions) do
  2596.                                             if pos[1]==1 then commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..pos[2]..": 1st place\",color:green}");
  2597.                                             elseif pos[1]==2 then commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..pos[2]..": 2nd place\",color:yellow}");
  2598.                                             elseif pos[1]==3 then commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..pos[2]..": 3rd place\",color:gold}");
  2599.                                             else commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..pos[2]..": "..pos[1].."th place\",color:gray}");
  2600.                                             end
  2601.                                         end
  2602.                                     end
  2603.                                     sleep(2);
  2604.                                     if startQuittingParkour() then break; end
  2605.                                 end
  2606.                             end
  2607.                         end
  2608.                     end
  2609.                     pd.wasOnCheckpoint=true;
  2610.                 else
  2611.                     pd.wasOnCheckpoint=false;
  2612.                 end
  2613.                 --[[if commands.testfor(incompleteSelector.."x="..current[#current].exit[1]..",y="..(current[#current].exit[2]+1)..",z="..current[#current].exit[3]..",r=1]") then
  2614.                     if wasOnCheckpoint then
  2615.                         genNextStructset();
  2616.                         currentIndex=1;
  2617.                         commands.async.tellraw(selector.." {text:\"Checkpoint!\",color:yellow}");
  2618.                     end
  2619.                     wasOnCheckpoint=true;
  2620.                 else
  2621.                     wasOnCheckpoint=false;
  2622.                 end]]--
  2623.                
  2624.                 --Give resistance if some time has passed
  2625.                 if pd.resistanceCounter<10 then
  2626.                     pd.resistanceCounter=pd.resistanceCounter+1;
  2627.                 else
  2628.                     pd.resistanceCounter=0;
  2629.                     commands.async.effect(pd.selector.." 11 3600 255");
  2630.                     commands.async.effect(pd.selector.." 23 3600");
  2631.                 end
  2632.             end
  2633.         end
  2634.     end
  2635.     local allOk=true;
  2636.     --[[local coroutines={};
  2637.     local loopError=false;
  2638.     for id=1,playerCount do
  2639.         local c=coroutine.create(playerLoop);
  2640.         local quitNext=false;
  2641.         local ev={id};
  2642.         while true do
  2643.             local ok;
  2644.             ok,loopError=coroutine.resume(c,unpack(ev));
  2645.             if ok then
  2646.                 if quitNext then
  2647.                     loopError=false;
  2648.                     break;
  2649.                 elseif loopError=="initialized" then
  2650.                     quitNext=true;
  2651.                     os.queueEvent("continue");
  2652.                 end
  2653.                 loopError=false;
  2654.             else
  2655.                 break;
  2656.             end
  2657.             ev={os.pullEvent()};
  2658.         end
  2659.         coroutines[id]=c;
  2660.     end
  2661.     if not loopError then
  2662.         local periodicTimer=-1;
  2663.         print("Loops started, press Q to quit");
  2664.         while true do
  2665.             for id,c in ipairs(coroutines) do
  2666.                 if fs.exists("/stopvirtualparkournow.idequalsvirtualparkour") then
  2667.                     fs.delete("/stopvirtualparkournow.idequalsvirtualparkour");
  2668.                     loopError="parkour.exiting.ok";
  2669.                     break;
  2670.                 end
  2671.                 local ev={"parkour_heartbeat"};
  2672.                 while true do
  2673.                     if ev[1]=="key" and ev[2]==keys.q then
  2674.                         loopError="parkour.exiting.ok";
  2675.                         break;
  2676.                     end
  2677.                     local ok;
  2678.                     ok,loopError=coroutine.resume(c,unpack(ev));
  2679.                     if ok then
  2680.                         if loopError=="heartbeat_done" then
  2681.                             coroutine.resume(c);
  2682.                             loopError=false;
  2683.                             break;
  2684.                         end
  2685.                         loopError=false;
  2686.                     else
  2687.                         break;
  2688.                     end
  2689.                     ev={os.pullEvent()};
  2690.                 end
  2691.                 if loopError then break; end
  2692.             end
  2693.             if loopError then break; end
  2694.         end
  2695.     end
  2696.     if loopError and loopError~="parkour.exiting.ok" then
  2697.         for i=1,playerCount do
  2698.             commands.async.tellraw(selectorFor(i).."] {text:\"Error in Parkour\",color:dark_red}");
  2699.         end
  2700.         print();
  2701.         print("Error in player loop:");
  2702.         printError(loopError);
  2703.         allOk=false;
  2704.     end]]
  2705.     local parkourTimer=os.startTimer(0);
  2706.     function miscTasks()
  2707.         while true do
  2708.             local ev={os.pullEventRaw()};
  2709.             if ev[1]=="key" and ev[2]==keys.q then
  2710.                 shouldQuitNow=true;
  2711.             elseif ev[1]=="timer" and ev[2]==parkourTimer then
  2712.                 parkourTimer=os.startTimer(1);
  2713.                 for i=1,playerCount do
  2714.                     local pd=playerData[i];
  2715.                     if not pd.finished then
  2716.                         pd.time=pd.time+1;
  2717.                         if sidebarMode==2 then updateScoreboard(i); end
  2718.                     end
  2719.                 end
  2720.             end
  2721.         end
  2722.     end
  2723.     local miscCoroutine=coroutine.create(miscTasks);
  2724.     local mainCoroutine=coroutine.create(mainLoop);
  2725.     local ok,err;
  2726.     print("Press [Q] to quit parkour");
  2727.     while true do
  2728.         local ev={os.pullEventRaw()};
  2729.         local ok1,err1=coroutine.resume(miscCoroutine,unpack(ev));
  2730.         ok,err=coroutine.resume(mainCoroutine,unpack(ev));
  2731.         if coroutine.status(mainCoroutine)=="dead" then
  2732.             break;
  2733.         elseif coroutine.status(miscCoroutine)=="dead" then
  2734.             ok=ok1;
  2735.             err=err1;
  2736.             break;
  2737.         end
  2738.     end
  2739.     if not ok then
  2740.         for i=1,playerCount do
  2741.             commands.async.tellraw(selectorFor(i).."] {text:\"Error in Parkour\",color:dark_red}");
  2742.         end
  2743.         print();
  2744.         print("Error in main loop:");
  2745.         printError(tostring(err));
  2746.         if monitor then
  2747.             monitor.setTextColor(colors.red);
  2748.             monPrint("Error in mainLoop:");
  2749.             monPrint(tostring(err));
  2750.             monitor.setTextColor(colors.white);
  2751.         end
  2752.         allOk=false;
  2753.     end
  2754.     local ok2,err=pcall(reallyQuitParkour,ok);
  2755.     if not ok2 then
  2756.         if not ok then
  2757.             print("Guess what? Quitting parkour errored too!");
  2758.             printError(err);
  2759.         else
  2760.             print();
  2761.             print("Error quitting parkour:");
  2762.             printError(err);
  2763.         end
  2764.         if monitor then
  2765.             monitor.setTextColor(colors.red);
  2766.             monPrint("");
  2767.             monPrint("Error quitting parkour:");
  2768.             monPrint(tostring(err));
  2769.             monitor.setTextColor(colors.white);
  2770.         end
  2771.         allOk=false;
  2772.     end
  2773.     if not allOk then
  2774.         print("Read the errors, then press a key");
  2775.         if fromMonitor then
  2776.             monPrint("Read the errors, then touch anywhere");
  2777.         end
  2778.         while true do
  2779.             local ev={os.pullEvent()};
  2780.             if ev[1]=="key" or ev[1]=="mouse_click" or (fromMonitor and ev[1]=="monitor_touch" and ev[2]==monitorName) then
  2781.                 break;
  2782.             end
  2783.         end
  2784.         if not fromMonitor then sleep(0.1);error(); end
  2785.     end
  2786. end
  2787. while true do
  2788.     local ok,err=pcall(superLoop);
  2789.     if not ok then
  2790.         if err then
  2791.             term.setBackgroundColor(colors.black);
  2792.             term.clear();
  2793.             term.setCursorPos(1,1);
  2794.             term.setTextColor(colors.red);
  2795.             print("Error in pre-parkour:");
  2796.             print(tostring(err));
  2797.         end
  2798.         break;
  2799.     end
  2800. end
Add Comment
Please, Sign In to add comment