KingKj52

battleship

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