mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-26 00:46:54 +00:00
Delete existing treasure disks
This commit is contained in:
parent
76c3e4c155
commit
697e9449cf
@ -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
|
Loading…
Reference in New Issue
Block a user