Jump To …

tetris.coffee

$ ->
  $.g = new Tetris(15, 10)



class Tetris extends Game

  constructor: (rows, cols) ->
    @rows = rows
    @cols = cols
    @name = 'Tetris'
    @game_key = 'tetris'
    @nextPiece = false
    @rowBreakCount = 0
    @snd =
      break: new Audio('/lib/games/snd/break.mp3')
      drop: new Audio('/lib/games/snd/drop.mp3')
      music: new Audio('/lib/games/snd/tetris-music.mp3')
    @snd.break.volume = 0.8
    @snd.music.volume = 0.5
    @snd.music.loop = true
    @snd.music.volume = 0.4

    super

    $(window).on('keypress', @keyCheck)
    @cellSize = 18
    @boardWidth = @cellSize * @cols

    @shapes = [

a 2x2 block

      [
        [true, true]
        [true, true]
      ],

a line, 4 blocks long

      [
        [true, true, true, true]
      ],

an S shape

      [
        [false, true, true]
        [true, true, false]
      ],

the other S shape

      [
        [true, true, false]
        [false, true, true]
      ],

miniture t shape

      [
        [false, true, false]
        [true, true, true]
      ],

an L shape

      [
        [true,false,false],
        [true,true,true]
      ],

the other L shape

      [
        [true,true,true]
        [true,false,false],
      ],



    ]

    @colors = ['red','blue','yellow','magenta','cyan','orange','green']

    @newGame()

  pause: ->
    super
    @snd.music.pause()

  resume: ->
    super
    @snd.music.play()

  newGame: =>
    super

    @nextPiece = @shapes[Math.floor(Math.random()*@shapes.length)]
    @nextColor = @colors[Math.floor(Math.random()*@colors.length)]

    @gameIsOver = false
    @fallingPiece = false
    @fallingColor = false
    @paused = false

    @board = []
    @rowViews = []
    @cellViews = []
    @buildUI()
    @snd.music.play()

  buildUI: ->
    super
    @boardView = $('<div class="tboard"></div>').appendTo(@el)
    @rightCol = $('<div class="rightcol"></div>').appendTo(@el)

    @nextPieceView = $('<div id="fallingPiece" class="next_piece_view"></div>').appendTo(
      $('<div class="next_piece_wrapper"><h2>Next Piece</h2></div>').appendTo(@rightCol)
    )
    $('<h2>Level</h2><div>Coming Soon!</div><h2>Controls</h2><div><p>A = Left</p><p>D = Right</p><p>S = Down</p><p>W = Rotate</p><p>Q = Pause</p></div>')
      .appendTo(@rightCol)

    for i in [0...@rows] by 1
      @board[i] = []
      @cellViews[i] = []
      @rowViews[i] = @createEmptyRow(i, true).appendTo(@boardView)
      
        
    @fallingPieceView = $('<div id="fallingPiece"></div>').appendTo(@boardView)
    @newFallingPiece();

  destroyUI: ->
    super
    1

  createEmptyRow: (i) ->
    rv = $('<div class="trow clearfix"></div>').css({width:@cellSize*@cols+@cols*2+'px',height:@cellSize+'px'})
    for j in [0..@cols] by 1
      if (arguments.length>1)
        @board[i][j] = 0
      @cellViews[i][j] = $('<div class="tcell"></div>').appendTo(rv)#.css({width:@cellSize+'px',height:@cellSize+'px'})
    return rv

  gameOver: ->
    super
    @snd.music.pause()
    @gameIsOver = true
    @stopFall()
    @hi.addScore(@score)
    alert('Game Over!')


  newFallingPiece: () ->

    return if @gameIsOver || @paused

    window.clearInterval(window.fallInterval) unless not window.fallInterval

    @fallingPieceView.html('')


    if (arguments.length>0)
      @fallingPiece = @shapes[arguments[0]]
      ci = Math.floor(Math.random()*@colors.length)
      @fallingColor = @colors[ci]
    else
      @fallingPiece = @nextPiece
      @fallingColor = @nextColor

      @nextPiece = @shapes[Math.floor(Math.random()*@shapes.length)]
      @nextColor = @colors[Math.floor(Math.random()*@colors.length)]

    @drawFallingPiece()
    @drawNextPiece()

    l = Math.ceil( (@cols - @fallingPiece[0].length) / 2) * @cellSize + 'px'
    @fallingPieceView.css({left:l,top:0})
    @fallingCol = Math.ceil( (@cols-@fallingPiece[0].length) / 2)
    @fallingRow = 0
    ll = (@fallingCol * @cellSize) + 'px'
    @fallingPieceView.css({left:ll,top:0})
    window.fallInterval = window.setInterval(@fall, 1000)
    if !@fallingPieceIsLegal()
      @gameOver()

  drawFallingPiece: ->
    @fallingPieceView.html('')
    for i in [0...@fallingPiece.length]
      h = $('<div class="piece-row"></div>')
      for j in [0...@fallingPiece[i].length]
        hh = $('<div class="piece-cell"></div>').appendTo(h)
        if @fallingPiece[i][j]
          hh.addClass(@fallingColor)
      h.appendTo(@fallingPieceView)  

  drawNextPiece: ->
    @nextPieceView.html('').css('width',@nextPiece[0].length*@cellSize+'px')
    for i in [0...@nextPiece.length]
      h2 = $('<div class="piece-row clearfix"></div>')
      for j in [0...@nextPiece[i].length]
        hh2 = $('<div class="piece-cell"></div>').appendTo(h2)
        if @nextPiece[i][j]
          hh2.addClass(@nextColor)
      h2.appendTo(@nextPieceView)
    1

  moveFallingPiece: (drow, dcol) ->
    return if @paused
    currentRow = @fallingRow;
    currentCol = @fallingCol;

    @fallingRow += drow
    @fallingCol += dcol

    r = true

    if (!@fallingPieceIsLegal())
      @fallingCol = currentCol
      @fallingRow = currentRow
      r = false

    l = @fallingCol * @cellSize + 'px'
    t = @fallingRow * @cellSize + 'px'
    @fallingPieceView.css({left:l,top:t})
    r

  stopFall: ->
    window.clearInterval(window.fallInterval)

  startFall: ->
    window.fallInterval = window.setInterval(@fall, 1000)

  fallingPieceIsLegal: ->

is the piece out of bounds?

    if (@fallingCol < 0)
      return false
    if (@fallingCol+@fallingPiece[0].length > @cols+1)
      return false
    if (@fallingRow+@fallingPiece.length > @rows)
      return false

    for i in [0...@fallingPiece.length] by 1
      for j in [0...@fallingPiece[0].length] by 1
        if @fallingPiece[i][j] && @board[i + @fallingRow][j + @fallingCol]
          return false

    true


  fall: () =>
    return if @paused

    if !@moveFallingPiece(1,0)
      window.fallInterval = window.clearInterval(window.fallInterval)
      @placeFallingPiece()
      @newFallingPiece()

  placeFallingPiece: ->
    @snd.drop.play();
    for i in [0...@fallingPiece.length]
      for j in [0...@fallingPiece[0].length]
        if @fallingPiece[i][j]
          boardI = i + @fallingRow 
          boardJ = j + @fallingCol
          @board[boardI][boardJ] = 1
          @cellViews[boardI][boardJ].addClass(@fallingColor)
    @rowBreakCount = 0
    @checkBoardRows()

  keyCheck: (e) =>
    if (e.charCode==113 || e.charCode == 81) # q to pause
      if @paused
        @resume()
      else
        @pause()
      return
    else if @paused
      return


    if (e.charCode==49)
      @newFallingPiece(0)
      return
    if (e.charCode==50)
      @newFallingPiece(1)
      return
    if (e.charCode==51)
      @newFallingPiece(2)
      return
    if (e.charCode==52)
      @newFallingPiece(3)
      return
    if (e.charCode==53)
      @newFallingPiece(4)
      return
    if (e.charCode==54)
      @newFallingPiece(5)
      return
    if (e.charCode==55)
      @newFallingPiece(6)
      return


    if (e.charCode == 65 || e.charCode == 97)
      @moveFallingPiece(0,-1)
    else if (e.charCode == 68 || e.charCode == 100)
      @moveFallingPiece(0,1)
    else if (e.charCode == 83 || e.charCode == 115)
      @stopFall()
      if !@moveFallingPiece(1,0)
        @placeFallingPiece()
        @newFallingPiece()
      else
        @startFall()
    else if (e.charCode == 87 || e.charCode == 119)
      @rotateFallingPiece()
    else
      @newFallingPiece()

  rotateFallingPiece: ->
    origFallingPiece = @fallingPiece
    new_piece = new Array()

    
    for i in [0..@fallingPiece.length-1] by 1
      for j in [0..@fallingPiece[0].length-1] by 1
        
        x = origFallingPiece[i][j]
        newj = i #Math.abs(i - origFallingPiece.length) - 1
        newi = Math.abs(j - origFallingPiece[0].length) - 1
        new_piece[newi] = new Array() unless new_piece[newi]?
        new_piece[newi][newj] = x

    @fallingPiece = new_piece

    if @fallingPieceIsLegal()
      @drawFallingPiece()

@fallingRow = @fallingPiece.length @fallingCol = @fallingPiece[0].length

    else
      @fallingPiece = origFallingPiece

    false

  checkBoardRows: ->
    fullRow = false
    for i in [@rows-1..0] by -1
      rowFull = true
      for j in [0..@cols] by 1
        if (!@board[i][j])
          rowFull = false 
      if rowFull
        fullRow = i
        break

    if fullRow
      points = 10 * ++@rowBreakCount
      @addToScore(points)
      @removeRow(fullRow)
      window.setTimeout('$.g.checkBoardRows();', 300)


    1

  removeRow: (i) ->
    @snd.break.play()

    @rowViews[i].remove()
    @rowViews.splice(i,1)
    @cellViews.splice(i,1)
    @cellViews.unshift(new Array(@cols))
    @rowViews.unshift(@createEmptyRow(0))
    @rowViews[0].prependTo(@boardView)

    x = @board.splice(i,1)
    @board.unshift(new Array(@cols))



  oldGarbageRemoveRow: ->
    newBoard = []
    for j in [0...@rows]
      if j != i
        newBoard[j] = @copyRow(j)


    x = @board.splice(0,i);
    return;

    @rowViews[i].remove()
    @cellViews = @cellViews.splice(0,i-1) + @cellViews.splice(i+1)
    @rowViews = @rowViews.splice(0,i-1) + @rowViews.splice(i+1)
    @rowViews.unshift(@createEmptyRow(0).prependTo(@boardView))

  copyRow: (rowNum) ->
    copy = []
    for j in [0...@cols] by 1
      copy[j] = @board[rowNum][j]
    copy