Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local parkourVersion=1.2;
- local updateURL="http://pastebin.com/raw.php?i=rKXPu5LU";
- if not commands then
- print("This program only runs on a Command Computer(creative only)");
- error();
- end
- local pos={commands.getBlockPosition()};
- local selectionPos={pos[1],pos[2],pos[3],4};
- local forceMonitorSelection=false;
- local canStayInCreative=false;
- local allowPlayerUnlocking=true;
- local confiscateItems=true;
- if fs.exists("parkour_config") then
- local h=fs.open("parkour_config","r");
- local lineNum=0;
- while true do
- local line=h.readLine();
- if line==nil then break; end
- lineNum=lineNum+1;
- local equal=line:find("=");
- if equal then
- local param=line:sub(1,equal-1);
- local value=line:sub(equal+1);
- if param=="player_selection_x" then
- selectionPos[1]=tonumber(value);
- if selectionPos[1]==nil then
- h.close();
- printError("Config line #"..lineNum.." player_selection_x value is invalid");
- error();
- end
- selectionPos[1]=math.floor(selectionPos[1]);
- elseif param=="player_selection_y" then
- selectionPos[2]=tonumber(value);
- if selectionPos[2]==nil then
- h.close();
- printError("Config line #"..lineNum.." player_selection_y value is invalid");
- error();
- end
- selectionPos[2]=math.floor(selectionPos[2]);
- elseif param=="player_selection_z" then
- selectionPos[3]=tonumber(value);
- if selectionPos[3]==nil then
- h.close();
- printError("Config line #"..lineNum.." player_selection_z value is invalid");
- error();
- end
- selectionPos[3]=math.floor(selectionPos[3]);
- elseif param=="player_selection_shift_x" then
- local dist=tonumber(value);
- if dist==nil then
- h.close();
- printError("Config line #"..lineNum.." player_selection_shift_x value is invalid");
- error();
- end
- selectionPos[1]=math.ceil(selectionPos[1]+dist);
- elseif param=="player_selection_shift_y" then
- local dist=tonumber(value);
- if dist==nil then
- h.close();
- printError("Config line #"..lineNum.." player_selection_shift_y value is invalid");
- error();
- end
- selectionPos[2]=math.ceil(selectionPos[2]+dist);
- elseif param=="player_selection_shift_z" then
- local dist=tonumber(value);
- if dist==nil then
- h.close();
- printError("Config line #"..lineNum.." player_selection_shift_z value is invalid");
- error();
- end
- selectionPos[3]=math.ceil(selectionPos[3]+dist);
- elseif param=="player_selection_range" then
- selectionPos[4]=tonumber(value);
- if selectionPos[4]==nil then
- h.close();
- printError("Config line #"..lineNum.." player_selection_range value is invalid");
- error();
- end
- selectionPos[4]=math.ceil(selectionPos[4]);
- elseif param=="always_allow_monitor_selection" then
- if value=="true" then
- forceMonitorSelection=true;
- elseif value=="false" then
- forceMonitorSelection=false;
- else
- h.close();
- printError("Config line #"..lineNum.." always_allow_monitor_selection value is invalid, it can only be true or false, not \""..value.."\"");
- error();
- end
- elseif param=="can_stay_in_creative" then
- if value=="true" then
- canStayInCreative=true;
- elseif value=="false" then
- canStayInCreative=false;
- else
- h.close();
- printError("Config line #"..lineNum.." can_stay_in_creative value is invalid, it can only be true or false, not \""..value.."\"");
- error();
- end
- elseif param=="allow_player_unlocking" then
- if value=="true" then
- allowPlayerUnlocking=true;
- elseif value=="false" then
- allowPlayerUnlocking=false;
- else
- h.close();
- printError("Config line #"..lineNum.." allow_player_unlocking value is invalid, it can only be true or false, not \""..value.."\"");
- error();
- end
- elseif param=="confiscate_items" then
- if value=="true" then
- confiscateItems=true;
- elseif value=="false" then
- confiscateItems=false;
- else
- h.close();
- printError("Config line #"..lineNum.." confiscate_items value is invalid, it can only be true or false, not \""..value.."\"");
- error();
- end
- elseif line~="" and line:sub(1,1)~="#" then
- h.close();
- printError("Invalid config line #"..lineNum..": Invalid parameter \""..param.."\"");
- error();
- end
- else
- if line~="" and line:sub(1,1)~="#" then
- h.close();
- printError("Invalid config line #"..lineNum..": No = found");
- error();
- end
- end
- end
- h.close();
- end
- local structures={
- empty=function(what,s)
- if what=="place_marker" then
- return true;
- elseif what=="build" then
- return {
- blocks={},
- areaCheck={},
- exit={0,0,0},
- next="x+",
- };
- end
- end,
- };
- local structNames={}; --For structure debugging
- function structNamePresent(name)
- local isItThere=false;
- for i,s in ipairs(structNames) do
- if s==name then isItThere=true;break; end
- end
- return isItThere;
- end
- local checkpointStride=100;
- local structsetPool={};
- local final; --The last structure, relative point to place new structures
- function transform(b)
- if final.next=="x-" then
- b[1]=-b[1];
- b[3]=-b[3];
- elseif final.next=="z+" then
- local tx=b[1];
- local tz=b[3];
- b[1]=-tz;
- b[3]=tx;
- elseif final.next=="z-" then
- local tx=b[1];
- local tz=b[3];
- b[1]=tz;
- b[3]=-tx;
- end
- b[1]=final.exit[1]+b[1];
- b[2]=final.exit[2]+b[2];
- b[3]=final.exit[3]+b[3];
- return b;
- end
- function dirToNumber(f)
- if f=="x+" then return 0;
- elseif f=="z+" then return 1;
- elseif f=="x-" then return 2;
- elseif f=="z-" then return 3;
- else error("Invalid direction "..tostring(f),2); end
- end
- function numberToDir(n)
- n=n%4;
- if n==0 then return "x+";
- elseif n==1 then return "z+";
- elseif n==2 then return "x-";
- elseif n==3 then return "z-";
- else error("Invalid number "..tostring(n),2); end
- end
- -------------------------------------------------------------WARNING-------------------------------------------------------------------
- --Some TileEntities have a problem with setblock, and they simply ignore the metadata in setblock commands.
- --There is no method (that I know) to make them work. I included them here in case it gets fixed.
- --The metadatas given are correct, it just doesnt set with setblock
- --This is why bedrock cage has the chest sometimes pointing wrongly
- --Blocks that dont work: dispenser, dropper, furnace, lit_furnace, chest, and trapped_chest
- --TileEntities that work: hopper, anvil, wall_sign, unpowered_comparator and powered_comparator(Deprecated block. Could not find if it's a TileEntity or not)
- --All other blocks work
- function getMetaFor(block,dir)
- if block=="torch" or block=="redstone_torch" then
- if dir=="x+" then return 1;
- elseif dir=="x-" then return 2;
- elseif dir=="z+" then return 3;
- elseif dir=="z-" then return 4;
- elseif dir=="y+" then return 5;
- else error("Invalid direction "..tostring(dir),2); end
- elseif block=="piston" or block=="sticky_piston" or block=="piston_head" or block=="dispenser" or block=="dropper" or block=="hopper" then
- if dir=="y-" then return 0;
- elseif dir=="y+" and block~="hopper" then return 1;
- elseif dir=="z-" then return 2;
- elseif dir=="z+" then return 3;
- elseif dir=="x-" then return 4;
- elseif dir=="x+" then return 5;
- else error("Invalid direction "..tostring(dir),2); end
- --elseif block=="stairs" then
- elseif block=="oak_stairs" or block=="brick_stairs" or block=="stone_brick_stairs" or block=="nether_brick_stairs" or block=="spruce_stairs"
- or block=="birch_stairs" or block=="jungle_stairs" or block=="quartz_stairs" or block=="stone_stairs" or block=="sandstone_stairs"
- or block=="acacia_stairs" or block=="dark_oak_stairs" or --[[And for future versions,]]block=="red_sandstone_stairs" then
- if dir=="x+" then return 0;
- elseif dir=="x-" then return 1;
- elseif dir=="z+" then return 2;
- elseif dir=="z-" then return 3;
- else error("Invalid direction "..tostring(dir),2); end
- elseif block=="rail" or block=="golden_rail" or block=="detector_rail" or block=="anvil" then
- if dir=="z+" or dir=="z-" then return 0;
- elseif dir=="x+" or dir=="x-" then return 1;
- else error("Invalid direction "..tostring(dir),2); end
- elseif block=="ladder" or block=="furnace" or block=="lit_furnace" or block=="chest" or block=="trapped_chest" or block=="wall_sign" then
- if dir=="z-" then return 2;
- elseif dir=="z+" then return 3;
- elseif dir=="x-" then return 4;
- elseif dir=="x+" then return 5;
- else error("Invalid direction "..tostring(dir),2); end
- elseif block=="lever" then
- if dir=="y-" then return 0;
- elseif dir=="x+" then return 1;
- elseif dir=="x-" then return 2;
- elseif dir=="z+" then return 3;
- elseif dir=="z-" then return 4;
- elseif dir=="y+" then return 5;
- else error("Invalid direction "..tostring(dir),2); end
- elseif block=="stone_button" or block=="wooden_button" then
- if dir=="x+" then return 1;
- elseif dir=="x-" then return 2;
- elseif dir=="z+" then return 3;
- elseif dir=="z-" then return 4;
- else error("Invalid direction "..tostring(dir),2); end
- elseif block=="pumpkin" or block=="lit_pumpkin" or block=="fence_gate" or block=="end_portal_frame" or block=="tripwire_hook" then
- if dir=="z+" then return 0;
- elseif dir=="x-" then return 1;
- elseif dir=="z-" then return 2;
- elseif dir=="x+" then return 3;
- else error("Invalid direction "..tostring(dir),2); end
- elseif block=="unpowered_repeater" or block=="powered_repeater" or block=="unpowered_comparator" or block=="powered_comparator" then
- if dir=="z-" then return 0;
- elseif dir=="x+" then return 1;
- elseif dir=="z+" then return 2;
- elseif dir=="x-" then return 3;
- else error("Invalid direction "..tostring(dir),2); end
- else return 0; end
- end
- function autoMeta(b,dir)
- local name=b[4];
- if name:sub(1,10)=="minecraft:" then
- name=name:sub(11);
- end
- if dir==nil then
- dir=b[5];
- end
- if type(dir)=="number" then
- dir=numberToDir(dir);
- end
- --print("Getting meta for block "..tostring(name)..", Direction "..tostring(dir));
- local ok,meta=pcall(getMetaFor,name,dir);
- if ok then
- b[5]=meta;
- --print("Meta is "..tostring(meta));
- else
- error(meta,2);
- end
- end
- local playerCount;
- local sidebarMode;
- local fromMonitor;
- local monitor;
- local monitorName;
- local realMode;
- local checkpointGoal;
- local cycleSidebar;
- local quitPlayer;
- --Make sure some stuff is accessible to structures
- _G.dirToNumber=dirToNumber;
- _G.numberToDir=numberToDir;
- _G.final=function()return final;end;
- _G.structures=function()return structures;end;
- _G.structNames=function()return structNames;end;
- _G.structNamePresent=structNamePresent;
- _G.checkpointStride=function()return checkpointStride;end;
- _G.parkourVersion=parkourVersion;
- _G.playerCount=function()return playerCount;end;
- _G.getMetaFor=getMetaFor;
- _G.sidebarMode=function()return sidebarMode;end;
- _G.fromMonitor=function()return fromMonitor;end;
- _G.getMonitor=function()return monitor;end;
- _G.monitorName=function()return monitorName;end;
- _G.parkourGamemode=function()return realMode;end;
- _G.checkpointGoal=function()
- if realMode==1 then error("Attempt to get checkpoint goal in freeplay");
- else return checkpointGoal; end
- end
- _G.cycleSidebar=cycleSidebar;
- structures.heightWarp=function(what)
- if what=="build" then
- local dist=175-final.exit[2];
- function teleport(s)
- commands.async.tp(s.playerData.selector,s.blocks[5][1],s.blocks[5][2]+1,s.blocks[5][3]);
- end
- return {
- blocks={
- {1,0,0,"minecraft:quartz_block"},
- {2,0,0,"minecraft:quartz_block"},
- {3,0,0,"minecraft:quartz_block"},
- {4,0,0,"minecraft:quartz_block",2},
- {4,dist,0,"minecraft:quartz_block",2},
- },
- areaCheck={
- {0,0,0,4},
- {3,0,0,4},
- {4,1,0,1,teleport},
- {4,dist,0,4},
- },
- exit={5,dist,0},
- next="x+",
- };
- end
- end
- local reallyStartParkour;
- structures.first=function(what)
- if what=="build" then
- --[[local b={};
- for i=31,35 do
- b[#b+1]={0,i,0,"minecraft:stone"};
- end
- return {
- blocks=b,
- areaCheck={{0,35,0,10}},
- exit={0,35,0},
- next="x+",
- };]]--
- function doTick(firstS)
- if not confiscateItems then
- if not firstS.allPlayers.alreadyGone then
- local s=firstS;
- local allInit=true;
- for i,pd in ipairs(s.allPlayers) do
- if not pd.initialized then allInit=false;break; end
- end
- if allInit then
- s.allPlayers.alreadyGone=true;
- for i,pd in ipairs(s.allPlayers) do
- local tf=pd.current[1].exit;
- commands.async.clear(pd.selector);
- commands.tp(pd.selector,tf[1],tf[2]+1,tf[3]);
- sleep(0.1);
- commands.async.give(pd.selector.." ComputerCraft:pocketComputer 1 1 {display:{Name:Parkour Manager},computerID:"..os.computerID().."}");
- end
- reallyStartParkour();
- end
- end
- elseif firstS.playerData.nowQuitting then
- local s=firstS.playerData.previous;
- if s.playerData.readyToGo==false then
- s.playerData.readyToGo=nil;
- s.playerData.initialTime=s.playerData.time;
- commands.async.tellraw(s.playerData.selector.." \"You can get your items now. After that press the button.\"");
- 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},\".\"]");
- for i=2,11 do
- commands.async.setblock(unpack(s.blocks[i]));
- end
- s.playerData.time=0;
- s.playerData.finished=false;
- cycleSidebar(2);
- end
- if not s.playerData.readyToGo and commands.getBlockInfo(s.blocks[3][1],s.blocks[3][2],s.blocks[3][3]).name=="minecraft:stonebrick" then
- s.playerData.readyToGo=true;
- commands.async.tellraw(s.playerData.selector.." {text:\"Goodbye.\",color:green}");
- s.playerData.finished=true;
- commands.async.tp(s.playerData.selector.." ~ ~4 ~");
- --commands.async.setblock(s.blocks[10][1],s.blocks[10][2],s.blocks[10][3],"minecraft:lava");
- --commands.async.setblock(s.blocks[11][1],s.blocks[11][2],s.blocks[11][3],"minecraft:lava");
- for i=2,13 do
- commands.async.setblock(s.blocks[i][1],s.blocks[i][2],s.blocks[i][3],"minecraft:lava");
- end
- sleep(2);
- quitPlayer(s.playerID);
- local allReady=true;
- for i,pd in ipairs(s.allPlayers) do
- if not pd.readyToGo then allReady=false;break; end
- end
- if allReady then
- return true;
- end
- end
- if not s.playerData.readyToGo then
- if s.playerData.time>=30 then
- commands.async.tellraw(s.playerData.selector.." \"Your 30 seconds are up, your button will be pushed automatically.\"");
- local b;
- b=s.blocks[2];
- commands.async.setblock(b[1],b[2],b[3],b[4],bit.bor(b[5],8));
- b=s.blocks[3];
- commands.async.setblock(b[1],b[2],b[3],"minecraft:stonebrick",3);
- commands.async.playSound("random.click "..s.playerData.selector);
- end
- end
- else
- local s=firstS.playerData.previous;
- if not s.playerData.readyToGo and commands.getBlockInfo(s.blocks[3][1],s.blocks[3][2],s.blocks[3][3]).name=="minecraft:stonebrick" then
- s.playerData.readyToGo=true;
- local allReady=true;
- for i,pd in ipairs(s.allPlayers) do
- if i==s.playerID then commands.async.tellraw(pd.selector.." \"You are ready to start.\"");
- else commands.async.tellraw(pd.selector.." \"Player #"..s.playerID.." is ready to start.\""); end
- if not pd.readyToGo then allReady=false; end
- end
- if allReady then
- for i,pd in ipairs(s.allPlayers) do
- --pd.readyToGo=nil;
- local tf=pd.current[1];
- commands.async.clear(pd.selector); --Clear now so theres no cheating
- commands.tp(pd.selector,tf.exit[1],tf.exit[2]+1,tf.exit[3]);
- sleep(0.1);
- commands.async.give(pd.selector.." ComputerCraft:pocketComputer 1 1 {display:{Name:Parkour Manager},computerID:"..os.computerID().."}");
- pd.time=0;
- end
- reallyStartParkour();
- end
- end
- if not s.playerData.readyToGo then
- if s.playerData.time>=30 then
- commands.async.tellraw(s.playerData.selector.." \"Your 30 seconds are up, all buttons will be pushed automatically.\"");
- local b;
- b=s.blocks[2];
- commands.async.setblock(b[1],b[2],b[3],b[4],bit.bor(b[5],8));
- b=s.blocks[3];
- commands.async.setblock(b[1],b[2],b[3],"minecraft:stonebrick",3);
- commands.async.playSound("random.click "..s.playerData.selector);
- end
- end
- end
- end
- return {
- blocks={},
- areaCheck={{-4,0,0,4},{0,0,0,10,doTick}},
- exit={0,0,0},
- next="x+",
- };
- end
- end
- structures.itemDeposit=function(what,s)
- if what=="checkpoint" then
- commands.async.tp(
- s.playerData
- .selector,
- s.blocks[1][1],
- s.blocks[1][2],
- s.blocks[1][3]);
- return true;
- elseif what=="build" then
- local bb={
- --Extras
- {0,36,0,reference=true},
- {0,36,-1,"minecraft:stone_button","x-",autoMeta=true},
- {1,36,0,"minecraft:command_block",0,"replace"},
- {1,36,-1},
- {0,38,0,"minecraft:lit_pumpkin"},
- {0,38,-1,"minecraft:lit_pumpkin"},
- {0,37,0,"minecraft:air"},
- {0,37,-1,"minecraft:air"},
- {0,36,0,"minecraft:air"},
- {1,38,0,"minecraft:air"},
- {1,38,-1,"minecraft:air"},
- {1,37,0,"minecraft:chest"},--,"x-",autoMeta=true}, Sadly chest orientation does not work due to a minecraft bug.
- {1,37,-1,"minecraft:chest"},--,"x-",autoMeta=true}, See getMetaFor() definition for more info
- --Floor
- {-1,35,-2},{-1,35,-1},{-1,35,0},{-1,35,1},{0,35,-2},{0,35,-1},{0,35,0},{0,35,1},
- {1,35,-2},{1,35,-1},{1,35,0},{1,35,1},{2,35,-2},{2,35,-1},{2,35,0},{2,35,1},
- --1st Wall
- {-1,36,-2},{-1,36,-1},{-1,36,0},{-1,36,1},{0,36,-2},{0,36,1},
- {1,36,-2},{1,36,1},{2,36,-2},{2,36,-1},{2,36,0},{2,36,1},
- --2nd Wall
- {-1,37,-2},{-1,37,-1},{-1,37,0},{-1,37,1},{0,37,-2},{0,37,1},
- {1,37,-2},{1,37,1},{2,37,-2},{2,37,-1},{2,37,0},{2,37,1},
- --3rd Wall
- {-1,38,-2},{-1,38,-1},{-1,38,0},{-1,38,1},{0,38,-2},{0,38,1},
- {1,38,-2},{1,38,1},{2,38,-2},{2,38,-1},{2,38,0},{2,38,1},
- --Roof
- {-1,39,-2},{-1,39,-1},{-1,39,0},{-1,39,1},{0,39,-2},{0,39,-1},{0,39,0},{0,39,1},
- {1,39,-2},{1,39,-1},{1,39,0},{1,39,1},{2,39,-2},{2,39,-1},{2,39,0},{2,39,1},
- };
- for i,b in ipairs(bb) do
- if not b[4] then b[4]="minecraft:bedrock"; end
- end
- return {
- blocks=bb,
- areaCheck={{1,35,0,8}},
- exit={4,35,0},
- next="x+",
- };
- elseif what=="post_transform" then
- local b=s.blocks[3];
- b[7]="{Command:\"setblock "..b[1].." "..b[2].." "..b[3].." minecraft:stonebrick 3\"}";
- end
- end
- function buildCheckpoint(struct)
- if not struct.func("place_marker",struct) then
- if struct.exit[1]~=pos[1] or struct.exit[2]~=pos[2] or struct.exit[3]~=pos[3] then
- commands.async.setblock(struct.exit[1],struct.exit[2],struct.exit[3],"minecraft:obsidian 0 replace");
- end
- end
- end
- function buildStructure(struct)
- for i,b in ipairs(struct.blocks) do
- if not b.disableAutobuild and not b.reference then
- commands.async.setblock(unpack(b));
- if i%200==0 then sleep(0.05); end
- end
- end
- end
- function deleteCheckpoint(struct)
- if not struct.func("remove_marker",struct) then
- if struct.exit[1]~=pos[1] or struct.exit[2]~=pos[2] or struct.exit[3]~=pos[3] then
- commands.async.setblock(struct.exit[1],struct.exit[2],struct.exit[3],"minecraft:air 0 replace");
- end
- end
- end
- function deleteStructure(struct)
- for i,b in ipairs(struct.blocks) do
- if not b.reference then
- commands.async.setblock(b[1],b[2],b[3],"minecraft:air 0 replace");
- if i%200==0 then sleep(0.05); end
- end
- end
- end
- function genNewStructset(s)
- local structset={};
- local sLength=0;
- function genSingleStruct(space)
- local struct;
- if s then struct=s("build",final.nextAngle);struct.func=s; end
- while not struct do
- local candidates={};
- local probabilities={};
- local totalWeight=0;
- for i,sf in ipairs(structures) do
- local str=sf("build",final.nextAngle);
- if (str.length or checkpointStride)<=space then
- str.func=sf;
- candidates[#candidates+1]=str;
- probabilities[#probabilities+1]=sf("weight") or 1;
- totalWeight=totalWeight+probabilities[#probabilities];
- end
- end
- if totalWeight>0 then
- local r=math.random()*totalWeight;
- local currentProb=0;
- for i,str in ipairs(candidates) do
- currentProb=currentProb+probabilities[i];
- if r<=currentProb then
- struct=str;
- break;
- end
- end
- end
- space=space+20;
- end
- --Transform direction
- struct.nextAngle=final.nextAngle or 0;
- if type(struct.next)=="number" then
- local absAngle=struct.next%(2*math.pi);
- if absAngle<0.25*math.pi then struct.next="x+";struct.nextAngle=absAngle;
- elseif absAngle<0.75*math.pi then struct.next="z+";struct.nextAngle=absAngle-0.5*math.pi;
- elseif absAngle<1.25*math.pi then struct.next="x-";struct.nextAngle=absAngle-math.pi;
- elseif absAngle<1.75*math.pi then struct.next="z-";struct.nextAngle=absAngle-1.5*math.pi;
- else struct.next="x+";struct.nextAngle=absAngle-2*math.pi;
- end
- end
- struct.next=numberToDir(dirToNumber(final.next)+dirToNumber(struct.next));
- --Transform blocks
- for i,b in ipairs(struct.blocks) do
- b=transform(b);
- if b.autoMeta then
- if type(b[5])=="string" then b[5]=dirToNumber(b[5]); end
- b[5]=dirToNumber(final.next)+b[5];
- autoMeta(b);
- end
- end
- --Transform exit
- struct.exit=transform(struct.exit);
- --Transform areaCheck
- local unorganizedAC=struct.areaCheck;
- local cpac=false;
- for i,b in ipairs(unorganizedAC) do
- if b[1]==0 and b[2]==0 and b[3]==0 then cpac=true; end
- unorganizedAC[i]=transform(b);
- unorganizedAC[i][4]=math.ceil(unorganizedAC[i][4]);
- end
- --Make sure there's an areaCheck for the checkpoint, but don't add duplicates
- if not cpac then unorganizedAC[#unorganizedAC+1]=transform({0,0,0,5}); end
- struct.areaCheck={};
- for i,area in ipairs(unorganizedAC) do
- if type(area[5])=="function" then
- struct.areaCheck[#struct.areaCheck+1]=area;
- end
- end
- for i,area in ipairs(unorganizedAC) do
- if type(area[5])~="function" then
- struct.areaCheck[#struct.areaCheck+1]=area;
- end
- end
- final=struct;
- return struct;
- end
- while sLength<checkpointStride do
- local struct=genSingleStruct(checkpointStride-sLength);
- structset[#structset+1]=struct;
- sLength=sLength+(struct.length or checkpointStride);
- end
- structsetPool[#structsetPool+1]=structset;
- end
- function getNextStructset(pid)
- local pd=playerData[pid];
- --Get our raw structset data from pool
- pd.currentStructset=pd.currentStructset+1;
- --If there is no next structset, generate it.
- if structsetPool[pd.currentStructset]==nil then
- if realMode==1 or pd.checkpoints<checkpointGoal then
- local adjustHeight=final.exit[2]>230 or final.exit[2]<30;
- if not adjustHeight then
- for i=1,playerCount do
- if not adjustHeight then
- adjustHeight=commands.getBlockInfo(final.exit[1]+pd.xOffset,final.exit[2]-20,final.exit[3]).name~="minecraft:air";
- if not adjustHeight then
- adjustHeight=commands.getBlockInfo(final.exit[1]+pd.xOffset,final.exit[2]-15,final.exit[3]).name~="minecraft:air";
- if not adjustHeight then
- adjustHeight=commands.getBlockInfo(final.exit[1]+pd.xOffset,final.exit[2]-10,final.exit[3]).name~="minecraft:air";
- end
- end
- end
- end
- end
- if adjustHeight then
- genNewStructset(structures.heightWarp);
- else
- genNewStructset();
- end
- else
- genNewStructset(structures.empty);
- end
- end
- local rawStructset=structsetPool[pd.currentStructset];
- --Do a semi-deep copy, and offset any X that needs to be offsetted.
- local structset={};
- for i,rawStruct in ipairs(rawStructset) do
- function deepCopy(orig,dest,lvl)
- for name,value in pairs(orig) do
- if type(value)=="table" and not value.global then
- dest[name]={};
- deepCopy(value,dest[name],lvl+1);
- if lvl==1 then
- if name=="blocks" then
- for j,b in ipairs(dest.blocks) do
- b[1]=b[1]+pd.xOffset;
- end
- elseif name=="areaCheck" then
- for j,ac in ipairs(dest.areaCheck) do
- ac[1]=ac[1]+pd.xOffset;
- end
- elseif name=="exit" then
- dest.exit[1]=dest.exit[1]+pd.xOffset;
- end
- end
- else
- dest[name]=value;
- end
- end
- end
- structset[i]={};
- deepCopy(rawStruct,structset[i],1);
- structset[i].playerData=pd;
- structset[i].playerID=pid;
- structset[i].allPlayers=playerData;
- structset[i].func("post_transform",structset[i]);
- end
- if pd.currentStructset>3 then
- deleteStructure(pd.previous);
- deleteCheckpoint(pd.previous);
- else
- pd.itemDeposit=pd.previous;
- end
- for i,struct in ipairs(pd.current) do
- if i~=#pd.current then
- deleteStructure(struct);
- end
- end
- pd.previous=pd.current[#pd.current];
- pd.current=structset;
- for i,struct in ipairs(pd.current) do
- buildStructure(struct);
- if i==#pd.current then
- buildCheckpoint(struct);
- end
- end
- end
- local localStructs={};
- function superLoop()
- local sWidth;
- local sHeight;
- sWidth,sHeight=term.getSize();
- local onlineStructs={};
- local onlineError=false;
- local scroll=0;
- local mScroll=0;
- local debuggingStructs=false;
- local updateData=false;
- local canStart=false;
- local canStartChange=false;
- local shallQuit=false;
- monitor=false;
- monitorName="";
- fromMonitor=false;
- for _,name in ipairs(peripheral.getNames()) do
- if peripheral.getType(name)=="monitor" then
- local m=peripheral.wrap(name);
- if m.isColor() then
- m.setTextScale(1);
- local w,h=m.getSize();
- if w>=18 and h>=12 then
- monitor=m;
- monitorName=name;
- break;
- end
- end
- end
- end
- function updateLocalStructs()
- if not fs.isDir("structures") then
- fs.delete("structures");
- fs.makeDir("structures");
- end
- local files=fs.list("structures");
- local oldSelections={};
- for _,ls in ipairs(localStructs) do
- local name=ls[1];
- if ls[4] then
- name=name.."."..ls[4];
- end
- oldSelections[name]=ls[3];
- end
- localStructs={};
- for i,filename in ipairs(files) do
- if not fs.isDir("structures/"..filename) then
- local struct,err=loadfile("structures/"..filename);
- local ver;
- local dot=filename:find("%.");
- local name=filename;
- if dot then
- ver=tonumber(filename:sub(dot+1));
- name=name:sub(1,dot-1);
- end
- if struct then
- setfenv(struct,_G);
- local selected=oldSelections[filename];
- if selected==nil then selected=true; end
- localStructs[#localStructs+1]={name,struct,selected,ver};
- else
- localStructs[#localStructs+1]={name,err,false,ver};
- end
- end
- end
- end
- function beautifulName(n)
- n=string.upper(n:sub(1,1))..n:sub(2);
- while true do
- local underscore=n:find("_");
- if not underscore then break; end
- n=(n:sub(1,underscore-1)).." "..string.upper(n:sub(underscore+1,underscore+1))..(n:sub(underscore+2));
- end
- return n;
- end
- function renderStructList()
- term.setBackgroundColor(colors.orange);
- term.clear();
- term.setTextColor(colors.black);
- for i=1,sHeight-3 do
- term.setBackgroundColor(colors.white);
- local ls=localStructs[i+scroll];
- if ls then
- if ls[3] then term.setBackgroundColor(colors.green); end
- end
- term.setCursorPos(2,i+1);
- term.write(string.rep(" ",sWidth-2));
- if ls then
- term.setCursorPos(2,i+1);
- if type(ls[2])=="string" then
- term.setTextColor(colors.red);
- term.write("Error in "..beautifulName(ls[1]));
- term.setTextColor(colors.black);
- else
- term.write(beautifulName(ls[1]));
- if ls[4] then
- local v;
- if math.floor(ls[4])==ls[4] then
- v=ls[4]..".0";
- else
- v=tostring(ls[4]);
- end
- term.setCursorPos(sWidth-3-#v,i+1);
- term.write(v);
- end
- end
- term.setBackgroundColor(colors.red);
- term.setCursorPos(sWidth-2,i+1);
- term.write(" -");
- end
- end
- term.setBackgroundColor(colors.blue);
- term.setCursorPos(2,sHeight-1);
- term.write(string.rep(" ",sWidth-2));
- term.setCursorPos(2,sHeight-1);
- term.write("Get structures");
- term.setCursorPos(sWidth-9,sHeight-1);
- term.setBackgroundColor(colors.lightGray);
- term.setTextColor(colors.white);
- term.write("< >");
- term.setCursorPos(sWidth-8,sHeight-1);
- term.setTextColor(colors.gray);
- if checkpointStride==60 then
- term.setBackgroundColor(colors.green);
- term.write("E");
- elseif checkpointStride==100 then
- term.setBackgroundColor(colors.yellow);
- term.write("N");
- elseif checkpointStride==150 then
- term.setBackgroundColor(colors.red);
- term.write("H");
- else
- term.setBackgroundColor(colors.blue);
- term.write("?");
- end
- term.setCursorPos(sWidth-5,sHeight-1);
- local any=false;
- if canStart then
- for i,ls in ipairs(localStructs) do
- if ls[3] then
- any=true;
- break;
- end
- end
- end
- if any then term.setBackgroundColor(colors.green); term.setTextColor(colors.white);
- else term.setBackgroundColor(colors.gray);term.setTextColor(colors.black); end
- term.write("Start");
- term.setCursorPos(1,1);
- term.setTextColor(colors.white);
- term.setBackgroundColor(colors.red);
- term.write("X");
- renderMonitor();
- end
- function renderMonitor()
- if not monitor then return; end
- monitor.setTextScale(0.5);
- local mWidth;
- local mHeight;
- mWidth,mHeight=monitor.getSize();
- monitor.setBackgroundColor(colors.orange);
- monitor.clear();
- monitor.setTextColor(colors.black);
- for i=1,mHeight-3 do
- monitor.setBackgroundColor(colors.white);
- local ls=localStructs[i+mScroll];
- if ls then
- if ls[3] then monitor.setBackgroundColor(colors.green); end
- end
- monitor.setCursorPos(2,i+1);
- monitor.write(string.rep(" ",mWidth-2));
- local doScroll=#localStructs-(mHeight-3)>0;
- if ls then
- monitor.setCursorPos(2,i+1);
- if type(ls[2])=="string" then
- monitor.setTextColor(colors.red);
- monitor.write("Error in "..beautifulName(ls[1]));
- monitor.setTextColor(colors.black);
- else
- monitor.write(beautifulName(ls[1]));
- if ls[4] then
- local v;
- if math.floor(ls[4])==ls[4] then
- v=ls[4]..".0";
- else
- v=tostring(ls[4]);
- end
- if doScroll then monitor.setCursorPos(mWidth-2-#v,i+1);
- else monitor.setCursorPos(mWidth-#v,i+1); end
- monitor.write(v);
- end
- end
- end
- if doScroll then
- monitor.setBackgroundColor(colors.lightGray);
- monitor.setCursorPos(mWidth-2,i+1);
- if i==1 then monitor.write("/\\");
- elseif i==mHeight-3 then monitor.write("\\/");
- else monitor.write(" "); end
- end
- end
- term.setBackgroundColor(colors.blue);
- term.setCursorPos(2,mHeight-1);
- term.write(string.rep(" ",mWidth-2));
- monitor.setCursorPos(mWidth-16,mHeight-1);
- monitor.setBackgroundColor(colors.lightGray);
- monitor.setTextColor(colors.white);
- monitor.write("<- ->");
- monitor.setCursorPos(mWidth-14,mHeight-1);
- monitor.setTextColor(colors.gray);
- if checkpointStride==60 then
- monitor.setBackgroundColor(colors.green);
- monitor.write(" Easy ");
- elseif checkpointStride==100 then
- monitor.setBackgroundColor(colors.yellow);
- monitor.write("Normal");
- elseif checkpointStride==150 then
- monitor.setBackgroundColor(colors.red);
- monitor.write(" Hard ");
- else
- monitor.setBackgroundColor(colors.blue);
- monitor.write("?");
- end
- monitor.setCursorPos(mWidth-5,mHeight-1);
- local any=false;
- if canStart then
- for i,ls in ipairs(localStructs) do
- if ls[3] then
- any=true;
- break;
- end
- end
- end
- if any then monitor.setBackgroundColor(colors.green); monitor.setTextColor(colors.white);
- else monitor.setBackgroundColor(colors.gray);monitor.setTextColor(colors.black); end
- monitor.write("Start");
- end
- function updateOnlineStructs()
- if not http then
- onlineError="HTTP disabled";
- renderStore();
- return;
- end
- onlineError="Connecting...";
- renderStore();
- local onlineList=http.get("http://pastebin.com/raw.php?i=TWzu3GRU");
- if onlineList then
- onlineStructs={};
- local debugAmount=tonumber(onlineList.readLine());
- if not debugAmount then debugAmount=0; end
- while true do
- local name=onlineList.readLine();
- local url=onlineList.readLine();
- local author=onlineList.readLine();
- local desc=onlineList.readLine();
- local ver=tonumber(onlineList.readLine() or true);
- onlineList.readLine();
- if name==nil or url==nil or author==nil or desc==nil then break; end
- local new=true;
- local update=false;
- for i,l in ipairs(localStructs) do
- if l[1]==name then
- if ver then
- if not l[4] then
- update=true;
- elseif ver>l[4] then
- update=true;
- end
- end
- new=false;
- break;
- end
- end
- if debugAmount<=0 or debuggingStructs then
- onlineStructs[#onlineStructs+1]={name,url,false,debugAmount>0,author,desc,new,ver,update};
- end
- debugAmount=debugAmount-1;
- end
- onlineList.close();
- if #onlineStructs==0 then
- onlineError="No online structures left";
- else
- onlineError=false;
- end
- else
- onlineError="Error connecting to store";
- end
- renderStore();
- end
- function renderStore()
- term.setBackgroundColor(colors.blue);
- term.clear();
- term.setBackgroundColor(colors.white);
- term.setTextColor(colors.black);
- for i=1,sHeight-2 do
- term.setBackgroundColor(colors.white);
- local ls=onlineStructs[i+scroll];
- if ls and not onlineError then
- if ls[3] then
- term.setBackgroundColor(colors.cyan);
- elseif ls[4] then
- term.setBackgroundColor(colors.yellow);
- end
- end
- term.setCursorPos(2,i+1);
- term.write(string.rep(" ",sWidth-2));
- if ls and not onlineError then
- term.setCursorPos(2,i+1);
- if not ls[7] then
- if ls[9] then term.setTextColor(colors.black);
- else term.setTextColor(colors.lightGray); end
- end
- term.write(beautifulName(ls[1]));
- if not ls[7] then
- local msg;
- if ls[9] then msg="Update";
- else msg="Installed"; end
- term.setCursorPos(sWidth-#msg,i+1);
- term.write(msg);
- end
- term.setTextColor(colors.black);
- if ls[3] then
- term.setCursorPos(sWidth-11,i+1);
- term.write("Downloading");
- end
- end
- end
- if onlineError then
- term.setCursorPos(2,2);
- term.write(tostring(onlineError));
- end
- term.setCursorPos(1,1);
- term.setBackgroundColor(colors.blue);
- term.setTextColor(colors.white);
- term.write("<");
- end
- function downloadStruct(i)
- local ls=onlineStructs[i];
- ls[3]=true;
- renderStore();
- local structRaw=false;
- if http then structRaw=http.get("http://pastebin.com/raw.php?i="..textutils.urlEncode(ls[2])); end
- if structRaw then
- local files=fs.list("structures/");
- for _,file in ipairs(files) do
- if file:sub(1,#ls[1])==ls[1] then
- local nextChar=file:sub(#ls[1]+1,#ls[1]+1);
- if nextChar=="." or nextChar=="" then
- fs.delete("structures/"..file);
- end
- end
- end
- local filename="structures/"..ls[1];
- if ls[8] then
- filename=filename.."."..ls[8];
- end
- local handle=fs.open(filename,"w");
- handle.write(structRaw.readAll());
- handle.close();
- structRaw.close();
- ls[3]=false;
- updateLocalStructs();
- updateOnlineStructs();
- else
- ls[3]=false;
- renderStore();
- end
- end
- function scrollOStructs(n)
- local old=scroll;
- scroll=scroll+n;
- if scroll>#onlineStructs-(sHeight-2) then scroll=#onlineStructs-(sHeight-2); end
- if scroll<0 then scroll=0; end
- if scroll~=old then renderStore(); end
- end
- function scrollLStructs(n)
- local old=scroll;
- scroll=scroll+n;
- if scroll>#localStructs-(sHeight-3) then scroll=#localStructs-(sHeight-3); end
- if scroll<0 then scroll=0; end
- if scroll~=old then renderStructList(); end
- end
- function scrollMLStructs(n)
- if not monitor then return; end
- local mWidth,mHeight=monitor.getSize();
- local old=mScroll;
- mScroll=mScroll+n;
- if mScroll>#localStructs-(mHeight-3) then mScroll=#localStructs-(mHeight-3); end
- if mScroll<0 then mScroll=0; end
- if mScroll~=old then renderMonitor(); end
- end
- function downloadUpdate()
- renderStructList();
- term.setBackgroundColor(colors.yellow);
- term.setTextColor(colors.black);
- local my=math.ceil(sHeight/2);
- local mx=math.ceil(sWidth/2);
- for i=1,3 do
- term.setCursorPos(mx-20,my-2+i);
- term.write(string.rep(" ",41));
- end
- local msg="Update Available";
- term.setCursorPos(mx-#msg/2,my-1);
- term.write(msg);
- msg="An update is available from "..parkourVersion.." to "..updateData[1];
- term.setCursorPos(mx-#msg/2,my);
- term.write(msg);
- msg="Do you wish to update? (Y/N)";
- term.setCursorPos(mx-#msg/2,my+1);
- term.write(msg);
- local yes;
- while true do
- local ev={os.pullEvent()};
- if ev[1]=="key" then
- if ev[2]==keys.y then yes=true;break;
- elseif ev[2]==keys.n then yes=false;break; end
- end
- end
- if yes then
- term.setBackgroundColor(colors.black);
- term.setTextColor(colors.orange);
- term.clear();
- term.setCursorPos(1,1);
- print("Downloading V"..updateData[1]);
- local newest=http.get("http://pastebin.com/raw.php?i="..updateData[2]);
- if newest then
- local handle=fs.open(shell.getRunningProgram(),"w");
- handle.write(newest.readAll());
- handle.close();
- newest.close();
- print("V"..updateData[1].." downloaded. Running new version...");
- sleep(1);
- shell.run(shell.getRunningProgram());
- shallQuit=true;
- error();
- else
- print("Error downloading V"..updateData[1]);
- print("Using current V"..parkourVersion);
- sleep(1);
- end
- end
- updateData=false;
- canStart=true;
- renderStructList();
- end
- function mainGUI()
- function startParkour()
- local any=false;
- if canStart then
- for i,ls in ipairs(localStructs) do
- if ls[3] then
- any=true;
- break;
- end
- end
- end
- if any then
- for i,_ in ipairs(structures) do
- structures[i]=nil;
- end
- for i,ls in ipairs(localStructs) do
- if ls[3] then
- structNames[#structures+1]=ls[1];
- structures[#structures+1]=ls[2];
- end
- end
- term.setBackgroundColor(colors.black);
- term.setTextColor(colors.white);
- term.clear();
- term.setCursorPos(1,1);
- if monitor then
- monitor.setBackgroundColor(colors.black);
- monitor.setTextColor(colors.white);
- monitor.clear();
- monitor.setCursorPos(1,1);
- end
- return true;
- end
- return false;
- end
- function changeDifficulty(dir)
- if dir=="up" then
- if checkpointStride==100 then checkpointStride=150;renderStructList();
- elseif checkpointStride==60 then checkpointStride=100;renderStructList();
- end
- elseif dir=="down" then
- if checkpointStride==100 then checkpointStride=60;renderStructList();
- elseif checkpointStride==150 then checkpointStride=100;renderStructList();
- end
- end
- end
- updateLocalStructs();
- renderStructList();
- while true do
- if updateData then
- downloadUpdate();
- end
- if canStartChange then
- canStartChange=false;
- renderStructList();
- end
- local ev={os.pullEvent()};
- if updateData then
- downloadUpdate();
- end
- if canStartChange then
- canStartChange=false;
- renderStructList();
- end
- if ev[1]=="mouse_click" then
- if ev[3]>1 and ev[3]<sWidth and ev[4]>1 and ev[4]<sHeight then
- if ev[4]==sHeight-1 then
- if ev[3]>=sWidth-5 then
- --Start
- fromMonitor=false;
- if startParkour() then break; end
- elseif ev[3]>=sWidth-9 and ev[3]<sWidth-6 then
- if ev[3]==sWidth-9 then
- changeDifficulty("down");
- elseif ev[3]==sWidth-7 then
- changeDifficulty("up");
- end
- else
- --Store
- scroll=0;
- updateOnlineStructs();
- while true do
- local ev={os.pullEvent()};
- if ev[1]=="mouse_click" then
- if ev[3]==1 and ev[4]==1 then break;
- elseif ev[3]>1 and ev[3]<sWidth and ev[4]>1 and ev[4]<sHeight then
- local ls=onlineStructs[ev[4]-1+scroll];
- if ls and (ls[7] or ls[9]) then
- term.setBackgroundColor(colors.cyan);
- term.setTextColor(colors.black);
- local my=math.ceil(sHeight/2);
- local mx=math.ceil(sWidth/2);
- for y=my-4,my+4 do
- term.setCursorPos(mx-20,y);
- term.write(string.rep(" ",41));
- end
- local msg="Download "..beautifulName(ls[1]).."?";
- term.setCursorPos(mx-#msg/2,my-4);
- term.write(msg);
- term.setCursorPos(mx-19,my-3);
- term.write("by "..ls[5]:sub(1,37));
- if ls[8] then
- if math.floor(ls[8])==ls[8] then
- msg=ls[8]..".0";
- else
- msg=tostring(ls[8]);
- end
- term.setCursorPos(mx+20-#msg,my-3);
- term.write("V"..msg);
- end
- function wordWrap(str)
- function trim(s)
- while s:sub(1,1)==" " do
- s=s:sub(2);
- end
- while s:sub(#s,#s)==" " do
- s=s:sub(1,#s-1);
- end
- return s;
- end
- str=trim(str);
- if #str<=41 then
- return str;
- else
- local index=41;
- for i=41,1,-1 do
- if str:sub(i,i)==" " then
- index=i;
- break;
- end
- end
- return trim(str:sub(1,index)).."\n"..wordWrap(str:sub(index+1));
- end
- end
- if ls[6]:sub(1,16)=="#external-desc: " then
- if http then
- term.setCursorPos(mx-20,my-2);
- term.write("Loading...");
- local desc=http.get("http://pastebin.com/raw.php?i="..textutils.urlEncode(ls[6]:sub(17)));
- if desc then
- msg=desc.readAll();
- desc.close();
- else
- msg="Error getting external description in "..tostring(ls[6]:sub(17));
- end
- term.setCursorPos(mx-20,my-2);
- term.write(string.rep(" ",10));
- else
- msg="Can't get description, HTTP disabled";
- end
- else
- msg=ls[6];
- end
- msg=wordWrap(msg);
- local lastLine=1;
- for i=0,5 do
- local newline=msg:find("\n",lastLine);
- local line;
- if newline then
- line=msg:sub(lastLine,newline-1);
- lastLine=newline+1;
- else
- if lastLine<=#msg then
- line=msg:sub(lastLine);
- lastLine=#msg+1;
- else
- line="";
- end
- end
- term.setCursorPos(mx-20,my-2+i);
- term.write(line);
- end
- msg="[Y]Download [N]Cancel";
- term.setCursorPos(mx-#msg/2,my+4);
- term.write(msg);
- while true do
- local evi={os.pullEvent()};
- if evi[1]=="key" then
- if evi[2]==keys.y then
- downloadStruct(ev[4]-1+scroll);
- break;
- elseif evi[2]==keys.n then
- renderStore();
- break;
- end
- end
- end
- end
- end
- elseif ev[1]=="mouse_scroll" then
- scrollOStructs(ev[2]);
- elseif ev[1]=="key" then
- if ev[2]==keys.up then scrollOStructs(-1);
- elseif ev[2]==keys.down then scrollOStructs(1);
- elseif ev[2]==keys.d then
- if not debuggingStructs then
- debuggingStructs=true;
- updateOnlineStructs();
- end
- end
- end
- end
- scroll=0;
- renderStructList();
- end
- else
- local ls=localStructs[ev[4]-1+scroll];
- if ls then
- if ev[3]>sWidth-3 then
- local msg="Are you sure you want to delete? (Y/N)";
- term.setBackgroundColor(colors.white);
- term.setTextColor(colors.red);
- term.setCursorPos(math.ceil(sWidth/2-#msg/2),math.ceil(sHeight/2));
- term.write(msg);
- while true do
- local ev={os.pullEvent()};
- if ev[1]=="key" then
- if ev[2]==keys.y then
- local filename="structures/"..ls[1];
- if ls[4] then
- filename=filename.."."..ls[4];
- end
- fs.delete(filename);
- updateLocalStructs();
- break;
- elseif ev[2]==keys.n then
- break;
- end
- end
- end
- renderStructList();
- else
- if type(ls[2])=="function" then
- ls[3]=not ls[3];
- renderStructList();
- end
- end
- end
- end
- elseif ev[3]==1 and ev[4]==1 then
- term.setBackgroundColor(colors.black);
- term.setTextColor(colors.white);
- term.clear();
- term.setCursorPos(1,1);
- if monitor then
- monitor.setBackgroundColor(colors.black);
- monitor.clear();
- end
- shallQuit=true;
- error();
- end
- elseif ev[1]=="mouse_scroll" then
- scrollLStructs(ev[2]);
- elseif ev[1]=="monitor_touch" and ev[2]==monitorName then
- local mWidth;
- local mHeight;
- mWidth,mHeight=monitor.getSize();
- if ev[4]>1 and ev[4]<mHeight-1 then
- if ev[3]<mWidth and ev[3]>1 then
- if (ev[3]==mWidth-1 or ev[3]==mWidth-2) and #localStructs-(mHeight-3)>0 then
- if ev[4]==2 or ev[4]==3 then
- scrollMLStructs(-1);
- elseif ev[4]==mHeight-2 or ev[4]==mHeight-3 then
- scrollMLStructs(1);
- end
- else
- local ls=localStructs[ev[4]-1+mScroll];
- if ls then
- if type(ls[2])=="function" then
- ls[3]=not ls[3];
- renderStructList();
- end
- end
- end
- end
- elseif ev[4]==mHeight-1 then
- if ev[3]>=mWidth-5 then
- --Start
- fromMonitor=true;
- if startParkour() then break; end
- elseif ev[3]==mWidth-16 or ev[3]==mWidth-15 then
- changeDifficulty("down");
- elseif ev[3]==mWidth-7 or ev[3]==mWidth-8 then
- changeDifficulty("up");
- end
- end
- elseif ev[1]=="monitor_resize" and ev[2]==monitorName then
- renderMonitor();
- elseif ev[1]=="key" then
- if ev[2]==keys.up then scrollLStructs(-1);
- elseif ev[2]==keys.down then scrollLStructs(1);
- end
- end
- end
- end
- function updateAsync()
- local tempCanStart=true;
- if http then
- local last=http.get(updateURL);
- if last then
- local ver=tonumber(last.readLine());
- if type(ver)=="number" then
- if ver>parkourVersion then
- local newurl=last.readLine();
- if type(newurl)=="string" then
- tempCanStart=false;
- updateData={ver,newurl};
- os.queueEvent("update_available");
- end
- end
- end
- last.close();
- end
- end
- canStart=tempCanStart;
- canStartChange=true;
- os.queueEvent("canStart_changed");
- while true do os.pullEvent(); end
- end
- function limitUpdateTime()
- sleep(2);
- canStart=true;
- canStartChange=true;
- os.queueEvent("canStart_changed");
- while true do os.pullEvent(); end
- end
- parallel.waitForAny(mainGUI,updateAsync,limitUpdateTime);
- structsetPool={};
- final={exit={pos[1],pos[2],pos[3]},next=numberToDir(math.random(0,1)*2+1)};
- if shallQuit then
- error();
- end
- function monPrint(str)
- local mx1,my1=monitor.getCursorPos();
- monitor.write(str);
- monitor.setCursorPos(1,my1+1);
- end
- --Print compilation errors, if any
- local compErrors=false;
- for i,struct in ipairs(localStructs) do
- if type(struct[2])=="string" then
- printError("Non-LUA file in structures directory: "..struct[1]);
- printError(struct[2]);
- compErrors=true;
- end
- end
- if compErrors then
- print("Read the errors, then press a key");
- os.pullEvent("key");
- end
- --Test Structures for errors
- term.write("Testing Structures... ");
- local tx,ty=term.getCursorPos();
- print();
- local mx;
- local my;
- if fromMonitor then
- monitor.write("Testing Structures... ");
- mx,my=monitor.getCursorPos();
- monitor.setCursorPos(1,my+1);
- end
- local warnings=0;
- function structError(i,msg)
- printError("Error in structure ["..structNames[i].."]");
- printError(tostring(msg));
- if fromMonitor then
- monPrint("Error in "..structNames[i]);
- monPrint("Try deactivating the structure");
- monPrint("And telling the structure maker");
- end
- error();
- end
- function structWarning(i,msg)
- warnings=warnings+1;
- printError("Warning in structure ["..structNames[i].."]");
- printError(tostring(msg));
- end
- for i,func in ipairs(structures) do
- local ok,struct=pcall(func,"build",0);
- if not ok then
- structError(i,struct);
- end
- if type(struct)~="table" then
- structError(i,"Function must return a structure table");
- end
- --Check struct.blocks
- if type(struct.blocks)~="table" then
- structError(i,"StructureTable must contain a \"blocks\" subtable");
- elseif #struct.blocks==0 then
- structError(i,"StructureTable.blocks is empty");
- end
- for j,b in ipairs(struct.blocks) do
- if type(b)~="table" then
- structError(i,"StructureTable.blocks entry #"..j.." is not a table");
- end
- if type(b[1])~="number" then
- structError(i,"StructureTable.blocks entry #"..j.."'s first element(X) is not a number: "..tostring(b[1]));
- elseif type(b[2])~="number" then
- structError(i,"StructureTable.blocks entry #"..j.."'s second element(Y) is not a number: "..tostring(b[2]));
- elseif type(b[3])~="number" then
- structError(i,"StructureTable.blocks entry #"..j.."'s third element(Z) is not a number: "..tostring(b[3]));
- elseif b.autoMeta then
- if type(b[5])~="number" and b[5]~="x+" and b[5]~="z+" and b[5]~="x-" and b[5]~="z-" then
- structError(i,"StructureTable.blocks entry #"..j.."'s fifth element (Orientation) is an invalid orientation for autoMeta: "..tostring(b[5]));
- end
- end
- end
- --Check struct.areaCheck
- if type(struct.areaCheck)~="table" then
- structError(i,"StructureTable must contain an \"areaCheck\" subtable");
- elseif #struct.areaCheck==0 then
- structWarning(i,"StructureTable.areaCheck is empty, players may wander freely");
- end
- for j,b in ipairs(struct.areaCheck) do
- if type(b)~="table" then
- structError(i,"StructureTable.areaCheck entry #"..j.." is not a table");
- end
- if type(b[1])~="number" then
- structError(i,"StructureTable.areaCheck entry #"..j.."'s first element(X) is not a number");
- elseif type(b[2])~="number" then
- structError(i,"StructureTable.areaCheck entry #"..j.."'s second element(Y) is not a number");
- elseif type(b[3])~="number" then
- structError(i,"StructureTable.areaCheck entry #"..j.."'s third element(Z) is not a number");
- elseif type(b[4])~="number" then
- structError(i,"StructureTable.areaCheck entry #"..j.."'s fourth element(Range) is not a number");
- end
- end
- --Check struct.exit
- if type(struct.exit)~="table" then
- structError(i,"StructureTable.exit is not a table");
- end
- if type(struct.exit[1])~="number" then
- structError(i,"StructureTable.exit first element(X) is not a number");
- elseif type(struct.exit[2])~="number" then
- structError(i,"StructureTable.exit second element(Y) is not a number");
- elseif type(struct.exit[3])~="number" then
- structError(i,"StructureTable.exit third element(Z) is not a number");
- end
- --Check struct.next
- if type(struct.next)=="string" then
- if struct.next~="x+" and struct.next~="z+" and struct.next~="x-" and struct.next~="z-" then
- structError(i,"StructureTable.next must be x+, z+, x- or z-, not "..struct.next);
- end
- elseif type(struct.next)=="number" then
- if struct.next==math.huge or struct.next==-math.huge or struct.next~=struct.next then
- structError(i,"StructureTable.next is not a valid number");
- end
- else
- structError(i,"StructureTable.next is neither a string or a number");
- end
- --Check "weight" func
- local ok,t=pcall(func,"weight");
- if not ok then
- structError(i,t);
- end
- if t~=nil and type(t)~="number" then
- if type(t)=="number" then
- if t<0 or t==math.huge or t~=t then
- structError(i,"Structure call with \"weight\" argument must return a positive valid number");
- end
- else
- structError(i,"Structure call with \"weight\" argument must return a number or nil, not "..tostring(t));
- end
- end
- end
- if warnings==0 then
- sleep(0.2);
- local tx1,ty1=term.getCursorPos();
- term.setCursorPos(tx,ty);
- term.write("No errors");
- term.setCursorPos(tx1,ty1);
- if fromMonitor then
- local mx1,my1=monitor.getCursorPos();
- monitor.setCursorPos(mx,my);
- monitor.write("No errors");
- monitor.setCursorPos(mx1,my1);
- end
- sleep(0.3);
- else
- print("Finished testing, found "..warnings.." warnings");
- sleep(2);
- end
- playerCount=-1;
- local playersLocked=false;
- local selectedMode=0;
- realMode=1;
- checkpointGoal=5;
- function renderSelection()
- term.setBackgroundColor(colors.lime);
- term.clear();
- for i=1,sHeight-2 do
- if i==1 then term.setBackgroundColor(colors.green);
- elseif playersLocked and i==6 then term.setBackgroundColor(colors.lime);
- elseif playersLocked and i==7 then term.setBackgroundColor(colors.green);
- else term.setBackgroundColor(colors.white); end
- term.setCursorPos(2,i+1);
- term.write(string.rep(" ",sWidth-2));
- if i==1 then
- term.setTextColor(colors.white);
- local msg="Player Selection";
- term.setCursorPos(math.ceil(sWidth/2-#msg/2),i+1);
- term.write(msg);
- elseif i==2 then
- term.setTextColor(colors.lightGray);
- term.setCursorPos(2,i+1);
- if selectionPos[4]==-1 then term.write("Players are selected from the whole world");
- else term.write("Players are selected from a "..(selectionPos[4]*2).."-diameter sphere"); end
- elseif i==3 then
- term.setTextColor(colors.lightGray);
- term.setCursorPos(2,i+1);
- term.write("15 players max, chosen by proximity");
- elseif not confiscateItems and i==4 then
- term.setTextColor(colors.red);
- term.setCursorPos(2,i+1);
- term.write("WARNING: All your items will be destroyed!");
- elseif i==5 then
- local msg;
- if playerCount==-1 then
- msg="Searching for players...";
- elseif playerCount==0 then
- msg="No players found";
- elseif playerCount==1 then
- msg="1 player found";
- else
- msg=playerCount.." players found";
- end
- term.setTextColor(colors.black);
- term.setCursorPos(2,i+1);
- term.write(msg);
- end
- end
- if playersLocked then
- local msg;
- if playerCount==1 then msg=" Singleplayer ";
- else msg=" Multiplayer "; end
- term.setCursorPos(sWidth/2-#msg/2,8);
- term.setBackgroundColor(colors.green);
- term.setTextColor(colors.white);
- term.write(msg);
- if playerCount==1 then
- msg="Freeplay";
- if selectedMode==1 then term.setBackgroundColor(colors.green);
- else term.setBackgroundColor(colors.lightGray); end
- term.setCursorPos(3,10);
- term.write(string.rep(" ",sWidth-4));
- term.setCursorPos(sWidth/2-#msg/2,10);
- term.write(msg);
- else
- msg="Race";
- if selectedMode==1 then term.setBackgroundColor(colors.green);
- else term.setBackgroundColor(colors.lightGray); end
- term.setCursorPos(3,10);
- term.write(string.rep(" ",sWidth-4));
- term.setCursorPos(sWidth/2-#msg/2,10);
- term.write(msg);
- msg="Skill";
- if selectedMode==2 then term.setBackgroundColor(colors.green);
- else term.setBackgroundColor(colors.lightGray); end
- term.setCursorPos(3,11);
- term.write(string.rep(" ",sWidth-4));
- term.setCursorPos(sWidth/2-#msg/2,11);
- term.write(msg);
- msg="Freeplay";
- if selectedMode==3 then term.setBackgroundColor(colors.green);
- else term.setBackgroundColor(colors.lightGray); end
- term.setCursorPos(3,12);
- term.write(string.rep(" ",sWidth-4));
- term.setCursorPos(sWidth/2-#msg/2,12);
- term.write(msg);
- if selectedMode==1 or selectedMode==2 then
- local goalX=math.floor(sWidth/2)-13;
- term.setCursorPos(goalX+1,14);
- term.setBackgroundColor(colors.white);
- term.write(" ");
- msg=tostring(checkpointGoal);
- term.setCursorPos(goalX+4-#msg,14);
- term.setTextColor(colors.black);
- term.write(msg);
- term.setTextColor(colors.lightGray);
- term.setCursorPos(goalX+6,14);
- term.write("Checkpoints to finish");
- term.setBackgroundColor(colors.gray);
- term.setTextColor(colors.white);
- term.setCursorPos(goalX,14);
- term.write("<");
- term.setCursorPos(goalX+4,14);
- term.write(">");
- end
- end
- term.setCursorPos(sWidth-5,sHeight-1);
- if selectedMode==0 then term.setBackgroundColor(colors.gray);term.setTextColor(colors.black);
- else term.setBackgroundColor(colors.green);term.setTextColor(colors.white); end
- term.write("Start");
- end
- term.setCursorPos(2,sHeight-1);
- if playersLocked then term.setBackgroundColor(colors.green);term.setTextColor(colors.white);
- elseif playerCount>0 then term.setBackgroundColor(colors.green);term.setTextColor(colors.white);
- else term.setBackgroundColor(colors.gray);term.setTextColor(colors.black); end
- if playersLocked then term.write("Unlock Players");
- else term.write(" Lock Players "); end
- term.setCursorPos(1,1);
- term.setBackgroundColor(colors.lime);
- term.setTextColor(colors.white);
- term.write("<");
- renderMonitorSelection();
- end
- function renderMonitorSelection()
- if not monitor then return; end
- monitor.setTextScale(0.5);
- local mWidth,mHeight=monitor.getSize();
- monitor.setBackgroundColor(colors.lime);
- monitor.clear();
- for i=1,mHeight-2 do
- if i==1 then monitor.setBackgroundColor(colors.green);
- elseif playersLocked and i==6 then monitor.setBackgroundColor(colors.lime);
- elseif playersLocked and i==7 then monitor.setBackgroundColor(colors.green);
- else monitor.setBackgroundColor(colors.white); end
- monitor.setCursorPos(2,i+1);
- monitor.write(string.rep(" ",mWidth-2));
- if i==1 then
- monitor.setTextColor(colors.white);
- local msg="Player Selection";
- monitor.setCursorPos(math.ceil(mWidth/2-#msg/2),i+1);
- monitor.write(msg);
- elseif i==2 then
- monitor.setTextColor(colors.lightGray);
- monitor.setCursorPos(2,i+1);
- monitor.write("Player selection area:");
- elseif i==3 then
- monitor.setTextColor(colors.lightGray);
- monitor.setCursorPos(2,i+1);
- if selectionPos[4]==-1 then monitor.write("Whole world");
- else monitor.write((selectionPos[4]*2).."-diameter sphere"); end
- elseif i==4 then
- monitor.setTextColor(colors.lightGray);
- monitor.setCursorPos(2,i+1);
- if confiscateItems then monitor.write("15 players max");
- else monitor.setTextColor(colors.red);monitor.write("WARNING: Will destroy your items!"); end
- elseif i==5 then
- local msg;
- if playerCount==-1 then
- msg="Searching for players...";
- elseif playerCount==0 then
- msg="No players found";
- elseif playerCount==1 then
- msg="1 player found";
- else
- msg=playerCount.." players found";
- end
- monitor.setTextColor(colors.black);
- monitor.setCursorPos(2,i+1);
- monitor.write(msg);
- end
- end
- if playersLocked then
- local msg;
- if playerCount==1 then msg=" Singleplayer ";
- else msg=" Multiplayer "; end
- monitor.setCursorPos(mWidth/2-#msg/2,8);
- monitor.setBackgroundColor(colors.green);
- monitor.setTextColor(colors.white);
- monitor.write(msg);
- if playerCount==1 then
- msg="Freeplay";
- if selectedMode==1 then monitor.setBackgroundColor(colors.green);
- else monitor.setBackgroundColor(colors.lightGray); end
- monitor.setCursorPos(3,10);
- monitor.write(string.rep(" ",mWidth-4));
- monitor.setCursorPos(mWidth/2-#msg/2,10);
- monitor.write(msg);
- else
- msg="Race";
- if selectedMode==1 then monitor.setBackgroundColor(colors.green);
- else monitor.setBackgroundColor(colors.lightGray); end
- monitor.setCursorPos(3,10);
- monitor.write(string.rep(" ",mWidth-4));
- monitor.setCursorPos(mWidth/2-#msg/2,10);
- monitor.write(msg);
- msg="Skill";
- if selectedMode==2 then monitor.setBackgroundColor(colors.green);
- else monitor.setBackgroundColor(colors.lightGray); end
- monitor.setCursorPos(3,11);
- monitor.write(string.rep(" ",mWidth-4));
- monitor.setCursorPos(mWidth/2-#msg/2,11);
- monitor.write(msg);
- msg="Freeplay";
- if selectedMode==3 then monitor.setBackgroundColor(colors.green);
- else monitor.setBackgroundColor(colors.lightGray); end
- monitor.setCursorPos(3,12);
- monitor.write(string.rep(" ",mWidth-4));
- monitor.setCursorPos(mWidth/2-#msg/2,12);
- monitor.write(msg);
- if selectedMode==1 or selectedMode==2 then
- local goalX=math.floor(mWidth/2)-14;
- monitor.setCursorPos(goalX+2,14);
- monitor.setBackgroundColor(colors.white);
- monitor.write(" ");
- msg=tostring(checkpointGoal);
- monitor.setCursorPos(goalX+5-#msg,14);
- monitor.setTextColor(colors.black);
- monitor.write(msg);
- monitor.setTextColor(colors.lightGray);
- monitor.setCursorPos(goalX+8,14);
- monitor.write("Checkpoints to finish");
- monitor.setBackgroundColor(colors.gray);
- if fromMonitor or forceMonitorSelection then monitor.setTextColor(colors.white);
- else monitor.setTextColor(colors.black); end
- monitor.setCursorPos(goalX,14);
- monitor.write("<-");
- monitor.setCursorPos(goalX+5,14);
- monitor.write("->");
- end
- end
- monitor.setCursorPos(mWidth-5,mHeight-1);
- if (not fromMonitor and not forceMonitorSelection) or selectedMode==0 then monitor.setBackgroundColor(colors.gray);monitor.setTextColor(colors.black);
- else monitor.setBackgroundColor(colors.green);monitor.setTextColor(colors.white); end
- monitor.write("Start");
- end
- monitor.setCursorPos(2,mHeight-1);
- if playersLocked then
- if allowPlayerUnlocking then
- if fromMonitor or forceMonitorSelection then
- monitor.setBackgroundColor(colors.green);
- monitor.setTextColor(colors.white);
- else
- monitor.setBackgroundColor(colors.gray);
- monitor.setTextColor(colors.black);
- end
- else
- monitor.setBackgroundColor(colors.white);
- monitor.setTextColor(colors.lightGray);
- end
- else
- if fromMonitor or forceMonitorSelection then
- monitor.setBackgroundColor(colors.green);
- monitor.setTextColor(colors.white);
- else
- monitor.setBackgroundColor(colors.gray);
- monitor.setTextColor(colors.black);
- end
- end
- if playersLocked then
- if allowPlayerUnlocking then monitor.write("Unlock Players");
- else monitor.write("Players Locked"); end
- else monitor.write(" Lock Players "); end
- monitor.setCursorPos(1,1);
- monitor.setBackgroundColor(colors.lime);
- if fromMonitor or forceMonitorSelection then monitor.setTextColor(colors.white);
- else monitor.setTextColor(colors.gray); end
- monitor.write("<");
- end
- function lockPlayers()
- for i=1,playerCount do
- commands.async.scoreboard("players add @p[x="..selectionPos[1]..",y="..selectionPos[2]..",z="..selectionPos[3]..",c="..i.."] parkour_id 1");
- end
- playersLocked=true;
- end
- function unlockPlayers()
- commands.scoreboard("objectives remove parkour_id");
- commands.async.scoreboard("objectives add parkour_id dummy");
- playersLocked=false;
- selectedMode=0;
- realMode=1;
- end
- renderSelection();
- --Computers cant get redstone output out of their commands.
- --So better build a commandblock contraption and get the redstone level
- unlockPlayers();
- --15 is no artificial limit, comparator's max output is 15
- if selectionPos[4]==-1 then
- commands.async.setblock(pos[1],255,pos[3],"minecraft:command_block",0,"replace",
- "{Command:\"testfor @p[c=15]\"}");
- else
- commands.async.setblock(pos[1],255,pos[3],"minecraft:command_block",0,"replace",
- "{Command:\"testfor @p[c=15,x="..selectionPos[1]..",y="..selectionPos[2]..",z="..selectionPos[3]..",r="..selectionPos[4].."]\"}");
- end
- commands.async.setblock(pos[1]+1,254,pos[3],"minecraft:stone");
- commands.async.setblock(pos[1]+1,255,pos[3],"minecraft:unpowered_comparator",1);
- commands.async.setblock(pos[1]+2,254,pos[3],"minecraft:stone");
- commands.async.setblock(pos[1]+2,255,pos[3],"minecraft:redstone_wire");
- sleep(0.1);
- local why=0;
- parallel.waitForAny(function()
- while true do
- if playersLocked then
- os.pullEvent();
- else
- commands.setblock(pos[1],254,pos[3],"minecraft:air");
- commands.setblock(pos[1],254,pos[3],"minecraft:redstone_block");
- sleep(0.2);
- playerCount=commands.getBlockInfo(pos[1]+2,255,pos[3]).metadata;
- renderSelection();
- end
- end
- end,function()
- while true do
- local ev={os.pullEvent()};
- if ev[1]=="mouse_click" then
- if ev[3]==1 and ev[4]==1 then
- why=1;
- return;
- elseif playersLocked then
- if playerCount==1 then
- if ev[3]>2 and ev[3]<sWidth-1 then
- if ev[4]==10 then
- selectedMode=1;
- realMode=1;
- renderSelection();
- end
- end
- else
- if ev[3]>2 and ev[3]<sWidth-1 then
- if ev[4]==10 then
- selectedMode=1;
- realMode=2;
- renderSelection();
- elseif ev[4]==11 then
- selectedMode=2;
- realMode=3;
- renderSelection();
- elseif ev[4]==12 then
- selectedMode=3;
- realMode=1;
- renderSelection();
- end
- end
- if ev[4]==14 and (selectedMode==1 or selectedMode==2) then
- local goalX=math.floor(sWidth/2)-13;
- if ev[3]==goalX and checkpointGoal>1 then
- checkpointGoal=checkpointGoal-1;
- renderSelection();
- elseif ev[3]==goalX+4 and checkpointGoal<999 then
- checkpointGoal=checkpointGoal+1;
- renderSelection();
- end
- end
- end
- if selectedMode~=0 and ev[4]==sHeight-1 and ev[3]>=sWidth-5 and ev[3]<sWidth then
- why=2;
- fromMonitor=false;
- return;
- end
- if ev[4]==sHeight-1 and ev[3]>=2 and ev[3]<=15 and playerCount>0 then
- unlockPlayers();
- renderSelection();
- end
- else
- if ev[4]==sHeight-1 and ev[3]>=2 and ev[3]<=15 and playerCount>0 then
- lockPlayers();
- renderSelection();
- end
- end
- elseif (fromMonitor or forceMonitorSelection) and ev[1]=="monitor_touch" and ev[2]==monitorName then
- local mWidth,mHeight=monitor.getSize();
- if ev[3]==1 and ev[4]==1 then
- why=1;
- return;
- elseif playersLocked then
- if playerCount==1 then
- if ev[3]>2 and ev[3]<mWidth-1 then
- if ev[4]==10 then
- selectedMode=1;
- realMode=1;
- renderSelection();
- end
- end
- else
- if ev[3]>2 and ev[3]<mWidth-1 then
- if ev[4]==10 then
- selectedMode=1;
- realMode=2;
- renderSelection();
- elseif ev[4]==11 then
- selectedMode=2;
- realMode=3;
- renderSelection();
- elseif ev[4]==12 then
- selectedMode=3;
- realMode=1;
- renderSelection();
- end
- end
- if ev[4]==14 and (selectedMode==1 or selectedMode==2) then
- local goalX=math.floor(mWidth/2)-14;
- if (ev[3]==goalX or ev[3]==goalX+1) and checkpointGoal>1 then
- checkpointGoal=checkpointGoal-1;
- renderSelection();
- elseif (ev[3]==goalX+5 or ev[3]==goalX+6) and checkpointGoal<999 then
- checkpointGoal=checkpointGoal+1;
- renderSelection();
- end
- end
- end
- if selectedMode~=0 and ev[4]==mHeight-1 and ev[3]>=mWidth-5 and ev[3]<mWidth then
- why=2;
- fromMonitor=true;
- return;
- end
- if allowPlayerUnlocking and ev[4]==mHeight-1 and ev[3]>=2 and ev[3]<=15 and playerCount>0 then
- unlockPlayers();
- renderSelection();
- end
- else
- if ev[4]==mHeight-1 and ev[3]>=2 and ev[3]<=15 and playerCount>0 then
- lockPlayers();
- renderSelection();
- end
- end
- end
- end
- end);
- commands.async.setblock(pos[1],255,pos[3],"minecraft:air");
- commands.async.setblock(pos[1]+1,255,pos[3],"minecraft:air");
- commands.async.setblock(pos[1]+1,254,pos[3],"minecraft:air");
- commands.async.setblock(pos[1]+2,255,pos[3],"minecraft:air");
- commands.async.setblock(pos[1]+2,254,pos[3],"minecraft:air");
- commands.async.setblock(pos[1],254,pos[3],"minecraft:air");
- if why==1 then
- commands.async.scoreboard("objectives remove parkour_id");
- return;
- end
- term.setBackgroundColor(colors.black);
- term.setTextColor(colors.white);
- term.clear();
- term.setCursorPos(1,1);
- if monitor then
- monitor.setBackgroundColor(colors.black);
- monitor.setTextColor(colors.white);
- monitor.clear();
- monitor.setCursorPos(1,1);
- end
- function selectorFor(id)
- return "@p[score_parkour_id_min="..id..",score_parkour_id="..id;
- end
- playerData={};
- for id=1,playerCount do
- playerData[id]={
- deaths=0,
- checkpoints=-1,
- time=0,
- };
- --commands.async.tellraw(selectorFor(id).."] {text:\"You are player #"..id.."\"}");
- end
- sidebarMode=0;
- function updateScoreboard(pid)
- if sidebarMode==0 then commands.async.scoreboard("players set "..selectorFor(pid).."] parkour_deaths "..playerData[pid].deaths);
- elseif sidebarMode==1 then commands.async.scoreboard("players set "..selectorFor(pid).."] parkour_checks "..playerData[pid].checkpoints);
- elseif sidebarMode==2 then
- local btime="";
- local msg;
- local rtime=playerData[pid].time;
- local ttime=rtime;
- if rtime>=3600 then
- btime=btime..tostring(math.floor(ttime/3600));
- ttime=ttime%3600;
- end
- if rtime>=60 then
- msg=tostring(math.floor(ttime/60));
- if #msg==1 then msg="0"..msg; end
- btime=btime..msg;
- ttime=ttime%60;
- end
- msg=tostring(math.floor(ttime));
- if #msg==1 then msg="0"..msg; end
- btime=btime..msg;
- commands.async.scoreboard("players set "..selectorFor(pid).."] parkour_time "..btime);
- --elseif sidebarMode==3 then --It doesnt matter, IDs aren't updated
- end
- end
- cycleSidebar=function(o)
- if o then sidebarMode=o;
- else sidebarMode=(sidebarMode+1)%4; end
- for i=1,playerCount do
- updateScoreboard(i);
- end
- if sidebarMode==0 then commands.async.scoreboard("objectives setdisplay sidebar parkour_deaths");
- elseif sidebarMode==1 then commands.async.scoreboard("objectives setdisplay sidebar parkour_checks");
- elseif sidebarMode==2 then commands.async.scoreboard("objectives setdisplay sidebar parkour_time");
- elseif sidebarMode==3 then commands.async.scoreboard("objectives setdisplay sidebar parkour_number");
- end
- end
- commands.async.scoreboard("objectives add parkour_deaths dummy Deaths");
- commands.async.scoreboard("objectives add parkour_checks dummy Checkpoints Reached");
- commands.async.scoreboard("objectives add parkour_time dummy Time");
- commands.async.scoreboard("objectives add parkour_number dummy Player Number");
- for i=1,playerCount do
- commands.async.scoreboard("players set "..selectorFor(i).."] parkour_number "..i);
- end
- cycleSidebar(3);
- local turnOnCBO=false;
- if (({commands.gamerule("commandBlockOutput")})[2][1]):find("true") then
- if fromMonitor then
- commands.gamerule("commandBlockOutput false");
- print("Command Block Output turned off for the session.");
- turnOnCBO=true;
- monitor.write("Turned off CommandBlockOutput for this time");
- local _,y=monitor.getCursorPos();
- monitor.setCursorPos(1,y+1);
- else
- print("Command Block Output is on");
- print("Do you wish to turn it off for the session? (Y/N)");
- while true do
- local ev={os.pullEvent()};
- if ev[1]=="char" then
- if ev[2]=="y" then
- commands.gamerule("commandBlockOutput false");
- print("Command Block Output turned off for the session.");
- turnOnCBO=true;
- break;
- elseif ev[2]=="n" then
- print("Command Block Output left on.");
- break;
- end
- end
- end
- end
- end
- local allAdventure=true;
- for j=1,playerCount do
- local pd=playerData[j];
- local selector=selectorFor(j);
- pd.oldMode=-1;
- for i=0,3 do
- if commands.testfor(selector..",m="..i.."]") then pd.oldMode=i;break; end
- end
- if pd.oldMode~=2 then allAdventure=false; end
- end
- if not allAdventure then
- if not canStayInCreative or fromMonitor then
- for i=1,playerCount do
- commands.async.gamemode("2 "..selectorFor(i).."]");
- end
- monitor.write("Put all players in adventure mode");
- local _,y=monitor.getCursorPos();
- monitor.setCursorPos(1,y+1);
- print("Put all players in adventure mode for the session");
- else
- print("Some players are not in adventure mode");
- print("Do you wish them to be? (Y/N)");
- while true do
- local ev={os.pullEvent()};
- if ev[1]=="char" then
- if ev[2]=="y" then
- for i=1,playerCount do
- commands.async.gamemode("2 "..selectorFor(i).."]");
- end
- print("Put them in adventure mode for the session");
- break;
- elseif ev[2]=="n" then
- print("Playing in their respective gamemodes");
- for i=1,playerCount do
- playerData[i].oldMode=nil;
- end
- break;
- end
- end
- end
- end
- end
- print("Running parkour with "..#structures.." structures");
- if monitor then
- monitor.write("Running parkour with "..#structures.." structures");
- local _,y=monitor.getCursorPos();
- monitor.setCursorPos(1,y+1);
- end
- if fs.exists("/startup") then
- fs.delete("/startupbackup.donteverusethisname.idequalsvirtualparkour");
- fs.copy("/startup","/startupbackup.donteverusethisname.idequalsvirtualparkour");
- end
- local handle=fs.open("/startup","w");
- handle.writeLine("if not pocket then");
- handle.writeLine(" fs.delete(\"/startup\");");
- handle.writeLine(" pcall(function() fs.move(\"/startupbackup.donteverusethisname.idequalsvirtualparkour\",\"/startup\") end);");
- handle.writeLine(" os.reboot();");
- handle.writeLine("end");
- handle.writeLine("term.setBackgroundColor(colors.red);");
- handle.writeLine("term.setTextColor(colors.white);");
- handle.writeLine("term.clear();");
- handle.writeLine("term.setCursorPos(2,5);");
- handle.writeLine("term.write(\"[X] key to quit parkour\");");
- handle.writeLine("term.setCursorPos(2,6);");
- handle.writeLine("term.write(\"[C] key to cycle stats\");");
- handle.writeLine("while true do");
- handle.writeLine(" local ev={os.pullEventRaw()};");
- handle.writeLine(" if ev[1]==\"key\" then");
- handle.writeLine(" if ev[2]==keys.x then");
- handle.writeLine(" fs.open(\"/stopvirtualparkournow.idequalsvirtualparkour\",\"w\").close();");
- handle.writeLine(" os.shutdown();");
- handle.writeLine(" elseif ev[2]==keys.c then");
- handle.writeLine(" fs.open(\"/cyclethroughtheobjectives.idequalsvirtualparkour\",\"w\").close();")
- handle.writeLine(" end");
- handle.writeLine(" end");
- handle.writeLine("end");
- handle.close();
- --for i=1,playerCount do
- -- commands.give(selectorFor(i).."] ComputerCraft:pocketComputer 1 1 {display:{Name:Parkour Manager},computerID:"..os.computerID().."}");
- --end
- --sleep(0.1);
- reallyStartParkour=function()
- if realMode==2 then cycleSidebar(1);
- else cycleSidebar(0); end
- for i=1,playerCount do
- local selector=selectorFor(i);
- commands.async.tellraw(selector.."] {text:\"Parkour Started\",color:gray}");
- if realMode==1 then
- commands.async.tellraw(selector.."] {text:\"In freeplay you can jump freely through the parkour\",color:green}");
- --commands.async.tellraw(selector.."] {text:\"Also check your brand new Parkour Manager\",color:gray}");
- elseif realMode==2 then
- commands.async.tellraw(selector.."] {text:\"Race to the end of this parkour, quickly!\",color:green}");
- --commands.async.tellraw(selector.."] {text:\"You can use your brand new Parkour Manager to quit the parkour\",color:gray}");
- elseif realMode==3 then
- commands.async.tellraw(selector.."] {text:\"You must get to the finish dying as little as possible\",color:green}");
- --commands.async.tellraw(selector.."] {text:\"You can use your brand new Parkour Manager to check your stats\",color:gray}");
- end
- end
- end
- if confiscateItems then
- for i=1,playerCount do
- commands.async.tellraw(selectorFor(i).."] {text:\"IMPORTANT:\",color:red}");
- commands.async.tellraw(selectorFor(i).."] [{text:\"Deposit \"},{text:\"ALL\",bold:true},{text:\" your items in the chest, then click the button.\"}]");
- commands.async.tellraw(selectorFor(i).."] [{text:\"Any item in your inventory after this will be \"},{text:\"destroyed\",color:red},\".\"]");
- commands.async.tellraw(selectorFor(i).."] \"After 30 seconds the button will be pushed automatically.\"");
- end
- end
- local baseOffset=math.random(-5,5)-math.floor(playerCount/2)*40;
- local takingItems=false;
- function quitPlayer(id)
- local pd=playerData[id];
- for i,struct in ipairs(pd.current) do
- pcall(struct.func,"death",struct);
- end
- deleteStructure(pd.previous);
- deleteCheckpoint(pd.previous);
- for i,struct in ipairs(pd.current) do
- deleteStructure(struct);
- if i==#pd.current then
- deleteCheckpoint(struct);
- end
- end
- local selector=selectorFor(id);
- commands.tp(selector.."]",pos[1],pos[2]+1,pos[3]);
- if pd.oldMode then
- commands.async.gamemode(pd.oldMode,selector.."]");
- end
- commands.async.effect(selector.."] clear");
- end
- function reallyQuitParkour(dontQuitPlayers)
- fs.delete("/startup");
- pcall(function() fs.move("/startupbackup.donteverusethisname.idequalsvirtualparkour","/startup") end);
- --Double negation FTW!
- if not dontQuitPlayers then
- for id=1,playerCount do
- local ok,err=pcall(quitPlayer,id);
- if not ok then
- print("Error quitting player #"..id..":");
- printError(tostring(err));
- print("But continuing on. Anyways, this isn't the first, right?");
- end
- end
- end
- commands.async.scoreboard("objectives setdisplay sidebar");
- commands.async.scoreboard("objectives remove parkour_deaths");
- commands.async.scoreboard("objectives remove parkour_checks");
- commands.async.scoreboard("objectives remove parkour_time");
- commands.async.scoreboard("objectives remove parkour_number");
- commands.async.scoreboard("objectives remove parkour_id");
- if turnOnCBO then
- commands.gamerule("commandBlockOutput true");
- end
- print("Quitted Parkour");
- end
- function startQuittingParkour()
- for id=1,playerCount do
- local pd=playerData[id];
- for i,struct in ipairs(pd.current) do
- pcall(struct.func,"death",struct);
- end
- if pd.currentStructset>2 then
- deleteStructure(pd.previous);
- deleteCheckpoint(pd.previous);
- end
- for i,struct in ipairs(pd.current) do
- deleteStructure(struct);
- if i==#pd.current then
- deleteCheckpoint(struct);
- end
- end
- local selector=selectorFor(id);
- commands.async.clear(selector.."]");
- pd.currentStructset=1;
- pd.current={pd.itemDeposit};
- getNextStructset(id);
- pd.readyToGo=false;
- pd.nowQuitting=true;
- if confiscateItems then pd.previous.func("checkpoint",pd.previous); end
- end
- if confiscateItems then
- takingItems=true;
- else
- reallyQuitParkour();
- end
- return not confiscateItems;
- end
- local shouldQuitNow=false;
- function mainLoop()
- genNewStructset(structures.itemDeposit);
- genNewStructset(structures.first);
- local parkourID=0;
- while true do
- parkourID=parkourID+1;
- if parkourID>playerCount then
- if not takingItems then
- local allF=true;
- for i=1,playerCount do
- if not playerData[i].finished then allF=false;break; end
- end
- if allF then error("All players have finished"); end
- end
- parkourID=1;
- end
- local pd=playerData[parkourID];
- if pd.initialized then
- if fs.exists("/stopvirtualparkournow.idequalsvirtualparkour") or shouldQuitNow then
- shouldQuitNow=false;
- fs.delete("/stopvirtualparkournow.idequalsvirtualparkour");
- if startQuittingParkour() then break; end
- elseif fs.exists("/cyclethroughtheobjectives.idequalsvirtualparkour") then
- fs.delete("/cyclethroughtheobjectives.idequalsvirtualparkour");
- cycleSidebar();
- end
- else
- pd.xOffset=baseOffset+(parkourID-1)*40;
- pd.selector=selectorFor(parkourID).."]";
- pd.incompleteSelector=selectorFor(parkourID)..",";
- pd.previous={blocks={},exit=pos,func=structures.first};
- pd.currentIndex=1;
- pd.current={{blocks={},exit=pos,func=structures.first,next="z+"}};
- pd.currentStructset=0;
- getNextStructset(parkourID);
- getNextStructset(parkourID);
- if not pd.previous.func("checkpoint",pd.previous) then
- commands.tp(pd.selector,pd.previous.exit[1],pd.previous.exit[2]+1,pd.previous.exit[3]);
- end
- pd.resistanceCounter=1000000;
- pd.wasOnCheckpoint=false;
- pd.initialized=true;
- end
- if takingItems then
- if pd.current[1].areaCheck[1][5](pd.current[1],1) then
- break;
- end
- sleep(0.1);
- elseif pd.finished then
- local s=pd.previous;
- local ret=s.func("check_exit",s);
- if (type(ret)=="boolean" and not ret)
- 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
- if not s.func("checkpoint",s) then
- commands.async.tp(pd.selector,s.exit[1],s.exit[2]+1,s.exit[3]);
- end
- end
- else
- --Check if player is inside any of the structure's area, else TP to the previous exit
- local out=#pd.current[pd.currentIndex].areaCheck>0;
- function checkStructure(struct)
- for i,area in ipairs(struct.areaCheck) do
- if commands.testfor(pd.incompleteSelector.."x="..area[1]..",y="..area[2]..",z="..area[3]..",r="..area[4].."]") then
- out=false;
- if type(area[5])=="function" then
- area[5](struct,i);
- else
- break; --Since non-functional detectors are (reorganized by genNewStructset) after functional ones, we can quit safely
- end
- end
- if i%500==0 then sleep(0.05); end
- end
- end
- --First check current structure
- checkStructure(pd.current[pd.currentIndex]);
- if out then
- --If it isn't there, check following structures
- for i=pd.currentIndex+1,#pd.current do
- checkStructure(pd.current[i]);
- if not out then
- pd.currentIndex=i;
- break;
- end
- end
- if out then
- --And if it still isn't found, check structures before
- for i=1,pd.currentIndex-1 do
- checkStructure(pd.current[i]);
- if not out then
- pd.currentIndex=i;
- break;
- end
- end
- end
- end
- if out then
- if not pd.previous.func("checkpoint",pd.previous) then
- commands.async.tp(pd.selector,pd.previous.exit[1],(pd.previous.exit[2]+1),pd.previous.exit[3]);
- end
- commands.async.tellraw(pd.selector.." {text:\"You died.\",color:red}");
- pd.deaths=pd.deaths+1;
- updateScoreboard(parkourID);
- for i=1,#pd.current do
- pd.current[i].func("death",pd.current[i]);
- end
- pd.currentIndex=1;
- end
- --Testfor current structure's exit
- if pd.currentIndex<#pd.current then
- local ret=pd.current[pd.currentIndex].func("check_exit",pd.current[pd.currentIndex]);
- if type(ret)=="boolean" then
- if ret then
- pd.currentIndex=pd.currentIndex+1;
- end
- else
- 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
- pd.currentIndex=pd.currentIndex+1;
- end
- end
- end
- --Testfor current structset's exit
- local ret=pd.current[#pd.current].func("check_exit",pd.current[#pd.current]);
- if (type(ret)=="boolean" and ret)
- 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
- if pd.wasOnCheckpoint then
- pd.checkpoints=pd.checkpoints+1;
- updateScoreboard(parkourID);
- getNextStructset(parkourID);
- pd.currentIndex=1;
- if realMode==1 or pd.checkpoints<checkpointGoal then
- if pd.currentStructset>3 then commands.async.tellraw(pd.selector.." {text:\"Checkpoint!\",color:yellow}"); end
- else
- pd.finished=true;
- local finishCount=0;
- for i=1,playerCount do
- if playerData[i].finished then finishCount=finishCount+1; end
- end
- if realMode==2 then
- for i=1,playerCount do
- if i==parkourID then
- local color="white";
- if finishCount==1 then playerData.winner=parkourID;color="green";
- elseif finishCount==2 then color="yellow";
- elseif finishCount==3 then color="gold";
- end
- commands.async.tellraw(pd.selector.." {text:\"You are #"..finishCount.." in the race\",color:"..color.."}");
- else
- commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..parkourID.." is #"..finishCount.." in the race\"}");
- end
- end
- elseif realMode==3 then
- for i=1,playerCount do
- if i==parkourID then
- commands.async.tellraw(pd.selector.." {text:\"You've got to the end dying "..pd.deaths.." times\"}");
- else
- commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..parkourID.." got to the end dying "..pd.deaths.." times\"}");
- end
- end
- end
- if finishCount>=playerCount then
- if realMode==2 then
- for i=1,playerCount do
- commands.async.tellraw(playerData[i].selector..
- " {text:\"The race has ended, player #"..playerData.winner.." has won in "..playerData[playerData.winner].time.." seconds\"}");
- end
- sleep(2);
- if startQuittingParkour() then break; end
- elseif realMode==3 then
- local posFound=0;
- local positions={};
- local lastBest=-1;
- local lastPos=1;
- local lastRealPos=1;
- while posFound<playerCount do
- local ld=-1;
- local nextID=0;
- for i,opd in ipairs(playerData) do
- if not opd.checkedPos and opd.deaths>=lastBest and (opd.deaths<ld or ld==-1) then
- nextID=i;
- ld=opd.deaths;
- end
- end
- if ld>lastBest then lastRealPos=lastPos; end
- lastPos=lastPos+1;
- posFound=posFound+1;
- lastBest=ld;
- positions[#positions+1]={lastRealPos,nextID};
- playerData[nextID].checkedPos=true;
- end
- for i=1,playerCount do
- commands.async.tellraw(playerData[i].selector.." {text:\"The game has ended, positions are:\"}");
- for id,pos in ipairs(positions) do
- if pos[1]==1 then commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..pos[2]..": 1st place\",color:green}");
- elseif pos[1]==2 then commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..pos[2]..": 2nd place\",color:yellow}");
- elseif pos[1]==3 then commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..pos[2]..": 3rd place\",color:gold}");
- else commands.async.tellraw(playerData[i].selector.." {text:\"Player #"..pos[2]..": "..pos[1].."th place\",color:gray}");
- end
- end
- end
- sleep(2);
- if startQuittingParkour() then break; end
- end
- end
- end
- end
- pd.wasOnCheckpoint=true;
- else
- pd.wasOnCheckpoint=false;
- end
- --[[if commands.testfor(incompleteSelector.."x="..current[#current].exit[1]..",y="..(current[#current].exit[2]+1)..",z="..current[#current].exit[3]..",r=1]") then
- if wasOnCheckpoint then
- genNextStructset();
- currentIndex=1;
- commands.async.tellraw(selector.." {text:\"Checkpoint!\",color:yellow}");
- end
- wasOnCheckpoint=true;
- else
- wasOnCheckpoint=false;
- end]]--
- --Give resistance if some time has passed
- if pd.resistanceCounter<10 then
- pd.resistanceCounter=pd.resistanceCounter+1;
- else
- pd.resistanceCounter=0;
- commands.async.effect(pd.selector.." 11 3600 255");
- commands.async.effect(pd.selector.." 23 3600");
- end
- end
- end
- end
- local allOk=true;
- --[[local coroutines={};
- local loopError=false;
- for id=1,playerCount do
- local c=coroutine.create(playerLoop);
- local quitNext=false;
- local ev={id};
- while true do
- local ok;
- ok,loopError=coroutine.resume(c,unpack(ev));
- if ok then
- if quitNext then
- loopError=false;
- break;
- elseif loopError=="initialized" then
- quitNext=true;
- os.queueEvent("continue");
- end
- loopError=false;
- else
- break;
- end
- ev={os.pullEvent()};
- end
- coroutines[id]=c;
- end
- if not loopError then
- local periodicTimer=-1;
- print("Loops started, press Q to quit");
- while true do
- for id,c in ipairs(coroutines) do
- if fs.exists("/stopvirtualparkournow.idequalsvirtualparkour") then
- fs.delete("/stopvirtualparkournow.idequalsvirtualparkour");
- loopError="parkour.exiting.ok";
- break;
- end
- local ev={"parkour_heartbeat"};
- while true do
- if ev[1]=="key" and ev[2]==keys.q then
- loopError="parkour.exiting.ok";
- break;
- end
- local ok;
- ok,loopError=coroutine.resume(c,unpack(ev));
- if ok then
- if loopError=="heartbeat_done" then
- coroutine.resume(c);
- loopError=false;
- break;
- end
- loopError=false;
- else
- break;
- end
- ev={os.pullEvent()};
- end
- if loopError then break; end
- end
- if loopError then break; end
- end
- end
- if loopError and loopError~="parkour.exiting.ok" then
- for i=1,playerCount do
- commands.async.tellraw(selectorFor(i).."] {text:\"Error in Parkour\",color:dark_red}");
- end
- print();
- print("Error in player loop:");
- printError(loopError);
- allOk=false;
- end]]
- local parkourTimer=os.startTimer(0);
- function miscTasks()
- while true do
- local ev={os.pullEventRaw()};
- if ev[1]=="key" and ev[2]==keys.q then
- shouldQuitNow=true;
- elseif ev[1]=="timer" and ev[2]==parkourTimer then
- parkourTimer=os.startTimer(1);
- for i=1,playerCount do
- local pd=playerData[i];
- if not pd.finished then
- pd.time=pd.time+1;
- if sidebarMode==2 then updateScoreboard(i); end
- end
- end
- end
- end
- end
- local miscCoroutine=coroutine.create(miscTasks);
- local mainCoroutine=coroutine.create(mainLoop);
- local ok,err;
- print("Press [Q] to quit parkour");
- while true do
- local ev={os.pullEventRaw()};
- local ok1,err1=coroutine.resume(miscCoroutine,unpack(ev));
- ok,err=coroutine.resume(mainCoroutine,unpack(ev));
- if coroutine.status(mainCoroutine)=="dead" then
- break;
- elseif coroutine.status(miscCoroutine)=="dead" then
- ok=ok1;
- err=err1;
- break;
- end
- end
- if not ok then
- for i=1,playerCount do
- commands.async.tellraw(selectorFor(i).."] {text:\"Error in Parkour\",color:dark_red}");
- end
- print();
- print("Error in main loop:");
- printError(tostring(err));
- if monitor then
- monitor.setTextColor(colors.red);
- monPrint("Error in mainLoop:");
- monPrint(tostring(err));
- monitor.setTextColor(colors.white);
- end
- allOk=false;
- end
- local ok2,err=pcall(reallyQuitParkour,ok);
- if not ok2 then
- if not ok then
- print("Guess what? Quitting parkour errored too!");
- printError(err);
- else
- print();
- print("Error quitting parkour:");
- printError(err);
- end
- if monitor then
- monitor.setTextColor(colors.red);
- monPrint("");
- monPrint("Error quitting parkour:");
- monPrint(tostring(err));
- monitor.setTextColor(colors.white);
- end
- allOk=false;
- end
- if not allOk then
- print("Read the errors, then press a key");
- if fromMonitor then
- monPrint("Read the errors, then touch anywhere");
- end
- while true do
- local ev={os.pullEvent()};
- if ev[1]=="key" or ev[1]=="mouse_click" or (fromMonitor and ev[1]=="monitor_touch" and ev[2]==monitorName) then
- break;
- end
- end
- if not fromMonitor then sleep(0.1);error(); end
- end
- end
- while true do
- local ok,err=pcall(superLoop);
- if not ok then
- if err then
- term.setBackgroundColor(colors.black);
- term.clear();
- term.setCursorPos(1,1);
- term.setTextColor(colors.red);
- print("Error in pre-parkour:");
- print(tostring(err));
- end
- break;
- end
- end
Add Comment
Please, Sign In to add comment