XxLen_KagaminexX

Battleship

Apr 27th, 2023
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 23.71 KB | Gaming | 0 0
  1. --[[
  2. battleship,
  3. by GopherAtl, 2013
  4. Do whatever you want, just don't judge me by
  5. what a mess this code is.
  6. --]]
  7. local args={...}
  8. local action=args[1]
  9. local opponentID=nil
  10. local openedSide=nil
  11. local opponent=nil
  12. local myName=""
  13. local opponentReady=false
  14. local myTurn
  15. local targetX,targetY
  16. local shipsLeft=5
  17. local oppShipsLeft=5
  18.  
  19. local originalTerm = term.current()
  20.  
  21. --bounding box of the target grid
  22. local targetGridBounds={
  23.     minX=16, maxX=25,
  24.     minY=4, maxY=13
  25.   }
  26.  
  27.  
  28. local function doColor(text,background)
  29.   term.setTextColor(text)
  30.   term.setBackgroundColor(background)
  31. end
  32.  
  33. local function doColor_mono(text,background)
  34.   if text==colors.blue or text==colors.red or text==colors.black or text==colors.lime or background==colors.lightGray then
  35.     term.setTextColor(colors.black)
  36.     term.setBackgroundColor(colors.white)
  37.   else
  38.     term.setTextColor(colors.white)
  39.     term.setBackgroundColor(colors.black)
  40.   end
  41. end
  42.  
  43. local function doScreenColor()
  44.   if term.isColor() then
  45.     doColor(colors.white,colors.lightGray)
  46.   else
  47.     doColor(colors.black,colors.white)
  48.   end
  49. end
  50.  
  51. local function toGridRef(x,y)
  52.   return string.sub("ABCDEFGHIJ",x,x)..string.sub("1234567890",y,y)
  53. end
  54.  
  55.  
  56. if not term.isColor() then
  57.   doColor=doColor_mono
  58. end
  59.  
  60. local function quit()
  61.   if openedSide then
  62.     rednet.close(openedSide)
  63.   end
  64.   term.redirect( originalTerm )
  65.   term.setCursorPos(term.getSize())
  66.   print()
  67.   error()
  68. end
  69.  
  70. local foundModem=false
  71. --find modem
  72. for k,v in pairs(redstone.getSides()) do
  73.   if peripheral.getType(v)=="modem" then
  74.     foundModem=true
  75.     if not rednet.isOpen(v) then
  76.       rednet.open(v)
  77.       openedSide=v
  78.     end
  79.     break
  80.   end
  81. end
  82.  
  83. if not foundModem then
  84.   print("You must have a modem to play!")
  85.   return
  86. end
  87.  
  88. if action==nil or (action~="join" and action~="host") then
  89.   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")
  90.   quit()
  91. end
  92.  
  93. --get player name
  94. while true do
  95.   doColor(colors.cyan,colors.black)
  96.   write("player name: ")
  97.   doColor(colors.gray,colors.black)
  98.   myName=read()
  99.   if myName=="" then
  100.     doColor(colors.red,colors.black)
  101.     print("You have to give a name!")
  102.   elseif #myName>11 then
  103.     doColor(colors.red,colors.black)
  104.     print("Max name is 11 characters!")
  105.   else
  106.     break
  107.   end
  108. end
  109.  
  110. if action=="join" then
  111.   print("Attempting to join a game...\n(press q to cancel)")
  112.   while true do
  113.     local retryTimer=os.startTimer(1);
  114.     rednet.broadcast("bs join "..myName);
  115.  
  116.     while true do
  117.       local event,p1,p2,p3=os.pullEvent();
  118.       if event=="rednet_message" then
  119.         opponent=string.match(p2,"bs accept %s*(.+)%s*")
  120.         if opponent then
  121.           opponentID=p1
  122.           break
  123.         end
  124.       elseif event=="timer" and p1==retryTimer then
  125.         break
  126.       elseif event=="char" and (p1=="q" or p1=="Q") then
  127.         print("Couldn't find an opponent; quitting")
  128.         quit()
  129.       end
  130.     end
  131.     local joined=false
  132.  
  133.     if opponentID then
  134.       print("Joining game!")
  135.       rednet.send(opponentID,"bs start")
  136.       break
  137.     end
  138.   end
  139. elseif action=="host" then
  140.   print("Waiting for challenger...\n(Press q to cancel)")
  141.   while true do
  142.     while true do
  143.       local event,p1,p2=os.pullEvent()
  144.       if event=="rednet_message" then
  145.         opponent=string.match(p2,"bs join %s*(.+)%s*")        if opponent then
  146.           print("found player, inviting..")
  147.           opponentID=p1
  148.           break
  149.         end
  150.       elseif event=="char" and (p1=="q" or p1=="Q") then
  151.         print("Couldn't find opponent, quitting")
  152.         quit()
  153.       end
  154.     end
  155.  
  156.     if opponentID then
  157.       rednet.send(opponentID,"bs accept "..myName)
  158.       local timeout=os.startTimer(1)
  159.       while true do
  160.         local event,p1,p2=os.pullEvent()
  161.         if event=="rednet_message" and p2=="bs start" then
  162.           print("player joined!")
  163.           break
  164.         elseif event=="timer" and p1==timeout then
  165.           print("player joined another game. Waiting for another...")
  166.           opponentID=nil
  167.           break
  168.         end
  169.       end
  170.  
  171.       if opponentID then
  172.         break
  173.       end
  174.     end
  175.   end
  176. end
  177.  
  178. local ships={
  179.   {pos=nil,dir="h",size=5,name="carrier",hits=0},
  180.   {pos=nil,dir="h",size=4,name="battleship",hits=0},
  181.   {pos=nil,dir="h",size=3,name="cruiser",hits=0},
  182.   {pos=nil,dir="h",size=3,name="submarine",hits=0},
  183.   {pos=nil,dir="h",size=2,name="destroyer",hits=0},
  184. }
  185.  
  186. local myShotTable={ {1,1,true},{5,5,false} }
  187. local oppShotTable={ }
  188.  
  189. local myGrid,oppGrid={title=myName},{title=opponent}
  190.  
  191. --setup grids
  192. for i=1,10 do
  193.   myGrid[i]={}
  194.   oppGrid[i]={}
  195.   for j=1,10 do
  196.     myGrid[i][j]={hit=false,ship=false}
  197.     oppGrid[i][j]={hit=false,ship=false}
  198.   end
  199. end
  200.  
  201. local function drawShipsToGrid(ships,grid)
  202.   for i=1,#ships do
  203.     local x,y=table.unpack(ships[i].pos)
  204.     local stepX=ships[i].dir=="h" and 1 or 0
  205.     local stepY=stepX==1 and 0 or 1
  206.     for j=1,ships[i].size do
  207.       grid[x][y].ship=i
  208.       x,y=x+stepX,y+stepY
  209.     end
  210.   end
  211. end
  212.  
  213. local function drawShotToGrid(shot,grid)
  214.   grid[shot[1]][shot[2]].shot=true
  215.   grid[shot[1]][shot[2]].hit=shot[3]
  216. end
  217.  
  218. local function makeShot(x,y,grid)
  219.   local tile=grid[x][y]
  220.   if tile.shot==true then
  221.     return nil --already shot here!
  222.   end
  223.  
  224.   local shot={x,y,tile.ship}
  225.   drawShotToGrid(shot,grid)
  226.   if tile.ship then
  227.     ships[tile.ship].hits=ships[tile.ship].hits+1
  228.     if ships[tile.ship].hits==ships[tile.ship].size then
  229.       os.queueEvent("shipsunk",tile.ship)
  230.     end
  231.   end
  232.   return shot
  233. end
  234.  
  235.  
  236. local function drawTile(scrX,scrY,tile)
  237.   term.setCursorPos(scrX,scrY)
  238.  
  239.   if tile.ship then
  240.     if tile.shot then
  241.       doColor(colors.red,colors.gray)
  242.       term.write("@")
  243.     else
  244.       doColor(colors.white,colors.gray)
  245.       term.write("O")
  246.     end
  247.   else
  248.     if tile.hit then
  249.       doColor(colors.red,colors.gray)
  250.       term.write("x")
  251.     elseif tile.shot then
  252.       doColor(colors.white,colors.lightBlue)
  253.       term.write(".")
  254.     else
  255.       doColor(colors.white,colors.lightBlue)
  256.       term.write(" ")
  257.     end
  258.   end
  259. end
  260.  
  261. local function drawGrid(scrX,scrY,grid)
  262.   doColor(colors.white,colors.black)
  263.   term.setCursorPos(scrX,scrY+1)
  264.   term.write(" ")
  265.   doColor(colors.white,colors.gray)
  266.   term.setCursorPos(scrX,scrY)
  267.   local pad=11-#grid.title
  268.   term.write(string.rep(" ",math.ceil(pad/2))..grid.title..string.rep(" ",math.floor(pad/2)))
  269.  
  270.   for gx=1,10 do
  271.     term.setTextColor(colors.white)
  272.     term.setBackgroundColor(colors.black)
  273.     term.setCursorPos(scrX+gx,scrY+1)
  274.     term.write(gx==10 and "0" or string.char(string.byte("0")+gx))
  275.  
  276.     term.setCursorPos(scrX,scrY+gx+1)
  277.     term.write(string.char(string.byte("A")+gx-1))
  278.     for gy=1,10 do
  279.       drawTile(scrX+gx,scrY+gy+1,grid[gx][gy])
  280.     end
  281.   end
  282.   doColor(colors.white,colors.black)
  283. end
  284.  
  285. function moveTargetIndicator(newX,newY)
  286.   --if x has changed...
  287.   if targetX and targetY then
  288.     drawTile(targetX+targetGridBounds.minX-1,targetY+targetGridBounds.minY-1,oppGrid[targetX][targetY])
  289.   end
  290.   doColor(colors.yellow,colors.lightGray)
  291.   if newX~=targetX then
  292.     --space over old
  293.     if targetX then
  294.       term.setCursorPos(targetGridBounds.minX+targetX-1,targetGridBounds.maxY+1)
  295.       term.write(" ")
  296.       term.setCursorPos(targetGridBounds.minX+targetX-1,targetGridBounds.minY-3)
  297.       term.write(" ")
  298.     end
  299.     --draw new
  300.     term.setCursorPos(targetGridBounds.minX+newX-1,targetGridBounds.maxY+1)
  301.     term.write("^")
  302.     term.setCursorPos(targetGridBounds.minX+newX-1,targetGridBounds.minY-3)
  303.     term.write("v")
  304.  
  305.     targetX=newX
  306.   end
  307.   if newY~=targetY then
  308.     --space over old
  309.     if targetY then
  310.       term.setCursorPos(targetGridBounds.maxX+1,targetGridBounds.minY+targetY-1)
  311.       term.write(" ")
  312.       term.setCursorPos(targetGridBounds.minX-2,targetGridBounds.minY+targetY-1)
  313.       term.write(" ")
  314.     end
  315.     --draw new
  316.     term.setCursorPos(targetGridBounds.maxX+1,targetGridBounds.minY+newY-1)
  317.     term.write("<")
  318.     term.setCursorPos(targetGridBounds.minX-2,targetGridBounds.minY+newY-1)
  319.     term.write(">")
  320.  
  321.     targetY=newY
  322.   end
  323.   term.setCursorPos(15,15)
  324.   term.write("Target : "..toGridRef(targetX,targetY))
  325.   --if the target tile is a valid target, draw a "+"
  326.   if not oppGrid[targetX][targetY].shot then
  327.     term.setCursorPos(targetX+targetGridBounds.minX-1,targetY+targetGridBounds.minY-1)
  328.     doColor(colors.yellow,colors.lightBlue)
  329.     term.write("+")
  330.   end
  331. end
  332.  
  333. local log={}
  334.  
  335. local termWidth,termHeight=term.getSize()
  336.  
  337. local logHeight=termHeight-3
  338. local logWidth=termWidth-28
  339.  
  340. for i=1,logHeight do
  341.   log[i]=""
  342. end
  343.  
  344. local function printLog()
  345.   doColor(colors.white,colors.black)
  346.   for i=1,logHeight do
  347.     term.setCursorPos(28,1+i)
  348.     local name,line=string.match(log[i],"(<[^>]+> )(.*)")
  349.     if name then
  350.       doColor(colors.lightBlue,colors.black)
  351.       write(name)
  352.       doColor(colors.white,colors.black)
  353.       write(line..string.rep(" ",logWidth-#log[i]))
  354.     else
  355.       write(log[i]..string.rep(" ",logWidth-#log[i]))
  356.     end
  357.   end
  358. end
  359.  
  360.  
  361.  
  362. --shipX/Y are the position of ship on grid; gridX/Y are the offset of the top-left of grid
  363. local function drawShip(size,align,x,y,char)
  364.   local stepX=align=="h" and 1 or 0
  365.   local stepY=stepX==1 and 0 or 1
  366.   for j=1,size do
  367.     term.setCursorPos(x,y)
  368.     term.write(char)
  369.     x,y=x+stepX,y+stepY
  370.   end
  371. end
  372.  
  373. local function setStatusLine(lineNum,text)
  374.   doScreenColor()
  375.   local pad=math.floor((termWidth-#text)/2)
  376.   term.setCursorPos(1,16+lineNum)
  377.   term.write((" "):rep(pad)..text..(" "):rep(termWidth-#text-pad))
  378. end
  379.  
  380.  
  381. doScreenColor()
  382. term.clear()
  383.  
  384. drawGrid(2,2,myGrid)
  385.  
  386. setStatusLine(1,"Started game with "..opponent.." at computer #"..(opponentID or "nil"))
  387.  
  388. local function getShipBounds(ship)
  389.   return {
  390.      minX=ship.pos[1],
  391.      minY=ship.pos[2],
  392.      maxX=ship.pos[1]+(ship.dir=="h" and ship.size-1 or 0),
  393.      maxY=ship.pos[2]+(ship.dir=="v" and ship.size-1 or 0)
  394.    }
  395. end
  396.  
  397. local function getPointBounds(x,y)
  398.   return {
  399.     minX=x,
  400.     minY=y,
  401.     maxX=x,
  402.     maxY=y,
  403.   }
  404. end
  405.  
  406. local function boundsIntersect(boundsA,boundsB)
  407.   return not (
  408.       boundsA.minX>boundsB.maxX or
  409.       boundsA.maxX<boundsB.minX or
  410.       boundsA.minY>boundsB.maxY or
  411.       boundsA.maxY<boundsB.minY
  412.     )
  413. end
  414.  
  415.  
  416. local function checkShipCollision(shipIndex)
  417.   local myBounds=getShipBounds(ships[shipIndex])
  418.   for i=1,#ships do
  419.     if i~=shipIndex and ships[i].pos then
  420.       if boundsIntersect(myBounds,getShipBounds(ships[i])) then
  421.         return i
  422.       end
  423.     end
  424.   end
  425.   return 0
  426. end
  427.  
  428.  
  429.  
  430. local function randomizeShips()
  431.   for i=1,5 do
  432.     ships[i].pos=nil
  433.   end
  434.   for i=1,5 do
  435.     local ship=ships[i]
  436.     local dir
  437.     local x,y
  438.     repeat
  439.       --random orientation
  440.       dir=math.random(2)==1 and "v" or "h"
  441.       --random position
  442.       x = math.random(dir=="v" and 10 or (10-ship.size))
  443.       y = math.random(dir=="h" and 10 or (10-ship.size))
  444.       ship.pos={x,y}
  445.       ship.dir=dir
  446.     until checkShipCollision(i)==0
  447.   end
  448. end
  449.  
  450.  
  451.  
  452. local function shipPlacement()
  453.   local selection=1
  454.   local collidesWith=0
  455.   local dragging=false
  456.   local moveShip=nil
  457.   local clickedOn=nil
  458.   local clickedAt=nil
  459.  
  460.   doScreenColor()
  461.   term.setCursorPos(28,3)
  462.   write("use arrows to move ship")
  463.   term.setCursorPos(28,4)
  464.   write("press space to rotate")
  465.   term.setCursorPos(28,5)
  466.   write("tab selects next ship")
  467.   if term.isColor() then
  468.     term.setCursorPos(28,6)
  469.     write("click and drag ships")
  470.     term.setCursorPos(28,7)
  471.     write("right-click ship to")
  472.     term.setCursorPos(28,8)
  473.     write("  rotate")
  474.   end
  475.   term.setCursorPos(28,9)
  476.   write('"r" to randomize ships')
  477.   term.setCursorPos(28,10)
  478.   write('"f" when finished')
  479.   randomizeShips()
  480.   setStatusLine(1,"Arrange your ships on the grid")
  481.  
  482.   while true do
  483.     --local placed=0
  484.     --draw sea
  485.     doColor(colors.white,colors.lightBlue)
  486.     for i=1,10 do
  487.       term.setCursorPos(3,3+i)
  488.       term.write("          ")
  489.     end
  490.     --draw ships
  491.     for i=1,#ships do
  492.      --draw ship at sea if it's placed
  493.       if ships[i].pos then
  494.         if collidesWith~=0 and (collidesWith==i or selection==i) then
  495.           doColor(selection==i and colors.red or colors.pink,colors.gray)
  496.           drawShip(ships[i].size,ships[i].dir,2+ships[i].pos[1],3+ships[i].pos[2],"@")
  497.         else
  498.           doColor(selection==i and colors.lime or colors.white,colors.gray)
  499.           drawShip(ships[i].size,ships[i].dir,2+ships[i].pos[1],3+ships[i].pos[2],"O")
  500.         end
  501.       end
  502.     end
  503.  
  504.     local event,p1,p2,p3=os.pullEvent()
  505.     if event=="key" then
  506.       if not dragging then
  507.         if p1==keys.tab then
  508.           if collidesWith==0 then
  509.             selection=(selection%5)+1
  510.           else
  511.             local t=selection
  512.             selection=collidesWith
  513.             collidesWith=t
  514.           end
  515.         elseif p1==keys.up then
  516.           moveShip={0,-1}
  517.         elseif p1==keys.down then
  518.           moveShip={0,1}
  519.         elseif p1==keys.left then
  520.           moveShip={-1,0}
  521.         elseif p1==keys.right then
  522.           moveShip={1,0}
  523.         elseif p1==keys.space then
  524.           moveShip={0,0}
  525.           ships[selection].dir=ships[selection].dir=="h" and "v" or "h"
  526.         elseif p1==keys.f then
  527.           if collidesWith~=0 then
  528.             setStatusLine(2,"You can't finalize with ships overlapping!")
  529.           else
  530.             break
  531.           end
  532.         elseif p1==keys.r then
  533.           randomizeShips();
  534.         end
  535.       end
  536.     elseif event=="mouse_click" then
  537.       clickedOn=nil
  538.       --click event! figure out what we clicked on
  539.       local clickBounds=getPointBounds(p2,p3)
  540.       local clickGridBounds=getPointBounds(p2-2,p3-3)
  541.  
  542.       for i=1,#ships do
  543.         if ships[i].pos and boundsIntersect(clickGridBounds,getShipBounds(ships[i])) and
  544.            (collidesWith==0 or collidesWith==i or i==selection) then
  545.           --select it
  546.           --if we're switching between the colliding ships, swap selection
  547.           if collidesWith~=0 and i~=selection then
  548.             collidesWith=selection
  549.           end
  550.           --mode="place"
  551.           clickedOn=ships[i]
  552.           clickedOffset={p2-2-ships[i].pos[1],p3-3-ships[i].pos[2]}
  553.           selection=i
  554.           break
  555.         --[[else
  556.           local labelBounds={minX=15,maxX=24,minY=2*i,maxY=1+2*i}
  557.           if boundsIntersect(clickBounds,labelBounds) and
  558.              (collidesWith==0 or collidesWith==i or i==selection) then
  559.             if collidesWith~=0 then
  560.               if i~=selection then
  561.                 collidesWith=selection
  562.               end
  563.             else
  564.               mode="select"
  565.             end
  566.             clickedOn=ships[i]
  567.             clickedOffset={0,0}
  568.             selection=i
  569.             if ships[i].pos==nil then
  570.               ships[i].pos={1,1}
  571.               collidesWith=checkShipCollision(selection)
  572.               break
  573.             end
  574.           end--]]
  575.         end
  576.       end
  577.       if not clickedOn and collidesWith==0 and
  578.           boundsIntersect(clickBounds,{minX=15,maxX=22,minY=13,maxY=13}) then
  579.         break
  580.       elseif clickedOn and p1==2 then
  581.         --can't drag from a right-click!
  582.         clickedOn=nil
  583.         if ships[selection].dir=="h" then
  584.           ships[selection].dir="v"
  585.           moveShip={p2-2-ships[selection].pos[1],-(p2-2-ships[selection].pos[1])}
  586.         else
  587.           ships[selection].dir="h"
  588.           moveShip={p3-3-(ships[selection].pos[2]+ships[selection].size-1),p3-3-(ships[selection].pos[2])}
  589.         end
  590.       end
  591.     elseif event=="mouse_drag" and clickedOn~=nil then
  592.       --mode="place"
  593.       moveShip={
  594.          p2-2-clickedOffset[1]-ships[selection].pos[1],
  595.          p3-3-clickedOffset[2]-ships[selection].pos[2]}
  596.     end
  597.  
  598.     if moveShip then
  599.       local curShip=ships[selection]
  600.       --calc position limits based on ship size and alignment
  601.       local maxX=curShip.dir=="h" and (11-curShip.size) or 10
  602.       local maxY=curShip.dir=="v" and (11-curShip.size) or 10
  603.       --apply move and clamp to limits
  604.       local newPos={
  605.         math.min(math.max(curShip.pos[1]+moveShip[1],1),maxX),
  606.         math.min(math.max(curShip.pos[2]+moveShip[2],1),maxY)
  607.       }
  608.       --place the ship
  609.       ships[selection].pos=newPos
  610.       --check for collisions with other ships
  611.  
  612.       collidesWith=checkShipCollision(selection)
  613.       moveShip=nil
  614.     end
  615.   end
  616. end
  617.  
  618. local function displayGameHelp()
  619.   doScreenColor()
  620.   term.setCursorPos(28,3)
  621.   write("arrows to move cursor")
  622.   term.setCursorPos(28,4)
  623.   write("space to fire")
  624.   if term.isColor() then
  625.     term.setCursorPos(28,6)
  626.     write("click on grid to fire")
  627.   end
  628. end
  629.  
  630. local function hideHelpArea()
  631.   doScreenColor()
  632.   for y=3,13 do
  633.     term.setCursorPos(28,y)
  634.     write(string.rep(" ",32))
  635.   end
  636. end
  637.  
  638.  
  639. local function runGame()
  640.  
  641.   --first, ship placement phase!!
  642.   shipPlacement()
  643.  
  644.   hideHelpArea()
  645.  
  646.   --hide the old help, draw the new
  647.  
  648.   --tell the other guy we're done
  649.   rednet.send(opponentID,"bs ready")
  650.   if not opponentReady then
  651.     setStatusLine(1,"Waiting for opponent to finish placing ships")
  652.     while not opponentReady do
  653.       os.pullEvent()
  654.     end
  655.   end
  656.  
  657.   --now, play the game
  658.   --draw my final ship positions intto the grid
  659.   drawShipsToGrid(ships,myGrid)
  660.  
  661.  
  662.   --if I'm host, flip a coin
  663.   if action=="host" then
  664.     math.randomseed(os.time())
  665.     myTurn=math.floor(100*math.random())%2==0
  666.     rednet.send(opponentID,"bs cointoss "..tostring(not myTurn))
  667.     if myTurn then
  668.       setStatusLine(2,"Your turn, take your shot!")
  669.     else
  670.       setStatusLine(2,"Opponent's turn, waiting...")
  671.     end
  672.   else
  673.     --I joined, wait for coin toss
  674.     setStatusLine(2,"waiting for coin toss...")
  675.     while myTurn==nil do
  676.       os.pullEvent()
  677.     end
  678.   end
  679.  
  680.  
  681.   setStatusLine(1,"")
  682.   if myTurn then
  683.     --I won, I go first
  684.     displayGameHelp()
  685.   end
  686.  
  687.   --draw a target grid
  688.   drawGrid(2,2,myGrid)
  689.   drawGrid(15,2,oppGrid)
  690.   --initialize target indicators
  691.   moveTargetIndicator(5,5)
  692.   --game turn loop
  693.   while true do
  694.     --wait for my turn
  695.     while not myTurn do
  696.       os.pullEvent()
  697.     end
  698.     --my turn!
  699.     while true do
  700.       local e,p1,p2,p3,p4,p5=os.pullEvent()
  701.       if e=="mouse_click" then
  702.         local clickBounds=getPointBounds(p2,p3)
  703.         if boundsIntersect(clickBounds,targetGridBounds) then
  704.           moveTargetIndicator(p2-15,p3-3)
  705.           local shot=makeShot(targetX,targetY,oppGrid)
  706.           if shot then
  707.             --valid shot, tell the other guy
  708.             rednet.send(opponentID,"bs shot "..targetX.." "..targetY)
  709.             break
  710.           end
  711.         end
  712.       elseif e=="char" then
  713.         p1=string.lower(p1)
  714.         if p1>="a" and p1<="j" then
  715.           --row selected
  716.           moveTargetIndicator(targetX,string.byte(p1)-string.byte("a")+1)
  717.         elseif p1>="0" and p1<="9" then
  718.           local t=string.byte(p1)-string.byte("0")
  719.           if t==0 then t=10 end
  720.           moveTargetIndicator(t,targetY)
  721.         end
  722.       elseif e=="key" then
  723.         if p1==keys.enter or p1==keys.space and targetX and targetY then
  724.           local shot=makeShot(targetX,targetY,oppGrid)
  725.           if shot then
  726.             rednet.send(opponentID,"bs shot "..targetX.." "..targetY)
  727.             break
  728.           end
  729.         elseif p1==keys.up then
  730.           moveTargetIndicator(targetX,math.max(targetY-1,1))
  731.         elseif p1==keys.down then
  732.           moveTargetIndicator(targetX,math.min(targetY+1,10))
  733.         elseif p1==keys.left then
  734.           moveTargetIndicator(math.max(targetX-1,1),targetY)
  735.         elseif p1==keys.right then
  736.           moveTargetIndicator(math.min(targetX+1,10),targetY)
  737.         end
  738.       end
  739.     end
  740.     --shot sent, wait for my turn to resolve (top coroutine will switch turns and draw the hit to the grid)
  741.     setStatusLine(2,"Waiting for opponent...")
  742.     while myTurn do
  743.       os.pullEvent()
  744.     end
  745.   end
  746. end
  747.  
  748. local gameRoutine=coroutine.create(runGame)
  749. --if advanced terminal, default focus to chat, can play with mouse
  750. local inChat=term.isColor()
  751. local savedCursorPos={7,19}
  752.  
  753. --redirect just to block scroll
  754. local redir={}
  755. for k,v in pairs(originalTerm) do
  756.   if k~="scroll" then
  757.     redir[k]=v
  758.   else
  759.     redir[k]=function() end
  760.   end
  761. end
  762. originalTerm = term.redirect(redir)
  763.  
  764. --run the game routine once
  765. coroutine.resume(gameRoutine)
  766. --hide cursor
  767. term.setCursorBlink(false)
  768.  
  769. while true do
  770.   local e,p1,p2,p3,p4,p5=os.pullEventRaw()
  771.   if e=="terminate" then
  772.     quit()
  773.   elseif e=="shipsunk" then
  774.     setStatusLine(1,opponent.." sank your "..ships[p1].name.."!")
  775.     rednet.send(opponentID,"bs sink")
  776.     shipsLeft=shipsLeft-1
  777.     if shipsLeft==1 then
  778.       setStatusLine(3,"You only have 1 ship left!")
  779.     elseif shipsLeft>1 then
  780.       setStatusLine(3,"You have "..shipsLeft.." ships left!")
  781.     else
  782.       rednet.send(opponentID,"bs win")
  783.       setStatusLine(3,"You lost the game!")
  784.       break
  785.     end
  786.   elseif e=="rednet_message" then
  787.     local cmd,args=string.match(p2,"^bs (%S+)%s?(.*)")
  788.     if cmd=="ready" then
  789.       opponentReady=true
  790.       os.queueEvent("kickcoroutine")
  791.     elseif cmd=="cointoss" then
  792.       myTurn=args=="true"
  793.       if myTurn then
  794.         setStatusLine(2,"Your turn, take your shot!")
  795.       else
  796.         setStatusLine(2,"Opponent's turn, waiting...")
  797.       end
  798.       os.queueEvent("kickcoroutine")
  799.     elseif cmd=="shot" then
  800.       if myTurn then
  801.         setStatusLine(3,"What the?! Got a shot but not their turn! Ignoring")
  802.       else
  803.         local tx, ty=string.match(args,"(%d+) (%d+)")
  804.         tx,ty=tonumber(tx),tonumber(ty)
  805.         local tile=myGrid[tx][ty]
  806.         local shot=makeShot(tx,ty,myGrid)
  807.         rednet.send(opponentID,"bs result "..(shot[3] and "hit" or "miss"))
  808.         drawTile(2+tx,3+ty,tile)
  809.         myTurn=true
  810.         os.queueEvent("kickcoroutine")
  811.         displayGameHelp()
  812.         setStatusLine(1,opponent.." fired at "..toGridRef(tx,ty).." and "..(shot[3] and "hit" or "missed"))
  813.         setStatusLine(2,"Your turn, take your shot!")
  814.       end
  815.     elseif cmd=="sink" then
  816.       setStatusLine(1,"You sank one of "..opponent.."'s ships!")
  817.       oppShipsLeft=oppShipsLeft-1
  818.       if oppShipsLeft==0 then
  819.         setStatusLine(2,opponent.." has no ships left!")
  820.       elseif oppShipsLeft==1 then
  821.         setStatusLine(2,"Sink 1 more to win!")
  822.       else
  823.         setStatusLine(2,"They have "..oppShipsLeft.." ships left.")
  824.       end
  825.     elseif cmd=="result" then
  826.       if not myTurn then
  827.         setStatusLine(3,"What the?! Got a shot result but not my turn! Ignoring")
  828.       else
  829.         local tile=oppGrid[targetX][targetY]
  830.         tile.hit=args=="hit"
  831.         drawTile(targetX+15,targetY+3,tile)
  832.         myTurn=false
  833.         doColor(tile.hit and colors.red or colors.white,colors.lightGray)
  834.         term.setCursorPos(17,16)
  835.         term.write(tile.hit and "HIT!" or "MISS")
  836.         setStatusLine(2,"Waiting for opponent...")
  837.         os.queueEvent("kickcoroutine")
  838.       end
  839.  
  840.     elseif cmd=="win" then
  841.       --we won!
  842.       setStatusLine(3,"You won the game! Congratulations!")
  843.       break
  844.     end
  845.   --everything else goes to gameRoutine
  846.   else
  847.     --all other events go to this routine
  848.     local succ,err=coroutine.resume(gameRoutine,e,p1,p2,p3,p4,p5)
  849.     if not succ then
  850.       print("game coroutine crashed with the following error: "..err)
  851.       quit()
  852.     end
  853.  
  854.     if coroutine.status(gameRoutine)=="dead" then
  855.       --game over
  856.       break
  857.     end
  858.   end
  859.  
  860. end
  861.  
  862. term.setCursorPos(1,19)
  863. term.clearLine()
  864. term.write("  Press any key to continue...")
  865. os.pullEvent("key")
  866. --if a char event was queued following the key event, this will eat it
  867. os.sleep(0)
  868.  
  869. term.setTextColor(colors.white)
  870. term.setBackgroundColor(colors.black)
  871. term.clear()
  872. quit()
  873. --
Add Comment
Please, Sign In to add comment