diff --git a/css/style.css b/css/style.css
index 07e80b5..d90f4e8 100644
--- a/css/style.css
+++ b/css/style.css
@@ -53,7 +53,7 @@ canvas {
.stats-values {
text-align: right;
- min-width: 80px;
+ min-width: 100px;
}
.matrix {
diff --git a/js/webtris.js b/js/webtris.js
index dcd6b79..e62e487 100644
--- a/js/webtris.js
+++ b/js/webtris.js
@@ -1,13 +1,20 @@
Array.prototype.add = function(other) {
return this.map((x, i) => x + other[i])
}
+
+Array.prototype.mul = function(k) {
+ return this.map(x => k * x)
+}
+
Array.prototype.translate = function(vector) {
return this.map(pos => pos.add(vector))
}
+
Array.prototype.rotate = function(spin) {
return [-spin*this[1], spin*this[0]]
}
-Array.prototype.sample = function() {
+
+Array.prototype.pick = function() {
return this.splice(Math.floor(Math.random()*this.length), 1)[0]
}
@@ -25,7 +32,7 @@ const FALLING_PIECE_POSITION = [4, 0]
const NEXT_PIECES_POSITIONS = Array.from({length: NEXT_PIECES}, (v, k) => [2, k*4+2])
const LOCK_DELAY = 500
const FALL_DELAY = 1000
-const AUTOREPEAT_DELAY = 300
+const AUTOREPEAT_DELAY = 250
const AUTOREPEAT_PERIOD = 10
const MOVEMENT = {
LEFT: [-1, 0],
@@ -37,7 +44,7 @@ const SPIN = {
CCW: -1
}
const T_SPIN = {
- NULL: "",
+ NONE: "",
MINI: "MINI\nT-SPIN",
T_SPIN: "T-SPIN"
}
@@ -48,13 +55,19 @@ const T_SLOT = {
D: 2
}
const SCORES = [
- {LINES_CLEAR_NAME: "", NO_T_SPIN: 0, MINI_T_SPIN: 1, T_SPIN: 4},
- {LINES_CLEAR_NAME: "SINGLE", NO_T_SPIN: 1, MINI_T_SPIN: 2, T_SPIN: 8},
- {LINES_CLEAR_NAME: "DOUBLE", NO_T_SPIN: 3, T_SPIN: 12},
- {LINES_CLEAR_NAME: "TRIPLE", NO_T_SPIN: 5, T_SPIN: 16},
- {LINES_CLEAR_NAME: "TETRIS", NO_T_SPIN: 8},
+ {linesClearedName: "", "": 0, "MINI\nT-SPIN": 1, "T-SPIN": 4},
+ {linesClearedName: "SINGLE", "": 1, "MINI\nT-SPIN": 2, "T-SPIN": 8},
+ {linesClearedName: "DOUBLE", "": 3, "T-SPIN": 12},
+ {linesClearedName: "TRIPLE", "": 5, "T-SPIN": 16},
+ {linesClearedName: "TETRIS", "": 8},
]
const REPEATABLE_ACTIONS = [moveLeft, moveRight, softDrop]
+const T_SLOT_POS = [[-1, -1], [1, -1], [1, 1], [-1, 1]]
+const STATE = {
+ PLAYING: "",
+ PAUSE: "PAUSE",
+ GAME_OVER: "GAME\nOVER"
+}
class Scheduler {
@@ -63,12 +76,12 @@ class Scheduler {
this.timeoutTasks = new Map()
}
- setInterval(func, delay) {
- this.intervalTasks.set(func, window.setInterval(func, delay))
+ setInterval(func, delay, ...args) {
+ this.intervalTasks.set(func, window.setInterval(func, delay, ...args))
}
- setTimeout(func, delay) {
- this.timeoutTasks.set(func, window.setTimeout(func, delay))
+ setTimeout(func, delay, ...args) {
+ this.timeoutTasks.set(func, window.setTimeout(func, delay, ...args))
}
clearInterval(func) {
@@ -95,109 +108,126 @@ class Tetromino {
this.rotatedLast = false
this.rotationPoint5Used = false
this.holdEnabled = true
- this.srs = {
- CW: [
- [[0, 0], [-1, 0], [-1, -1], [0, 2], [-1, 2]],
- [[0, 0], [ 1, 0], [ 1, 1], [0, -2], [ 1, -2]],
- [[0, 0], [ 1, 0], [ 1, -1], [0, 2], [ 1, 2]],
- [[0, 0], [-1, 0], [-1, 1], [0, 2], [-1, -2]],
- ],
- CCW: [
- [[0, 0], [ 1, 0], [ 1, -1], [0, 2], [ 1, 2]],
- [[0, 0], [ 1, 0], [ 1, 1], [0, -2], [ 1, -2]],
- [[0, 0], [-1, 0], [-1, -1], [0, 2], [-1, 2]],
- [[0, 0], [-1, 0], [-1, 1], [0, 2], [-1, -2]],
- ],
- }
+ this.locked = false
+ this.srs = {}
+ this.srs[SPIN.CW] = [
+ [[0, 0], [-1, 0], [-1, -1], [0, 2], [-1, 2]],
+ [[0, 0], [ 1, 0], [ 1, 1], [0, -2], [ 1, -2]],
+ [[0, 0], [ 1, 0], [ 1, -1], [0, 2], [ 1, 2]],
+ [[0, 0], [-1, 0], [-1, 1], [0, -2], [-1, -2]],
+ ]
+ this.srs[SPIN.CCW] = [
+ [[0, 0], [ 1, 0], [ 1, -1], [0, 2], [ 1, 2]],
+ [[0, 0], [ 1, 0], [ 1, 1], [0, -2], [ 1, -2]],
+ [[0, 0], [-1, 0], [-1, -1], [0, 2], [-1, 2]],
+ [[0, 0], [-1, 0], [-1, 1], [0, 2], [-1, -2]],
+ ]
if (shape)
this.shape = shape
else {
if (!shapes.length)
shapes = ['I', 'J', 'L', 'O', 'S', 'T', 'Z']
- this.shape = shapes.sample()
+ this.shape = shapes.pick()
}
switch(this.shape) {
case 'I':
- this.color = "rgb(132, 225, 225)"
- this.ghostColor = "rgba(40, 164, 164, 0.5)"
+ this.color = "cyan"
+ this.lightColor = "rgb(234, 250, 250)"
+ this.ghostColor = "rgba(234, 250, 250, 0.5)"
this.minoesPos = [[-1, 0], [0, 0], [1, 0], [2, 0]]
- this.srs = {
- CW: [
- [[ 1, 0], [-1, 0], [ 2, 0], [-1, 1], [ 2, -2]],
- [[ 0, 1], [-1, 1], [ 2, 1], [-1, -1], [ 2, 2]],
- [[-1, 0], [ 1, 0], [-2, 0], [ 1, -1], [-2, 2]],
- [[ 0, 1], [ 1, -1], [-2, -1], [ 1, 1], [-2, -2]],
- ],
- CCW: [
- [[ 0, 1], [-1, 1], [ 2, 1], [-1, -1], [ 2, 2]],
- [[-1, 0], [ 1, 0], [-2, 0], [ 1, -1], [-2, 2]],
- [[ 0, -1], [ 1, -1], [-2, -1], [ 1, 1], [-2, -2]],
- [[ 1, 0], [-1, 0], [ 2, 0], [-1, 1], [ 2, -2]],
- ],
- }
+ this.srs[SPIN.CW] = [
+ [[ 1, 0], [-1, 0], [ 2, 0], [-1, 1], [ 2, -2]],
+ [[ 0, 1], [-1, 1], [ 2, 1], [-1, -1], [ 2, 2]],
+ [[-1, 0], [ 1, 0], [-2, 0], [ 1, -1], [-2, 2]],
+ [[ 0, 1], [ 1, -1], [-2, -1], [ 1, 1], [-2, -2]],
+ ]
+ this.srs[SPIN.CCW] = [
+ [[ 0, 1], [-1, 1], [ 2, 1], [-1, -1], [ 2, 2]],
+ [[-1, 0], [ 1, 0], [-2, 0], [ 1, -1], [-2, 2]],
+ [[ 0, -1], [ 1, -1], [-2, -1], [ 1, 1], [-2, -2]],
+ [[ 1, 0], [-1, 0], [ 2, 0], [-1, 1], [ 2, -2]],
+ ]
break
case 'J':
- this.color = "rgb(102, 163, 255)"
- this.ghostColor = "rgba(0, 82, 204, 0.5)"
+ this.color = "blue"
+ this.lightColor = "rgb(230, 240, 255)"
+ this.ghostColor = "rgba(230, 240, 255, 0.5)"
this.minoesPos = [[-1, -1], [-1, 0], [0, 0], [1, 0]]
break
case 'L':
- this.color = "rgb(255, 148, 77)"
- this.ghostColor = "rgba(204, 82, 0, 0.5)"
+ this.color = "orange"
+ this.lightColor = "rgb(255, 224, 204)"
+ this.ghostColor = "rgba(255, 224, 204, 0.5)"
this.minoesPos = [[-1, 0], [0, 0], [1, 0], [1, -1]]
break
case 'O':
- this.color = "rgb(255, 255, 102)"
- this.ghostColor = "rgba(204, 204, 0, 0.5)"
+ this.color = "yellow"
+ this.lightColor = "rgb(255, 255, 230)"
+ this.ghostColor = "rgba(255, 255, 230, 0.5)"
this.minoesPos = [[0, 0], [1, 0], [0, -1], [1, -1]]
- this.srs = {
- CW: [[]],
- CCW: [[]]
- }
+ this.srs[SPIN.CW] = [[]]
+ this.srs[SPIN.CCW] = [[]]
break
case 'S':
- this.color = "rgb(159, 255, 128)"
- this.ghostColor = "rgb(38, 153, 0, 0.5)"
- this.minoesPos = [[-1, -1], [0, -1], [0, 0], [1, 0]]
+ this.color = "green"
+ this.lightColor = "rgb(236, 255, 230)"
+ this.ghostColor = "rgba(236, 255, 230, 0.5)"
+ this.minoesPos = [[-1, 0], [0, 0], [0, -1], [1, -1]]
break
case 'T':
- this.color = "rgb(179, 102, 255)"
- this.ghostColor = "rgba(102, 0, 204, 0.5)"
+ this.color = "magenta"
+ this.lightColor= "rgb(242, 230, 255)"
+ this.ghostColor = "rgba(242, 230, 255, 0.5)"
this.minoesPos = [[-1, 0], [0, 0], [1, 0], [0, -1]]
break
case 'Z':
- this.color = "rgb(255, 51, 51)"
- this.ghostColor = "rgba(204, 0, 0, 0.5)"
+ this.color = "red"
+ this.lightColor = "rgb(255, 230, 230)"
+ this.ghostColor = "rgba(255, 230, 230, 0.5)"
this.minoesPos = [[-1, -1], [0, -1], [0, 0], [1, 0]]
break
}
}
- get absMinoesPos() {
+ get minoesAbsPos() {
return this.minoesPos.translate(this.pos)
}
- draw(context, ghostYOffset=0) {
- if (ghostYOffset) {
+ draw(context, ghost_pos=[0, 0]) {
+ const color = this.locked ? this.lightColor : this.color
+ if (ghost_pos[1]) {
context.save()
context.shadowColor = this.ghostColor
context.shadowOffsetX = 0
- context.shadowOffsetY = ghostYOffset * MINO_SIZE
+ context.shadowOffsetY = (ghost_pos[1]-this.pos[1]) * MINO_SIZE
context.shadowBlur = 3
- }
- this.absMinoesPos.map(pos => draw_mino(context, ...pos, this.color))
- if (ghostYOffset)
+ this.minoesAbsPos.forEach(pos => drawMino(context, pos, color))
context.restore()
+ }
+ this.minoesAbsPos.forEach(pos => drawMino(context, pos, this.lightColor, color, ghost_pos))
}
}
-function draw_mino(context, x, y, color, ghostYOffset) {
- context.fillStyle = color
- context.fillRect(x*MINO_SIZE, y*MINO_SIZE, MINO_SIZE, MINO_SIZE)
+function drawMino(context, pos, color1, color2=null, spotlight=[0, 0]) {
+ if (color2) {
+ var center = pos.add([0.5, 0.5])
+ spotlight = spotlight.add([0.5, 0.5])
+ var glint = spotlight.mul(0.1).add(center.mul(0.9)).mul(MINO_SIZE)
+ const gradient = context.createRadialGradient(
+ ...glint, 2, ...glint.add([6, 4]), 2*MINO_SIZE
+ )
+ gradient.addColorStop(0, color1)
+ gradient.addColorStop(1, color2)
+ context.fillStyle = gradient
+ }
+ else
+ context.fillStyle = color1
+ var topLeft = pos.mul(MINO_SIZE)
+ context.fillRect(...topLeft, MINO_SIZE, MINO_SIZE)
context.lineWidth = 0.5
context.strokeStyle = "white"
- context.strokeRect(x*MINO_SIZE, y*MINO_SIZE, MINO_SIZE, MINO_SIZE)
+ context.strokeRect(...topLeft, MINO_SIZE, MINO_SIZE)
}
@@ -216,16 +246,17 @@ class HoldQueue {
}
}
+
timeFormat = new Intl.DateTimeFormat("en-US", {
hour: "numeric", minute: "2-digit", second: "2-digit", hourCycle: "h24", timeZone: "UTC"
}).format
+
class Stats {
- constructor (div, start_level=1) {
+ constructor (div) {
this.div = div
this._score = 0
this.highScore = 0
- this.level = start_level - 1
this.goal = 0
this.linesCleared = 0
this.startTime = Date.now()
@@ -244,8 +275,11 @@ class Stats {
this.highScore = score
}
- new_level() {
- this.level++
+ newLevel(level=null) {
+ if (level)
+ this.level = level
+ else
+ this.level++
this.goal += 5 * this.level
if (this.level <= 20)
this.fallDelay = 1000 * Math.pow(0.8 - ((this.level - 1) * 0.007), this.level - 1)
@@ -253,13 +287,41 @@ class Stats {
this.lockDelay = 500 * Math.pow(0.9, this.level - 15)
}
+ locksDown(tSpin, linesCleared) {
+ var pattern_name = []
+ var pattern_score = 0
+ var combo_score = 0
+
+ if (tSpin)
+ pattern_name.push(tSpin)
+ if (linesCleared) {
+ pattern_name.push(SCORES[linesCleared].linesClearedName)
+ this.combo++
+ }
+ else
+ this.combo = -1
+
+ if (linesCleared || tSpin) {
+ pattern_score = SCORES[linesCleared][tSpin]
+ this.goal -= pattern_score
+ pattern_score *= 100 * this.level
+ pattern_name = pattern_name.join("\n")
+ }
+ if (this.combo >= 1)
+ combo_score = (linesCleared == 1 ? 20 : 50) * this.combo * this.level
+
+ this.score += pattern_score + combo_score
+
+ //console.log(pattern_name, pattern_score, this.combo, combo_score)
+ }
+
print() {
- this.div.innerHTML = this.score
- this.div.innerHTML += "
" + this.highScore
- this.div.innerHTML += "
" + this.level
- this.div.innerHTML += "
" + this.goal
- this.div.innerHTML += "
" + this.linesCleared
- this.div.innerHTML += "
" + timeFormat(Date.now() - this.startTime)
+ this.div.innerHTML = this.score + "
"
+ + this.highScore + "
"
+ + this.level + "
"
+ + this.goal + "
"
+ + this.linesCleared + "
"
+ + timeFormat(Date.now() - this.startTime)
}
}
@@ -267,26 +329,23 @@ class Stats {
class Matrix {
constructor(context) {
this.context = context
- this.cells = Array.from(Array(MATRIX_COLUMNS), x => Array(MATRIX_ROWS))
+ this.cells = Array.from(Array(MATRIX_ROWS+3), row => Array(MATRIX_COLUMNS))
this.width = MATRIX_COLUMNS*MINO_SIZE
this.height = MATRIX_ROWS*MINO_SIZE
this.piece = null
}
cellIsOccupied(x, y) {
- return 0 <= x && x < MATRIX_COLUMNS && y < MATRIX_ROWS ? this.cells[x][y] : true
+ return 0 <= x && x < MATRIX_COLUMNS && y < MATRIX_ROWS ? this.cells[y+3][x] : true
}
- spaceToMove(absMinoesPos) {
- for (const pos of absMinoesPos) {
- if (this.cellIsOccupied(...pos))
- return false
- }
- return true
+ spaceToMove(minoesAbsPos) {
+ return !minoesAbsPos.some(pos => this.cellIsOccupied(...pos))
}
draw() {
this.context.clearRect(0, 0, this.width, this.height)
+
// grid
this.context.strokeStyle = "rgba(128, 128, 128, 128)"
this.context.lineWidth = 0.5
@@ -300,10 +359,19 @@ class Matrix {
this.context.lineTo(this.width, y);
}
this.context.stroke()
+
+ // ghost position
+ for (var ghost_pos = Array.from(this.piece.pos); this.spaceToMove(this.piece.minoesPos.translate(ghost_pos)); ghost_pos[1]++) {}
+ ghost_pos[1]--
+
+ // locked minoes
+ this.cells.slice(3).forEach((row, y) => row.forEach((colors, x) => {
+ if (colors) drawMino(this.context, [x, y], ...colors, ghost_pos)
+ }))
+
// falling piece
if (this.piece)
- for (var ghostYOffset = 0; this.spaceToMove(this.piece.minoesPos.translate([this.piece.pos[0], this.piece.pos[1]+ghostYOffset])); ghostYOffset++) {}
- this.piece.draw(this.context, --ghostYOffset)
+ this.piece.draw(this.context, ghost_pos)
}
}
@@ -318,15 +386,55 @@ class NextQueue {
draw() {
this.context.clearRect(0, 0, this.width, this.height)
- this.pieces.map(piece => piece.draw(this.context))
+ this.pieces.forEach(piece => piece.draw(this.context))
}
}
-function move(movement) {
- const test_pos = matrix.piece.pos.add(movement)
- if (matrix.spaceToMove(matrix.piece.minoesPos.translate(test_pos))) {
- matrix.piece.pos = test_pos
+function newLevel(startLevel) {
+ stats.newLevel(startLevel)
+ generationPhase()
+}
+
+function generationPhase(held_piece=null) {
+ if (!held_piece) {
+ matrix.piece = nextQueue.pieces.shift()
+ nextQueue.pieces.push(new Tetromino())
+ nextQueue.pieces.forEach((piece, i) => piece.pos = NEXT_PIECES_POSITIONS[i])
+ }
+ matrix.piece.pos = FALLING_PIECE_POSITION
+ if (matrix.spaceToMove(matrix.piece.minoesPos.translate(matrix.piece.pos)))
+ fallingPhase()
+ else
+ gameOver()
+}
+
+function fallingPhase() {
+ scheduler.clearTimeout(lockPhase)
+ scheduler.clearTimeout(locksDown)
+ matrix.piece.locked = false
+ scheduler.setTimeout(lockPhase, stats.fallDelay)
+}
+
+function lockPhase() {
+ if (!move(MOVEMENT.DOWN))
+ locksDown()
+}
+
+function move(movement, lock=true, testMinoesPos=matrix.piece.minoesPos) {
+ const testPos = matrix.piece.pos.add(movement)
+ if (matrix.spaceToMove(testMinoesPos.translate(testPos))) {
+ matrix.piece.pos = testPos
+ matrix.piece.minoesPos = testMinoesPos
+ if (movement != MOVEMENT.DOWN)
+ matrix.piece.rotatedLast = false
+ if (matrix.spaceToMove(matrix.piece.minoesPos.translate(matrix.piece.pos.add(MOVEMENT.DOWN))))
+ fallingPhase()
+ else if (lock) {
+ matrix.piece.locked = true
+ scheduler.clearTimeout(locksDown)
+ scheduler.setTimeout(locksDown, stats.lockDelay)
+ }
return true
}
else {
@@ -336,77 +444,65 @@ function move(movement) {
function rotate(spin) {
const test_minoes_pos = matrix.piece.minoesPos.map(pos => pos.rotate(spin))
- rotation_point = 0
- for (const movement of matrix.piece.srs[spin==SPIN.CW?"CW":"CCW"][matrix.piece.orientation]) {
- const test_pos = matrix.piece.pos.add(movement)
- if (matrix.spaceToMove(test_minoes_pos.translate(test_pos))) {
- matrix.piece.pos = test_pos
- matrix.piece.minoesPos = test_minoes_pos
+ rotationPoint = 1
+ for (const movement of matrix.piece.srs[spin][matrix.piece.orientation]) {
+ if (move(movement, false, test_minoes_pos)) {
matrix.piece.orientation = (matrix.piece.orientation + spin + 4) % 4
- break;
+ matrix.piece.rotatedLast = true
+ if (rotationPoint == 5)
+ matrix.piece.rotationPoint5Used = true
+ return true
}
- rotation_point++
+ rotationPoint++
+ }
+ return false
+}
+
+function locksDown(){
+ scheduler.clearInterval(move)
+ if (matrix.piece.minoesAbsPos.every(pos => pos.y < 0))
+ game_over()
+ else {
+ matrix.piece.minoesAbsPos.forEach(pos => matrix.cells[pos[1]+3][pos[0]] = [matrix.piece.lightColor, matrix.piece.color])
+
+ // T-Spin detection
+ var tSpin = T_SPIN.NONE
+ const tSlots = T_SLOT_POS.translate(matrix.piece.pos).map(pos => matrix.cellIsOccupied(pos))
+ if (matrix.piece.rotatedLast && matrix.piece.shape == "T") {
+ const a = tSlots[(matrix.piece.orientation+T_SLOT.A)%4],
+ b = tSlots[(matrix.piece.orientation+T_SLOT.B)%4],
+ c = tSlots[(matrix.piece.orientation+T_SLOT.C)%4],
+ d = tSlots[(matrix.piece.orientation+T_SLOT.D)%4]
+ if (a && b && (c || d))
+ tSpin = T_SPIN.T_SPIN
+ else if (c && d && (a || b))
+ tSpin = matrix.piece.rotationPoint5Used ? T_SPIN.T_SPIN : T_SPIN.MINI
+ }
+
+ // Complete lines
+ var linesCleared = 0
+ matrix.cells.forEach((row, y) => {
+ if (row.filter(mino => mino.length).length == MATRIX_COLUMNS) {
+ matrix.cells.splice(y, 1)
+ matrix.cells.unshift(Array(MATRIX_COLUMNS))
+ linesCleared++
+ }
+ })
+
+ stats.locksDown(tSpin, linesCleared)
+
+ if (stats.goal <= 0)
+ newLevel()
+ else
+ generationPhase()
}
}
-function fall() {
- move(MOVEMENT.DOWN);
-}
-
-function moveLeft() {
- move(MOVEMENT.LEFT);
-}
-
-function moveRight() {
- move(MOVEMENT.RIGHT)
-}
-
-function softDrop() {
- move(MOVEMENT.DOWN)
-}
-
-function hardDrop() {
- while(move(MOVEMENT.DOWN)) {
-
- }
-}
-
-function rotateCW() {
- rotate(SPIN.CW)
-}
-
-function rotateCCW() {
- rotate(SPIN.CCW)
-}
-
-function hold() {
- if (this.matrix.piece.holdEnabled) {
- this.matrix.piece.holdEnabled = false
- clearInterval(lockPhaseIntervalID)
- var shape = this.matrix.piece.shape
- this.matrix.piece = this.holdQueue.piece
- this.holdQueue.piece = new Tetromino(HELD_PIECE_POSITION, shape)
- this.generationPhase(this.matrix.piece)
- }
-}
-
-function new_level() {
- stats.new_level()
- fallIntervalID = setInterval(fall, stats.fallDelay)
- generationPhase()
-}
-
-function generationPhase(held_piece=null) {
- if (!held_piece) {
- this.matrix.piece = this.nextQueue.pieces.shift()
- this.nextQueue.pieces.push(new Tetromino())
- this.nextQueue.pieces.map((piece, i, pieces) => piece.pos = NEXT_PIECES_POSITIONS[i])
- }
- this.matrix.piece.pos = FALLING_PIECE_POSITION
- /*if (this.matrix.spaceToMove(this.matrix.piece.minoesPos.translate(this.matrix.piece.pos)))
- fallingPhase()
- else
- gameOver()*/
+function gameOver() {
+ state = STATE.GAME_OVER
+ scheduler.clearTimeout(lockPhase)
+ scheduler.clearTimeout(locksDown)
+ console.log("GAME OVER")
}
function autorepeat() {
@@ -456,12 +552,56 @@ function keyUpHandler(e) {
}
}
+function moveLeft() {
+ move(MOVEMENT.LEFT);
+}
+
+function moveRight() {
+ move(MOVEMENT.RIGHT)
+}
+
+function softDrop() {
+ if (move(MOVEMENT.DOWN))
+ stats.score++
+}
+
+function hardDrop() {
+ scheduler.clearTimeout(lockPhase)
+ scheduler.clearTimeout(locksDown)
+ while(move(MOVEMENT.DOWN, false)) {
+ stats.score += 2
+ }
+ locksDown()
+}
+
+function rotateCW() {
+ rotate(SPIN.CW)
+}
+
+function rotateCCW() {
+ rotate(SPIN.CCW)
+}
+
+function hold() {
+ if (this.matrix.piece.holdEnabled) {
+ this.matrix.piece.holdEnabled = false
+ scheduler.clearInterval(move)
+ scheduler.clearInterval(locksDown)
+ var shape = this.matrix.piece.shape
+ this.matrix.piece = this.holdQueue.piece
+ this.holdQueue.piece = new Tetromino(HELD_PIECE_POSITION, shape)
+ this.generationPhase(this.matrix.piece)
+ }
+}
+
function draw() {
holdQueue.draw()
stats.print()
matrix.draw()
nextQueue.draw()
- requestAnimationFrame(draw)
+
+ if (state != STATE.GAME_OVER)
+ requestAnimationFrame(draw)
}
window.onload = function() {
@@ -486,5 +626,6 @@ window.onload = function() {
addEventListener("keyup", keyUpHandler, false)
requestAnimationFrame(draw)
- this.new_level()
+ state = STATE.PLAYING
+ this.newLevel(1)
}
\ No newline at end of file