mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-31 13:42:59 +00:00 
			
		
		
		
	Delete existing treasure disks
This commit is contained in:
		| @@ -1,875 +0,0 @@ | ||||
| --[[ | ||||
| battleship, | ||||
|  | ||||
| by GopherAtl, 2013 | ||||
|  | ||||
| Do whatever you want, just don't judge me by | ||||
| what a mess this code is. | ||||
| --]] | ||||
| local args={...} | ||||
| local action=args[1] | ||||
| local opponentID=nil | ||||
| local openedSide=nil | ||||
| local opponent=nil | ||||
| local myName="" | ||||
| local opponentReady=false | ||||
| local myTurn | ||||
| local targetX,targetY | ||||
| local shipsLeft=5 | ||||
| local oppShipsLeft=5 | ||||
|  | ||||
| local originalTerm = term.current() | ||||
|  | ||||
| --bounding box of the target grid | ||||
| local targetGridBounds={ | ||||
|     minX=16, maxX=25, | ||||
|     minY=4, maxY=13 | ||||
|   } | ||||
|  | ||||
|  | ||||
| local function doColor(text,background) | ||||
|   term.setTextColor(text) | ||||
|   term.setBackgroundColor(background) | ||||
| end | ||||
|  | ||||
| local function doColor_mono(text,background) | ||||
|   if text==colors.blue or text==colors.red or text==colors.black or text==colors.lime or background==colors.lightGray then | ||||
|     term.setTextColor(colors.black) | ||||
|     term.setBackgroundColor(colors.white) | ||||
|   else | ||||
|     term.setTextColor(colors.white) | ||||
|     term.setBackgroundColor(colors.black) | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function doScreenColor() | ||||
|   if term.isColor() then | ||||
|     doColor(colors.white,colors.lightGray) | ||||
|   else | ||||
|     doColor(colors.black,colors.white) | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function toGridRef(x,y) | ||||
|   return string.sub("ABCDEFGHIJ",x,x)..string.sub("1234567890",y,y) | ||||
| end | ||||
|  | ||||
|  | ||||
| if not term.isColor() then | ||||
|   doColor=doColor_mono | ||||
| end | ||||
|  | ||||
| local function quit() | ||||
|   if openedSide then | ||||
|     rednet.close(openedSide) | ||||
|   end | ||||
|   term.redirect( originalTerm ) | ||||
|   term.setCursorPos(term.getSize()) | ||||
|   print() | ||||
|   error() | ||||
| end | ||||
|  | ||||
| local foundModem=false | ||||
| --find modem | ||||
| for k,v in pairs(redstone.getSides()) do | ||||
|   if peripheral.getType(v)=="modem" then | ||||
|     foundModem=true | ||||
|     if not rednet.isOpen(v) then | ||||
|       rednet.open(v) | ||||
|       openedSide=v | ||||
|     end | ||||
|     break | ||||
|   end | ||||
| end | ||||
|  | ||||
| if not foundModem then | ||||
|   print("You must have a modem to play!") | ||||
|   return | ||||
| end | ||||
|  | ||||
| if action==nil or (action~="join" and action~="host") then | ||||
|   print("Invalid parameters. Usage:\n> battleship host\nHosts a game, waits for another computer to join\n> battleship join\nLooks for another game to join") | ||||
|   quit() | ||||
| end | ||||
|  | ||||
| --get player name | ||||
| while true do | ||||
|   doColor(colors.cyan,colors.black) | ||||
|   write("player name: ") | ||||
|   doColor(colors.gray,colors.black) | ||||
|   myName=read() | ||||
|   if myName=="" then | ||||
|     doColor(colors.red,colors.black) | ||||
|     print("You have to give a name!") | ||||
|   elseif #myName>11 then | ||||
|     doColor(colors.red,colors.black) | ||||
|     print("Max name is 11 characters!") | ||||
|   else | ||||
|     break | ||||
|   end | ||||
| end | ||||
|  | ||||
| if action=="join" then | ||||
|   print("Attempting to join a game...\n(press q to cancel)") | ||||
|   while true do | ||||
|     local retryTimer=os.startTimer(1); | ||||
|     rednet.broadcast("bs join "..myName); | ||||
|  | ||||
|     while true do | ||||
|       local event,p1,p2,p3=os.pullEvent(); | ||||
|       if event=="rednet_message" then | ||||
|         opponent=string.match(p2,"bs accept %s*(.+)%s*") | ||||
|         if opponent then | ||||
|           opponentID=p1 | ||||
|           break | ||||
|         end | ||||
|       elseif event=="timer" and p1==retryTimer then | ||||
|         break | ||||
|       elseif event=="char" and (p1=="q" or p1=="Q") then | ||||
|         print("Couldn't find an opponent; quitting") | ||||
|         quit() | ||||
|       end | ||||
|     end | ||||
|     local joined=false | ||||
|  | ||||
|     if opponentID then | ||||
|       print("Joining game!") | ||||
|       rednet.send(opponentID,"bs start") | ||||
|       break | ||||
|     end | ||||
|   end | ||||
| elseif action=="host" then | ||||
|   print("Waiting for challenger...\n(Press q to cancel)") | ||||
|   while true do | ||||
|     while true do | ||||
|       local event,p1,p2=os.pullEvent() | ||||
|       if event=="rednet_message" then | ||||
|         opponent=string.match(p2,"bs join %s*(.+)%s*")        if opponent then | ||||
|           print("found player, inviting..") | ||||
|           opponentID=p1 | ||||
|           break | ||||
|         end | ||||
|       elseif event=="char" and (p1=="q" or p1=="Q") then | ||||
|         print("Couldn't find opponent, quitting") | ||||
|         quit() | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     if opponentID then | ||||
|       rednet.send(opponentID,"bs accept "..myName) | ||||
|       local timeout=os.startTimer(1) | ||||
|       while true do | ||||
|         local event,p1,p2=os.pullEvent() | ||||
|         if event=="rednet_message" and p2=="bs start" then | ||||
|           print("player joined!") | ||||
|           break | ||||
|         elseif event=="timer" and p1==timeout then | ||||
|           print("player joined another game. Waiting for another...") | ||||
|           opponentID=nil | ||||
|           break | ||||
|         end | ||||
|       end | ||||
|  | ||||
|       if opponentID then | ||||
|         break | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | ||||
| local ships={ | ||||
|   {pos=nil,dir="h",size=5,name="carrier",hits=0}, | ||||
|   {pos=nil,dir="h",size=4,name="battleship",hits=0}, | ||||
|   {pos=nil,dir="h",size=3,name="cruiser",hits=0}, | ||||
|   {pos=nil,dir="h",size=3,name="submarine",hits=0}, | ||||
|   {pos=nil,dir="h",size=2,name="destroyer",hits=0}, | ||||
| } | ||||
|  | ||||
| local myShotTable={ {1,1,true},{5,5,false} } | ||||
| local oppShotTable={ } | ||||
|  | ||||
| local myGrid,oppGrid={title=myName},{title=opponent} | ||||
|  | ||||
| --setup grids | ||||
| for i=1,10 do | ||||
|   myGrid[i]={} | ||||
|   oppGrid[i]={} | ||||
|   for j=1,10 do | ||||
|     myGrid[i][j]={hit=false,ship=false} | ||||
|     oppGrid[i][j]={hit=false,ship=false} | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function drawShipsToGrid(ships,grid) | ||||
|   for i=1,#ships do | ||||
|     local x,y=table.unpack(ships[i].pos) | ||||
|     local stepX=ships[i].dir=="h" and 1 or 0 | ||||
|     local stepY=stepX==1 and 0 or 1 | ||||
|     for j=1,ships[i].size do | ||||
|       grid[x][y].ship=i | ||||
|       x,y=x+stepX,y+stepY | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function drawShotToGrid(shot,grid) | ||||
|   grid[shot[1]][shot[2]].shot=true | ||||
|   grid[shot[1]][shot[2]].hit=shot[3] | ||||
| end | ||||
|  | ||||
| local function makeShot(x,y,grid) | ||||
|   local tile=grid[x][y] | ||||
|   if tile.shot==true then | ||||
|     return nil --already shot here! | ||||
|   end | ||||
|  | ||||
|   local shot={x,y,tile.ship} | ||||
|   drawShotToGrid(shot,grid) | ||||
|   if tile.ship then | ||||
|     ships[tile.ship].hits=ships[tile.ship].hits+1 | ||||
|     if ships[tile.ship].hits==ships[tile.ship].size then | ||||
|       os.queueEvent("shipsunk",tile.ship) | ||||
|     end | ||||
|   end | ||||
|   return shot | ||||
| end | ||||
|  | ||||
|  | ||||
| local function drawTile(scrX,scrY,tile) | ||||
|   term.setCursorPos(scrX,scrY) | ||||
|  | ||||
|   if tile.ship then | ||||
|     if tile.shot then | ||||
|       doColor(colors.red,colors.gray) | ||||
|       term.write("@") | ||||
|     else | ||||
|       doColor(colors.white,colors.gray) | ||||
|       term.write("O") | ||||
|     end | ||||
|   else | ||||
|     if tile.hit then | ||||
|       doColor(colors.red,colors.gray) | ||||
|       term.write("x") | ||||
|     elseif tile.shot then | ||||
|       doColor(colors.white,colors.lightBlue) | ||||
|       term.write(".") | ||||
|     else | ||||
|       doColor(colors.white,colors.lightBlue) | ||||
|       term.write(" ") | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function drawGrid(scrX,scrY,grid) | ||||
|   doColor(colors.white,colors.black) | ||||
|   term.setCursorPos(scrX,scrY+1) | ||||
|   term.write(" ") | ||||
|   doColor(colors.white,colors.gray) | ||||
|   term.setCursorPos(scrX,scrY) | ||||
|   local pad=11-#grid.title | ||||
|   term.write(string.rep(" ",math.ceil(pad/2))..grid.title..string.rep(" ",math.floor(pad/2))) | ||||
|  | ||||
|   for gx=1,10 do | ||||
|     term.setTextColor(colors.white) | ||||
|     term.setBackgroundColor(colors.black) | ||||
|     term.setCursorPos(scrX+gx,scrY+1) | ||||
|     term.write(gx==10 and "0" or string.char(string.byte("0")+gx)) | ||||
|  | ||||
|     term.setCursorPos(scrX,scrY+gx+1) | ||||
|     term.write(string.char(string.byte("A")+gx-1)) | ||||
|     for gy=1,10 do | ||||
|       drawTile(scrX+gx,scrY+gy+1,grid[gx][gy]) | ||||
|     end | ||||
|   end | ||||
|   doColor(colors.white,colors.black) | ||||
| end | ||||
|  | ||||
| function moveTargetIndicator(newX,newY) | ||||
|   --if x has changed... | ||||
|   if targetX and targetY then | ||||
|     drawTile(targetX+targetGridBounds.minX-1,targetY+targetGridBounds.minY-1,oppGrid[targetX][targetY]) | ||||
|   end | ||||
|   doColor(colors.yellow,colors.lightGray) | ||||
|   if newX~=targetX then | ||||
|     --space over old | ||||
|     if targetX then | ||||
|       term.setCursorPos(targetGridBounds.minX+targetX-1,targetGridBounds.maxY+1) | ||||
|       term.write(" ") | ||||
|       term.setCursorPos(targetGridBounds.minX+targetX-1,targetGridBounds.minY-3) | ||||
|       term.write(" ") | ||||
|     end | ||||
|     --draw new | ||||
|     term.setCursorPos(targetGridBounds.minX+newX-1,targetGridBounds.maxY+1) | ||||
|     term.write("^") | ||||
|     term.setCursorPos(targetGridBounds.minX+newX-1,targetGridBounds.minY-3) | ||||
|     term.write("v") | ||||
|  | ||||
|     targetX=newX | ||||
|   end | ||||
|   if newY~=targetY then | ||||
|     --space over old | ||||
|     if targetY then | ||||
|       term.setCursorPos(targetGridBounds.maxX+1,targetGridBounds.minY+targetY-1) | ||||
|       term.write(" ") | ||||
|       term.setCursorPos(targetGridBounds.minX-2,targetGridBounds.minY+targetY-1) | ||||
|       term.write(" ") | ||||
|     end | ||||
|     --draw new | ||||
|     term.setCursorPos(targetGridBounds.maxX+1,targetGridBounds.minY+newY-1) | ||||
|     term.write("<") | ||||
|     term.setCursorPos(targetGridBounds.minX-2,targetGridBounds.minY+newY-1) | ||||
|     term.write(">") | ||||
|  | ||||
|     targetY=newY | ||||
|   end | ||||
|   term.setCursorPos(15,15) | ||||
|   term.write("Target : "..toGridRef(targetX,targetY)) | ||||
|   --if the target tile is a valid target, draw a "+" | ||||
|   if not oppGrid[targetX][targetY].shot then | ||||
|     term.setCursorPos(targetX+targetGridBounds.minX-1,targetY+targetGridBounds.minY-1) | ||||
|     doColor(colors.yellow,colors.lightBlue) | ||||
|     term.write("+") | ||||
|   end | ||||
| end | ||||
|  | ||||
| local log={} | ||||
|  | ||||
| local termWidth,termHeight=term.getSize() | ||||
|  | ||||
| local logHeight=termHeight-3 | ||||
| local logWidth=termWidth-28 | ||||
|  | ||||
| for i=1,logHeight do | ||||
|   log[i]="" | ||||
| end | ||||
|  | ||||
| local function printLog() | ||||
|   doColor(colors.white,colors.black) | ||||
|   for i=1,logHeight do | ||||
|     term.setCursorPos(28,1+i) | ||||
|     local name,line=string.match(log[i],"(<[^>]+> )(.*)") | ||||
|     if name then | ||||
|       doColor(colors.lightBlue,colors.black) | ||||
|       write(name) | ||||
|       doColor(colors.white,colors.black) | ||||
|       write(line..string.rep(" ",logWidth-#log[i])) | ||||
|     else | ||||
|       write(log[i]..string.rep(" ",logWidth-#log[i])) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | ||||
|  | ||||
|  | ||||
| --shipX/Y are the position of ship on grid; gridX/Y are the offset of the top-left of grid | ||||
| local function drawShip(size,align,x,y,char) | ||||
|   local stepX=align=="h" and 1 or 0 | ||||
|   local stepY=stepX==1 and 0 or 1 | ||||
|   for j=1,size do | ||||
|     term.setCursorPos(x,y) | ||||
|     term.write(char) | ||||
|     x,y=x+stepX,y+stepY | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function setStatusLine(lineNum,text) | ||||
|   doScreenColor() | ||||
|   local pad=math.floor((termWidth-#text)/2) | ||||
|   term.setCursorPos(1,16+lineNum) | ||||
|   term.write((" "):rep(pad)..text..(" "):rep(termWidth-#text-pad)) | ||||
| end | ||||
|  | ||||
|  | ||||
| doScreenColor() | ||||
| term.clear() | ||||
|  | ||||
| drawGrid(2,2,myGrid) | ||||
|  | ||||
| setStatusLine(1,"Started game with "..opponent.." at computer #"..(opponentID or "nil")) | ||||
|  | ||||
| local function getShipBounds(ship) | ||||
|   return { | ||||
|      minX=ship.pos[1], | ||||
|      minY=ship.pos[2], | ||||
|      maxX=ship.pos[1]+(ship.dir=="h" and ship.size-1 or 0), | ||||
|      maxY=ship.pos[2]+(ship.dir=="v" and ship.size-1 or 0) | ||||
|    } | ||||
| end | ||||
|  | ||||
| local function getPointBounds(x,y) | ||||
|   return { | ||||
|     minX=x, | ||||
|     minY=y, | ||||
|     maxX=x, | ||||
|     maxY=y, | ||||
|   } | ||||
| end | ||||
|  | ||||
| local function boundsIntersect(boundsA,boundsB) | ||||
|   return not ( | ||||
|       boundsA.minX>boundsB.maxX or | ||||
|       boundsA.maxX<boundsB.minX or | ||||
|       boundsA.minY>boundsB.maxY or | ||||
|       boundsA.maxY<boundsB.minY | ||||
|     ) | ||||
| end | ||||
|  | ||||
|  | ||||
| local function checkShipCollision(shipIndex) | ||||
|   local myBounds=getShipBounds(ships[shipIndex]) | ||||
|   for i=1,#ships do | ||||
|     if i~=shipIndex and ships[i].pos then | ||||
|       if boundsIntersect(myBounds,getShipBounds(ships[i])) then | ||||
|         return i | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|   return 0 | ||||
| end | ||||
|  | ||||
|  | ||||
|  | ||||
| local function randomizeShips() | ||||
|   for i=1,5 do | ||||
|     ships[i].pos=nil | ||||
|   end | ||||
|   for i=1,5 do | ||||
|     local ship=ships[i] | ||||
|     local dir | ||||
|     local x,y | ||||
|     repeat | ||||
|       --random orientation | ||||
|       dir=math.random(2)==1 and "v" or "h" | ||||
|       --random position | ||||
|       x = math.random(dir=="v" and 10 or (10-ship.size)) | ||||
|       y = math.random(dir=="h" and 10 or (10-ship.size)) | ||||
|       ship.pos={x,y} | ||||
|       ship.dir=dir | ||||
|     until checkShipCollision(i)==0 | ||||
|   end | ||||
| end | ||||
|  | ||||
|  | ||||
|  | ||||
| local function shipPlacement() | ||||
|   local selection=1 | ||||
|   local collidesWith=0 | ||||
|   local dragging=false | ||||
|   local moveShip=nil | ||||
|   local clickedOn=nil | ||||
|   local clickedAt=nil | ||||
|  | ||||
|   doScreenColor() | ||||
|   term.setCursorPos(28,3) | ||||
|   write("use arrows to move ship") | ||||
|   term.setCursorPos(28,4) | ||||
|   write("press space to rotate") | ||||
|   term.setCursorPos(28,5) | ||||
|   write("tab selects next ship") | ||||
|   if term.isColor() then | ||||
|     term.setCursorPos(28,6) | ||||
|     write("click and drag ships") | ||||
|     term.setCursorPos(28,7) | ||||
|     write("right-click ship to") | ||||
|     term.setCursorPos(28,8) | ||||
|     write("  rotate") | ||||
|   end | ||||
|   term.setCursorPos(28,9) | ||||
|   write('"r" to randomize ships') | ||||
|   term.setCursorPos(28,10) | ||||
|   write('"f" when finished') | ||||
|   randomizeShips() | ||||
|   setStatusLine(1,"Arrange your ships on the grid") | ||||
|  | ||||
|   while true do | ||||
|     --local placed=0 | ||||
|     --draw sea | ||||
|     doColor(colors.white,colors.lightBlue) | ||||
|     for i=1,10 do | ||||
|       term.setCursorPos(3,3+i) | ||||
|       term.write("          ") | ||||
|     end | ||||
|     --draw ships | ||||
|     for i=1,#ships do | ||||
|      --draw ship at sea if it's placed | ||||
|       if ships[i].pos then | ||||
|         if collidesWith~=0 and (collidesWith==i or selection==i) then | ||||
|           doColor(selection==i and colors.red or colors.pink,colors.gray) | ||||
|           drawShip(ships[i].size,ships[i].dir,2+ships[i].pos[1],3+ships[i].pos[2],"@") | ||||
|         else | ||||
|           doColor(selection==i and colors.lime or colors.white,colors.gray) | ||||
|           drawShip(ships[i].size,ships[i].dir,2+ships[i].pos[1],3+ships[i].pos[2],"O") | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     local event,p1,p2,p3=os.pullEvent() | ||||
|     if event=="key" then | ||||
|       if not dragging then | ||||
|         if p1==keys.tab then | ||||
|           if collidesWith==0 then | ||||
|             selection=(selection%5)+1 | ||||
|           else | ||||
|             local t=selection | ||||
|             selection=collidesWith | ||||
|             collidesWith=t | ||||
|           end | ||||
|         elseif p1==keys.up then | ||||
|           moveShip={0,-1} | ||||
|         elseif p1==keys.down then | ||||
|           moveShip={0,1} | ||||
|         elseif p1==keys.left then | ||||
|           moveShip={-1,0} | ||||
|         elseif p1==keys.right then | ||||
|           moveShip={1,0} | ||||
|         elseif p1==keys.space then | ||||
|           moveShip={0,0} | ||||
|           ships[selection].dir=ships[selection].dir=="h" and "v" or "h" | ||||
|         elseif p1==keys.f then | ||||
|           if collidesWith~=0 then | ||||
|             setStatusLine(2,"You can't finalize with ships overlapping!") | ||||
|           else | ||||
|             break | ||||
|           end | ||||
|         elseif p1==keys.r then | ||||
|           randomizeShips(); | ||||
|         end | ||||
|       end | ||||
|     elseif event=="mouse_click" then | ||||
|       clickedOn=nil | ||||
|       --click event! figure out what we clicked on | ||||
|       local clickBounds=getPointBounds(p2,p3) | ||||
|       local clickGridBounds=getPointBounds(p2-2,p3-3) | ||||
|  | ||||
|       for i=1,#ships do | ||||
|         if ships[i].pos and boundsIntersect(clickGridBounds,getShipBounds(ships[i])) and | ||||
|            (collidesWith==0 or collidesWith==i or i==selection) then | ||||
|           --select it | ||||
|           --if we're switching between the colliding ships, swap selection | ||||
|           if collidesWith~=0 and i~=selection then | ||||
|             collidesWith=selection | ||||
|           end | ||||
|           --mode="place" | ||||
|           clickedOn=ships[i] | ||||
|           clickedOffset={p2-2-ships[i].pos[1],p3-3-ships[i].pos[2]} | ||||
|           selection=i | ||||
|           break | ||||
|         --[[else | ||||
|           local labelBounds={minX=15,maxX=24,minY=2*i,maxY=1+2*i} | ||||
|           if boundsIntersect(clickBounds,labelBounds) and | ||||
|              (collidesWith==0 or collidesWith==i or i==selection) then | ||||
|             if collidesWith~=0 then | ||||
|               if i~=selection then | ||||
|                 collidesWith=selection | ||||
|               end | ||||
|             else | ||||
|               mode="select" | ||||
|             end | ||||
|             clickedOn=ships[i] | ||||
|             clickedOffset={0,0} | ||||
|             selection=i | ||||
|             if ships[i].pos==nil then | ||||
|               ships[i].pos={1,1} | ||||
|               collidesWith=checkShipCollision(selection) | ||||
|               break | ||||
|             end | ||||
|           end--]] | ||||
|         end | ||||
|       end | ||||
|       if not clickedOn and collidesWith==0 and | ||||
|           boundsIntersect(clickBounds,{minX=15,maxX=22,minY=13,maxY=13}) then | ||||
|         break | ||||
|       elseif clickedOn and p1==2 then | ||||
|         --can't drag from a right-click! | ||||
|         clickedOn=nil | ||||
|         if ships[selection].dir=="h" then | ||||
|           ships[selection].dir="v" | ||||
|           moveShip={p2-2-ships[selection].pos[1],-(p2-2-ships[selection].pos[1])} | ||||
|         else | ||||
|           ships[selection].dir="h" | ||||
|           moveShip={p3-3-(ships[selection].pos[2]+ships[selection].size-1),p3-3-(ships[selection].pos[2])} | ||||
|         end | ||||
|       end | ||||
|     elseif event=="mouse_drag" and clickedOn~=nil then | ||||
|       --mode="place" | ||||
|       moveShip={ | ||||
|          p2-2-clickedOffset[1]-ships[selection].pos[1], | ||||
|          p3-3-clickedOffset[2]-ships[selection].pos[2]} | ||||
|     end | ||||
|  | ||||
|     if moveShip then | ||||
|       local curShip=ships[selection] | ||||
|       --calc position limits based on ship size and alignment | ||||
|       local maxX=curShip.dir=="h" and (11-curShip.size) or 10 | ||||
|       local maxY=curShip.dir=="v" and (11-curShip.size) or 10 | ||||
|       --apply move and clamp to limits | ||||
|       local newPos={ | ||||
|         math.min(math.max(curShip.pos[1]+moveShip[1],1),maxX), | ||||
|         math.min(math.max(curShip.pos[2]+moveShip[2],1),maxY) | ||||
|       } | ||||
|       --place the ship | ||||
|       ships[selection].pos=newPos | ||||
|       --check for collisions with other ships | ||||
|  | ||||
|       collidesWith=checkShipCollision(selection) | ||||
|       moveShip=nil | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function displayGameHelp() | ||||
|   doScreenColor() | ||||
|   term.setCursorPos(28,3) | ||||
|   write("arrows to move cursor") | ||||
|   term.setCursorPos(28,4) | ||||
|   write("space to fire") | ||||
|   if term.isColor() then | ||||
|     term.setCursorPos(28,6) | ||||
|     write("click on grid to fire") | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function hideHelpArea() | ||||
|   doScreenColor() | ||||
|   for y=3,13 do | ||||
|     term.setCursorPos(28,y) | ||||
|     write(string.rep(" ",32)) | ||||
|   end | ||||
| end | ||||
|  | ||||
|  | ||||
| local function runGame() | ||||
|  | ||||
|   --first, ship placement phase!! | ||||
|   shipPlacement() | ||||
|  | ||||
|   hideHelpArea() | ||||
|  | ||||
|   --hide the old help, draw the new | ||||
|  | ||||
|   --tell the other guy we're done | ||||
|   rednet.send(opponentID,"bs ready") | ||||
|   if not opponentReady then | ||||
|     setStatusLine(1,"Waiting for opponent to finish placing ships") | ||||
|     while not opponentReady do | ||||
|       os.pullEvent() | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   --now, play the game | ||||
|   --draw my final ship positions intto the grid | ||||
|   drawShipsToGrid(ships,myGrid) | ||||
|  | ||||
|  | ||||
|   --if I'm host, flip a coin | ||||
|   if action=="host" then | ||||
|     math.randomseed(os.time()) | ||||
|     myTurn=math.floor(100*math.random())%2==0 | ||||
|     rednet.send(opponentID,"bs cointoss "..tostring(not myTurn)) | ||||
|     if myTurn then | ||||
|       setStatusLine(2,"Your turn, take your shot!") | ||||
|     else | ||||
|       setStatusLine(2,"Opponent's turn, waiting...") | ||||
|     end | ||||
|   else | ||||
|     --I joined, wait for coin toss | ||||
|     setStatusLine(2,"waiting for coin toss...") | ||||
|     while myTurn==nil do | ||||
|       os.pullEvent() | ||||
|     end | ||||
|   end | ||||
|  | ||||
|  | ||||
|   setStatusLine(1,"") | ||||
|   if myTurn then | ||||
|     --I won, I go first | ||||
|     displayGameHelp() | ||||
|   end | ||||
|  | ||||
|   --draw a target grid | ||||
|   drawGrid(2,2,myGrid) | ||||
|   drawGrid(15,2,oppGrid) | ||||
|   --initialize target indicators | ||||
|   moveTargetIndicator(5,5) | ||||
|   --game turn loop | ||||
|   while true do | ||||
|     --wait for my turn | ||||
|     while not myTurn do | ||||
|       os.pullEvent() | ||||
|     end | ||||
|     --my turn! | ||||
|     while true do | ||||
|       local e,p1,p2,p3,p4,p5=os.pullEvent() | ||||
|       if e=="mouse_click" then | ||||
|         local clickBounds=getPointBounds(p2,p3) | ||||
|         if boundsIntersect(clickBounds,targetGridBounds) then | ||||
|           moveTargetIndicator(p2-15,p3-3) | ||||
|           local shot=makeShot(targetX,targetY,oppGrid) | ||||
|           if shot then | ||||
|             --valid shot, tell the other guy | ||||
|             rednet.send(opponentID,"bs shot "..targetX.." "..targetY) | ||||
|             break | ||||
|           end | ||||
|         end | ||||
|       elseif e=="char" then | ||||
|         p1=string.lower(p1) | ||||
|         if p1>="a" and p1<="j" then | ||||
|           --row selected | ||||
|           moveTargetIndicator(targetX,string.byte(p1)-string.byte("a")+1) | ||||
|         elseif p1>="0" and p1<="9" then | ||||
|           local t=string.byte(p1)-string.byte("0") | ||||
|           if t==0 then t=10 end | ||||
|           moveTargetIndicator(t,targetY) | ||||
|         end | ||||
|       elseif e=="key" then | ||||
|         if p1==keys.enter or p1==keys.space and targetX and targetY then | ||||
|           local shot=makeShot(targetX,targetY,oppGrid) | ||||
|           if shot then | ||||
|             rednet.send(opponentID,"bs shot "..targetX.." "..targetY) | ||||
|             break | ||||
|           end | ||||
|         elseif p1==keys.up then | ||||
|           moveTargetIndicator(targetX,math.max(targetY-1,1)) | ||||
|         elseif p1==keys.down then | ||||
|           moveTargetIndicator(targetX,math.min(targetY+1,10)) | ||||
|         elseif p1==keys.left then | ||||
|           moveTargetIndicator(math.max(targetX-1,1),targetY) | ||||
|         elseif p1==keys.right then | ||||
|           moveTargetIndicator(math.min(targetX+1,10),targetY) | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|     --shot sent, wait for my turn to resolve (top coroutine will switch turns and draw the hit to the grid) | ||||
|     setStatusLine(2,"Waiting for opponent...") | ||||
|     while myTurn do | ||||
|       os.pullEvent() | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | ||||
| local gameRoutine=coroutine.create(runGame) | ||||
| --if advanced terminal, default focus to chat, can play with mouse | ||||
| local inChat=term.isColor() | ||||
| local savedCursorPos={7,19} | ||||
|  | ||||
| --redirect just to block scroll | ||||
| local redir={} | ||||
| for k,v in pairs(originalTerm) do | ||||
|   if k~="scroll" then | ||||
|     redir[k]=v | ||||
|   else | ||||
|     redir[k]=function() end | ||||
|   end | ||||
| end | ||||
| originalTerm = term.redirect(redir) | ||||
|  | ||||
| --run the game routine once | ||||
| coroutine.resume(gameRoutine) | ||||
| --hide cursor | ||||
| term.setCursorBlink(false) | ||||
|  | ||||
| while true do | ||||
|   local e,p1,p2,p3,p4,p5=os.pullEventRaw() | ||||
|   if e=="terminate" then | ||||
|     quit() | ||||
|   elseif e=="shipsunk" then | ||||
|     setStatusLine(1,opponent.." sank your "..ships[p1].name.."!") | ||||
|     rednet.send(opponentID,"bs sink") | ||||
|     shipsLeft=shipsLeft-1 | ||||
|     if shipsLeft==1 then | ||||
|       setStatusLine(3,"You only have 1 ship left!") | ||||
|     elseif shipsLeft>1 then | ||||
|       setStatusLine(3,"You have "..shipsLeft.." ships left!") | ||||
|     else | ||||
|       rednet.send(opponentID,"bs win") | ||||
|       setStatusLine(3,"You lost the game!") | ||||
|       break | ||||
|     end | ||||
|   elseif e=="rednet_message" then | ||||
|     local cmd,args=string.match(p2,"^bs (%S+)%s?(.*)") | ||||
|     if cmd=="ready" then | ||||
|       opponentReady=true | ||||
|       os.queueEvent("kickcoroutine") | ||||
|     elseif cmd=="cointoss" then | ||||
|       myTurn=args=="true" | ||||
|       if myTurn then | ||||
|         setStatusLine(2,"Your turn, take your shot!") | ||||
|       else | ||||
|         setStatusLine(2,"Opponent's turn, waiting...") | ||||
|       end | ||||
|       os.queueEvent("kickcoroutine") | ||||
|     elseif cmd=="shot" then | ||||
|       if myTurn then | ||||
|         setStatusLine(3,"What the?! Got a shot but not their turn! Ignoring") | ||||
|       else | ||||
|         local tx, ty=string.match(args,"(%d+) (%d+)") | ||||
|         tx,ty=tonumber(tx),tonumber(ty) | ||||
|         local tile=myGrid[tx][ty] | ||||
|         local shot=makeShot(tx,ty,myGrid) | ||||
|         rednet.send(opponentID,"bs result "..(shot[3] and "hit" or "miss")) | ||||
|         drawTile(2+tx,3+ty,tile) | ||||
|         myTurn=true | ||||
|         os.queueEvent("kickcoroutine") | ||||
|         displayGameHelp() | ||||
|         setStatusLine(1,opponent.." fired at "..toGridRef(tx,ty).." and "..(shot[3] and "hit" or "missed")) | ||||
|         setStatusLine(2,"Your turn, take your shot!") | ||||
|       end | ||||
|     elseif cmd=="sink" then | ||||
|       setStatusLine(1,"You sank one of "..opponent.."'s ships!") | ||||
|       oppShipsLeft=oppShipsLeft-1 | ||||
|       if oppShipsLeft==0 then | ||||
|         setStatusLine(2,opponent.." has no ships left!") | ||||
|       elseif oppShipsLeft==1 then | ||||
|         setStatusLine(2,"Sink 1 more to win!") | ||||
|       else | ||||
|         setStatusLine(2,"They have "..oppShipsLeft.." ships left.") | ||||
|       end | ||||
|     elseif cmd=="result" then | ||||
|       if not myTurn then | ||||
|         setStatusLine(3,"What the?! Got a shot result but not my turn! Ignoring") | ||||
|       else | ||||
|         local tile=oppGrid[targetX][targetY] | ||||
|         tile.hit=args=="hit" | ||||
|         drawTile(targetX+15,targetY+3,tile) | ||||
|         myTurn=false | ||||
|         doColor(tile.hit and colors.red or colors.white,colors.lightGray) | ||||
|         term.setCursorPos(17,16) | ||||
|         term.write(tile.hit and "HIT!" or "MISS") | ||||
|         setStatusLine(2,"Waiting for opponent...") | ||||
|         os.queueEvent("kickcoroutine") | ||||
|       end | ||||
|  | ||||
|     elseif cmd=="win" then | ||||
|       --we won! | ||||
|       setStatusLine(3,"You won the game! Congratulations!") | ||||
|       break | ||||
|     end | ||||
|   --everything else goes to gameRoutine | ||||
|   else | ||||
|     --all other events go to this routine | ||||
|     local succ,err=coroutine.resume(gameRoutine,e,p1,p2,p3,p4,p5) | ||||
|     if not succ then | ||||
|       print("game coroutine crashed with the following error: "..err) | ||||
|       quit() | ||||
|     end | ||||
|  | ||||
|     if coroutine.status(gameRoutine)=="dead" then | ||||
|       --game over | ||||
|       break | ||||
|     end | ||||
|   end | ||||
|  | ||||
| end | ||||
|  | ||||
| term.setCursorPos(1,19) | ||||
| term.clearLine() | ||||
| term.write("  Press any key to continue...") | ||||
| os.pullEvent("key") | ||||
| --if a char event was queued following the key event, this will eat it | ||||
| os.sleep(0) | ||||
|  | ||||
| term.setTextColor(colors.white) | ||||
| term.setBackgroundColor(colors.black) | ||||
| term.clear() | ||||
| quit() | ||||
| -- | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,327 +0,0 @@ | ||||
| --[[ | ||||
| 	Project info: | ||||
|  | ||||
| 	Name: Maze | ||||
| 	Creator: Jesusthekiller | ||||
| 	Language: Lua (CC) | ||||
| 	Website: None | ||||
| 	License: GNU GPL | ||||
| 		License file can be fount at www.jesusthekiller.com/license-gpl.html | ||||
|  | ||||
| 	Version: 1.2 | ||||
| ]]-- | ||||
|  | ||||
| --[[ | ||||
| 	Changelog: | ||||
| 	  1.0: | ||||
| 	    Initial Release | ||||
| 	  1.1: | ||||
| 	    Typos D: | ||||
| 	  1.2: | ||||
| 	    New logo | ||||
| 	    Time fixed | ||||
| ]]-- | ||||
|  | ||||
| --[[ | ||||
| 	LICENSE: | ||||
|  | ||||
| 	Maze | ||||
| 	Copyright (c) 2013 Jesusthekiller | ||||
|  | ||||
| 	This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. | ||||
|  | ||||
| 	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||
|  | ||||
| 	See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| ]]-- | ||||
|  | ||||
| -- The maze | ||||
|  | ||||
| -- The cprint | ||||
| local function cwrite(msg) | ||||
| 	msg = tostring(msg) | ||||
| 	local x, y = term.getCursorPos() | ||||
| 	term.setCursorPos((51-#msg)/2, y) | ||||
| 	write(msg) | ||||
| end | ||||
|  | ||||
| local function cprint(msg) | ||||
| 	cwrite(msg.."\n") | ||||
| end | ||||
|  | ||||
| -- The splash | ||||
| term.setBackgroundColor(colors.black) | ||||
| term.setTextColor(colors.white) | ||||
| term.clear() | ||||
|  | ||||
| term.setCursorPos(27, 8) | ||||
| print("Nano maze!") | ||||
|  | ||||
| paintutils.drawImage({[1]={[1]=1,[2]=1,[3]=1,[4]=1,[5]=1,[6]=1,[7]=1,[8]=1,[9]=1,[10]=1,[11]=0,[12]=1,[13]=0,[14]=0,[15]=1,[16]=0,[17]=0,[18]=1,[19]=0,[20]=0,[21]=1,[22]=0,[23]=0,[24]=1,[25]=0,[26]=0,[27]=1,},[2]={[1]=1,[2]=0,[3]=0,[4]=0,[5]=0,[6]=1,[7]=0,[8]=0,[9]=0,[10]=1,[11]=0,[12]=1,[13]=1,[14]=0,[15]=1,[16]=0,[17]=1,[18]=0,[19]=1,[20]=0,[21]=1,[22]=1,[23]=0,[24]=1,[25]=0,[26]=1,[27]=0,[28]=1,},[3]={[1]=1,[2]=1,[3]=1,[4]=1,[5]=0,[6]=1,[7]=1,[8]=1,[9]=0,[10]=1,[11]=0,[12]=1,[13]=0,[14]=1,[15]=1,[16]=0,[17]=1,[18]=0,[19]=1,[20]=0,[21]=1,[22]=0,[23]=1,[24]=1,[25]=0,[26]=0,[27]=1,},[4]={[1]=1,[2]=0,[3]=0,[4]=0,[5]=0,[6]=0,[7]=0,[8]=1,[9]=0,[10]=1,},[5]={[1]=1,[2]=0,[3]=1,[4]=1,[5]=1,[6]=1,[7]=0,[8]=1,[9]=0,[10]=1,[11]=0,[12]=1,[13]=0,[14]=0,[15]=0,[16]=1,[17]=0,[18]=0,[19]=1,[20]=0,[21]=0,[22]=1,[23]=1,[24]=0,[25]=0,[26]=1,[27]=1,[28]=1,},[6]={[1]=1,[2]=0,[3]=0,[4]=0,[5]=1,[6]=0,[7]=0,[8]=0,[9]=0,[10]=1,[11]=0,[12]=1,[13]=1,[14]=0,[15]=1,[16]=1,[17]=0,[18]=1,[19]=0,[20]=1,[21]=0,[22]=0,[23]=1,[24]=0,[25]=0,[26]=1,[27]=1,},[7]={[1]=1,[2]=1,[3]=1,[4]=1,[5]=1,[6]=1,[7]=1,[8]=1,[9]=1,[10]=1,[11]=0,[12]=1,[13]=0,[14]=1,[15]=0,[16]=1,[17]=0,[18]=1,[19]=0,[20]=1,[21]=0,[22]=0,[23]=1,[24]=1,[25]=0,[26]=1,[27]=1,[28]=1,},}, 13, 5) | ||||
|  | ||||
| parallel.waitForAny( | ||||
| 	function() coroutine.yield(); os.pullEvent("key"); coroutine.yield() end, | ||||
| 	function() term.setBackgroundColor(colors.black); term.setTextColor(colors.white) while true do term.setCursorPos(18, 14); term.write("Press any key.."); sleep(0.5); term.clearLine(); sleep(0.5) end end | ||||
| ) | ||||
|  | ||||
| -- The size | ||||
| local size | ||||
|  | ||||
| repeat | ||||
| 	term.setCursorPos(1, 14) | ||||
| 	term.clearLine() | ||||
|  | ||||
| 	cwrite("Enter maze size (5-99):") | ||||
| 	size = read() | ||||
|  | ||||
| 	size = tonumber(size) | ||||
| 	if not size then | ||||
| 		size = 0 | ||||
| 	end | ||||
| until size > 4 and size < 100 | ||||
|  | ||||
| -- The generate | ||||
| local function mazeGen(mx, my) | ||||
|  | ||||
| 	--[[ | ||||
| 		Format: | ||||
|  | ||||
| 		maze.x.y.(1/2/3/4) = true/false | ||||
|  | ||||
| 		1 - top | ||||
| 		2 - bottom | ||||
| 		3 - right | ||||
| 		4 - left | ||||
| 	]]-- | ||||
|  | ||||
| 	local maze = {} | ||||
| 	for i = 1, mx do | ||||
| 		maze[i] = {} | ||||
| 		for j = 1, my do | ||||
| 			maze[i][j] = {} | ||||
| 			for k = 1, 4 do | ||||
| 				maze[i][j][k] = true | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	local vis = 1 | ||||
| 	local tot = mx * my | ||||
| 	local curr = {} | ||||
| 	curr.x = math.random(1, mx) | ||||
| 	curr.y = math.random(1, my) | ||||
| 	local stack = {} | ||||
|  | ||||
| 	while vis < tot do | ||||
| 		local intact = {} | ||||
| 		local x = curr.x | ||||
| 		local y = curr.y | ||||
|  | ||||
| 		if x - 1 >= 1 and maze[x-1][y][1] and maze[x-1][y][2] and maze[x-1][y][3] and maze[x-1][y][4] then -- Check for full cells | ||||
| 			intact[#intact+1] = {x-1, y, 1} | ||||
| 		end | ||||
|  | ||||
| 		if x + 1 <= mx and maze[x+1][y][1] and maze[x+1][y][2] and maze[x+1][y][3] and maze[x+1][y][4] then | ||||
| 			intact[#intact+1] = {x+1, y, 2} | ||||
| 		end | ||||
|  | ||||
| 		if y + 1 <= my and maze[x][y+1][1] and maze[x][y+1][2] and maze[x][y+1][3] and maze[x][y+1][4] then | ||||
| 			intact[#intact+1] = {x, y+1, 3} | ||||
| 		end | ||||
|  | ||||
| 		if y - 1 >= 1 and maze[x][y-1][1] and maze[x][y-1][2] and maze[x][y-1][3] and maze[x][y-1][4] then | ||||
| 			intact[#intact+1] = {x, y-1, 4} | ||||
| 		end | ||||
|  | ||||
| 		if #intact > 0 then | ||||
| 			local i = math.random(1, #intact) -- Choose random | ||||
|  | ||||
| 			if intact[i][3] == 1 then -- Set intact's attached wall to false | ||||
| 				maze[intact[i][1]][intact[i][2]][2] = false | ||||
| 			elseif intact[i][3] == 2 then | ||||
| 				maze[intact[i][1]][intact[i][2]][1] = false | ||||
| 			elseif intact[i][3] == 3 then | ||||
| 				maze[intact[i][1]][intact[i][2]][4] = false | ||||
| 			elseif intact[i][3] == 4 then | ||||
| 				maze[intact[i][1]][intact[i][2]][3] = false | ||||
| 			end | ||||
|  | ||||
| 			maze[x][y][intact[i][3]] = false -- Set attached wall to false | ||||
|  | ||||
| 			vis = vis + 1 -- Increase vis | ||||
|  | ||||
| 			stack[#stack+1] = intact[i] -- Add to stack | ||||
| 		else | ||||
| 			local tmp = table.remove(stack) -- Get last cell | ||||
| 			curr.x = tmp[1] | ||||
| 			curr.y = tmp[2] | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	return maze | ||||
| end | ||||
|  | ||||
| local m = mazeGen(size, size) | ||||
|  | ||||
| -- The game init | ||||
| local posx = 2 | ||||
| local posy = 2 | ||||
|  | ||||
| local offsetx = 51/2-2 | ||||
| local offsety = 19/2-2 | ||||
|  | ||||
| local stime = os.clock() | ||||
|  | ||||
| -- The maze-to-table | ||||
| local tab = {} | ||||
|  | ||||
| for x = 1, size * 2 + 1 do | ||||
| 	tab[x] = {} | ||||
|  | ||||
| 	for y = 1, size * 2 + 1 do | ||||
| 		if x % 2 == 0 and y % 2 == 0 then -- Fill cells (empty) | ||||
| 			tab[x][y] = false | ||||
| 		elseif x % 2 == 1 and y % 2 == 1 then -- Fill corners (full) | ||||
| 			tab[x][y] = true | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| for x, tV in ipairs(m) do | ||||
| 	for y, v in ipairs(tV) do | ||||
| 		tab[x*2-1][y*2] = v[1] -- Up | ||||
| 		tab[x*2+1][y*2] = v[2] -- Down | ||||
| 		tab[x*2][y*2+1] = v[3] -- Right | ||||
| 		tab[x*2][y*2-1] = v[4] -- Left | ||||
| 	end | ||||
| end | ||||
|  | ||||
| -- The game itself | ||||
| repeat | ||||
| 	-- Print map | ||||
| 	term.setBackgroundColor(colors.white) | ||||
| 	term.clear() | ||||
|  | ||||
| 	if posx == 2 and posy == 2 then | ||||
| 		term.setCursorPos(1, 1) | ||||
| 		term.setTextColor(colors.black) | ||||
| 		print("Controls: WASD") | ||||
| 		print("Back to start: R") | ||||
| 		print("Quit: Q") | ||||
| 		print("Goal: Step on # (It's on bottom right corner)") | ||||
| 		print("\nGood Luck!") | ||||
| 	end | ||||
|  | ||||
| 	--[[ | ||||
| 		term.setTextColor(colors.black) | ||||
| 		term.setCursorPos(1, 19) | ||||
| 		write("X: "..posx.."   Y: "..posy) | ||||
| 	]] | ||||
|  | ||||
| 	for x, tV in ipairs(tab) do -- Print the map | ||||
| 		for y, v in ipairs(tV) do | ||||
| 			if offsety+y > 20 then | ||||
| 				break | ||||
| 			end | ||||
|  | ||||
| 			term.setCursorPos(offsetx+x, offsety+y) | ||||
|  | ||||
| 			if v then | ||||
| 				term.setBackgroundColor(colors.black) | ||||
| 			else | ||||
| 				term.setBackgroundColor(colors.white) | ||||
| 			end | ||||
|  | ||||
| 			if offsety+y < 20 and offsety+y > 0 and offsetx+x < 52 and offsetx+x > 0 then | ||||
| 				if x == size*2 and y == size*2 then | ||||
| 					if term.isColor() then | ||||
| 						term.setTextColor(colors.cyan) | ||||
| 					end | ||||
| 					write("#") | ||||
| 				else | ||||
| 					write(" ") | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
|  | ||||
| 		if offsetx+x > 51 then | ||||
| 			break | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	term.setCursorPos(51/2, 19/2) | ||||
| 	term.setBackgroundColor(colors.white) | ||||
|  | ||||
| 	if term.isColor() then | ||||
| 		term.setTextColor(colors.red) | ||||
| 	else | ||||
| 		term.setTextColor(colors.black) | ||||
| 	end | ||||
|  | ||||
| 	write("X") | ||||
|  | ||||
| 	-- Wait for key | ||||
|  | ||||
| 	local e, k = os.pullEvent("char") | ||||
|  | ||||
| 	if k == "a" and (not tab[posx-1][posy]) then | ||||
| 		posx = posx - 1 | ||||
| 		offsetx = offsetx + 1 | ||||
| 	end | ||||
|  | ||||
| 	if k == "d" and (not tab[posx+1][posy]) then | ||||
| 		posx = posx + 1 | ||||
| 		offsetx = offsetx - 1 | ||||
| 	end | ||||
|  | ||||
| 	if k == "w" and (not tab[posx][posy-1]) then | ||||
| 		posy = posy - 1 | ||||
| 		offsety = offsety + 1 | ||||
| 	end | ||||
|  | ||||
| 	if k == "s" and (not tab[posx][posy+1]) then | ||||
| 		posy = posy + 1 | ||||
| 		offsety = offsety - 1 | ||||
| 	end | ||||
|  | ||||
| 	if k == "q" then | ||||
| 		break | ||||
| 	end | ||||
|  | ||||
| 	if k == "r" then | ||||
| 		posx = 2 | ||||
| 		posy = 2 | ||||
|  | ||||
| 		offsetx = 51/2-2 | ||||
| 		offsety = 19/2-2 | ||||
| 	end | ||||
| until posx == size*2 and posy == size*2 | ||||
|  | ||||
| -- The win/loose message | ||||
| term.setBackgroundColor(colors.white) | ||||
| term.setTextColor(colors.black) | ||||
| term.clear() | ||||
| term.setCursorPos(1, 1) | ||||
|  | ||||
| if posx == size*2 and posy == size*2 then | ||||
| 	local ntime = os.clock() | ||||
| 	write("\n") | ||||
| 	cprint("Congratulations!") | ||||
| 	cprint("You made it in") | ||||
| 	cprint(tostring(math.floor((ntime-stime)/60)).." minutes and "..tostring(math.ceil((ntime-stime)%60)).." seconds") | ||||
| 	cprint("Size of maze: "..size) | ||||
| else | ||||
| 	write("\n") | ||||
| 	cprint("Oh noes D:") | ||||
| end | ||||
|  | ||||
| parallel.waitForAny( | ||||
| 	function() coroutine.yield(); os.pullEvent("key"); coroutine.yield() end, | ||||
| 	function() term.setBackgroundColor(colors.white); term.setTextColor(colors.black) while true do term.setCursorPos(18, 14); term.write("Press any key.."); sleep(0.5); term.clearLine(); sleep(0.5) end end | ||||
| ) | ||||
|  | ||||
| term.setBackgroundColor(colors.black) | ||||
| term.setTextColor(colors.white) | ||||
| term.clear() | ||||
| term.setCursorPos(1, 1) | ||||
| cprint("  Maze by JTK. Thanks for playing!") | ||||
| @@ -1,614 +0,0 @@ | ||||
| --[[ | ||||
| 	Project info: | ||||
|  | ||||
| 	Name: Maze 3D | ||||
| 	Creator: Jesusthekiller | ||||
| 	Language: Lua (CC) | ||||
| 	Website: None | ||||
| 	License: GNU GPL | ||||
| 		License file can be fount at www.jesusthekiller.com/license-gpl.html | ||||
|  | ||||
| 	Version: 2.1 | ||||
| ]]-- | ||||
|  | ||||
| --[[ | ||||
| 	Big thanks to Gopher for 3D engine! | ||||
| 	http://www.computercraft.info/forums2/index.php?/topic/10786-wolf3d-style-3d-engine-proof-of-concept/page__hl__wolf3d | ||||
| ]]-- | ||||
|  | ||||
| --[[ | ||||
| 	Changelog: | ||||
| 	  1.0: | ||||
| 	    Initial Release | ||||
| 	  2.0: | ||||
| 	    No-HTTP version for Treasure disk | ||||
| 	  2.1: | ||||
| 	    No more temp files! | ||||
| ]]-- | ||||
|  | ||||
| --[[ | ||||
| 	LICENSE: | ||||
|  | ||||
| 	Maze 3D | ||||
| 	Copyright (c) 2013 Jesusthekiller | ||||
|  | ||||
| 	This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. | ||||
|  | ||||
| 	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||
|  | ||||
| 	See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| ]]-- | ||||
|  | ||||
| -- The color check | ||||
| if (not term.isColor()) or turtle then | ||||
| 	print("This program has to be run on advanced computer.") | ||||
| 	error() | ||||
| end | ||||
|  | ||||
| -- The cprint | ||||
| local function cwrite(msg) | ||||
| 	msg = tostring(msg) | ||||
| 	local x, y = term.getCursorPos() | ||||
| 	term.setCursorPos((51-#msg)/2, y) | ||||
| 	write(msg) | ||||
| end | ||||
|  | ||||
| local function cprint(msg) | ||||
| 	cwrite(msg.."\n") | ||||
| end | ||||
|  | ||||
| -- The splash | ||||
| term.setBackgroundColor(colors.black) | ||||
| term.setTextColor(colors.white) | ||||
| term.clear() | ||||
|  | ||||
| paintutils.drawImage({[1]={[1]=1,[2]=0,[3]=0,[4]=0,[5]=1,[6]=0,[7]=0,[8]=1,[9]=1,[10]=0,[11]=0,[12]=0,[13]=1,[14]=1,[15]=1,[16]=1,[17]=0,[18]=1,[19]=1,[20]=1,[21]=1,},[2]={[1]=1,[2]=1,[3]=0,[4]=1,[5]=1,[6]=0,[7]=1,[8]=0,[9]=0,[10]=1,[11]=0,[12]=0,[13]=0,[14]=0,[15]=0,[16]=1,[17]=0,[18]=1,[19]=0,[20]=0,[21]=0,},[3]={[1]=1,[2]=0,[3]=1,[4]=0,[5]=1,[6]=0,[7]=1,[8]=1,[9]=1,[10]=1,[11]=0,[12]=0,[13]=0,[14]=1,[15]=1,[16]=0,[17]=0,[18]=1,[19]=1,[20]=1,[21]=0,},[4]={[1]=1,[2]=0,[3]=0,[4]=0,[5]=1,[6]=0,[7]=1,[8]=0,[9]=0,[10]=1,[11]=0,[12]=0,[13]=1,[14]=0,[15]=0,[16]=0,[17]=0,[18]=1,[19]=0,[20]=0,[21]=0,},[5]={[1]=1,[2]=0,[3]=0,[4]=0,[5]=1,[6]=0,[7]=1,[8]=0,[9]=0,[10]=1,[11]=0,[12]=0,[13]=1,[14]=1,[15]=1,[16]=1,[17]=0,[18]=1,[19]=1,[20]=1,[21]=1,},[6]={[1]=0,[2]=0,[3]=0,[4]=0,[5]=0,[6]=0,[7]=0,[8]=0,},[7]={[1]=0,[2]=0,[3]=0,[4]=16384,[5]=16384,[6]=16384,[7]=16384,[8]=0,[9]=0,[10]=0,[11]=0,[12]=512,[13]=512,[14]=512,[15]=512,[16]=0,[17]=0,[18]=0,[19]=0,[20]=0,[21]=0,},[8]={[1]=0,[2]=0,[3]=0,[4]=0,[5]=128,[6]=128,[7]=128,[8]=16384,[9]=0,[10]=0,[11]=0,[12]=512,[13]=128,[14]=128,[15]=128,[16]=512,[17]=0,[18]=0,[19]=0,[20]=0,[21]=0,},[9]={[1]=0,[2]=0,[3]=0,[4]=16384,[5]=16384,[6]=16384,[7]=16384,[8]=0,[9]=128,[10]=0,[11]=0,[12]=512,[13]=128,[14]=0,[15]=0,[16]=512,[17]=128,[18]=0,[19]=0,[20]=0,[21]=0,},[10]={[1]=0,[2]=0,[3]=0,[4]=0,[5]=128,[6]=128,[7]=128,[8]=16384,[9]=0,[10]=0,[11]=0,[12]=512,[13]=128,[14]=0,[15]=0,[16]=512,[17]=128,[18]=0,[19]=0,[20]=0,[21]=0,},[11]={[1]=0,[2]=0,[3]=0,[4]=16384,[5]=16384,[6]=16384,[7]=16384,[8]=0,[9]=128,[10]=0,[11]=0,[12]=512,[13]=512,[14]=512,[15]=512,[16]=128,[17]=128,[18]=0,[19]=0,[20]=0,[21]=0,},[12]={[1]=0,[2]=0,[3]=0,[4]=0,[5]=128,[6]=128,[7]=128,[8]=128,[9]=0,[10]=0,[11]=0,[12]=0,[13]=128,[14]=128,[15]=128,[16]=128,},}, 15, 3) | ||||
|  | ||||
| parallel.waitForAny( | ||||
| 	function() coroutine.yield(); os.pullEvent("key"); coroutine.yield() end, | ||||
| 	function() term.setBackgroundColor(colors.black); term.setTextColor(colors.white) while true do term.setCursorPos(18, 16); term.write("Press any key.."); sleep(0.5); term.clearLine(); sleep(0.5) end end | ||||
| ) | ||||
|  | ||||
| -- The size | ||||
| local size | ||||
|  | ||||
| repeat | ||||
| 	term.setCursorPos(1, 16) | ||||
| 	term.clearLine() | ||||
|  | ||||
| 	cwrite("Enter maze size (5-99):") | ||||
| 	size = read() | ||||
|  | ||||
| 	size = tonumber(size) | ||||
| 	if not size then | ||||
| 		size = 0 | ||||
| 	end | ||||
| until size > 4 and size < 100 | ||||
|  | ||||
| -- The generate | ||||
| local function mazeGen(mx, my) | ||||
|  | ||||
| 	--[[ | ||||
| 		Format: | ||||
|  | ||||
| 		maze.x.y.(1/2/3/4) = true/false | ||||
|  | ||||
| 		1 - top | ||||
| 		2 - bottom | ||||
| 		3 - right | ||||
| 		4 - left | ||||
| 	]]-- | ||||
|  | ||||
| 	local maze = {} | ||||
| 	for i = 1, mx do | ||||
| 		maze[i] = {} | ||||
| 		for j = 1, my do | ||||
| 			maze[i][j] = {} | ||||
| 			for k = 1, 4 do | ||||
| 				maze[i][j][k] = true | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	local vis = 1 | ||||
| 	local tot = mx * my | ||||
| 	local curr = {} | ||||
| 	curr.x = math.random(1, mx) | ||||
| 	curr.y = math.random(1, my) | ||||
| 	local stack = {} | ||||
|  | ||||
| 	while vis < tot do | ||||
| 		local intact = {} | ||||
| 		local x = curr.x | ||||
| 		local y = curr.y | ||||
|  | ||||
| 		if x - 1 >= 1 and maze[x-1][y][1] and maze[x-1][y][2] and maze[x-1][y][3] and maze[x-1][y][4] then -- Check for full cells | ||||
| 			intact[#intact+1] = {x-1, y, 1} | ||||
| 		end | ||||
|  | ||||
| 		if x + 1 <= mx and maze[x+1][y][1] and maze[x+1][y][2] and maze[x+1][y][3] and maze[x+1][y][4] then | ||||
| 			intact[#intact+1] = {x+1, y, 2} | ||||
| 		end | ||||
|  | ||||
| 		if y + 1 <= my and maze[x][y+1][1] and maze[x][y+1][2] and maze[x][y+1][3] and maze[x][y+1][4] then | ||||
| 			intact[#intact+1] = {x, y+1, 3} | ||||
| 		end | ||||
|  | ||||
| 		if y - 1 >= 1 and maze[x][y-1][1] and maze[x][y-1][2] and maze[x][y-1][3] and maze[x][y-1][4] then | ||||
| 			intact[#intact+1] = {x, y-1, 4} | ||||
| 		end | ||||
|  | ||||
| 		if #intact > 0 then | ||||
| 			local i = math.random(1, #intact) -- Choose random | ||||
|  | ||||
| 			if intact[i][3] == 1 then -- Set intact's attached wall to false | ||||
| 				maze[intact[i][1]][intact[i][2]][2] = false | ||||
| 			elseif intact[i][3] == 2 then | ||||
| 				maze[intact[i][1]][intact[i][2]][1] = false | ||||
| 			elseif intact[i][3] == 3 then | ||||
| 				maze[intact[i][1]][intact[i][2]][4] = false | ||||
| 			elseif intact[i][3] == 4 then | ||||
| 				maze[intact[i][1]][intact[i][2]][3] = false | ||||
| 			end | ||||
|  | ||||
| 			maze[x][y][intact[i][3]] = false -- Set attached wall to false | ||||
|  | ||||
| 			vis = vis + 1 -- Increase vis | ||||
|  | ||||
| 			stack[#stack+1] = intact[i] -- Add to stack | ||||
| 		else | ||||
| 			local tmp = table.remove(stack) -- Get last cell | ||||
| 			curr.x = tmp[1] | ||||
| 			curr.y = tmp[2] | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	return maze | ||||
| end | ||||
|  | ||||
| local m = mazeGen(size, size) | ||||
|  | ||||
| -- The game init | ||||
| local posx = 2 | ||||
| local posy = 2 | ||||
|  | ||||
| local offsetx = 51/2-2 | ||||
| local offsety = 19/2-2 | ||||
|  | ||||
| -- The maze-to-table | ||||
| local tab = {} | ||||
|  | ||||
| for x = 1, size * 2 + 1 do | ||||
| 	tab[x] = {} | ||||
|  | ||||
| 	for y = 1, size * 2 + 1 do | ||||
| 		if x % 2 == 0 and y % 2 == 0 then -- Fill cells (empty) | ||||
| 			tab[x][y] = " " | ||||
| 		elseif x % 2 == 1 and y % 2 == 1 then -- Fill corners (full) | ||||
| 			tab[x][y] = "1" | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| for x, tV in ipairs(m) do | ||||
| 	for y, v in ipairs(tV) do | ||||
| 		if x == size and y == size then | ||||
| 			v[1] = v[1] and "2" or " " | ||||
| 			v[2] = v[2] and "2" or " " | ||||
| 			v[3] = v[3] and "2" or " " | ||||
| 			v[4] = v[4] and "2" or " " | ||||
| 			tab[x*2-1][y*2] = v[1] -- Up | ||||
| 			tab[x*2+1][y*2] = v[2] -- Down | ||||
| 			tab[x*2][y*2+1] = v[3] -- Right | ||||
| 			tab[x*2][y*2-1] = v[4] -- Left | ||||
| 		else | ||||
| 			v[1] = v[1] and "1" or " " | ||||
| 			v[2] = v[2] and "1" or " " | ||||
| 			v[3] = v[3] and "1" or " " | ||||
| 			v[4] = v[4] and "1" or " " | ||||
| 			tab[x*2-1][y*2] = v[1] -- Up | ||||
| 			tab[x*2+1][y*2] = v[2] -- Down | ||||
| 			tab[x*2][y*2+1] = v[3] -- Right | ||||
| 			tab[x*2][y*2-1] = v[4] -- Left | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| local gtab = {} | ||||
|  | ||||
| for k, v in ipairs(tab) do | ||||
| 	gtab[#gtab+1] = table.concat(v) | ||||
| end | ||||
|  | ||||
| size = size * 2 + 1 | ||||
|  | ||||
| --[[ | ||||
| local template = fs.open("maze3d_template", "r") | ||||
| local game = fs.open("maze3d_game", "w") | ||||
|  | ||||
| game.writeLine("local mapH, mapW = "..size..","..size) | ||||
| game.writeLine("local dir = "..(gtab[2]:sub(3,3) == " " and '0' or '88')) | ||||
| game.writeLine("local map = {") | ||||
|  | ||||
| for k, v in ipairs(gtab) do | ||||
| 	game.writeLine('"'..v..'",') | ||||
| end | ||||
|  | ||||
| game.writeLine("}") | ||||
|  | ||||
| game.writeLine(template.readAll()) | ||||
| game.close() | ||||
| template.close() | ||||
|  | ||||
| shell.run("maze3d_game") | ||||
|  | ||||
| fs.delete("maze3d_game") | ||||
| fs.delete("maze3d_template")]] | ||||
|  | ||||
| local mapH, mapW = size, size | ||||
| local dir = gtab[2]:sub(3,3) == " " and '0' or '88' | ||||
| local map = gtab | ||||
| local startdir = dir | ||||
|  | ||||
| ------------------------------------------------------------------------------------------------------ | ||||
| --GOPHER'S CODE HERE | ||||
|  | ||||
| local buffer=term | ||||
| local loadedAPI=false | ||||
|  | ||||
| local stime = os.clock() | ||||
|  | ||||
| if redirect then | ||||
|   buffer=redirect.createRedirectBuffer() | ||||
|   print("redirect API found, using buffer") | ||||
| else | ||||
|   local pe=printError | ||||
|   rawset(_G,"printError",error) | ||||
|   local ok, err=pcall(os.loadAPI,"redirect") | ||||
|   if not ok then | ||||
|     print("trying "..shell.dir().."/redirect") | ||||
|     ok,err=pcall(os.loadAPI,shell.dir().."/redirect") | ||||
|   end | ||||
|   if ok then | ||||
|     print("Loaded redirect API, using buffer") | ||||
|     buffer=redirect.createRedirectBuffer() | ||||
|     loadedAPI=true | ||||
|   else | ||||
|     print("redirect API not found or could not be loaded, drawing directly; this may cause flickering.") | ||||
|   end | ||||
|   rawset(_G,"printError",pe) | ||||
| end | ||||
|  | ||||
| local colorSchemes = { | ||||
|   {0,8}, --white+gray | ||||
|   {3,11}, --blue | ||||
|   {6,14}, --red | ||||
|   {5,13}, --green | ||||
|   {4,1}, --yellow/orange | ||||
| } | ||||
|  | ||||
|  | ||||
| local function cast(cx,cy,angle) | ||||
|   --direction vector | ||||
|   local vx,vy=math.cos(angle), math.sin(angle) | ||||
|   local slope=vy/vx | ||||
|   --next distance, x and y axis points | ||||
|   local ndx, ndy | ||||
|   --steps, distance and block | ||||
|   local dsx, dsy, bsx, bsy | ||||
|   if vx<0 then | ||||
|     local x=(cx%1) | ||||
|     bsx=-1 | ||||
|     ndx=math.sqrt(x*x*(1+slope*slope)) | ||||
|     dsx=math.sqrt((1+slope*slope)) | ||||
|   else | ||||
|     local x=1-(cx%1) | ||||
|     bsx=1 | ||||
|     ndx=math.sqrt(x*x*(1+slope*slope)) | ||||
|     dsx=math.sqrt((1+slope*slope)) | ||||
|   end | ||||
|  | ||||
|   if vy<0 then | ||||
|     local y=(cy%1) | ||||
|     bsy=-1 | ||||
|     ndy=math.sqrt(y*y*(1+1/(slope*slope))) | ||||
|     dsy=math.sqrt((1+1/(slope*slope))) | ||||
|   else | ||||
|     local y=1-(cy%1) | ||||
|     bsy=1 | ||||
|     ndy=math.sqrt(y*y*(1+1/(slope*slope))) | ||||
|     dsy=math.sqrt((1+1/(slope*slope))) | ||||
|   end | ||||
|  | ||||
|   local x,y=math.floor(cx),math.floor(cy) | ||||
|   while x>0 and x<=mapW and y>0 and y<=mapH do | ||||
|     local hitD | ||||
|     local isX | ||||
|     if ndx<ndy then | ||||
|       --x crossing is next | ||||
|       x=x+bsx | ||||
|       isX=true | ||||
|       hitD=ndx | ||||
|       ndx=ndx+dsx | ||||
|     else | ||||
|       y=y+bsy | ||||
|       isX=false | ||||
|       hitD=ndy | ||||
|       ndy=ndy+dsy | ||||
|     end | ||||
|     local wall=map[y]:sub(x,x) | ||||
|     if wall~=" " then | ||||
|  | ||||
|       return colorSchemes[tonumber(wall)][isX and 1 or 2], hitD | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | ||||
| local w,h=term.getSize() | ||||
| local centerX, centerY=math.floor((w+1)/2), math.floor((h+1)/2) | ||||
|  | ||||
| local px, py=2.5,2.5 | ||||
| --local dir=0 | ||||
| local fx,fy | ||||
| local speed=.1 | ||||
| local turnSpeed=4 | ||||
|  | ||||
| local function turn(amt) | ||||
|   dir=dir+amt | ||||
|   fx,fy=math.cos(math.rad(dir)), math.sin(math.rad(dir)) | ||||
| end | ||||
|  | ||||
| turn(0) | ||||
|  | ||||
| --build table of angles and base distances per scanline | ||||
| local screenDist=.55*w | ||||
| local scan={} | ||||
|  | ||||
| for x=1,w do | ||||
|   local t={} | ||||
|   scan[x]=t | ||||
|   t.angle=math.atan2(x-centerX,screenDist) | ||||
|   t.dist=((x-centerX)^2+screenDist^2)^.5/screenDist | ||||
| end | ||||
|  | ||||
| local function redraw() | ||||
|   local oldTerm | ||||
|   if buffer.isBuffer then | ||||
|     oldTerm = term.redirect(buffer) | ||||
|   end | ||||
|   for x=1,w do | ||||
|     local wall,dist=cast(px,py,math.rad(dir)+scan[x].angle) | ||||
|     if wall then | ||||
|       --calc wall height based on distance | ||||
|       local height=scan[x].dist/dist | ||||
|       height=math.floor(math.min(height*centerY,(h+1)/2)) | ||||
|       term.setBackgroundColor(colors.gray) | ||||
|       for y=1,(h+1)/2-height-1 do | ||||
|         term.setCursorPos(x,y) | ||||
|         term.write(" ") | ||||
|       end | ||||
|       for y=centerY+height+1,h do | ||||
|         term.setCursorPos(x,y) | ||||
|         term.write(" ") | ||||
|       end | ||||
|       term.setBackgroundColor(2^wall) | ||||
|       for y=centerY-height,centerY+height do | ||||
|         term.setCursorPos(x,y) | ||||
|         term.write(" ") | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|   if buffer.isBuffer then | ||||
|     term.redirect(oldTerm) | ||||
|     buffer.blit() | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function clampCollision(x,y,radius) | ||||
|   --am I *in* a block? | ||||
|   local gx,gy=math.floor(x),math.floor(y) | ||||
|   if map[gy]:sub(gx,gx)~=" " then | ||||
|     --I am. Complete fail, do nothing. | ||||
|     return x,y | ||||
|   end | ||||
|  | ||||
|   --ok, check the neighbors. | ||||
|   local right=math.floor(x+radius)>gx | ||||
|   local left=math.floor(x-radius)<gx | ||||
|   local front=math.floor(y-radius)<gy | ||||
|   local back=math.floor(y+radius)>gy | ||||
|  | ||||
|   local pushed=false | ||||
|  | ||||
|   if right and map[gy]:sub(gx+1,gx+1)~=" " then | ||||
|     --push left | ||||
|     pushed=true | ||||
|     x=gx+1-radius | ||||
|   elseif left  and map[gy]:sub(gx-1,gx-1)~=" " then | ||||
|     --push right | ||||
|     pushed=true | ||||
|     x=gx+radius | ||||
|   end | ||||
|  | ||||
|   if front and map[gy-1]:sub(gx,gx)~=" " then | ||||
|     --push back | ||||
|     pushed=true | ||||
|     y=gy+radius | ||||
|   elseif back and map[gy+1]:sub(gx,gx)~=" " then | ||||
|     --push forward | ||||
|     pushed=true | ||||
|  | ||||
|  | ||||
|  | ||||
|     y=gy+1-radius | ||||
|   end | ||||
|  | ||||
|   --if I wasn't pushed out on any side, I might be hitting a corner | ||||
|   if not pushed then | ||||
|     --square rad | ||||
|     local r2=radius^2 | ||||
|     local pushx,pushy=0,0 | ||||
|     if left then | ||||
|       if front and map[gy-1]:sub(gx-1,gx-1)~=" " then | ||||
|         --check front-left | ||||
|         local dist2=(gx-x)^2+(gy-y)^2 | ||||
|         if dist2<r2 then | ||||
|           local pushd=(r2-dist2)/2^.5 | ||||
|           pushx,pushy=pushd,pushd | ||||
|         end | ||||
|       elseif back and map[gy+1]:sub(gx-1,gx-1)~=" " then | ||||
|         local dist2=(gx-x)^2+(gy+1-y)^2 | ||||
|         if dist2<r2 then | ||||
|           local pushd=(r2-dist2)/2^.5 | ||||
|           pushx,pushy=pushd,-pushd | ||||
|         end | ||||
|       end | ||||
|     elseif right then | ||||
|       if front and map[gy-1]:sub(gx+1,gx+1)~=" " then | ||||
|         --check front-left | ||||
|         local dist2=(gx+1-x)^2+(gy-y)^2 | ||||
|         if dist2<r2 then | ||||
|           local pushd=(r2-dist2)/2^.5 | ||||
|           pushx,pushy=-pushd,pushd | ||||
|         end | ||||
|       elseif back and map[gy+1]:sub(gx+1,gx+1)~=" " then | ||||
|         local dist2=(gx+1-x)^2+(gy+1-y)^2 | ||||
|         if dist2<r2 then | ||||
|           local pushd=(r2-dist2)/2^.5 | ||||
|           pushx,pushy=-pushd,-pushd | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|     x=x+pushx | ||||
|     y=y+pushy | ||||
|   end | ||||
|  | ||||
|   return x,y | ||||
| end | ||||
|  | ||||
| term.setBackgroundColor(colors.black) | ||||
| --term.setTextColor(colors.white) | ||||
| term.clear() | ||||
| term.setCursorPos(1, 1) | ||||
|  | ||||
| term.setTextColor(colors.yellow) | ||||
| write("Move:                                          ") | ||||
| term.setTextColor(colors.lime) | ||||
| print("WASD") | ||||
|  | ||||
| term.setTextColor(colors.yellow) | ||||
| write("Turn:                              ") | ||||
| term.setTextColor(colors.lime) | ||||
| print("Left/Right arrow") | ||||
|  | ||||
| term.setTextColor(colors.yellow) | ||||
| write("Teleport to start:                                ") | ||||
| term.setTextColor(colors.lime) | ||||
| print("R") | ||||
|  | ||||
| term.setTextColor(colors.yellow) | ||||
| write("Quit:                                             ") | ||||
| term.setTextColor(colors.lime) | ||||
| print("Q\n") | ||||
|  | ||||
| term.setTextColor(colors.white) | ||||
| write("Goal: go to ") | ||||
| term.setTextColor(colors.lightBlue) | ||||
| write("blue") | ||||
| term.setTextColor(colors.white) | ||||
| print(" spot (opposite corner of the map)\n\n\n\n") | ||||
|  | ||||
| term.setTextColor(colors.white) | ||||
| cprint("Press any key to start!") | ||||
|  | ||||
| os.pullEvent("key") | ||||
|  | ||||
| local frameTimer=os.startTimer(0.5) | ||||
| local prevTick=0 | ||||
| local dirty=true | ||||
| local win = false | ||||
| while true do | ||||
|   px,py=clampCollision(px,py,.25) | ||||
|   if dirty then | ||||
|     redraw() | ||||
|     dirty=false | ||||
|   end | ||||
|  | ||||
|   local e={os.pullEvent()} | ||||
|   if e[1]=="key" then | ||||
|     if e[2]==keys.left then | ||||
|       turn(-turnSpeed) | ||||
|       dirty=true | ||||
|     elseif e[2]==keys.right then | ||||
|       turn(turnSpeed) | ||||
|       dirty=true | ||||
|     elseif e[2]==keys.up or e[2]==keys.w then | ||||
|       px=px+fx*speed | ||||
|       py=py+fy*speed | ||||
|       dirty=true | ||||
|     elseif e[2]==keys.down or e[2]==keys.s then | ||||
|       px=px-fx*speed | ||||
|       py=py-fy*speed | ||||
|       dirty=true | ||||
|     elseif e[2]==keys.a then | ||||
|       px=px+fy*speed | ||||
|       py=py-fx*speed | ||||
|       dirty=true | ||||
|     elseif e[2]==keys.d then | ||||
|       px=px-fy*speed | ||||
|       py=py+fx*speed | ||||
|       dirty=true | ||||
|     elseif e[2]==keys.q then | ||||
|       break | ||||
|     elseif e[2]==keys.r then | ||||
|       px,py = 2.5,2.5 | ||||
|       dir=startdir | ||||
|       dirty=true | ||||
|     end | ||||
|  | ||||
|     if px >= mapW-1 and py >= mapH-1 then | ||||
|       win = true | ||||
|       break | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | ||||
|  | ||||
| if loadedAPI then | ||||
|   os.unloadAPI("redirect") | ||||
| end | ||||
|  | ||||
| -- JESUS PART | ||||
|  | ||||
| -- The win/loose message | ||||
| term.setBackgroundColor(colors.white) | ||||
| term.setTextColor(colors.black) | ||||
| term.clear() | ||||
| term.setCursorPos(1, 1) | ||||
|  | ||||
| if win then | ||||
|   local ntime = os.clock() | ||||
|   write("\n") | ||||
|   cprint("Congratulations!") | ||||
|   cprint("You made it in") | ||||
|   cprint(tostring(math.floor((ntime-stime)/60)).." minutes and "..tostring(math.ceil((ntime-stime)%60)).." seconds") | ||||
|   cprint("Size of maze: "..(mapW-1)/2) | ||||
| sleep(1) | ||||
| else | ||||
|   write("\n") | ||||
|   cprint("Oh noes D:") | ||||
| end | ||||
|  | ||||
|  | ||||
|  | ||||
| parallel.waitForAny( | ||||
|   function() coroutine.yield(); os.pullEvent("key"); coroutine.yield() end, | ||||
|   function() term.setBackgroundColor(colors.white); term.setTextColor(colors.black) while true do term.setCursorPos(18, 14); term.write("Press any key.."); sleep(0.5); term.clearLine(); sleep(0.5) end end | ||||
| ) | ||||
|  | ||||
| term.setBackgroundColor(colors.black) | ||||
| term.setTextColor(colors.white) | ||||
| term.clear() | ||||
| term.setCursorPos(1, 1) | ||||
| cprint("  Maze 3D by JTK. Thanks for playing!") | ||||
| cprint("3D engine by Gopher, He is A-W-E-S-O-M-E") | ||||
| @@ -1,159 +0,0 @@ | ||||
| function new(_sizeX, _sizeY, _color) | ||||
| 	local redirect = {buffer = {text = {}, textColor = {}, backColor = {}, cursorX = 1, cursorY = 1, cursorBlink = false, curTextColor = "0", curBackColor = "f", sizeX = _sizeX or 51, sizeY = _sizeY or 19, color = _color}} | ||||
| 	redirect.write = function(text) | ||||
| 		text = tostring(text) | ||||
| 		local pos = redirect.buffer.cursorX | ||||
| 		if redirect.buffer.cursorY > redirect.buffer.sizeY or redirect.buffer.cursorY < 1 then | ||||
| 			redirect.buffer.cursorX = pos + #text | ||||
| 			return | ||||
| 		end | ||||
| 		local writeText | ||||
| 		if pos + #text <= 1 then | ||||
| 			--skip entirely. | ||||
| 			redirect.buffer.cursorX = pos + #text | ||||
| 			return | ||||
| 		elseif pos < 1 then | ||||
| 			--adjust text to fit on screen starting at one. | ||||
| 			writeText = string.sub(text, math.abs(redirect.buffer.cursorX) + 2) | ||||
| 			redirect.buffer.cursorX = 1 | ||||
| 		elseif pos > redirect.buffer.sizeX then | ||||
| 			--if we're off the edge to the right, skip entirely. | ||||
| 			redirect.buffer.cursorX = pos + #text | ||||
| 			return | ||||
| 		else | ||||
| 			writeText = text | ||||
| 		end | ||||
| 		local lineText = redirect.buffer.text[redirect.buffer.cursorY] | ||||
| 		local lineColor = redirect.buffer.textColor[redirect.buffer.cursorY] | ||||
| 		local lineBack = redirect.buffer.backColor[redirect.buffer.cursorY] | ||||
| 		local preStop = redirect.buffer.cursorX - 1 | ||||
| 		local preStart = math.min(1, preStop) | ||||
| 		local postStart = redirect.buffer.cursorX + string.len(writeText) | ||||
| 		local postStop = redirect.buffer.sizeX | ||||
| 		redirect.buffer.text[redirect.buffer.cursorY] = string.sub(lineText, preStart, preStop)..writeText..string.sub(lineText, postStart, postStop) | ||||
| 		redirect.buffer.textColor[redirect.buffer.cursorY] = string.sub(lineColor, preStart, preStop)..string.rep(redirect.buffer.curTextColor, #writeText)..string.sub(lineColor, postStart, postStop) | ||||
| 		redirect.buffer.backColor[redirect.buffer.cursorY] = string.sub(lineBack, preStart, preStop)..string.rep(redirect.buffer.curBackColor, #writeText)..string.sub(lineBack, postStart, postStop) | ||||
| 		redirect.buffer.cursorX = pos + string.len(text) | ||||
| 	end | ||||
| 	redirect.clear = function() | ||||
| 		for i=1, redirect.buffer.sizeY do | ||||
| 			redirect.buffer.text[i] = string.rep(" ", redirect.buffer.sizeX) | ||||
| 			redirect.buffer.textColor[i] = string.rep(redirect.buffer.curTextColor, redirect.buffer.sizeX) | ||||
| 			redirect.buffer.backColor[i] = string.rep(redirect.buffer.curBackColor, redirect.buffer.sizeX) | ||||
| 		end | ||||
| 	end | ||||
| 	redirect.clearLine = function() | ||||
| 		redirect.buffer.text[redirect.buffer.cursorY] = string.rep(" ", redirect.buffer.sizeX) | ||||
| 		redirect.buffer.textColor[redirect.buffer.cursorY] = string.rep(redirect.buffer.curTextColor, redirect.buffer.sizeX) | ||||
| 		redirect.buffer.backColor[redirect.buffer.cursorY] = string.rep(redirect.buffer.curBackColor, redirect.buffer.sizeX) | ||||
| 	end | ||||
| 	redirect.getCursorPos = function() | ||||
| 		return redirect.buffer.cursorX, redirect.buffer.cursorY | ||||
| 	end | ||||
| 	redirect.setCursorPos = function(x, y) | ||||
| 		redirect.buffer.cursorX = math.floor(tonumber(x)) or redirect.buffer.cursorX | ||||
| 		redirect.buffer.cursorY = math.floor(tonumber(y)) or redirect.buffer.cursorY | ||||
| 	end | ||||
| 	redirect.setCursorBlink = function(b) | ||||
| 		redirect.buffer.cursorBlink = b | ||||
| 	end | ||||
| 	redirect.getSize = function() | ||||
| 		return redirect.buffer.sizeX, redirect.buffer.sizeY | ||||
| 	end | ||||
| 	redirect.scroll = function(n) | ||||
| 		n = tonumber(n) or 1 | ||||
| 		if n > 0 then | ||||
| 			for i = 1, redirect.buffer.sizeY - n do | ||||
| 				if redirect.buffer.text[i + n] then | ||||
| 					redirect.buffer.text[i] = redirect.buffer.text[i + n] | ||||
| 					redirect.buffer.textColor[i] = redirect.buffer.textColor[i + n] | ||||
| 					redirect.buffer.backColor[i] = redirect.buffer.backColor[i + n] | ||||
| 				end | ||||
| 			end | ||||
| 			for i = redirect.buffer.sizeY, redirect.buffer.sizeY - n + 1, -1 do | ||||
| 				redirect.buffer.text[i] = string.rep(" ", redirect.buffer.sizeX) | ||||
| 				redirect.buffer.textColor[i] = string.rep(redirect.buffer.curTextColor, redirect.buffer.sizeX) | ||||
| 				redirect.buffer.backColor[i] = string.rep(redirect.buffer.curBackColor, redirect.buffer.sizeX) | ||||
| 			end | ||||
| 		elseif n < 0 then | ||||
| 			for i = redirect.buffer.sizeY, math.abs(n) + 1, -1 do | ||||
| 				if redirect.buffer.text[i + n] then | ||||
| 					redirect.buffer.text[i] = redirect.buffer.text[i + n] | ||||
| 					redirect.buffer.textColor[i] = redirect.buffer.textColor[i + n] | ||||
| 					redirect.buffer.backColor[i] = redirect.buffer.backColor[i + n] | ||||
| 				end | ||||
| 			end | ||||
| 			for i = 1, math.abs(n) do | ||||
| 				redirect.buffer.text[i] = string.rep(" ", redirect.buffer.sizeX) | ||||
| 				redirect.buffer.textColor[i] = string.rep(redirect.buffer.curTextColor, redirect.buffer.sizeX) | ||||
| 				redirect.buffer.backColor[i] = string.rep(redirect.buffer.curBackColor, redirect.buffer.sizeX) | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| 	redirect.setTextColor = function(clr) | ||||
| 		if clr and clr <= 32768 and clr >= 1 then | ||||
| 			if redirect.buffer.color then | ||||
| 				redirect.buffer.curTextColor = string.format("%x", math.floor(math.log(clr) / math.log(2))) | ||||
| 			elseif clr == 1 or clr == 32768 then | ||||
| 				redirect.buffer.curTextColor = string.format("%x", math.floor(math.log(clr) / math.log(2))) | ||||
| 			else | ||||
| 				return nil, "Colour not supported" | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| 	redirect.setTextColour = redirect.setTextColor | ||||
| 	redirect.setBackgroundColor = function(clr) | ||||
| 		if clr and clr <= 32768 and clr >= 1 then | ||||
| 			if redirect.buffer.color then | ||||
| 				redirect.buffer.curBackColor = string.format("%x", math.floor(math.log(clr) / math.log(2))) | ||||
| 			elseif clr == 32768 or clr == 1 then | ||||
| 				redirect.buffer.curBackColor = string.format("%x", math.floor(math.log(clr) / math.log(2))) | ||||
| 			else | ||||
| 				return nil, "Colour not supported" | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| 	redirect.setBackgroundColour = redirect.setBackgroundColor | ||||
| 	redirect.isColor = function() | ||||
| 		return redirect.buffer.color == true | ||||
| 	end | ||||
| 	redirect.isColour = redirect.isColor | ||||
| 	redirect.render = function(inputBuffer) | ||||
| 		for i = 1, redirect.buffer.sizeY do | ||||
| 			redirect.buffer.text[i] = inputBuffer.text[i] | ||||
| 			redirect.buffer.textColor[i] = inputBuffer.textColor[i] | ||||
| 			redirect.buffer.backColor[i] = inputBuffer.backColor[i] | ||||
| 		end | ||||
| 	end | ||||
| 	redirect.clear() | ||||
| 	return redirect | ||||
| end | ||||
|  | ||||
| function draw(buffer, current) | ||||
| 	for i=1, buffer.sizeY do | ||||
| 		term.setCursorPos(1,i) | ||||
| 		if (current and (buffer.text[i] ~= current.text[i] or buffer.textColor[i] ~= current.textColor[i] or buffer.backColor[i] ~= current.backColor[i])) or not current then | ||||
| 			local lineEnd = false | ||||
| 			local offset = 1 | ||||
| 			while not lineEnd do | ||||
| 				local textColorString = string.match(string.sub(buffer.textColor[i], offset), string.sub(buffer.textColor[i], offset, offset).."*") | ||||
| 				local backColorString = string.match(string.sub(buffer.backColor[i], offset), string.sub(buffer.backColor[i], offset, offset).."*") | ||||
| 				term.setTextColor(2 ^ tonumber(string.sub(textColorString, 1, 1), 16)) | ||||
| 				term.setBackgroundColor(2 ^ tonumber(string.sub(backColorString, 1, 1), 16)) | ||||
| 				term.write(string.sub(buffer.text[i], offset, offset + math.min(#textColorString, #backColorString) - 1)) | ||||
| 				offset = offset + math.min(#textColorString, #backColorString) | ||||
| 				if offset > buffer.sizeX then lineEnd = true end | ||||
| 			end | ||||
| 			if current then | ||||
| 				current.text[i] = buffer.text[i] | ||||
| 				current.textColor[i] = buffer.textColor[i] | ||||
| 				current.backColor[i] = buffer.backColor[i] | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| 	term.setCursorPos(buffer.cursorX, buffer.cursorY) | ||||
| 	term.setTextColor(2 ^ tonumber(buffer.curTextColor, 16)) | ||||
| 	term.setBackgroundColor(2 ^ tonumber(buffer.curBackColor, 16)) | ||||
| 	term.setCursorBlink(buffer.cursorBlink) | ||||
| 	return current | ||||
| end | ||||
| @@ -1,26 +0,0 @@ | ||||
| if not nsh then print("No nsh session!") return end | ||||
|  | ||||
| local args = {...} | ||||
|  | ||||
| if #args < 2 then | ||||
| 	print("Usage: get <remote> <local>") | ||||
| 	print("<remote>: any file on the server") | ||||
| 	print("<local>: any non-existant file on the client") | ||||
| 	return | ||||
| end | ||||
|  | ||||
| if fs.exists(args[1]) then | ||||
| 	nsh.send("FS:;t="..args[2]) | ||||
| 	local message = nsh.receive() | ||||
| 	if message == "FR:;ok" then | ||||
| 		nsh.send("FH:;"..args[1]) | ||||
| 		local handle = io.open(args[1], "r") | ||||
| 		if handle then | ||||
| 			nsh.send("FD:;t="..handle:read("*a")) | ||||
| 			handle:close() | ||||
| 		end | ||||
| 		nsh.send("FE:;end") | ||||
| 	else | ||||
| 		print("Client rejected file!") | ||||
| 	end | ||||
| end | ||||
| @@ -1,721 +0,0 @@ | ||||
| local args = { ... } | ||||
|  | ||||
| local connections = {} | ||||
|  | ||||
| local nshAPI = { | ||||
| 	connList = connections | ||||
| } | ||||
|  | ||||
| if not framebuffer then if not ((fs.exists("framebuffer") and os.loadAPI("framebuffer")) or (fs.exists("LyqydOS/framebuffer") and os.loadAPI("LyqydOS/framebuffer"))) then print("Couldn't find framebuffer API, using fallback") end end | ||||
|  | ||||
| local function rawSend(id, msg) | ||||
| 	if term.current then | ||||
| 		return rednet.send(id, msg, "tror") | ||||
| 	else | ||||
| 		return rednet.send(id, msg) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| local function rawRecv(id, timeout) | ||||
| 	if type(timeout) == "number" then timeout = os.startTimer(timeout) end | ||||
| 	while true do | ||||
| 		event = {os.pullEvent()} | ||||
| 		if event[1] == "rednet_message" and (id == nil and true or event[2] == id) and (not term.current and true or event[4] == "tror") then | ||||
| 			return event[3] | ||||
| 		elseif event[1] == "timer" and event[2] == timeout then | ||||
| 			return nil | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
|  | ||||
| nshAPI.getRemoteID = function() | ||||
| 	--check for connected clients with matching threads. | ||||
| 	for cNum, cInfo in pairs(nshAPI.connList) do | ||||
| 		if cInfo.thread == coroutine.running() then | ||||
| 			if cNum == "localShell" then | ||||
| 				--if we are a client running on the server, return the remote server ID. | ||||
| 				if nshAPI.serverNum then | ||||
| 					return nshAPI.serverNum | ||||
| 				else | ||||
| 					return nil | ||||
| 				end | ||||
| 			end | ||||
| 			return cNum | ||||
| 		end | ||||
| 	end | ||||
| 	--client running without local server, return remote server ID. | ||||
| 	if nshAPI.serverNum then return nshAPI.serverNum end | ||||
| 	return nil | ||||
| end | ||||
|  | ||||
| nshAPI.send = function(msg) | ||||
| 	local id = nshAPI.getRemoteID() | ||||
| 	if id then | ||||
| 		return rawSend(id, msg) | ||||
| 	end | ||||
| 	return nil | ||||
| end | ||||
|  | ||||
| nshAPI.receive = function(timeout) | ||||
| 	return rawRecv(nshAPI.getRemoteID(), timeout) | ||||
| end | ||||
|  | ||||
| nshAPI.getClientCapabilities = function() | ||||
| 	if nshAPI.clientCapabilities then return nshAPI.clientCapabilities end | ||||
| 	nshAPI.send("SP:;clientCapabilities") | ||||
| 	return nshAPI.receive(1) | ||||
| end | ||||
|  | ||||
| nshAPI.getRemoteConnections = function() | ||||
| 	local remotes = {} | ||||
| 	for cNum, cInfo in pairs(nshAPI.connList) do | ||||
| 		table.insert(remotes, cNum) | ||||
| 		if cInfo.outbound then | ||||
| 			table.insert(remotes, cInfo.outbound) | ||||
| 		end | ||||
| 	end | ||||
| 	return remotes | ||||
| end | ||||
|  | ||||
| nshAPI.packFile = function(path) | ||||
| 	local data = {} | ||||
| 	local count = 0 | ||||
| 	local handle = io.open(path, "rb") | ||||
| 	if handle then | ||||
| 		local byte = handle:read() | ||||
| 		repeat | ||||
| 			data[#data + 1] = byte | ||||
| 			count = count + 1 | ||||
| 			if count % 1000 == 0 then | ||||
| 				os.queueEvent("yield") | ||||
| 				os.pullEvent("yield") | ||||
| 			end | ||||
| 			byte = handle:read() | ||||
| 		until not byte | ||||
| 		handle:close() | ||||
| 	else | ||||
| 		return false | ||||
| 	end | ||||
| 	local outputTable = {} | ||||
| 	for i = 1, #data, 3 do | ||||
| 		local num1, num2, num3 = data[i], data[i + 1] or 0, data[i + 2] or 0 | ||||
| 		table.insert(outputTable, string.char(bit32.band(bit32.arshift(num1, 2), 63))) | ||||
| 		table.insert(outputTable, string.char(bit32.bor(bit32.band(bit32.lshift(num1, 4), 48), bit32.band(bit32.arshift(num2, 4), 15)))) | ||||
| 		table.insert(outputTable, string.char(bit32.bor(bit32.band(bit32.lshift(num2, 2), 60), bit32.band(bit32.arshift(num3, 6), 3)))) | ||||
| 		table.insert(outputTable, string.char(bit32.band(num3, 63))) | ||||
| 	end | ||||
| 	--mark non-data (invalid) bytes | ||||
| 	if #data % 3 == 1 then | ||||
| 		outputTable[#outputTable] = "=" | ||||
| 		outputTable[#outputTable - 1] = "=" | ||||
| 	elseif #data % 3 == 2 then | ||||
| 		outputTable[#outputTable] = "=" | ||||
| 	end | ||||
| 	return table.concat(outputTable, "") | ||||
| end | ||||
|  | ||||
| nshAPI.unpackAndSaveFile = function(path, data) | ||||
| 	local outputTable = {} | ||||
| 	for i=1, #data, 4 do | ||||
| 		local char1, char2, char3, char4 = string.byte(string.sub(data, i, i)), string.byte(string.sub(data, i + 1, i + 1)), string.byte(string.sub(data, i + 2, i + 2)), string.byte(string.sub(data, i + 3, i + 3)) | ||||
| 		table.insert(outputTable, bit32.band(bit32.bor(bit32.lshift(char1, 2), bit32.arshift(char2, 4)), 255)) | ||||
| 		table.insert(outputTable, bit32.band(bit32.bor(bit32.lshift(char2, 4), bit32.arshift(char3, 2)), 255)) | ||||
| 		table.insert(outputTable, bit32.band(bit32.bor(bit32.lshift(char3, 6), char4), 255)) | ||||
| 	end | ||||
| 	--clean invalid bytes if marked | ||||
| 	if string.sub(data, #data, #data) == "=" then | ||||
| 		table.remove(outputTable) | ||||
| 		if string.sub(data, #data - 1, #data - 1) == "=" then | ||||
| 			table.remove(outputTable) | ||||
| 		end | ||||
| 	end | ||||
| 	local handle = io.open(path, "wb") | ||||
| 	if handle then | ||||
| 		for i = 1, #outputTable do | ||||
| 			handle:write(outputTable[i]) | ||||
| 			if i % 10 == 0 then | ||||
| 				os.startTimer(0.1) | ||||
| 				os.pullEvent("timer") | ||||
| 			end | ||||
| 		end | ||||
| 		handle:close() | ||||
| 	end | ||||
| end | ||||
|  | ||||
| local packetConversion = { | ||||
| 	query = "SQ", | ||||
| 	response = "SR", | ||||
| 	data = "SP", | ||||
| 	close = "SC", | ||||
| 	fileQuery = "FQ", | ||||
| 	fileSend = "FS", | ||||
| 	fileResponse = "FR", | ||||
| 	fileHeader = "FH", | ||||
| 	fileData = "FD", | ||||
| 	fileEnd = "FE", | ||||
| 	textWrite = "TW", | ||||
| 	textCursorPos = "TC", | ||||
| 	textGetCursorPos = "TG", | ||||
| 	textGetSize = "TD", | ||||
| 	textInfo = "TI", | ||||
| 	textClear = "TE", | ||||
| 	textClearLine = "TL", | ||||
| 	textScroll = "TS", | ||||
| 	textBlink = "TB", | ||||
| 	textColor = "TF", | ||||
| 	textBackground = "TK", | ||||
| 	textIsColor = "TA", | ||||
| 	textTable = "TT", | ||||
| 	event = "EV", | ||||
| 	SQ = "query", | ||||
| 	SR = "response", | ||||
| 	SP = "data", | ||||
| 	SC = "close", | ||||
| 	FQ = "fileQuery", | ||||
| 	FS = "fileSend", | ||||
| 	FR = "fileResponse", | ||||
| 	FH = "fileHeader", | ||||
| 	FD = "fileData", | ||||
| 	FE = "fileEnd", | ||||
| 	TW = "textWrite", | ||||
| 	TC = "textCursorPos", | ||||
| 	TG = "textGetCursorPos", | ||||
| 	TD = "textGetSize", | ||||
| 	TI = "textInfo", | ||||
| 	TE = "textClear", | ||||
| 	TL = "textClearLine", | ||||
| 	TS = "textScroll", | ||||
| 	TB = "textBlink", | ||||
| 	TF = "textColor", | ||||
| 	TK = "textBackground", | ||||
| 	TA = "textIsColor", | ||||
| 	TT = "textTable", | ||||
| 	EV = "event", | ||||
| } | ||||
|  | ||||
| local function openModem() | ||||
| 	local modemFound = false | ||||
| 	for _, side in ipairs(rs.getSides()) do | ||||
| 		if peripheral.getType(side) == "modem" then | ||||
| 			if not rednet.isOpen(side) then rednet.open(side) end | ||||
| 			modemFound = true | ||||
| 			break | ||||
| 		end | ||||
| 	end | ||||
| 	return modemFound | ||||
| end | ||||
|  | ||||
| local function send(id, pType, message) | ||||
| 	if pType and message then | ||||
| 		return rawSend(id, packetConversion[pType]..":;"..message) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| local function awaitResponse(id, time) | ||||
| 	id = tonumber(id) | ||||
| 	local listenTimeOut = nil | ||||
| 	local messRecv = false | ||||
| 	if time then listenTimeOut = os.startTimer(time) end | ||||
| 	while not messRecv do | ||||
| 		local event, p1, p2 = os.pullEvent() | ||||
| 		if event == "timer" and p1 == listenTimeOut then | ||||
| 			return false | ||||
| 		elseif event == "rednet_message" then | ||||
| 			sender, message = p1, p2 | ||||
| 			if id == sender and message then | ||||
| 				if packetConversion[string.sub(message, 1, 2)] then packetType = packetConversion[string.sub(message, 1, 2)] end | ||||
| 				message = string.match(message, ";(.*)") | ||||
| 				messRecv = true | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| 	return packetType, message | ||||
| end | ||||
|  | ||||
| local function processText(conn, pType, value) | ||||
| 	if not pType then return false end | ||||
| 	if pType == "textWrite" and value then | ||||
| 		term.write(value) | ||||
| 	elseif pType == "textClear" then | ||||
| 		term.clear() | ||||
| 	elseif pType == "textClearLine" then | ||||
| 		term.clearLine() | ||||
| 	elseif pType == "textGetCursorPos" then | ||||
| 		local x, y = term.getCursorPos() | ||||
| 		send(conn, "textInfo", math.floor(x)..","..math.floor(y)) | ||||
| 	elseif pType == "textCursorPos" then | ||||
| 		local x, y = string.match(value, "(%-?%d+),(%-?%d+)") | ||||
| 		term.setCursorPos(tonumber(x), tonumber(y)) | ||||
| 	elseif pType == "textBlink" then | ||||
| 		if value == "true" then | ||||
| 			term.setCursorBlink(true) | ||||
| 		else | ||||
| 			term.setCursorBlink(false) | ||||
| 		end | ||||
| 	elseif pType == "textGetSize" then | ||||
| 		x, y = term.getSize() | ||||
| 		send(conn, "textInfo", x..","..y) | ||||
| 	elseif pType == "textScroll" and value then | ||||
| 		term.scroll(tonumber(value)) | ||||
| 	elseif pType == "textIsColor" then | ||||
| 		send(conn, "textInfo", tostring(term.isColor())) | ||||
| 	elseif pType == "textColor" and value then | ||||
| 		value = tonumber(value) | ||||
| 		if (value == 1 or value == 32768) or term.isColor() then | ||||
| 			term.setTextColor(value) | ||||
| 		end | ||||
| 	elseif pType == "textBackground" and value then | ||||
| 		value = tonumber(value) | ||||
| 		if (value == 1 or value == 32768) or term.isColor() then | ||||
| 			term.setBackgroundColor(value) | ||||
| 		end | ||||
| 	elseif pType == "textTable" then | ||||
| 		local linesTable = textutils.unserialize(value) | ||||
| 		for i=1, linesTable.sizeY do | ||||
| 			term.setCursorPos(1,i) | ||||
| 			local lineEnd = false | ||||
| 			local offset = 1 | ||||
| 			while not lineEnd do | ||||
| 				local textColorString = string.match(string.sub(linesTable.textColor[i], offset), string.sub(linesTable.textColor[i], offset, offset).."*") | ||||
| 				local backColorString = string.match(string.sub(linesTable.backColor[i], offset), string.sub(linesTable.backColor[i], offset, offset).."*") | ||||
| 				term.setTextColor(2 ^ tonumber(string.sub(textColorString, 1, 1), 16)) | ||||
| 				term.setBackgroundColor(2 ^ tonumber(string.sub(backColorString, 1, 1), 16)) | ||||
| 				term.write(string.sub(linesTable.text[i], offset, offset + math.min(#textColorString, #backColorString) - 1)) | ||||
| 				offset = offset + math.min(#textColorString, #backColorString) | ||||
| 				if offset > linesTable.sizeX then lineEnd = true end | ||||
| 			end | ||||
| 		end | ||||
| 		term.setCursorPos(linesTable.cursorX, linesTable.cursorY) | ||||
| 		term.setCursorBlink(linesTable.cursorBlink) | ||||
| 	end | ||||
| 	return | ||||
| end | ||||
|  | ||||
| local function textRedirect(id) | ||||
| 	local textTable = {} | ||||
| 	textTable.id = id | ||||
| 	textTable.write = function(text) | ||||
| 		return send(textTable.id, "textWrite", text) | ||||
| 	end | ||||
| 	textTable.clear = function() | ||||
| 		return send(textTable.id, "textClear", "nil") | ||||
| 	end | ||||
| 	textTable.clearLine = function() | ||||
| 		return send(textTable.id, "textClearLine", "nil") | ||||
| 	end | ||||
| 	textTable.getCursorPos = function() | ||||
| 		send(textTable.id, "textGetCursorPos", "nil") | ||||
| 		local pType, message = awaitResponse(textTable.id, 2) | ||||
| 		if pType and pType == "textInfo" then | ||||
| 			local x, y = string.match(message, "(%-?%d+),(%-?%d+)") | ||||
| 			return tonumber(x), tonumber(y) | ||||
| 		end | ||||
| 	end | ||||
| 	textTable.setCursorPos = function(x, y) | ||||
| 		return send(textTable.id, "textCursorPos", math.floor(x)..","..math.floor(y)) | ||||
| 	end | ||||
| 	textTable.setCursorBlink = function(b) | ||||
| 		if b then | ||||
| 			return send(textTable.id, "textBlink", "true") | ||||
| 		else | ||||
| 			return send(textTable.id, "textBlink", "false") | ||||
| 		end | ||||
| 	end | ||||
| 	textTable.getSize = function() | ||||
| 		send(textTable.id, "textGetSize", "nil") | ||||
| 		local pType, message = awaitResponse(textTable.id, 2) | ||||
| 		if pType and pType == "textInfo" then | ||||
| 			local x, y = string.match(message, "(%d+),(%d+)") | ||||
| 			return tonumber(x), tonumber(y) | ||||
| 		end | ||||
| 	end | ||||
| 	textTable.scroll = function(lines) | ||||
| 		return send(textTable.id, "textScroll", lines) | ||||
| 	end | ||||
| 	textTable.isColor = function() | ||||
| 		send(textTable.id, "textIsColor", "nil") | ||||
| 		local pType, message = awaitResponse(textTable.id, 2) | ||||
| 		if pType and pType == "textInfo" then | ||||
| 			if message == "true" then | ||||
| 				return true | ||||
| 			end | ||||
| 		end | ||||
| 		return false | ||||
| 	end | ||||
| 	textTable.isColour = textTable.isColor | ||||
| 	textTable.setTextColor = function(color) | ||||
| 		return send(textTable.id, "textColor", tostring(color)) | ||||
| 	end | ||||
| 	textTable.setTextColour = textTable.setTextColor | ||||
| 	textTable.setBackgroundColor = function(color) | ||||
| 		return send(textTable.id, "textBackground", tostring(color)) | ||||
| 	end | ||||
| 	textTable.setBackgroundColour = textTable.setBackgroundColor | ||||
| 	return textTable | ||||
| end | ||||
|  | ||||
| local function getServerID(server) | ||||
| 	if tonumber(server) then | ||||
| 		return tonumber(server) | ||||
| 	elseif term.current then | ||||
| 		return rednet.lookup("tror", args[1]) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| local function resumeThread(conn, event) | ||||
| 	local cInfo = connections[conn] | ||||
| 	if not connections[conn].filter or event[1] == connections[conn].filter then | ||||
| 		connections[conn].filter = nil | ||||
| 		local _oldTerm = term.redirect(connections[conn].target) | ||||
| 		local passback = {coroutine.resume(connections[conn].thread, table.unpack(event))} | ||||
| 		if passback[1] and passback[2] then | ||||
| 			connections[conn].filter = passback[2] | ||||
| 		end | ||||
| 		if coroutine.status(connections[conn].thread) == "dead" then | ||||
| 			send(conn, "close", "disconnect") | ||||
| 			connections[conn] = nil | ||||
| 		end | ||||
| 		if _oldTerm then | ||||
| 			term.redirect(_oldTerm) | ||||
| 		else | ||||
| 			term.restore() | ||||
| 		end | ||||
| 		if connections[conn] and conn ~= "localShell" and framebuffer then | ||||
| 			send(conn, "textTable", textutils.serialize(connections[conn].target.buffer)) | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| local eventFilter = { | ||||
| 	key = true, | ||||
| 	char = true, | ||||
| 	mouse_click = true, | ||||
| 	mouse_drag = true, | ||||
| 	mouse_scroll = true, | ||||
| } | ||||
|  | ||||
| local function newSession(conn, x, y, color) | ||||
| 	local session = {} | ||||
| 	local path = "/rom/programs/shell" | ||||
| 	if #args >= 2 and shell.resolveProgram(args[2]) then path = shell.resolveProgram(args[2]) end | ||||
| 	session.thread = coroutine.create(function() shell.run(path) end) | ||||
| 	if framebuffer then | ||||
| 		session.target = framebuffer.new(x, y, color) | ||||
| 	else | ||||
| 		session.target = textRedirect(conn) | ||||
| 	end | ||||
| 	session.status = "open" | ||||
| 	_oldTerm = term.redirect(session.target) | ||||
| 	coroutine.resume(session.thread) | ||||
| 	if _oldTerm then | ||||
| 		term.redirect(_oldTerm) | ||||
| 	else | ||||
| 		term.restore() | ||||
| 	end | ||||
| 	if framebuffer then | ||||
| 		send(conn, "textTable", textutils.serialize(session.target.buffer)) | ||||
| 	end | ||||
| 	return session | ||||
| end | ||||
|  | ||||
| if #args >= 1 and args[1] == "host" then | ||||
| 	_G.nsh = nshAPI | ||||
| 	if not openModem() then return end | ||||
| 	if term.current then | ||||
| 		if args[4] then | ||||
| 			rednet.host("tror", args[4]) | ||||
| 		elseif os.getComputerLabel() then | ||||
| 			rednet.host("tror", os.getComputerLabel()) | ||||
| 		else | ||||
| 			print("No label or hostname provided!") | ||||
| 			return | ||||
| 		end | ||||
| 	end | ||||
| 	local connInfo = {} | ||||
| 	connInfo.target = term.current and term.current() or term.native | ||||
| 	local path = "/rom/programs/shell" | ||||
| 	if #args >= 3 and shell.resolveProgram(args[3]) then path = shell.resolveProgram(args[3]) end | ||||
| 	connInfo.thread = coroutine.create(function() shell.run(path) end) | ||||
| 	connections.localShell = connInfo | ||||
| 	term.clear() | ||||
| 	term.setCursorPos(1,1) | ||||
| 	coroutine.resume(connections.localShell.thread) | ||||
|  | ||||
| 	while true do | ||||
| 		event = {os.pullEventRaw()} | ||||
| 		if event[1] == "rednet_message" then | ||||
| 			if type(event[3]) == "string" and packetConversion[string.sub(event[3], 1, 2)] then | ||||
| 				--this is a packet meant for us. | ||||
| 				conn = event[2] | ||||
| 				packetType = packetConversion[string.sub(event[3], 1, 2)] | ||||
| 				message = string.match(event[3], ";(.*)") | ||||
| 				if connections[conn] and connections[conn].status == "open" then | ||||
| 					if packetType == "event" or string.sub(packetType, 1, 4) == "text" then | ||||
| 						local eventTable = {} | ||||
| 						if packetType == "event" then | ||||
| 							eventTable = textutils.unserialize(message) | ||||
| 						else | ||||
| 							--we can pass the packet in raw, since this is not an event packet. | ||||
| 							eventTable = event | ||||
| 						end | ||||
| 						resumeThread(conn, eventTable) | ||||
| 					elseif packetType == "query" then | ||||
| 						local connType, color, x, y = string.match(message, "(%a+):(%a+);(%d+),(%d+)") | ||||
| 						if connType == "connect" or (connType == "resume" and (not framebuffer)) then | ||||
| 							--reset connection | ||||
| 							send(conn, "response", "OK") | ||||
| 							connections[conn] = newSession(conn, tonumber(x), tonumber(y), color == "true") | ||||
| 						elseif connType == "resume" then | ||||
| 							--restore connection | ||||
| 							send(conn, "response", "OK") | ||||
| 							send(conn, "textTable", textutils.serialize(connections[conn].target.buffer)) | ||||
| 						end | ||||
| 					elseif packetType == "close" then | ||||
| 						connections[conn] = nil | ||||
| 						send(conn, "close", "disconnect") | ||||
| 						--close connection | ||||
| 					else | ||||
| 						--we got a packet, have an open connection, but despite it being in the conversion table, don't handle it ourselves. Send it onward. | ||||
| 						resumeThread(conn, event) | ||||
| 					end | ||||
| 				elseif packetType ~= "query" then | ||||
| 					--usually, we would send a disconnect here, but this prevents one from hosting nsh and connecting to other computers.  Pass these to all shells as well. | ||||
| 					for cNum, cInfo in pairs(connections) do | ||||
| 						resumeThread(cNum, event) | ||||
| 					end | ||||
| 				else | ||||
| 					--open new connection | ||||
| 					send(conn, "response", "OK") | ||||
| 					local color, x, y = string.match(message, "connect:(%a+);(%d+),(%d+)") | ||||
| 					local connInfo = newSession(conn, tonumber(x), tonumber(y), color == "true") | ||||
| 					connections[conn] = connInfo | ||||
| 				end | ||||
| 			else | ||||
| 				--rednet message, but not in the correct format, so pass to all shells. | ||||
| 				for cNum, cInfo in pairs(connections) do | ||||
| 					resumeThread(cNum, event) | ||||
| 				end | ||||
| 			end | ||||
| 		elseif eventFilter[event[1]] then | ||||
| 			--user interaction. | ||||
| 			coroutine.resume(connections.localShell.thread, table.unpack(event)) | ||||
| 			if coroutine.status(connections.localShell.thread) == "dead" then | ||||
| 				for cNum, cInfo in pairs(connections) do | ||||
| 					if cNum ~= "localShell" then | ||||
| 						send(cNum, "close", "disconnect") | ||||
| 					end | ||||
| 				end | ||||
| 				return | ||||
| 			end | ||||
| 		else | ||||
| 			--dispatch all other events to all shells | ||||
| 			for cNum, cInfo in pairs(connections) do | ||||
| 				resumeThread(cNum, event) | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| elseif #args <= 2 and nsh and nsh.getRemoteID() then | ||||
| 	print(nsh.getRemoteID()) | ||||
| 	--forwarding mode | ||||
| 	local conns = nsh.getRemoteConnections() | ||||
| 	for i = 1, #conns do | ||||
| 		if conns[i] == serverNum then | ||||
| 			print("Cyclic connection refused.") | ||||
| 			return | ||||
| 		end | ||||
| 	end | ||||
| 	local fileTransferState = nil | ||||
| 	local fileData = nil | ||||
| 	local serverNum = getServerID(args[1]) | ||||
| 	if not serverNum then | ||||
| 		print("Server Not Found") | ||||
| 		return | ||||
| 	end | ||||
| 	send(serverNum, "query", "connect") | ||||
| 	local pType, message = awaitResponse(serverNum, 2) | ||||
| 	if pType ~= "response" then | ||||
| 		print("Connection Failed") | ||||
| 		return | ||||
| 	else | ||||
| 		nsh.connList[nsh.getRemoteID()].outbound = serverNum | ||||
| 		term.clear() | ||||
| 		term.setCursorPos(1,1) | ||||
| 	end | ||||
| 	local clientID = nsh.getRemoteID() | ||||
| 	local serverID = tonumber(args[1]) | ||||
| 	while true do | ||||
| 		event = {os.pullEvent()} | ||||
| 		if event[1] == "rednet_message" then | ||||
| 			if event[2] == clientID or event[2] == serverID then | ||||
| 				if event[2] == serverID and string.sub(event[3], 1, 2) == "SC" then break end | ||||
| 				rednet.send((event[2] == clientID and serverID or clientID), event[3]) | ||||
| 			end | ||||
| 		elseif eventFilter[event[1]] then | ||||
| 			rednet.send(serverID, "EV:;"..textutils.serialize(event)) | ||||
| 		end | ||||
| 	end | ||||
| 	nsh.connList[nsh.getRemoteID()].outbound = nil | ||||
| 	term.clear() | ||||
| 	term.setCursorPos(1, 1) | ||||
| 	print("Connection closed by server") | ||||
|  | ||||
| elseif #args >= 1 then --either no server running or we are the local shell on the server. | ||||
| 	if not openModem() then return end | ||||
| 	local serverNum = getServerID(args[1]) | ||||
| 	if not serverNum then | ||||
| 		print("Server Not Found") | ||||
| 		return | ||||
| 	end | ||||
| 	if nsh then | ||||
| 		local conns = nsh.getRemoteConnections() | ||||
| 		for i = 1, #conns do | ||||
| 			if conns[i] == serverNum then | ||||
| 				print("Connection refused.") | ||||
| 				return | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| 	local fileTransferState = nil | ||||
| 	local fileData = nil | ||||
| 	local fileBinaryData = nil | ||||
| 	local unpackCo = {} | ||||
| 	local color = term.isColor() | ||||
| 	local x, y = term.getSize() | ||||
| 	if args[2] == "resume" then | ||||
| 		send(serverNum, "query", "resume:"..tostring(color)..";"..tostring(x)..","..tostring(y)) | ||||
| 	else | ||||
| 		send(serverNum, "query", "connect:"..tostring(color)..";"..tostring(x)..","..tostring(y)) | ||||
| 	end | ||||
| 	local timeout = os.startTimer(2) | ||||
| 	while true do | ||||
| 		local event = {os.pullEvent()} | ||||
| 		if event[1] == "timer" and event[2] == timeout then | ||||
| 			print("Connection failed.") | ||||
| 			return | ||||
| 		elseif event[1] == "rednet_message" and event[2] == serverNum and string.sub(event[3], 1, 2) == "SR" then | ||||
| 			if nsh then nshAPI = nsh end | ||||
| 			if nshAPI.connList and nshAPI.connList.localShell then nshAPI.connList.localShell.outbound = serverNum end | ||||
| 			nshAPI.serverNum = serverNum | ||||
| 			nshAPI.clientCapabilities = "-fileTransfer-extensions-" | ||||
| 			term.clear() | ||||
| 			term.setCursorPos(1,1) | ||||
| 			break | ||||
| 		end | ||||
| 	end | ||||
|  | ||||
| 	while true do | ||||
| 		event = {os.pullEventRaw()} | ||||
| 		if #unpackCo > 0 then | ||||
| 			for i = #unpackCo, 1, -1 do | ||||
| 				if coroutine.status(unpackCo[i]) ~= "dead" then | ||||
| 					coroutine.resume(unpackCo[i], table.unpack(event)) | ||||
| 				else | ||||
| 					table.remove(unpackCo, i) | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 		if event[1] == "rednet_message" and event[2] == serverNum then | ||||
| 			if packetConversion[string.sub(event[3], 1, 2)] then | ||||
| 				packetType = packetConversion[string.sub(event[3], 1, 2)] | ||||
| 				message = string.match(event[3], ";(.*)") | ||||
| 				if string.sub(packetType, 1, 4) == "text" then | ||||
| 					processText(serverNum, packetType, message) | ||||
| 				elseif packetType == "data" then | ||||
| 					if message == "clientCapabilities" then | ||||
| 						rednet.send(serverNum, nshAPI.clientCapabilities) | ||||
| 					end | ||||
| 				elseif packetType == "fileQuery" then | ||||
| 					--send a file to the server | ||||
| 					local mode, file = string.match(message, "^(%a)=(.*)") | ||||
| 					if fs.exists(file) then | ||||
| 						send(serverNum, "fileHeader", file) | ||||
| 						if mode == "b" then | ||||
| 							local fileString = nshAPI.packFile(file) | ||||
| 							send(serverNum, "fileData", "b="..fileString) | ||||
| 						else | ||||
| 							local handle = io.open(file, "r") | ||||
| 							if handle then | ||||
| 								send(serverNum, "fileData", "t="..handle:read("*a")) | ||||
| 								handle:close() | ||||
| 							end | ||||
| 						end | ||||
| 					else | ||||
| 						send(serverNum, "fileHeader", "fileNotFound") | ||||
| 					end | ||||
| 					send(serverNum, "fileEnd", "end") | ||||
| 				elseif packetType == "fileSend" then | ||||
| 					--receive a file from the server, but don't overwrite existing files. | ||||
| 					local mode, file = string.match(message, "^(%a)=(.*)") | ||||
| 					if not fs.exists(file) then | ||||
| 						fileTransferState = "receive_wait:"..file | ||||
| 						send(serverNum, "fileResponse", "ok") | ||||
| 						if mode == "b" then | ||||
| 							fileBinaryData = "" | ||||
| 							fileData = nil | ||||
| 						else | ||||
| 							fileData = "" | ||||
| 							fileBinaryData = nil | ||||
| 						end | ||||
| 					else | ||||
| 						send(serverNum, "fileResponse", "reject") | ||||
| 					end | ||||
| 				elseif packetType == "fileHeader" then | ||||
| 					if message == "fileNotFound" then | ||||
| 						fileTransferState = nil | ||||
| 					end | ||||
| 				elseif packetType == "fileData" then | ||||
| 					if fileTransferState and string.match(fileTransferState, "(.-):") == "receive_wait" then | ||||
| 						if string.match(message, "^(%a)=") == "b" then | ||||
| 							fileBinaryData = fileBinaryData..string.match(message, "^b=(.*)") | ||||
| 						else | ||||
| 							fileData = fileData..string.match(message, "^t=(.*)") | ||||
| 						end | ||||
| 					end | ||||
| 				elseif packetType == "fileEnd" then | ||||
| 					if fileTransferState and string.match(fileTransferState, "(.-):") == "receive_wait" then | ||||
| 						if fileBinaryData then | ||||
| 							local co = coroutine.create(nshAPI.unpackAndSaveFile) | ||||
| 							coroutine.resume(co, string.match(fileTransferState, ":(.*)"), fileBinaryData) | ||||
| 							if coroutine.status(co) ~= "dead" then | ||||
| 								table.insert(unpackCo, co) | ||||
| 							end | ||||
| 						elseif fileData then | ||||
| 							local handle = io.open(string.match(fileTransferState, ":(.*)"), "w") | ||||
| 							if handle then | ||||
| 								handle:write(fileData) | ||||
| 								handle:close() | ||||
| 							end | ||||
| 						end | ||||
| 						fileTransferState = nil | ||||
| 					end | ||||
| 				elseif packetType == "close" then | ||||
| 					if term.isColor() then | ||||
| 						term.setBackgroundColor(colors.black) | ||||
| 						term.setTextColor(colors.white) | ||||
| 					end | ||||
| 					term.clear() | ||||
| 					term.setCursorPos(1, 1) | ||||
| 					print("Connection closed by server.") | ||||
| 					nshAPI.serverNum = nil | ||||
| 					if nshAPI.connList and nshAPI.connList.localShell then nshAPI.connList.localShell.outbound = nil end | ||||
| 					return | ||||
| 				end | ||||
| 			end | ||||
| 		elseif event[1] == "mouse_click" or event[1] == "mouse_drag" or event[1] == "mouse_scroll" or event[1] == "key" or event[1] == "char" then | ||||
| 			--pack up event | ||||
| 			send(serverNum, "event", textutils.serialize(event)) | ||||
| 		elseif event[1] == "terminate" then | ||||
| 			nshAPI.serverNum = nil | ||||
| 			if nshAPI.localShell then nshAPI.localShell.outbound = nil end | ||||
| 			term.clear() | ||||
| 			term.setCursorPos(1, 1) | ||||
| 			print("Connection closed locally.") | ||||
| 			return | ||||
| 		end | ||||
| 	end | ||||
| else | ||||
| 	print("Usage: nsh <serverID> [resume]") | ||||
| 	print("       nsh host [remote [local [name]]]") | ||||
| end | ||||
| @@ -1,35 +0,0 @@ | ||||
| if not nsh then print("No nsh session!") return end | ||||
|  | ||||
| local args = {...} | ||||
|  | ||||
| if #args < 2 then | ||||
| 	print("Usage: put <local> <remote>") | ||||
| 	print("<local>: any file on the client") | ||||
| 	print("<remote>: any file on the server") | ||||
| 	return | ||||
| end | ||||
|  | ||||
| local fileData = "" | ||||
|  | ||||
| nsh.send("FQ:;t="..args[1]) | ||||
| local message = nsh.receive() | ||||
| if message ~= "fileNotFound" then | ||||
| 	while true do | ||||
| 		message = nsh.receive() | ||||
| 		pType = string.sub(message, 1, 2) | ||||
| 		if pType == "FD" then | ||||
| 			fileData = fileData..string.match(message, "^FD:;t=(.*)") | ||||
| 		elseif pType == "FE" then | ||||
| 			break | ||||
| 		end | ||||
| 	end | ||||
| 	if #fileData > 0 then | ||||
| 		local handle = io.open(args[2], "w") | ||||
| 		if handle then | ||||
| 			handle:write(fileData) | ||||
| 			handle:close() | ||||
| 		end | ||||
| 	else | ||||
| 		print("Empty file not written!") | ||||
| 	end | ||||
| end | ||||
| @@ -1,444 +0,0 @@ | ||||
| --[[ | ||||
| Author: TheOriginalBIT | ||||
| Version: 1.1.2 | ||||
| Created: 26 APR 2013 | ||||
| Last Update: 30 APR 2013 | ||||
|  | ||||
| License: | ||||
|  | ||||
| COPYRIGHT NOTICE | ||||
| Copyright © 2013 Joshua Asbury a.k.a TheOriginalBIT [theoriginalbit@gmail.com] | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy of this software and | ||||
| associated documentation files (the "Software"), to deal in the Software without restriction, | ||||
| including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||
| copies of the Software, and to permit persons to whom the Software is furnished to do so, | ||||
| subject to the following conditions: | ||||
|  | ||||
| -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||||
| -Visible credit is given to the original author. | ||||
| -The software is distributed in a non-profit way. | ||||
|  | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | ||||
| WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||||
| COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||||
| ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
| ]]-- | ||||
|  | ||||
| -- make sure that its only a computer terminal that is displaying | ||||
| local sw, sh = term.getSize() | ||||
|  | ||||
| if sw ~= 51 and sh ~= 19 then | ||||
|   error("Sorry this game can only run on computers", 0) | ||||
| end | ||||
|  | ||||
| -- the wining directions | ||||
| local winCombos = { | ||||
|   -- horizontal | ||||
|   {1,2,3}, {4,5,6}, {7,8,9}, | ||||
|   -- vertical | ||||
|   {1,4,7}, {2,5,8}, {3,6,9}, | ||||
|   -- diagonal | ||||
|   {1,5,9}, {3,5,7} | ||||
| } | ||||
|  | ||||
| local players = {x = 'Player', o = 'The Computer'} | ||||
| -- whether an AI is active, could be used later to allow SP | ||||
| local activeAI = true | ||||
| local currentPlayer | ||||
| local opposites = { x = 'o', o = 'x' } | ||||
| local board | ||||
| local winner | ||||
| local move | ||||
| local allowedBgColors = { colors.orange, colors.lightBlue, colors.gray, colors.cyan, colors.purple, colors.blue, colors.brown, colors.green, colors.red, colors.black } | ||||
| local bg | ||||
|  | ||||
| local function clear(col) | ||||
|   term.setBackgroundColor(col or colors.black) | ||||
|   term.clear() | ||||
|   term.setCursorPos(1,1) | ||||
| end | ||||
|  | ||||
| -- function thanks to Mads... found here: http://www.computercraft.info/forums2/index.php?/topic/11771-print-coloured-text-easily/page__p__105389#entry105389 | ||||
| local function writeWithFormat(...) | ||||
|   local s = "&0" | ||||
|   for k, v in ipairs(arg) do | ||||
|     s = s .. v | ||||
|   end | ||||
|   s = s .. "&0" | ||||
|   local fields = {} | ||||
|   local lastcolor, lastpos = "0", 0 | ||||
|   for pos, clr in s:gmatch"()&(%x)" do | ||||
|     table.insert(fields, {s:sub(lastpos + 2, pos - 1), lastcolor}) | ||||
|     lastcolor, lastpos = clr , pos | ||||
|   end | ||||
|   for i = 2, #fields do | ||||
|     term.setTextColor(2 ^ (tonumber(fields[i][2], 16))) | ||||
|     write(fields[i][1]) | ||||
|   end | ||||
| end | ||||
|  | ||||
| -- modification of Mads' function to get the length of the string without the color modifiers | ||||
| local function countFormatters(text) | ||||
|   return #(text:gsub("()&(%x)", '')) | ||||
| end | ||||
|  | ||||
| -- print a color formatted string in the center of the screen | ||||
| local function cwriteWithFormat(text, y) | ||||
|   local sw,sh = term.getSize() | ||||
|   local _,cy = term.getCursorPos() | ||||
|   term.setCursorPos(math.floor((sw-countFormatters(text))/2)+(countFormatters(text) % 2 == 0 and 1 or 0), y or cy) | ||||
|   writeWithFormat(text) | ||||
| end | ||||
|  | ||||
| -- writes the text at the give location | ||||
| local function writeAt(text, x, y) | ||||
|   local _,cy = term.getCursorPos() | ||||
|   term.setCursorPos(x or 1, y or cy) | ||||
|   write(text) | ||||
| end | ||||
|  | ||||
| local function reset() | ||||
|   bg = allowedBgColors[math.random(1, #allowedBgColors)] | ||||
|   currentPlayer = 'x' | ||||
|   board = {} | ||||
|   for i = 1, 9 do | ||||
|     board[i] = ' ' | ||||
|   end | ||||
|   winner = nil | ||||
|   move = nil | ||||
| end | ||||
|  | ||||
| local function search(match) | ||||
|   for _, check in ipairs(winCombos) do | ||||
|     if board[check[1]] == board[check[2]] and board[check[1]] == match and board[check[3]] == ' ' then | ||||
|       return check[3] | ||||
|     elseif board[check[1]] == board[check[3]] and board[check[1]] == match and board[check[2]] == ' ' then | ||||
|       return check[2] | ||||
|     elseif board[check[2]] == board[check[3]] and board[check[2]] == match and board[check[1]] == ' ' then | ||||
|       return check[1] | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function getAIMove() | ||||
|   -- make it seem like the computer actually has to think about its move | ||||
|   sleep(0.8) | ||||
|  | ||||
|   -- check if AI can win and return the 3rd tile to create a win, if it cannot, check for a human attempt at winning and stop it, if there is none, return a random | ||||
|   return (search(currentPlayer) or search(opposites[currentPlayer])) or math.random(1,9) | ||||
| end | ||||
|  | ||||
| local function modread( _mask, _history, _limit ) | ||||
|   term.setCursorBlink(true) | ||||
|  | ||||
|   local input = "" | ||||
|   local pos = 0 | ||||
|   if _mask then | ||||
|     _mask = _mask:sub(1,1) | ||||
|   end | ||||
|   local historyPos = nil | ||||
|  | ||||
|   local sw, sh = term.getSize() | ||||
|   local sx, sy = term.getCursorPos() | ||||
|  | ||||
|   local function redraw( _special ) | ||||
|     local scroll = (sx + pos >= sw and (sx + pos) - sw or 0) | ||||
|     local replace = _special or _mask | ||||
|     term.setCursorPos( sx, sy ) | ||||
|     term.write( replace and string.rep(replace, #input - scroll) or input:sub(scroll + 1) ) | ||||
|     term.setCursorPos( sx + pos - scroll, sy ) | ||||
|   end | ||||
|  | ||||
|   while true do | ||||
|     local event = {os.pullEvent()} | ||||
|     if event[1] == 'char' and (not _limit or #input < _limit) then | ||||
|       input = input:sub(1, pos)..event[2]..input:sub(pos + 1) | ||||
|       pos = pos + 1 | ||||
|     elseif event[1] == 'key' then | ||||
|       if event[2] == keys.enter then | ||||
|         break | ||||
|       elseif event[2] == keys.backspace and pos > 0 then | ||||
|         redraw(' ') | ||||
|         input = input:sub(1, pos - 1)..input:sub(pos + 1) | ||||
|         pos = pos - 1 | ||||
|       elseif event[2] == keys.delete and pos < #input then | ||||
|         redraw(' ') | ||||
|         input = input:sub(1, pos)..input:sub(pos + 2) | ||||
|       elseif event[2] == keys.home then | ||||
|         pos = 0 | ||||
|       elseif event[2] == keys['end'] then | ||||
|         pos = #input | ||||
|       elseif event[2] == keys.left and pos > 0 then | ||||
|         pos = pos - 1 | ||||
|       elseif event[2] == keys.right and pos < #input then | ||||
|         pos = pos + 1 | ||||
|       elseif _history and event[2] == keys.up or event[2] == keys.down then | ||||
|         redraw(' ') | ||||
|         if event[2] == keys.up then | ||||
|           if not historyPos then | ||||
|             historyPos = #_history | ||||
|           elseif historyPos > 1 then | ||||
|             historyPos = historyPos - 1 | ||||
|           end | ||||
|         else | ||||
|           if historyPos ~= nil and historyPos < #_history then | ||||
|             historyPos = historyPos + 1 | ||||
|           elseif historyPos == #_history then | ||||
|             historyPos = nil | ||||
|           end | ||||
|         end | ||||
|  | ||||
|         if historyPos then | ||||
|           input = string.sub(_history[historyPos], 1, _limit) or "" | ||||
|           pos = #input | ||||
|         else | ||||
|           input = "" | ||||
|           pos = 0 | ||||
|         end | ||||
|       end | ||||
|     elseif event[1] == 'mouse_click' then | ||||
|       local xPos, yPos = event[3], event[4] | ||||
|       if xPos == sw and yPos == 1 then | ||||
|         -- exit and make sure to fool the catch-all | ||||
|         error('Terminated', 0) | ||||
|       end | ||||
|       local row = (xPos >= 16 and xPos <= 21) and 1 or (xPos >= 23 and xPos <= 28) and 2 or (xPos >= 30 and xPos <= 35) and 3 or 10 | ||||
|       local col = (yPos >= 4 and yPos <= 6) and 1 or (yPos >= 8 and yPos <= 10) and 2 or (yPos >= 12 and yPos <= 16) and 3 or 10 | ||||
|       local ret = (col - 1) * 3 + row | ||||
|       if ret >= 1 and ret <= 9 then | ||||
|         return ret | ||||
|       end | ||||
|     end | ||||
|  | ||||
|     redraw(_mask) | ||||
|   end | ||||
|  | ||||
|   term.setCursorBlink(false) | ||||
|   term.setCursorPos(1, sy + 1) | ||||
|  | ||||
|   return input | ||||
| end | ||||
|  | ||||
| local function getHumanMove() | ||||
|   writeWithFormat('&b[1-9] >>&f ') | ||||
|   return modread() | ||||
| end | ||||
|  | ||||
| local function processInput() | ||||
|   -- set the cursor pos ready for the input | ||||
|   term.setCursorPos(3, sh-1) | ||||
|   move = (currentPlayer == 'x' and getHumanMove or getAIMove)() | ||||
| end | ||||
|  | ||||
| local function output(msg) | ||||
|   -- if the player is not an AI, print the error | ||||
|   if not (activeAI and currentPlayer == 'o') then | ||||
|     term.setCursorPos(3, sh-1) | ||||
|     writeWithFormat('&eERROR >> '..msg) | ||||
|     sleep(2) | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function checkMove() | ||||
|   -- if the user typed exit | ||||
|   if not tonumber(move) and move:lower() == 'exit' then | ||||
|     -- exit and make sure to fool the catch-all | ||||
|     error('Terminated', 0) | ||||
|   end | ||||
|  | ||||
|   -- attempt to convert the move to a number | ||||
|   local nmove = tonumber(move) | ||||
|   -- if it wasn't a number | ||||
|   if not nmove then | ||||
|     output(tostring(move)..' is not a number between 1 and 9!') | ||||
|     return false | ||||
|   end | ||||
|   -- if it is not within range of the board | ||||
|   if nmove > 9 or nmove < 1 then | ||||
|     output('Must be a number between 1 and 9!') | ||||
|     return false | ||||
|   end | ||||
|   -- if the space is already taken | ||||
|   if board[nmove] ~= ' ' then | ||||
|     output('Position already taken!') | ||||
|     return false | ||||
|   end | ||||
|   -- keep the conversion | ||||
|   move = tonumber(move) | ||||
|   return true | ||||
| end | ||||
|  | ||||
| local function checkWin() | ||||
|   for _, check in ipairs(winCombos) do | ||||
|     if board[check[1]] ~= ' ' and board[check[1]] == board[check[2]] and board[check[1]] == board[check[3]] then | ||||
|       return board[check[1]] | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   for _, tile in ipairs(board) do | ||||
|     if tile == ' ' then | ||||
|       return nil | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   return 'tie' | ||||
| end | ||||
|  | ||||
| local function update() | ||||
|   if checkMove() then | ||||
|     board[move] = currentPlayer | ||||
|     winner = checkWin() | ||||
|  | ||||
|     currentPlayer = currentPlayer == 'x' and 'o' or 'x' | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function render() | ||||
|   -- clear the screen light blue | ||||
|   clear(bg) | ||||
|  | ||||
|   -- draw the ascii borders | ||||
|   term.setTextColor(colors.white) | ||||
|   for i = 2, sh-1 do | ||||
|     writeAt('|', 1, i) | ||||
|     writeAt('|', sw, i) | ||||
|   end | ||||
|   writeAt('+'..string.rep('-', sw-2)..'+', 1, 1) | ||||
|   writeAt('+'..string.rep('-', sw-2)..'+', 1, 3) | ||||
|   writeAt('+'..string.rep('-', sw-2)..'+', 1, sh-2) | ||||
|   writeAt('+'..string.rep('-', sw-2)..'+', 1, sh) | ||||
|  | ||||
|   if term.isColor and term.isColor() then | ||||
|     term.setCursorPos(sw, 1) | ||||
|     term.setBackgroundColor(colors.red) | ||||
|     term.setTextColor(colors.black) | ||||
|     writeWithFormat('X') | ||||
|   end | ||||
|  | ||||
|   -- set our colours | ||||
|   term.setBackgroundColor(colors.white) | ||||
|   term.setTextColor(colors.black) | ||||
|  | ||||
|   -- clear an area for the title | ||||
|   writeAt(string.rep(' ', sw-2), 2, 2) | ||||
|   writeAt('Tic-Tac-Toe!', sw/2-5, 2) | ||||
|  | ||||
|   -- clear an area for the input | ||||
|   writeAt(string.rep(' ', sw-2), 2, sh-1) | ||||
|  | ||||
|   -- clear the area for the board | ||||
|   local h = sh - 6 | ||||
|   for i = 0, h - 1 do | ||||
|     writeAt(string.rep(' ', sw - 2), 2, 4+i) | ||||
|   end | ||||
|  | ||||
|   -- draw the grid | ||||
|   for i = 0, 10 do | ||||
|     writeAt(((i == 3 or i == 7) and '------+------+------' or '      |      |      '), 16, i + 4) | ||||
|   end | ||||
|  | ||||
|   -- draw the first line moves | ||||
|   for i = 1, 3 do | ||||
|     if board[i] ~= ' ' then | ||||
|       writeAt((board[i] == 'x' and '\\/' or '/\\'), 18+((i-1)*7), 5) | ||||
|       writeAt((board[i] == 'x' and '/\\' or '\\/'), 18+((i-1)*7), 6) | ||||
|     end | ||||
|   end | ||||
|   -- draw the second line moves | ||||
|   for i = 1, 3 do | ||||
|     if board[i + 3] ~= ' ' then | ||||
|       writeAt((board[i + 3] == 'x' and '\\/' or '/\\'), 18+((i-1)*7), 9) | ||||
|       writeAt((board[i + 3] == 'x' and '/\\' or '\\/'), 18+((i-1)*7), 10) | ||||
|     end | ||||
|   end | ||||
|   -- draw the third line moves | ||||
|   for i = 1, 3 do | ||||
|     if board[i + 6] ~= ' ' then | ||||
|       writeAt((board[i + 6] == 'x' and '\\/' or '/\\'), 18+((i-1)*7), 13) | ||||
|       writeAt((board[i + 6] == 'x' and '/\\' or '\\/'), 18+((i-1)*7), 14) | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   -- draw the current player | ||||
|   term.setCursorPos(3, sh - 3) | ||||
|   if not winner then | ||||
|     writeWithFormat('&bCurrent Player: &f'..players[currentPlayer]) | ||||
|   end | ||||
| end | ||||
|  | ||||
| local function main(arc, argv) | ||||
|   clear() | ||||
|   writeWithFormat('&0Welcome to CCTicTacToe by &8TheOriginal&3BIT&0\n\nPlease enter your name\n\n&4>>&0 ') | ||||
|   players.x = read() or 'Player' | ||||
|  | ||||
|   -- setup the game, will later be used to | ||||
|   reset() | ||||
|  | ||||
|   -- initial render | ||||
|   render() | ||||
|  | ||||
|   -- game loop | ||||
|   while not winner do | ||||
|     processInput() | ||||
|     update() | ||||
|     render() | ||||
|  | ||||
|     -- highly unorthodox having something that isn't in input, update, render! | ||||
|     -- print the winner info | ||||
|     if winner then | ||||
|       writeWithFormat('&f'..(winner == 'tie' and 'There was no winner :(&f' or players[winner]..'&f is the winner!')) | ||||
|       -- allow the player to start a new game or quit | ||||
|       writeAt("Press 'R' to play again, 'Q' to quit...", 3, sh - 1) | ||||
|       while true do | ||||
|         local _, k = os.pullEvent('key') | ||||
|         if k == 16 then | ||||
|           break | ||||
|         elseif k == 19 then | ||||
|           reset() -- reset the game | ||||
|           render() -- render the new game ready to wait for input | ||||
|           break | ||||
|         end | ||||
|       end | ||||
|       os.pullEvent() -- remove the char event that would be waiting | ||||
|     end | ||||
|   end | ||||
|  | ||||
|   return true | ||||
| end | ||||
|  | ||||
| -- create a terminal object with a non-advanced computer safe version of setting colors | ||||
| local oldTermObj = term.current() | ||||
| local termObj = { | ||||
|   setTextColor = function(n) if term.isColor and term.isColor() then local ok, err = pcall(oldTermObj.setTextColor , n) if not ok then error(err, 2) end end end, | ||||
|   setBackgroundColor = function(n) if term.isColor and term.isColor() then local ok, err = pcall(oldTermObj.setBackgroundColor , n) if not ok then error(err, 2) end end end | ||||
| } | ||||
| -- also override the English spelling of the colour functions | ||||
| termObj.setTextColour = termObj.setTextColor | ||||
| termObj.setBackgroundColour = termObj.setBackgroundColor | ||||
|  | ||||
| -- make the terminal object refer to the native terminal for every other function | ||||
| termObj.__index = oldTermObj | ||||
| setmetatable(termObj, termObj) | ||||
|  | ||||
| -- redirect the terminal to the new object | ||||
| term.redirect(termObj) | ||||
|  | ||||
| -- run the program | ||||
| local ok, err = pcall(main, #{...}, {...}) | ||||
|  | ||||
| -- catch-all | ||||
| if not ok and err ~= 'Terminated' then | ||||
|   clear() | ||||
|   print('Error in runtime!') | ||||
|   print(err) | ||||
|   sleep(5) | ||||
| end | ||||
|  | ||||
| -- print thank you message | ||||
| clear() | ||||
| cwriteWithFormat('&4Thank you for playing CCTicTacToe v1.0', 1) | ||||
| cwriteWithFormat('&4By &8TheOriginal&3BIT\n', 2) | ||||
|  | ||||
| -- restore the default terminal object | ||||
| term.redirect( oldTermObj ) | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -1 +0,0 @@ | ||||
| print( "\"talk\" was removed in ComputerCraft 1.6, use the builtin \"chat\" program instead!" ) | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,18 +0,0 @@ | ||||
|                                                   | ||||
|                                              5    | ||||
|                                            bb8bb  | ||||
|                                             b8b   | ||||
|                                              8    | ||||
|              4          ddddddddddddddd      8    | ||||
|             bbbbcbbbbbbb                     8    | ||||
|              bbbc                            8    | ||||
|                 c                       4  4 8    | ||||
|                 c                      bbbbbbb    | ||||
|         bcbbbbbbbb                       bbbbbbb  | ||||
|          c               44                  bbbb | ||||
|          c   4          bbbb                  bbb | ||||
|      4bbbbbbbbbbc        bb    4     4         bb | ||||
|      bbbbbbbbbb c             bbbcbbbbb        bb | ||||
| b       bbb     c                c             bb | ||||
| bbb             c 4     4        c       0    bbb | ||||
| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | ||||
| @@ -1,18 +0,0 @@ | ||||
| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | ||||
| b        0                                      b | ||||
| bbbb     bb                 dddd          cb    b | ||||
| b      bbbb           e  cbb      4 4     cb 44 b | ||||
| bbbbbbbbbbbbbbbbbbbbcbbbbbbb    bbbbbbbbbbbb7777b | ||||
| b    b dddddddd     c                           b | ||||
| b     b             c            cbb            b | ||||
| b 44 b              c          cbbbb    4 4 4   b | ||||
| bbbbbbbbbbbbcbbbbbbbbbbbbbbbbbbbbbbbb7b7bbbbb7bbb | ||||
| bbbbbb4 bbbbcb4  4  4bb4  4bbbbbbbbbbb7bbb7b7b77b | ||||
| b bb     bbbcbbbbbbbbbbbbbbbbb                  b | ||||
| b           c                         7777777777b | ||||
| b           c     dd    ddddddddddddcb7777777777b | ||||
| bbbbbbbbbbbbbbbbbb  cbbb            cbbbb    5  b | ||||
| bbbbbbbbbbbbbbbbbb cbbb            c7b    8bbbbbb | ||||
| b                 cbbb             c7b    8 4 4 b | ||||
| b  4 4 4 4 4  cb  cbb              c7b  cbbbbbbbb | ||||
| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb777777777777b | ||||
| @@ -1,18 +0,0 @@ | ||||
|                                                   | ||||
|                                                   | ||||
|                                5                  | ||||
|   4 4                          8     4     4      | ||||
|  bbcbb                         8    bbbbbbcb      | ||||
|    c                           8          c       | ||||
|    c 4 dddd  4   e 4  dddddd   8          c       | ||||
|    bbbb    bbbbcbbbbbb         8          c       | ||||
|                c             4 8  e 4     c       | ||||
|                c           bbbbbbbbcbb    c       | ||||
|                c                   c      c       | ||||
|           4    c 4     0        4  c   4  c       | ||||
|       bbbcbbbbbbbbb            bbbbcbbbbbbbc      | ||||
|          c                                 c      | ||||
|          c                                 c      | ||||
|          c                                 c      | ||||
| b        c 4     4          4         4    c    b | ||||
| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | ||||
| @@ -1,18 +0,0 @@ | ||||
|  | ||||
| 7  4 547                                      0   | ||||
| 7ccbbbb7                                   cbbbbb | ||||
| 7cc77777                                   cb 4 4 | ||||
| 7cc                                        cb7777 | ||||
| 7cc7                                              | ||||
| 7cc7                                              | ||||
| 7cc                         4    dddd dddd dddd   | ||||
| 7cc77                   c777b777c    c    c    c7 | ||||
| 7cc7c77  dddddd 4    4  c      7c    c  e c    c7 | ||||
| 7cc c          bbbbcbbbbc      777777777777777777 | ||||
| 7777c7  7          c    c                         | ||||
| 7c77c 77           c    c4   4  dddddddd 4 4      | ||||
| 7c  c 77 4       bbbbc7b7b7b7b7c        bbbbbc    | ||||
| 7c  c  7bb           c         c             c    | ||||
| 77c777c777b   4      c         c             c    | ||||
| 7ece 7c  7bbbbbb     c         c     e       c    | ||||
| 7777777777777777777777777777777777777777777777777 | ||||
| @@ -1,18 +0,0 @@ | ||||
|                                                   | ||||
|                    88888888888                    | ||||
|      e  4        cb8         8bc        4  e      | ||||
|   cbbbbbbcdddddddcb8         8bcdddddddcbbbbbbc   | ||||
|   cb    bc        b8         8b        cb    bc   | ||||
|   cb    bc        b8         8b        cb    bc   | ||||
|   cb    bc        b8         8b        cb    bc   | ||||
|   cbb4  bc        b8    5    8b        cbb 4 bc   | ||||
|   cbbbbbbc        b8dddd dddd8b        cbbbbbbc   | ||||
|   cb    bcddddddddb8   4 4   8bddddddddcb    bc   | ||||
|   cb    b        cbcbbbbbbbbbcbc        b    bc   | ||||
|   cb    b        cbc         cbc        b    bc   | ||||
|   cb 4ebb        cbc    e    cbc        bbe4 bc   | ||||
|   cbbbbbb        cb77777777777bc        bbbbbbc   | ||||
|   c      b       c             c       b      c   | ||||
|   c       b      c             c      b       c   | ||||
|   c       4b     c      0      c     b4       c   | ||||
| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | ||||
| @@ -1,18 +0,0 @@ | ||||
|  ddddddd                                          | ||||
| c       bbbbcddddddd        dddc    d 4ed   dddd  | ||||
| c                   bbe44ebb   c 4 c bbb c 4    c | ||||
| c             4777  7b7777b7   bbbb       bbbb  c | ||||
| c             bb                                c | ||||
| c           4                     dd 4    4    4c | ||||
| c   4 e    bb     dd 4  4ddd        bb    bb   bc | ||||
| c   bbb             bb  b   c 4  0  bb          c | ||||
| c              4            c bb     4   4      c | ||||
| cb      4      bbb          c           bb      c | ||||
| cbb     bb     dddd   d 4d  c    4              c | ||||
| cbbbb              bbc bb   c         4   cd 5 dc | ||||
| c     4    4  4      c    4 c       bbb   c     c | ||||
| c    bb    4 bb      c    bb    4         c 444 c | ||||
| c 4        4      4 4c          bb    cbbbc 444 c | ||||
| c bbb             cbbc                c     444 c | ||||
| c                 c                   c         c | ||||
| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | ||||
| @@ -1,18 +0,0 @@ | ||||
|         8     bbbb  0 b     b                     | ||||
|         8b bbbb    8bbb  5  b                     | ||||
|         8b        8bbb   8  b                     | ||||
|         8bbbbbbbbbbbbbbbbbbbb                     | ||||
|         8                                         | ||||
|         8                          ddd          b | ||||
|         8                         c   bbbbbbbbbcb | ||||
|         8                         c      4bbb  cb | ||||
| b       8ddd ddddddd   e   dddddddc      bbb4  cb | ||||
| bbbbbbbbb   c       bbbbbbb       c      4bbb  cb | ||||
| b       b   c      bbb444bbb      c      bbb4  cb | ||||
| b   4   b   c     bbb4   4bbb     c      4bbb  cb | ||||
| b  bbb  b   c    bbb4     4bbb    c      7777  cb | ||||
| b 4   4 b   c   bbb4       4bbb   cbbbbbbbbbbbbbb | ||||
| b bbbbb b   c  bbb           bbb  c             b | ||||
| bddd4dddb   c bbbbbbbbbbbbbbbbbbb c cbbb        b | ||||
| b4  b  4    c      e       e      c cbbb 4444   b | ||||
| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | ||||
| @@ -1,18 +0,0 @@ | ||||
| bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb   bbbbbbbbbbbbbb | ||||
| b  8 8  8   8 8   b 4            5 b4 e      4  b | ||||
| b  888  8 8     8 bbbbbbbcbbbbc  8 bbbbbbbcbbbbbb | ||||
| b  8 8  8  88888  bbbb4bbcbbbbbbbbbb bbb  c     b | ||||
| b     e                  c      bbbb bbb  c     b | ||||
| bbbbbbbbbbbbbbbb77bcbbbbbbbbbbbcbbbb bbb  c 4  4b | ||||
| b  4     4  bb   bbc           cb44b bbb  c     b | ||||
| bbbbbbbbbbbbb     bc           cbbbbbbbb  c     b | ||||
| b   4     4 b4 4 4bc           c    4bbb4 c     b | ||||
| bbbbbbbbbbbbb4 4 4bc    bbbcbbbbbbbbbbbbbbbbbccbb | ||||
| b 4     4   b     bc    bbbc         e  4 cbbccbb | ||||
| bbbbbbbbbbbbbbbcbbbc   4bbbc   cbbbbbbbbbbcbbccbb | ||||
| b              c   cbbbbb  c4  c    bbbbbbcbbccbb | ||||
| b              c   cbbbbbcbbbbbcbbbbbb44  cbbccbb | ||||
| b    4  4  cbbbbbbbcbbbbbcbbbbbcbbbb4bbbbbbbbccbb | ||||
| bbbbbbbbbbbbbbbbbbbce    cbb4bbc      0      cc4b | ||||
| bb     bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | ||||
| 7777777777777777777777777777777777777777777777777 | ||||
| @@ -1,18 +0,0 @@ | ||||
|                                                   | ||||
|                         05          ddddd 4e4     | ||||
|      4ddddddd           cc               bbcbb    | ||||
|   bbbc       4          ccddddd     4      c      | ||||
|    4 c       4     dddddcc     bbbb        c  4   | ||||
|   bcbb       4    4     cc     b44bddd  4  c4     | ||||
|    c4       bbbbbbbb    cc     bbbb   bbbbbbbb    | ||||
|   bbcb        4  4      ccdddddddd    b  e 44b    | ||||
|     c        4    4     cc            b cbbbbb    | ||||
|     c ddd               cc         4  b4c    b    | ||||
|   bbbb   bbbbbbbbbbbb   cc      cbbb  bbcddc b    | ||||
|          bbbbbbbbbbbb   cc    4 c4    b c  c b    | ||||
|            b4    c  ddddcc  bbbbbbb   b4c4bc4b    | ||||
|          c bbbbbbc 4b   cc            bbbbbbbb    | ||||
|          c4b4   ec  b   cc                4       | ||||
|          bbbbbbbbbbbb   cc                        | ||||
| 7            e       4  cc  4                   7 | ||||
| 7b7b7b7b7b7b7b7b7b7b7b7b77b7b7b7b7b7b7b7b7b7b7b77 | ||||
| @@ -1,16 +0,0 @@ | ||||
|          8                   | ||||
|          8       4      e    | ||||
| 777      8      7777c7777777 | ||||
|   77     8     77   c        | ||||
|    77    8    77    c        | ||||
| 4e 777   8  4777    c   4    | ||||
| 77c7777  8  7777c777c7777777 | ||||
|   c   77 8 77   c            | ||||
|   c4e  77c77    c     4      | ||||
| c777c    c     7c77c777      | ||||
| c   c              c         | ||||
| c   c    4     e   c         | ||||
| c   c7777777c777777c77777c77 | ||||
| c           c            c   | ||||
| c           c  0         c   | ||||
| 7777777777777777777777777777 | ||||
| @@ -1,119 +0,0 @@ | ||||
| --[[ | ||||
| 		3D Print | ||||
| 		A printing program for use with NPaintPro | ||||
|  | ||||
| 		By NitrogenFingers | ||||
| ]]-- | ||||
|  | ||||
| local activeCommander = -1 | ||||
| local operatingPrint = false | ||||
|  | ||||
| --Whether or not the print can be ended | ||||
| local function endPrint() | ||||
| 	operatingPrint = false | ||||
| end | ||||
|  | ||||
| --The list of all commands the printer can be ginve | ||||
| local commandList = { | ||||
| 	["FW"] = { turtle.dig, turtle.forward }; | ||||
| 	["BK"] = turtle.back; | ||||
| 	["UP"] = { turtle.digUp, turtle.up }; | ||||
| 	["DW"] = { turtle.digDown, turtle.down }; | ||||
| 	["TL"] = turtle.turnLeft; | ||||
| 	["TR"] = turtle.turnRight; | ||||
| 	["TU"] = { turtle.turnLeft, turtle.turnLeft }; | ||||
| 	["PF"] = { turtle.dig, turtle.place }; | ||||
| 	["PU"] = { turtle.digUp, turtle.placeUp }; | ||||
| 	["PD"] = { turtle.digDown, turtle.placeDown }; | ||||
| 	["SS"] = turtle.select; | ||||
| 	["RF"] = turtle.refuel; | ||||
| 	["DE"] = endPrint; | ||||
| } | ||||
|  | ||||
| --Splits a string according to a pattern into a table | ||||
| local function split(str, pattern) | ||||
|   local t = { } | ||||
|   local fpat = "(.-)" .. pattern | ||||
|   local last_end = 1 | ||||
|   local s, e, cap = str:find(fpat, 1) | ||||
|   while s do | ||||
|     if s ~= 1 or cap ~= "" then | ||||
|       table.insert(t,cap) | ||||
|     end | ||||
|     last_end = e+1 | ||||
|     s, e, cap = str:find(fpat, last_end) | ||||
|   end | ||||
|   if last_end <= #str then | ||||
|     cap = str:sub(last_end) | ||||
|     table.insert(t, cap) | ||||
|   end | ||||
|   return t | ||||
| end | ||||
|  | ||||
| --Listens for any instructions given referring to identification and activation. Once activated, the mode exits. | ||||
| local function respondToQuery() | ||||
| 	while true do | ||||
| 		print("Listening for ACT/ID query") | ||||
| 		local id,key = rednet.receive() | ||||
| 		print("Received : "..key) | ||||
|  | ||||
| 		if key == "$3DPRINT IDENTIFY" then | ||||
| 			print("Requested Identification") | ||||
| 			rednet.send(id, "$3DPRINT IDACK "..os.getComputerLabel()) | ||||
|  | ||||
| 		elseif key == "$3DPRINT ACTIVATE" then | ||||
| 			print("Requested Activation") | ||||
| 			activeCommander = id | ||||
| 			rednet.send(id, "$3DPRINT ACTACK") | ||||
| 			break | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --Performs the print. Follows instrutions as given, and responds as necessary | ||||
| local function performPrint() | ||||
| 	operatingPrint = true | ||||
| 	while operatingPrint do | ||||
| 		local id,msg = rednet.receive() | ||||
| 		print("Command : "..msg) | ||||
|  | ||||
| 		if id == activeCommander and string.find(msg, "$PC") == 1 then | ||||
| 			local cmds = split(msg, " ") | ||||
|  | ||||
| 			--It's a bit of a hack, but those are the 2 methods required for a refuel | ||||
| 			if turtle.getFuelLevel() == 0 and cmds[2] ~= "SS" and cmds[2] ~= "RF" then | ||||
| 				rednet.send(id, "$3DPRINT OOF") | ||||
| 			elseif (tonumber(cmds[3])) and turtle.getItemCount(tonumber(cmds[3])) == 0 and | ||||
| 					turtle.getFuelLevel() ~= 0 then | ||||
| 				rednet.send(id, "$3DPRINT DEP") | ||||
| 			else | ||||
| 				if cmds[2] == "RF" then cmds[3] = "64" end | ||||
| 				if type(commandList[cmds[2]]) == "function" then | ||||
| 					commandList[cmds[2]](tonumber(cmds[3])) | ||||
| 				elseif type(commandList[cmds[2]]) == "table" then | ||||
| 					for i=1,#commandList[cmds[2]] do | ||||
| 						commandList[cmds[2]][i](tonumber(cmds[3])) | ||||
| 					end | ||||
| 				end | ||||
|  | ||||
| 				rednet.send(activeCommander, "$3DPRINT ACK") | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| rednet.open("right") | ||||
| term.clear() | ||||
| term.setCursorPos(1,1) | ||||
| if not os.getComputerLabel() then | ||||
| 	term.write("Name this computer:") | ||||
| 	os.setComputerLabel(io.read()) | ||||
| end | ||||
| print("3D printer online") | ||||
|  | ||||
| while true do | ||||
| 	--Wait for activation | ||||
| 	respondToQuery() | ||||
| 	--Perform the print | ||||
| 	performPrint() | ||||
| end | ||||
| @@ -1,615 +0,0 @@ | ||||
| --[[ | ||||
| 	GameUtil | ||||
| 	An API for drawing sprites and animations made in NPaintPro | ||||
| 	By NitrogenFingers | ||||
| ]]-- | ||||
|  | ||||
|  | ||||
| --The back buffer. Initialized as nil | ||||
| local backbuffer = nil | ||||
| --The bounds of the terminal the back buffer displays to | ||||
| local tw,th = nil, nil | ||||
|  | ||||
| --[[Constructs a new buffer. This must be done before the buffer can written to. | ||||
| 	Params: terminal:?table = The function table to draw to a screen. By default (nil) this refers | ||||
| 			to the native terminal, but monitor displays can be passed through as well: | ||||
| 			local leftMonitor = peripherals.wrap("left") | ||||
| 			initializeBuffer(leftMonitor) | ||||
| 	Returns:boolean = True if the buffer was successfully initialized; false otherwise | ||||
| ]]-- | ||||
| function initializeBuffer(terminal) | ||||
| 	if not terminal then terminal = term end | ||||
| 	if not terminal.getSize then | ||||
| 		error("Parameter cannot be used to initialize the backbuffer.") | ||||
| 	end | ||||
| 	if not terminal.isColour() then | ||||
| 		error("Parameter does not represent an advanced computer.") | ||||
| 	end | ||||
|  | ||||
| 	tw,th = terminal.getSize() | ||||
| 	backbuffer = { } | ||||
| 	for y=1,th do | ||||
| 		backbuffer[y] = { } | ||||
| 	end | ||||
| 	return true | ||||
| end | ||||
|  | ||||
| --[[Will clear the buffer and reset to nil, or to a colour if provided | ||||
| 	Params: colour:?number = The colour to set the back buffer to | ||||
| 	Returns:nil | ||||
| ]]-- | ||||
| function clearBuffer(colour) | ||||
| 	if not backbuffer then | ||||
| 		error("Back buffer not yet initialized!") | ||||
| 	end | ||||
|  | ||||
| 	for y=1,#backbuffer do | ||||
| 		backbuffer[y] = { } | ||||
| 		if colour then | ||||
| 			for x=1,tw do | ||||
| 				backbuffer[y][x] = colour | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --[[Draws the given entity to the back buffer | ||||
| 	Params: entity:table = the entity to draw to the buffer | ||||
| 	Returns:nil | ||||
| ]]-- | ||||
| function writeToBuffer(entity) | ||||
| 	if not backbuffer then | ||||
| 		error("Back buffer not yet initialized!") | ||||
| 	end | ||||
|  | ||||
| 	local image = nil | ||||
| 	if entity.type == "animation" then | ||||
| 		image = entity.frames[entity.currentFrame] | ||||
| 	else | ||||
| 		image = entity.image | ||||
| 	end | ||||
|  | ||||
| 	for y=1,image.dimensions.height do | ||||
| 		for x=1,image.dimensions.width do | ||||
| 			if image[y][x] then | ||||
| 				local xpos,ypos = x,y | ||||
| 				if entity.mirror.x then xpos = image.dimensions.width - x + 1 end | ||||
| 				if entity.mirror.y then ypos = image.dimensions.height - y + 1 end | ||||
|  | ||||
| 				--If the YPos doesn't exist, no need to loop through the rest of X! | ||||
| 				--Don't you love optimization? | ||||
| 				if not backbuffer[entity.y + ypos - 1] then break end | ||||
|  | ||||
| 				backbuffer[entity.y + ypos - 1][entity.x + xpos - 1] = image[y][x] | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --[[Draws the contents of the buffer to the screen. This will not clear the screen or the buffer. | ||||
| 	Params: terminal:table = the terminal to draw to | ||||
| 	Returns:nil | ||||
| ]]-- | ||||
| function drawBuffer(terminal) | ||||
| 	if not backbuffer then | ||||
| 		error("Back buffer not yet initialized!") | ||||
| 	end | ||||
| 	if not terminal then terminal = term end | ||||
| 	if not terminal.setCursorPos or not terminal.setBackgroundColour or not terminal.write then | ||||
| 		error("Parameter cannot be used to initialize the backbuffer.") | ||||
| 	end | ||||
| 	if not terminal.isColour() then | ||||
| 		error("Parameter does not represent an advanced computer.") | ||||
| 	end | ||||
|  | ||||
| 	for y=1,math.min(#backbuffer, th) do | ||||
| 		for x=1,tw do | ||||
| 			if backbuffer[y][x] then | ||||
| 				terminal.setCursorPos(x,y) | ||||
| 				terminal.setBackgroundColour(backbuffer[y][x]) | ||||
| 				terminal.write(" ") | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --[[Converts a hex digit into a colour value | ||||
| 	Params: hex:?string = the hex digit to be converted | ||||
| 	Returns:string A colour value corresponding to the hex, or nil if the character is invalid | ||||
| ]]-- | ||||
| local function getColourOf(hex) | ||||
| 	local value = tonumber(hex, 16) | ||||
| 	if not value then return nil end | ||||
| 	value = math.pow(2,value) | ||||
| 	return value | ||||
| end | ||||
|  | ||||
| --[[Converts every pixel of one colour in a given sprite to another colour | ||||
| 	Use for "reskinning". Uses OO function. | ||||
| 	Params: self:sprite = the sprite to reskin | ||||
| 			oldcol:number = the colour to replace | ||||
| 			newcol:number = the new colour | ||||
| 	Returns:nil | ||||
| ]]-- | ||||
| local function repaintS(self, oldcol, newcol) | ||||
| 	for y=1,self.image.bounds.height do | ||||
| 		for x=1, self.image.bounds.width do | ||||
| 			if self.image[y][x] == oldcol then | ||||
| 				self.image[y][x] = newcol | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --[[Converts every pixel of one colour in a given animation to another colour | ||||
| 	Use for "reskinning". Uses OO function. | ||||
| 	Params: self:animation = the animation to reskin | ||||
| 			oldcol:number = the colour to replace | ||||
| 			newcol:number = the new colour | ||||
| 	Returns:nil | ||||
| ]]-- | ||||
| local function repaintA(self, oldcol, newcol) | ||||
| 	for f=1,#self.frames do | ||||
| 		print(self.frames[f].bounds) | ||||
| 		for y=1,self.frames[f].bounds.height do | ||||
| 			for x=1, self.frames[f].bounds.width do | ||||
| 				if self.frames[f][y][x] == oldcol then | ||||
| 					self.frames[f][y][x] = newcol | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --[[Prints the sprite on the screen | ||||
| 	Params: self:sprite = the sprite to draw | ||||
| 	Returns:nil | ||||
| ]]-- | ||||
| local function drawS(self) | ||||
| 	local image = self.image | ||||
|  | ||||
| 	for y=1,image.dimensions.height do | ||||
| 		for x=1,image.dimensions.width do | ||||
| 			if image[y][x] then | ||||
| 				local xpos,ypos = x,y | ||||
| 				if self.mirror.x then xpos = image.dimensions.width - x + 1 end | ||||
| 				if self.mirror.y then ypos = image.dimensions.height - y + 1 end | ||||
|  | ||||
| 				term.setBackgroundColour(image[y][x]) | ||||
| 				term.setCursorPos(self.x + xpos - 1, self.y + ypos - 1) | ||||
| 				term.write(" ") | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --[[Prints the current frame of the animation on screen | ||||
| 	Params: self:anim = the animation to draw | ||||
| 			frame:?number = the specific frame to draw (default self.currentFrame) | ||||
| 	Returns:nil | ||||
| ]]-- | ||||
| local function drawA(self, frame) | ||||
| 	if not frame then frame = self.currentFrame end | ||||
| 	local image = self.frames[frame] | ||||
|  | ||||
| 	for y=1,image.dimensions.height do | ||||
| 		for x=1,image.dimensions.width do | ||||
| 			if image[y][x] then | ||||
| 				local xpos,ypos = x,y | ||||
| 				if self.mirror.x then xpos = image.dimensions.width - x + 1 end | ||||
| 				if self.mirror.y then ypos = image.dimensions.height - y + 1 end | ||||
|  | ||||
| 				term.setBackgroundColour(image[y][x]) | ||||
| 				term.setCursorPos(self.x + xpos - 1, self.y + ypos - 1) | ||||
| 				term.write(" ") | ||||
| 			end | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --[[Checks the animation timer provided to see whether or not the animation needs to be updated. | ||||
| 	If so, it makes the necessary change. | ||||
| 	Params: self:animation = the animation to be updated | ||||
| 			timerID:number = the ID of the most recent timer event | ||||
| 	Returns:bool = true if the animation was update; false otherwise | ||||
| ]]-- | ||||
| local function updateA(self, timerID) | ||||
| 	if self.timerID and timerID and self.timerID == timerID then | ||||
| 		self.currentFrame = self.currentFrame + 1 | ||||
| 		if self.currentFrame > self.upperBound then | ||||
| 			self.currentFrame = self.lowerBound | ||||
| 		end | ||||
| 		return true | ||||
| 	else | ||||
| 		return false | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --[[Moves immediately to the next frame in the sequence, as though an update had been called. | ||||
| 	Params: self:animation = the animation to update | ||||
| 	Returns:nil | ||||
| ]]-- | ||||
| local function nextA(self) | ||||
| 	self.currentFrame = self.currentFrame + 1 | ||||
| 	if self.currentFrame > self.upperBound then | ||||
| 		self.currentFrame = self.lowerBound | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --[[Moves immediately to the previous frame in the sequence | ||||
| 	Params: self:animation = the animation to update | ||||
| 	Returns:nil | ||||
| ]]-- | ||||
| local function previousA(self) | ||||
| 	self.currentFrame = self.currentFrame - 1 | ||||
| 	if self.currentFrame < self.lowerBound then | ||||
| 		self.currentFrame = self.upperBound | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --[[A simple debug function that displays the outline of the bounds | ||||
| 	on a given shape. Useful when testing collision detection or other game | ||||
| 	features. | ||||
| 	Params: entity:table = the bounded entity to represent | ||||
| 			colour:?number = the colour to draw the rectangle (default red) | ||||
| 	Returns:nil | ||||
| ]]-- | ||||
| local function drawBounds(entity, colour) | ||||
| 	if not colour then colour = colours.red end | ||||
| 	local image = nil | ||||
| 	if entity.type == "animation" then image = entity.frames[entity.currentFrame] | ||||
| 	else image = entity.image end | ||||
|  | ||||
| 	term.setBackgroundColour(colour) | ||||
|  | ||||
| 	corners = { | ||||
| 		topleft = { x = entity.x + image.bounds.x - 1, y = entity.y + image.bounds.y - 1 }; | ||||
| 		topright = { x = entity.x + image.bounds.x + image.bounds.width - 2, y = entity.y + image.bounds.y - 1 }; | ||||
| 		botleft = { x = entity.x + image.bounds.x - 1, y = entity.y + image.bounds.y + image.bounds.height - 2 }; | ||||
| 		botright = { x = entity.x + image.bounds.x + image.bounds.width - 2, y = entity.y + image.bounds.y + image.bounds.height - 2 }; | ||||
| 	} | ||||
|  | ||||
| 	term.setCursorPos(corners.topleft.x, corners.topleft.y) | ||||
| 	term.write(" ") | ||||
| 	term.setCursorPos(corners.topright.x, corners.topright.y) | ||||
| 	term.write(" ") | ||||
| 	term.setCursorPos(corners.botleft.x, corners.botleft.y) | ||||
| 	term.write(" ") | ||||
| 	term.setCursorPos(corners.botright.x, corners.botright.y) | ||||
| 	term.write(" ") | ||||
| end | ||||
|  | ||||
| --[[Creates a bounding rectangle object. Used in drawing the bounds and the rCollidesWith methods | ||||
| 	Params: self:table = the entity to create the rectangle | ||||
| 	Returns:table = the left, right, top and bottom edges of the rectangle | ||||
| ]]-- | ||||
| local function createRectangle(entity) | ||||
| 	local image = nil | ||||
| 	if entity.type == "animation" then | ||||
| 		image = entity.frames[entity.currentFrame] | ||||
| 	else | ||||
| 		image = entity.image | ||||
| 	end | ||||
| 	--Note that the origin is always 1, so we subtract 1 for every absolute coordinate we have to test. | ||||
| 	return { | ||||
| 		left = entity.x + image.bounds.x - 1; | ||||
| 		right = entity.x + image.bounds.x + image.bounds.width - 2; | ||||
| 		top = entity.y + image.bounds.y - 1; | ||||
| 		bottom = entity.y + image.bounds.y + image.bounds.height - 2; | ||||
| 	} | ||||
| end | ||||
|  | ||||
| --[[Performs a rectangle collision with another given entity. Entity can be of sprite or animation | ||||
| 	type (also true of the self). Bases collision using a least squared approach (rectangle precision). | ||||
| 	Params: self:sprite,animation = the object in question of the testing | ||||
| 			other:sprite,animation = the other object tested for collision | ||||
| 	Returns:bool = true if bounding rectangle intersect is true; false otherwse | ||||
| ]]-- | ||||
| local function rCollidesWith(self, other) | ||||
| 	--First we construct the rectangles | ||||
| 	local img1C, img2C = createRectangle(self), createRectangle(other) | ||||
|  | ||||
| 	--We then determine the "relative position" , in terms of which is farther left or right | ||||
| 	leftmost,rightmost,topmost,botmost = nil,nil,nil,nil | ||||
| 	if img1C.left < img2C.left then | ||||
| 		leftmost = img1C | ||||
| 		rightmost = img2C | ||||
| 	else | ||||
| 		leftmost = img2C | ||||
| 		rightmost = img1C | ||||
| 	end | ||||
| 	if img1C.top < img2C.top then | ||||
| 		topmost = img1C | ||||
| 		botmost = img2C | ||||
| 	else | ||||
| 		topmost = img2C | ||||
| 		botmost = img1C | ||||
| 	end | ||||
|  | ||||
| 	--Then we determine the distance between the "extreme" edges- | ||||
| 		--distance between leftmost/right edge and rightmost/left edge | ||||
| 		--distance between topmost/bottom edge and bottommost/top edge | ||||
| 	local xdist = rightmost.left - leftmost.right | ||||
| 	local ydist = botmost.top - topmost.bottom | ||||
|  | ||||
| 	--If both are negative, our rectangles intersect! | ||||
| 	return xdist <= 0 and ydist <= 0 | ||||
| end | ||||
|  | ||||
| --[[Performs a pixel collision test on another given entity. Either entity can be of sprite or animation | ||||
| 	type. This is done coarsegrain-finegrain, we first find the intersection between the rectangles | ||||
| 	(if there is one), and then test the space within that intersection for any intersecting pixels. | ||||
| 	Params: self:sprite,animation = the object in question of the testing | ||||
| 			other:sprite,animation = the other object being tested for collision | ||||
| 	Returns:?number,?number: The X and Y position in which the collision occurred. | ||||
| ]]-- | ||||
| local function pCollidesWith(self, other) | ||||
| 	--Identically to rCollidesWith, we create our rectangles... | ||||
| 	local img1C, img2C = createRectangle(self), createRectangle(other) | ||||
| 	--We'll also need the images to compare pixels later | ||||
| 	local img1, img2 = nil,nil | ||||
| 	if self.type == "animation" then img1 = self.frames[self.currentFrame] | ||||
| 	else img1 = self.image end | ||||
| 	if other.type == "animation" then img2 = other.frames[other.currentFrame] | ||||
| 	else img2 = other.image end | ||||
|  | ||||
| 	--...then we position them... | ||||
| 	leftmost,rightmost,topmost,botmost = nil,nil,nil,nil | ||||
| 	--We also keep track of which is left and which is right- it doesn't matter in a rectangle | ||||
| 	--collision but it does in a pixel collision. | ||||
| 	img1T,img2T = {},{} | ||||
|  | ||||
| 	if img1C.left < img2C.left then | ||||
| 		leftmost = img1C | ||||
| 		rightmost = img2C | ||||
| 		img1T.left = true | ||||
| 	else | ||||
| 		leftmost = img2C | ||||
| 		rightmost = img1C | ||||
| 		img2T.left = true | ||||
| 	end | ||||
| 	if img1C.top < img2C.top then | ||||
| 		topmost = img1C | ||||
| 		botmost = img2C | ||||
| 		img1T.top = true | ||||
| 	else | ||||
| 		topmost = img2C | ||||
| 		botmost = img1C | ||||
| 		img2T.top = true | ||||
| 	end | ||||
|  | ||||
| 	--...and we again find the distances between the extreme edges. | ||||
| 	local xdist = rightmost.left - leftmost.right | ||||
| 	local ydist = botmost.top - topmost.bottom | ||||
|  | ||||
| 	--If these distances are > 0 then we stop- no need to go any farther. | ||||
| 	if xdist > 0 or ydist > 0 then return false end | ||||
|  | ||||
|  | ||||
| 	for x = rightmost.left, rightmost.left + math.abs(xdist) do | ||||
| 		for y = botmost.top, botmost.top + math.abs(ydist) do | ||||
| 			--We know a collision has occurred if a pixel is occupied by both images. We do this by | ||||
| 			--first transforming the coordinates based on which rectangle is which, then testing if a | ||||
| 			--pixel is at that point | ||||
| 				-- The leftmost and topmost takes the distance on x and y and removes the upper component | ||||
| 				-- The rightmost and bottommost, being the farther extremes, compare from 1 upwards | ||||
| 			local testX,testY = 1,1 | ||||
| 			if img1T.left then testX = x - img1C.left + 1 | ||||
| 			else testX = x - img1C.left + 1 end | ||||
| 			if img1T.top then testY = y - img1C.top + 1 | ||||
| 			else testY = y - img1C.top + 1 end | ||||
|  | ||||
| 			local occupy1 = img1[testY + img1.bounds.y-1][testX + img1.bounds.x-1] ~= nil | ||||
|  | ||||
| 			if img2T.left then testX = x - img2C.left + 1 | ||||
| 			else testX = x - img2C.left + 1 end | ||||
| 			if img2T.top then testY = y - img2C.top + 1 | ||||
| 			else testY = y - img2C.top + 1 end | ||||
|  | ||||
| 			local occupy2 = img2[testY + img2.bounds.y-1][testX + img2.bounds.x-1] ~= nil | ||||
|  | ||||
| 			if occupy1 and occupy2 then return true end | ||||
| 		end | ||||
| 	end | ||||
| 	--If the looop terminates without returning, then no pixels overlap | ||||
| 	return false | ||||
| end | ||||
|  | ||||
| --[[Moves the sprite or animation to the specified coordinates. This performs the auto-centering, so | ||||
| 	the user doesn't have to worry about adjusting for the bounds of the shape. Recommended for absolute | ||||
| 	positioning operations (as relative direct access to the X will have unexpected results!) | ||||
| 	Params: self:table = the animation or sprite to move | ||||
| 	x:number = the new x position | ||||
| 	y:number = the new y position | ||||
| ]]-- | ||||
| local function moveTo(self, x, y) | ||||
| 	local image = nil | ||||
| 	if self.type == "animation" then | ||||
| 		image = self.frames[self.currentFrame] | ||||
| 	else | ||||
| 		image = self.image | ||||
| 	end | ||||
|  | ||||
| 	self.x = x - image.bounds.x + 1 | ||||
| 	self.y = y - image.bounds.y + 1 | ||||
| end | ||||
|  | ||||
| --[[ | ||||
| 	Sprites Fields: | ||||
| x:number = the x position of the sprite in the world | ||||
| y:number = the y position of the sprite in the world | ||||
| image:table = a table of the image. Indexed by height, a series of sub-tables, each entry being a pixel | ||||
| 		at [y][x]. It also contains: | ||||
| 	bounds:table = | ||||
| 		x:number = the relative x position of the bounding rectangle | ||||
| 		y:number = the relative y position of the bounding rectangle | ||||
| 		width:number = the width of the bounding rectangle | ||||
| 		height:number = the height of the bounding rectangle | ||||
| 	dimensions:table = | ||||
| 		width = the width of the entire image in pixels | ||||
| 		height = the height of the entire image in pixels | ||||
|  | ||||
| mirror:table = | ||||
| 	x:bool = whether or not the image is mirrored on the X axis | ||||
| 	y:bool = whether or not the image is mirrored on the Y axis | ||||
| repaint:function = see repaintS (above) | ||||
| rCollidesWith:function = see rCollidesWith (above) | ||||
| pCollidesWith:function = see pCollidesWith (above) | ||||
| draw:function = see drawS (above) | ||||
| ]]-- | ||||
|  | ||||
| --[[Loads a new sprite into a table, and returns it to the user. | ||||
| 	Params: path:string = the absolute path to the desired sprite | ||||
| 	x:number = the initial X position of the sprite | ||||
| 	y:number = the initial Y position of the sprite | ||||
| ]]-- | ||||
| function loadSprite(path, x, y) | ||||
| 	local sprite = { | ||||
| 		type = "sprite", | ||||
| 		x = x, | ||||
| 		y = y, | ||||
| 		image = { }, | ||||
| 		mirror = { x = false, y = false } | ||||
| 	} | ||||
|  | ||||
| 	if fs.exists(path) then | ||||
| 		local file = io.open(path, "r" ) | ||||
| 		local leftX, rightX = math.huge, 0 | ||||
| 		local topY, botY = nil,nil | ||||
|  | ||||
| 		local lcount = 0 | ||||
| 		for line in file:lines() do | ||||
| 			lcount = lcount+1 | ||||
| 			table.insert(sprite.image, {}) | ||||
| 			for i=1,#line do | ||||
| 				if string.sub(line, i, i) ~= " " then | ||||
| 					leftX = math.min(leftX, i) | ||||
| 					rightX = math.max(rightX, i) | ||||
| 					if not topY then topY = lcount end | ||||
| 					botY = lcount | ||||
| 				end | ||||
| 				sprite.image[#sprite.image][i] = getColourOf(string.sub(line,i,i)) | ||||
| 			end | ||||
| 		end | ||||
| 		file:close() | ||||
|  | ||||
| 		sprite.image.bounds = { | ||||
| 			x = leftX, | ||||
| 			width = rightX - leftX + 1, | ||||
| 			y = topY, | ||||
| 			height = botY - topY + 1 | ||||
| 		} | ||||
| 		sprite.image.dimensions = { | ||||
| 			width = rightX, | ||||
| 			height = botY | ||||
| 		} | ||||
|  | ||||
| 		sprite.x = sprite.x - leftX + 1 | ||||
| 		sprite.y = sprite.y - topY + 1 | ||||
|  | ||||
| 		sprite.repaint = repaintS | ||||
| 		sprite.rCollidesWith = rCollidesWith | ||||
| 		sprite.pCollidesWith = pCollidesWith | ||||
| 		sprite.draw = drawS | ||||
| 		sprite.moveTo = moveTo | ||||
| 		return sprite | ||||
| 	else | ||||
| 		error(path.." not found!") | ||||
| 	end | ||||
| end | ||||
|  | ||||
| --Animations contain | ||||
| 	--Everything a sprite contains, but the image is a series of frames, not just one image | ||||
| 	--An timerID that tracks the last animation | ||||
| 	--An upper and lower bound on the active animation | ||||
| 	--An update method that takes a timer event and updates the animation if necessary | ||||
|  | ||||
| --[[ | ||||
|  | ||||
| ]]-- | ||||
| function loadAnimation(path, x, y, currentFrame) | ||||
| 	local anim = { | ||||
| 		type = "animation", | ||||
| 		x = x, | ||||
| 		y = y, | ||||
| 		frames = { }, | ||||
| 		mirror = { x = false, y = false }, | ||||
| 		currentFrame = currentFrame | ||||
| 	} | ||||
|  | ||||
| 	table.insert(anim.frames, { }) | ||||
| 	if fs.exists(path) then | ||||
| 		local file = io.open(path, "r") | ||||
| 		local leftX, rightX = math.huge, 0 | ||||
| 		local topY, botY = nil,nil | ||||
|  | ||||
| 		local lcount = 0 | ||||
| 		for line in file:lines() do | ||||
| 			lcount = lcount+1 | ||||
| 			local cFrame = #anim.frames | ||||
| 			if line == "~" then | ||||
| 				anim.frames[cFrame].bounds = { | ||||
| 					x = leftX, | ||||
| 					y = topY, | ||||
| 					width = rightX - leftX + 1, | ||||
| 					height = botY - topY + 1 | ||||
| 				} | ||||
| 				anim.frames[cFrame].dimensions = { | ||||
| 					width = rightX, | ||||
| 					height = botY | ||||
| 				} | ||||
| 				table.insert(anim.frames, { }) | ||||
| 				leftX, rightX = math.huge, 0 | ||||
| 				topY, botY = nil,nil | ||||
| 				lcount = 0 | ||||
| 			else | ||||
| 				table.insert(anim.frames[cFrame], {}) | ||||
| 				for i=1,#line do | ||||
| 					if string.sub(line, i, i) ~= " " then | ||||
| 						leftX = math.min(leftX, i) | ||||
| 						rightX = math.max(rightX, i) | ||||
| 						if not topY then topY = lcount end | ||||
| 						botY = lcount | ||||
| 					end | ||||
| 					anim.frames[cFrame][#anim.frames[cFrame]] [i] = getColourOf(string.sub(line,i,i)) | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 		file:close() | ||||
| 		local cFrame = #anim.frames | ||||
| 		anim.frames[cFrame].bounds = { | ||||
| 			x = leftX, | ||||
| 			y = topY, | ||||
| 			width = rightX - leftX + 1, | ||||
| 			height = botY - topY + 1 | ||||
| 		} | ||||
| 		anim.frames[cFrame].dimensions = { | ||||
| 			width = rightX, | ||||
| 			height = botY | ||||
| 		} | ||||
| 		anim.x = anim.x - leftX + 1 | ||||
| 		anim.y = anim.y - topY + 1 | ||||
|  | ||||
| 		if not currentFrame or type(currentFrame) ~= "number" or currentFrame < 1 or | ||||
| 				currentFrame > #anim.frames then | ||||
| 			anim.currentFrame = 1 | ||||
| 		end | ||||
|  | ||||
| 		anim.timerID = nil | ||||
| 		anim.lowerBound = 1 | ||||
| 		anim.upperBound = #anim.frames | ||||
| 		anim.updating = false | ||||
|  | ||||
| 		anim.repaint = repaintA | ||||
| 		anim.rCollidesWith = rCollidesWith | ||||
| 		anim.pCollidesWith = pCollidesWith | ||||
| 		anim.draw = drawA | ||||
| 		anim.update = updateA | ||||
| 		anim.next = nextA | ||||
| 		anim.previous = previousA | ||||
| 		anim.moveTo = moveTo | ||||
| 		return anim | ||||
| 	else | ||||
| 		error(path.." not found!") | ||||
| 	end | ||||
| end | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,178 +0,0 @@ | ||||
| board = {} | ||||
| tArgs = { ... } | ||||
| generation = 0 | ||||
| sleeptime = 0.5 | ||||
|  | ||||
| if(tArgs[1] == "left" or tArgs[1] == "right" or tArgs[1] == "top" or tArgs[1] == "bottom" or tArgs[1] == "front" or tArgs[1] == "back")then | ||||
| 	mon = peripheral.wrap(tArgs[1]) | ||||
| else | ||||
| 	mon = term | ||||
| end | ||||
|  | ||||
| if(mon.isColor() or mon.isColor)then | ||||
| 	colored = true | ||||
| else | ||||
| 	colored = false | ||||
| end | ||||
|  | ||||
| w, h = mon.getSize() | ||||
| for x = 1, w do | ||||
| 	board[x] = {} | ||||
| 	for y = 1, h do | ||||
| 		board[x][y] = 0 | ||||
| 	end | ||||
| end | ||||
|  | ||||
| function drawScreen() | ||||
| 	w, h = mon.getSize() | ||||
| 	for x = 1, w do | ||||
| 		for y = 1, h do | ||||
| 			nei = getNeighbours(x, y) | ||||
| 			if(board[x][y] == 1)then | ||||
| 				if colored then | ||||
| 					if(nei < 2 or nei > 3)then | ||||
| 						mon.setBackgroundColor(colors.red) | ||||
| 					else | ||||
| 						mon.setBackgroundColor(colors.green) | ||||
| 					end | ||||
| 				else | ||||
| 					mon.setBackgroundColor(colors.white) | ||||
| 				end | ||||
| 			else | ||||
| 				if colored then | ||||
| 					if(nei == 3)then | ||||
| 						mon.setBackgroundColor(colors.yellow) | ||||
| 					else | ||||
| 						mon.setBackgroundColor(colors.black) | ||||
| 					end | ||||
| 				else | ||||
| 					mon.setBackgroundColor(colors.black) | ||||
| 				end | ||||
| 			end | ||||
| 			mon.setCursorPos(x, y) | ||||
| 			mon.write(" ") | ||||
| 		end | ||||
| 	end | ||||
| 	mon.setCursorPos(1,1) | ||||
| 	if colored then | ||||
| 		mon.setTextColor(colors.blue) | ||||
| 	end | ||||
| 	mon.write(generation) | ||||
| end | ||||
|  | ||||
| function getNeighbours(x, y) | ||||
| 	w, h = mon.getSize() | ||||
| 	total = 0 | ||||
| 	if(x > 1 and y > 1)then if(board[x-1][y-1] == 1)then total = total + 1 end end | ||||
| 	if(y > 1)then if(board[x][y-1] == 1)then total = total + 1 end end | ||||
| 	if(x < w and y > 1)then if(board[x+1][y-1] == 1)then total = total + 1 end end | ||||
| 	if(x > 1)then if(board[x-1][y] == 1)then total = total + 1 end end | ||||
| 	if(x < w)then if(board[x+1][y] == 1)then total = total + 1 end end | ||||
| 	if(x > 1 and y < h)then if(board[x-1][y+1] == 1)then total = total + 1 end end | ||||
| 	if(y < h)then if(board[x][y+1] == 1)then total = total + 1 end end | ||||
| 	if(x < w and y < h)then if(board[x+1][y+1] == 1)then total = total + 1 end end | ||||
| 	return total | ||||
| end | ||||
|  | ||||
| function compute() | ||||
| 	w, h = mon.getSize() | ||||
| 	while true do | ||||
| 		newBoard = {} | ||||
| 		for x = 1, w do | ||||
| 			newBoard[x] = {} | ||||
| 			for y = 1, h do | ||||
| 				nei = getNeighbours(x, y) | ||||
| 				if(board[x][y] == 1)then | ||||
| 					if(nei < 2)then | ||||
| 						newBoard[x][y] = 0 | ||||
| 					elseif(nei > 3)then | ||||
| 						newBoard[x][y] = 0 | ||||
| 					else | ||||
| 						newBoard[x][y] = 1 | ||||
| 					end | ||||
| 				else | ||||
| 					if(nei == 3)then | ||||
| 						newBoard[x][y] = 1 | ||||
| 					end | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 		board = newBoard | ||||
| 		generation = generation + 1 | ||||
| 		sleep(sleeptime) | ||||
| 	end | ||||
| end | ||||
|  | ||||
| function loop() | ||||
| 	while true do | ||||
| 		event, variable, xPos, yPos = os.pullEvent() | ||||
| 		if event == "mouse_click" or event == "monitor_touch" or event == "mouse_drag" then | ||||
| 			if variable == 1 then | ||||
| 				board[xPos][yPos] = 1 | ||||
| 			else | ||||
| 				board[xPos][yPos] = 0 | ||||
| 			end | ||||
| 		end | ||||
| 		if event == "key" then | ||||
| 			if tostring(variable) == "28" then | ||||
| 				return true | ||||
| 			elseif tostring(variable) == "57" then | ||||
| 				if(mon.isColor() or mon.isColor)then | ||||
| 					colored = not colored | ||||
| 				end | ||||
| 			elseif tostring(variable) == "200" then | ||||
| 				if sleeptime > 0.1 then | ||||
| 					sleeptime = sleeptime - 0.1 | ||||
| 				end | ||||
| 			elseif tostring(variable) == "208" then | ||||
| 				if sleeptime < 1 then | ||||
| 					sleeptime = sleeptime + 0.1 | ||||
| 				end | ||||
| 			end | ||||
| 		end | ||||
| 		drawScreen() | ||||
| 	end | ||||
| end | ||||
|  | ||||
| function intro() | ||||
| 	mon.setBackgroundColor(colors.black) | ||||
| 	mon.clear() | ||||
| 	mon.setCursorPos(1,1) | ||||
| 	mon.write("Conway's Game Of Life") | ||||
| 	mon.setCursorPos(1,2) | ||||
| 	mon.write("It is a game which represents life.") | ||||
| 	mon.setCursorPos(1,3) | ||||
| 	mon.write("The game runs by 4 basic rules:") | ||||
| 	mon.setCursorPos(1,4) | ||||
| 	mon.write("1. If a cell has less than 2 neighbours, it dies.") | ||||
| 	mon.setCursorPos(1,5) | ||||
| 	mon.write("2. If a cell has 2 or 3 neightbours, it lives.") | ||||
| 	mon.setCursorPos(1,6) | ||||
| 	mon.write("3. If a cell has more than 3 neighbours, it dies.") | ||||
| 	mon.setCursorPos(1,7) | ||||
| 	mon.write("4. If a cell has exactly 3 neighbours it is born.") | ||||
| 	mon.setCursorPos(1,9) | ||||
| 	mon.write("At the top left is the generation count.") | ||||
| 	mon.setCursorPos(1,10) | ||||
| 	mon.write("Press spacebar to switch between color modes") | ||||
| 	mon.setCursorPos(1,11) | ||||
| 	mon.write("Press enter to start  the game") | ||||
| 	mon.setCursorPos(1,13) | ||||
| 	mon.write("Colors:") | ||||
| 	mon.setCursorPos(1,14) | ||||
| 	mon.write("Red - Cell will die in next generation") | ||||
| 	mon.setCursorPos(1,15) | ||||
| 	mon.write("Green - Cell will live in next generation") | ||||
| 	mon.setCursorPos(1,16) | ||||
| 	mon.write("Yellow - Cell will be born in next generation") | ||||
| 	mon.setCursorPos(1,18) | ||||
| 	mon.write("Press any key to continue!") | ||||
| 	event, variable, xPos, yPos = os.pullEvent("key") | ||||
| end | ||||
|  | ||||
| intro() | ||||
| drawScreen() | ||||
| while true do | ||||
| 	loop() | ||||
| 	parallel.waitForAny(loop, compute) | ||||
| end | ||||
		Reference in New Issue
	
	Block a user
	 SquidDev
					SquidDev