--[[ GP1ASSIGNMENT1 Owen Canavan D13125137 --]] --define grid variables gridWidth = 9 gridHeight = 14 tileSize = 25 --sizes need for default corona iPhone 4 screen gap = 5 --tile data tiles = {} inGame = false --end game ui container weGotAPotentialKillScreenComingUp = {} --hit tracking missCount = 0 --total misses missStreak = 0 --misses in a row hitCount = 0 --total hits hitCounts = {} --hits per ship score = 0 --could be local MAX_SCORE = 1000000 --taunt strings FEATURE_CREEP = true --enable extra feedback messages taunts ={} taunts.hit = {"ouch!", "HEY! watch the paint-job!", "why...*sniff*...why?!", "That hurt", "I'm beginning to think you don't like me" } taunts.sunk = {"Abandon ship!", "Man the life rafts!", "I only just paid for that one...", "I didn't really need that one anyway"} taunts.miss = {"HAW HAW!", "missed me!", "Are you blind?", "miles away", "okay... that one was a bit too close", "colder...colder", "my blind granny can shoot better than you!", "come on, we haven't got all day", "someday they'll make a movie about this", "oh just give up already!" } --remove device status bar display.setStatusBar(display.HiddenStatusBar) --[[ FUNCTION name: init arguments: none description: initial setup for new game, called on app start or when 'Play Again' tapped --]] function init() --check to see if there is existing display objects, if so remove them cleanupGrid() --find grid top left position for grid; grid_x, grid_y screen_centre_x = display.contentWidth/2 screen_centre_y = display.contentHeight/2 total_grid_width = (gridWidth * tileSize) + (gap * (gridWidth-1)) total_grid_height = (gridHeight * tileSize) + (gap * (gridHeight-1)) grid_x = screen_centre_x - total_grid_width/2 grid_y = screen_centre_y - total_grid_height/2 grid_x = grid_x + tileSize/2 grid_y = grid_y + tileSize/2 --populate 2d array with rects tiles = {} for i=1,gridWidth,1 do --new column tiles[i] = {} for j=1,gridHeight,1 do --new row --draw new tile to screen, position from top left newTile = display.newRect(0, 0, tileSize, tileSize) --I originally set x and y in the newRect call, as I prefer working from top left, but after updating to 2.0 that now also works from centre --so setting it here for compatibility newTile.x = grid_x + ((tileSize + gap) * (i - 1)) newTile.y = grid_y + ((tileSize + gap) * (j - 1)) --no ship by default newTile.ship = false --tap event newTile:addEventListener("tap",tappedRect) tiles[i][j] = newTile end end --add four ships, size 2,3,4,5 for s=2,5,1 do addShip(s) end --yes we are playing inGame = true --set defaults score = 0 missCount = 0 hitCount = 0 hitCounts = {0,0,0,0,0} missStreak = 0 --extra feature: text feedback --position text below grid taunts.text = display.newText({ x=display.contentWidth/2, y=display.contentHeight - 16, text="", fontSize=16, font=native.systemFontBold, align="center" }) end --[[ FUNCTION name: addShip arguments: shipSize: int for length of ship description: add a new ship, of a set length, to the grid, without overlapping on existing ships --]] function addShip(shipSize) --[[ --debug ship colours colors = {} colors[2] = {r=255,g=0,b=255} colors[3] = {r=0,g=255,b=255} colors[4] = {r=0,g=127,b=255} colors[5] = {r=255,g=255,b=0} --]] --determine if this ship will be placed vertical (1) or horizontal(2) vertOrHoriz = math.random(1,2) --directional controls. makeshift ternary xo = vertOrHoriz == 2 and 1 or 0 yo = vertOrHoriz == 1 and 1 or 0 --loop until we have completed the ship while(true) do --find a random tile for first ship tile, subtract additional ship size from search range in appropriate direction tile_x = math.random(1,gridWidth - ((shipSize - 1)*xo)) tile_y = math.random(1,gridHeight - ((shipSize - 1)*yo)) --temp holder for valid tiles tempTiles = {} for i=1,shipSize,1 do --search out along ships length to see if all tiles are open for placement if tiles[tile_x+((i-1)*xo)] and tiles[tile_x+((i-1)*xo)][tile_y+((i-1)*yo)] then --still looking at a valid tile on the grid if tiles[tile_x+((i-1)*xo)][tile_y+((i-1)*yo)].ship then --there is already a ship here, invalid position for a new ship, break out start while loop again break else --so far so good --valid tile, store it in temp holder for later tempTiles[i] = tiles[tile_x+((i-1)*xo)][tile_y+((i-1)*yo)] if i == shipSize then --counted through all needed space and found no ships, good to go --loop back through temp tile holder and assign ship positions for t = 1,#tempTiles,1 do --this tile is indeed a bit of a ship tempTiles[t].ship = true --ohh shiney! brand new, never been hit... honest! tempTiles[t].hit = false --which ship does this bit belong to? tempTiles[t].shipId = shipSize --debug shipId --tempTiles[t]:setFillColor(colors[shipSize].r,colors[shipSize].g,colors[shipSize].b) end --full ship has been set, don't need this function any more, return to init() return end end else --sanity check, something went really wrong if we got here, break out and start while loop again. although it might be safer to return instead to avoid infinite loop since really-wrong-thing might happen again break end end end end --[[ FUNCTION name: tappedRect arguments: event: system tap event description: user tapped/clicked on a rect, provide feedback as to whether or not it hit a ship or missed. if all ships sunk, win/end game. --]] function tappedRect(event) --sanity check that we are still playing the game and so can only process valid shots if inGame then --default feedback message and message colour msg = '' msg_color = {r=255,g=255,b=255} --did we hit a ship? if event.target.ship then --yes we did! --did we already shoot at this tile? if not event.target.hit then --no, brand new shot event.target.hit = true --increment total valid hits hitCount = hitCount + 1 --increment total this for this ship hitCounts[event.target.shipId] = hitCounts[event.target.shipId] + 1 --hit something, so reset miss streak missStreak = 0 --have we sunk a complete ship? if hitCounts[event.target.shipId] == event.target.shipId then --ship sunk --was this the battleship (ship size 4)? if event.target.shipId == 4 then --YES!! --print("you sunk my battleship!") msg = "You sunk my battleship!" else --no... something else useless like an aircraft carrier or sub --pick a random taunt/response msg = taunts.sunk[math.random(1,#taunts.sunk)] end --red text for a sunk ship! msg_color = {r=255,g=0,b=0} else --no, just a partial hit --40% chance of a random taunt/response if math.random(1,100) > 60 then msg = taunts.hit[math.random(1,#taunts.hit)] end end --have hit all there is to hit? if hitCount >= 14 then --yes! --gameOver: winner gameOver() end --debug, colour hits by ship type --event.target:setFillColor(255 - (16 * event.target.shipId),0,0) --this tile is hit, colour it red event.target:setFillColor(255,0,0) end else --no we didn't! --did we already shoot at this tile? if not event.target.hit then --no, brand new shot event.target.hit = true --increment miss count for scoring purposes missCount = missCount + 1 --increment miss streak for extra snark missStreak = missStreak + 1 --10% chance of random miss taunt, 50% if missed 10 times in a row. hacky ternary is hacky tauntRate = missStreak > 10 and 50 or 90 if math.random(1,100) > tauntRate then --taunt msg = taunts.miss[math.random(1,#taunts.miss)] end --this tile is a miss, colour it green event.target:setFillColor(0,255,0) end end --should we actually use our extra feedback feature? if FEATURE_CREEP then shout(msg, msg_color) end end end --[[ FUNCTION name: shout arguments: msg: string of text to be displayed color: table of RGB values to colour text description: display a feedback response in the text ui element below the grid category: FEATURE CREEP! --]] function shout(msg, color) if FEATURE_CREEP then --sanity check the the ui element exists if taunts and taunts.text then if msg == '' then --no message was set, just ensure the ui is hidden so it doesn't display it's previous state taunts.text.isVisible = false else --we got a message --reset the text to new value taunts.text.text = msg --reset the colour to new RGB values --I've tried this in both ver 1 and 2.0, so it should work, if not comment out this block ---[[ if taunts.text.setFillColor then --corona v2.0 taunts.text:setFillColor(color.r/255,color.g/255,color.b/255) elseif taunts.text.setTextColor then --corona v1 taunts.text:setTextColor(color.r,color.g,color.b) end --]] --ensure the ui element is displaying taunts.text.isVisible = true end end end end --[[ FUNCTION name: gaveOver arguments: none description: remove the grid, display end game message and Play Again button --]] function gameOver() --remove the grid cleanupGrid() --enable paranoid mode inGame = false --calculate the score -- win with no misses, 1,000,000 points -- win by hitting every tile, 0 points -- using floor only in case MAX_SCORE were a lot smaller score = math.floor(MAX_SCORE * (1 - (missCount / ((gridWidth * gridHeight) - 14)))) --display win message, score and play again button weGotAPotentialKillScreenComingUp = {} weGotAPotentialKillScreenComingUp.bg = display.newRoundedRect(0,0, display.contentWidth, display.contentHeight/4,32) weGotAPotentialKillScreenComingUp.bg.x = display.contentWidth/2 weGotAPotentialKillScreenComingUp.bg.y = display.contentHeight/2 --would've done it in the function call, but again version differences... weGotAPotentialKillScreenComingUp.bg:setFillColor(224,0,0) --colour will vary depending on version. not worth checking here weGotAPotentialKillScreenComingUp.youWin = display.newText({ x=display.contentWidth/2, y=display.contentHeight/2 - 25, text="You Win!", fontSize=48, font=native.systemFontBold, align="center" }) weGotAPotentialKillScreenComingUp.score = display.newText({ x=display.contentWidth/2, y=display.contentHeight/2 + 8, text="Your score: "..score, fontSize=16, font=native.systemFontBold, align="center" }) weGotAPotentialKillScreenComingUp.playAgain = display.newText({ x=display.contentWidth/2, y=display.contentHeight/2 + 32, text="[Play Again]", fontSize=32, font=native.systemFontBold, align="center" }) weGotAPotentialKillScreenComingUp.playAgain:addEventListener("tap",playAgain) end function playAgain() --clean up the win screen ui. could've been in a parent to make it easier, but just being paranoid weGotAPotentialKillScreenComingUp.bg:removeSelf() weGotAPotentialKillScreenComingUp.youWin:removeSelf() weGotAPotentialKillScreenComingUp.score:removeSelf() weGotAPotentialKillScreenComingUp.playAgain:removeEventListener("tap",playAgain) weGotAPotentialKillScreenComingUp.playAgain:removeSelf() --build new game init() end --[[ FUNCTION name: cleanupGrid arguments: none description: clean up old data --]] function cleanupGrid() --loop through old data and remove event listeners and display objects if they exist if #tiles > 0 and #tiles[1] > 0 then --old data clean it up for i=1,#tiles,1 do for j=1,#tiles[i],1 do rect = tiles[i][j] rect:removeEventListener("tap",tappedRect) rect:removeSelf() end end end --reassign clean table tiles = {} --remove the feedback text ui because init() makes a new one if taunts and taunts.text then taunts.text:removeSelf() taunts.text = nil end end --first game starts here init()
www.oscan.net
oscan.net (2024) All Rights Reserved.
[Login]