rewrite
This commit is contained in:
parent
e39216f7bf
commit
01cecac2a5
@ -47,6 +47,7 @@
|
|||||||
|
|
||||||
.cleared-line {
|
.cleared-line {
|
||||||
background: white;
|
background: white;
|
||||||
|
transition: background 1s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.trail {
|
.trail {
|
||||||
|
154
js/webtris.js
154
js/webtris.js
@ -15,31 +15,45 @@ Array.prototype.pick = function() { return this.splice(Math.floor(Mat
|
|||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const NEXT_PIECES = 6
|
const NEXT_PIECES = 6
|
||||||
const HOLD_ROWS = 6
|
const HOLD = {
|
||||||
const HOLD_COLUMNS = 6
|
ROWS: 6,
|
||||||
const MATRIX_ROWS = 24
|
COLUMNS: 6
|
||||||
const MATRIX_INVISIBLE_ROWS = 4
|
}
|
||||||
const MATRIX_COLUMNS = 10
|
const MATRIX = {
|
||||||
const NEXT_ROWS = 24
|
ROWS: 24,
|
||||||
const NEXT_COLUMNS = 6
|
INVISIBLE_ROWS: 4,
|
||||||
const THEME_ROWS = 6
|
COLUMNS: 10
|
||||||
const THEME_COLUMNS = 6
|
}
|
||||||
const EMPTY_CELL_CLASS = "empty-cell"
|
const NEXT= {
|
||||||
const MINO_CLASS = "mino"
|
ROWS: 24,
|
||||||
const LOCKED_PIECE_CLASS = "locked-mino"
|
COLUMNS: 6
|
||||||
const TRAIL_CLASS = "trail"
|
}
|
||||||
const GHOST_CLASS = "ghost"
|
const THEME = {
|
||||||
const CLEARED_LINE_CLASS = "mino cleared-line"
|
ROWS: 6,
|
||||||
const HELD_PIECE_POSITION = [2, 3]
|
COLUMNS: 6
|
||||||
const FALLING_PIECE_POSITION = [4, 3]
|
}
|
||||||
const NEXT_PIECES_POSITIONS = Array.from({length: NEXT_PIECES}, (v, k) => [2, k*4+3])
|
const CLASSNAME = {
|
||||||
const THEME_PIECE_POSITION = [2, 3]
|
EMPTY_CELL: "empty-cell",
|
||||||
const LOCK_DELAY = 500
|
MINO: "mino",
|
||||||
const FALL_PERIOD = 1000
|
LOCKED: "locked-mino",
|
||||||
const AUTOREPEAT_DELAY = 300
|
TRAIL: "trail",
|
||||||
const AUTOREPEAT_PERIOD = 10
|
GHOST: "ghost",
|
||||||
const ANIMATION_DELAY = 100
|
CLEARED_LINE: "mino cleared-line"
|
||||||
const TEMP_TEXTS_DELAY = 700
|
}
|
||||||
|
const POSITION = {
|
||||||
|
HELD_PIECE: [2, 3],
|
||||||
|
FALLING_PIECE: [4, 3],
|
||||||
|
NEXT_PIECES: Array.from({length: NEXT_PIECES}, (v, k) => [2, k*4+3]),
|
||||||
|
THEMED_PIECE: [2, 3]
|
||||||
|
}
|
||||||
|
const DELAY = {
|
||||||
|
LOCK: 500,
|
||||||
|
FALL: 1000,
|
||||||
|
AUTOREPEAT_DELAY: 300,
|
||||||
|
AUTOREPEAT_PERIOD: 10,
|
||||||
|
ANIMATION: 100,
|
||||||
|
MESSAGE: 700
|
||||||
|
}
|
||||||
const MOVEMENT = {
|
const MOVEMENT = {
|
||||||
LEFT: [-1, 0],
|
LEFT: [-1, 0],
|
||||||
RIGHT: [ 1, 0],
|
RIGHT: [ 1, 0],
|
||||||
@ -60,7 +74,11 @@ const T_SLOT = {
|
|||||||
C: 3,
|
C: 3,
|
||||||
D: 2
|
D: 2
|
||||||
}
|
}
|
||||||
const T_SLOT_POS = [[-1, -1], [1, -1], [1, 1], [-1, 1]]
|
const T_SLOT_POS = [
|
||||||
|
[-1, -1],
|
||||||
|
[1, -1],
|
||||||
|
[1, 1],[-1, 1]
|
||||||
|
]
|
||||||
const SCORES = [
|
const SCORES = [
|
||||||
{linesClearedName: "", "": 0, "MINI\nT-SPIN": 1, "T-SPIN": 4},
|
{linesClearedName: "", "": 0, "MINI\nT-SPIN": 1, "T-SPIN": 4},
|
||||||
{linesClearedName: "SINGLE", "": 1, "MINI\nT-SPIN": 2, "T-SPIN": 8},
|
{linesClearedName: "SINGLE", "": 1, "MINI\nT-SPIN": 2, "T-SPIN": 8},
|
||||||
@ -186,7 +204,7 @@ class Tetromino {
|
|||||||
this.minoesPos = [[-1, -1], [0, -1], [0, 0], [1, 0]]
|
this.minoesPos = [[-1, -1], [0, -1], [0, 0], [1, 0]]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
this.className = MINO_CLASS + " " + this.shape + "-" + MINO_CLASS
|
this.className = CLASSNAME.MINO + " " + this.shape + "-" + CLASSNAME.MINO
|
||||||
}
|
}
|
||||||
|
|
||||||
get minoesAbsPos() {
|
get minoesAbsPos() {
|
||||||
@ -196,7 +214,7 @@ class Tetromino {
|
|||||||
get ghost() {
|
get ghost() {
|
||||||
var ghost = new Tetromino(Array.from(this.pos), this.shape)
|
var ghost = new Tetromino(Array.from(this.pos), this.shape)
|
||||||
ghost.minoesPos = Array.from(this.minoesPos)
|
ghost.minoesPos = Array.from(this.minoesPos)
|
||||||
ghost.className = GHOST_CLASS
|
ghost.className = CLASSNAME.GHOST
|
||||||
return ghost
|
return ghost
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -213,15 +231,14 @@ class MinoesTable {
|
|||||||
this.table.rows[y].cells[x].className = className
|
this.table.rows[y].cells[x].className = className
|
||||||
}
|
}
|
||||||
|
|
||||||
drawPiece(piece) {
|
drawPiece(piece=this.piece, className=piece.locked ? CLASSNAME.LOCKED + " "+ piece.className: piece.className) {
|
||||||
var className = piece.locked ? LOCKED_PIECE_CLASS + " "+ piece.className: piece.className
|
|
||||||
piece.minoesAbsPos.forEach(pos => this.drawMino(...pos, className))
|
piece.minoesAbsPos.forEach(pos => this.drawMino(...pos, className))
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTable() {
|
clearTable() {
|
||||||
for(var y = 0; y < this.rows; y++) {
|
for(var y = 0; y < this.rows; y++) {
|
||||||
for (var x = 0; x < this.columns; x++) {
|
for (var x = 0; x < this.columns; x++) {
|
||||||
this.drawMino(x, y, EMPTY_CELL_CLASS)
|
this.drawMino(x, y, CLASSNAME.EMPTY_CELL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,7 +246,7 @@ class MinoesTable {
|
|||||||
|
|
||||||
class HoldQueue extends MinoesTable {
|
class HoldQueue extends MinoesTable {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("hold", HOLD_ROWS, HOLD_COLUMNS)
|
super("hold", HOLD.ROWS, HOLD.COLUMNS)
|
||||||
}
|
}
|
||||||
|
|
||||||
newGame() {
|
newGame() {
|
||||||
@ -246,11 +263,11 @@ class HoldQueue extends MinoesTable {
|
|||||||
|
|
||||||
class Matrix extends MinoesTable {
|
class Matrix extends MinoesTable {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("matrix", MATRIX_ROWS, MATRIX_COLUMNS)
|
super("matrix", MATRIX.ROWS, MATRIX.COLUMNS)
|
||||||
}
|
}
|
||||||
|
|
||||||
newGame() {
|
newGame() {
|
||||||
this.lockedMinoes = Array.from(Array(MATRIX_ROWS+3), row => Array(MATRIX_COLUMNS))
|
this.lockedMinoes = Array.from(Array(MATRIX.ROWS+3), row => Array(MATRIX.COLUMNS))
|
||||||
this.piece = null
|
this.piece = null
|
||||||
this.clearedLines = []
|
this.clearedLines = []
|
||||||
this.trail = {
|
this.trail = {
|
||||||
@ -260,7 +277,7 @@ class Matrix extends MinoesTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cellIsOccupied(x, y) {
|
cellIsOccupied(x, y) {
|
||||||
return 0 <= x && x < MATRIX_COLUMNS && y < MATRIX_ROWS ? this.lockedMinoes[y][x] : true
|
return 0 <= x && x < MATRIX.COLUMNS && y < MATRIX.ROWS ? this.lockedMinoes[y][x] : true
|
||||||
}
|
}
|
||||||
|
|
||||||
spaceToMove(minoesAbsPos) {
|
spaceToMove(minoesAbsPos) {
|
||||||
@ -277,9 +294,9 @@ class Matrix extends MinoesTable {
|
|||||||
var className = this.lockedMinoes[y][x]
|
var className = this.lockedMinoes[y][x]
|
||||||
if (!className) {
|
if (!className) {
|
||||||
if (this.clearedLines.includes(y))
|
if (this.clearedLines.includes(y))
|
||||||
className = CLEARED_LINE_CLASS
|
className = CLASSNAME.CLEARED_LINE
|
||||||
else
|
else
|
||||||
className = EMPTY_CELL_CLASS
|
className = CLASSNAME.EMPTY_CELL
|
||||||
}
|
}
|
||||||
this.drawMino(x, y, className)
|
this.drawMino(x, y, className)
|
||||||
}
|
}
|
||||||
@ -289,7 +306,7 @@ class Matrix extends MinoesTable {
|
|||||||
if (this.trail.height) {
|
if (this.trail.height) {
|
||||||
this.trail.minoesPos.forEach(pos => {
|
this.trail.minoesPos.forEach(pos => {
|
||||||
for (var y = pos.y; y < pos.y + this.trail.height; y++)
|
for (var y = pos.y; y < pos.y + this.trail.height; y++)
|
||||||
this.drawMino(pos.x, y, TRAIL_CLASS)
|
this.drawMino(pos.x, y, CLASSNAME.TRAIL)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,11 +325,11 @@ class Matrix extends MinoesTable {
|
|||||||
|
|
||||||
class NextQueue extends MinoesTable {
|
class NextQueue extends MinoesTable {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("next", NEXT_ROWS, NEXT_COLUMNS)
|
super("next", NEXT.ROWS, NEXT.COLUMNS)
|
||||||
}
|
}
|
||||||
|
|
||||||
newGame() {
|
newGame() {
|
||||||
this.pieces = Array.from({length: NEXT_PIECES}, (v, k) => new Tetromino(NEXT_PIECES_POSITIONS[k]))
|
this.pieces = Array.from({length: NEXT_PIECES}, (v, k) => new Tetromino(POSITION.NEXT_PIECES[k]))
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
@ -326,8 +343,8 @@ class NextQueue extends MinoesTable {
|
|||||||
|
|
||||||
class ThemePreview extends MinoesTable {
|
class ThemePreview extends MinoesTable {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("themePreview", THEME_ROWS, THEME_COLUMNS)
|
super("themePreview", THEME.ROWS, THEME.COLUMNS)
|
||||||
this.piece = new Tetromino(THEME_PIECE_POSITION, "T")
|
this.piece = new Tetromino(POSITION.THEMED_PIECE, "T")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,8 +370,8 @@ class Stats {
|
|||||||
this.time = 0
|
this.time = 0
|
||||||
this.timeCell.innerText = timeFormat(0)
|
this.timeCell.innerText = timeFormat(0)
|
||||||
this.combo = -1
|
this.combo = -1
|
||||||
this.lockDelay = LOCK_DELAY
|
this.lockDelay = DELAY.LOCK
|
||||||
this.fallPeriod = FALL_PERIOD
|
this.fallPeriod = DELAY.FALL
|
||||||
}
|
}
|
||||||
|
|
||||||
get score() {
|
get score() {
|
||||||
@ -460,10 +477,10 @@ function generationPhase(held_piece=null) {
|
|||||||
if (!held_piece) {
|
if (!held_piece) {
|
||||||
matrix.piece = nextQueue.pieces.shift()
|
matrix.piece = nextQueue.pieces.shift()
|
||||||
nextQueue.pieces.push(new Tetromino())
|
nextQueue.pieces.push(new Tetromino())
|
||||||
nextQueue.pieces.forEach((piece, i) => piece.pos = NEXT_PIECES_POSITIONS[i])
|
nextQueue.pieces.forEach((piece, i) => piece.pos = POSITION.NEXT_PIECES[i])
|
||||||
}
|
}
|
||||||
nextQueue.draw()
|
nextQueue.draw()
|
||||||
matrix.piece.pos = FALLING_PIECE_POSITION
|
matrix.piece.pos = POSITION.FALLING_PIECE
|
||||||
if (matrix.spaceToMove(matrix.piece.minoesPos.translate(matrix.piece.pos))){
|
if (matrix.spaceToMove(matrix.piece.minoesPos.translate(matrix.piece.pos))){
|
||||||
scheduler.clearInterval(lockPhase)
|
scheduler.clearInterval(lockPhase)
|
||||||
scheduler.setInterval(lockPhase, stats.fallPeriod)
|
scheduler.setInterval(lockPhase, stats.fallPeriod)
|
||||||
@ -528,7 +545,7 @@ function rotate(spin) {
|
|||||||
|
|
||||||
function lockDown(){
|
function lockDown(){
|
||||||
scheduler.clearInterval(lockPhase)
|
scheduler.clearInterval(lockPhase)
|
||||||
if (matrix.piece.minoesAbsPos.every(pos => pos.y < MATRIX_INVISIBLE_ROWS)) {
|
if (matrix.piece.minoesAbsPos.every(pos => pos.y < MATRIX.INVISIBLE_ROWS)) {
|
||||||
matrix.piece.locked = false
|
matrix.piece.locked = false
|
||||||
matrix.draw()
|
matrix.draw()
|
||||||
gameOver()
|
gameOver()
|
||||||
@ -552,16 +569,16 @@ function lockDown(){
|
|||||||
// Complete lines
|
// Complete lines
|
||||||
matrix.clearedLines = []
|
matrix.clearedLines = []
|
||||||
matrix.lockedMinoes.forEach((row, y) => {
|
matrix.lockedMinoes.forEach((row, y) => {
|
||||||
if (row.filter(lockedMino => lockedMino.length).length == MATRIX_COLUMNS) {
|
if (row.filter(lockedMino => lockedMino.length).length == MATRIX.COLUMNS) {
|
||||||
matrix.lockedMinoes.splice(y, 1)
|
matrix.lockedMinoes.splice(y, 1)
|
||||||
matrix.lockedMinoes.unshift(Array(MATRIX_COLUMNS))
|
matrix.lockedMinoes.unshift(Array(MATRIX.COLUMNS))
|
||||||
matrix.clearedLines.push(y)
|
matrix.clearedLines.push(y)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
stats.lockDown(tSpin, matrix.clearedLines.length)
|
stats.lockDown(tSpin, matrix.clearedLines.length)
|
||||||
matrix.draw()
|
matrix.draw()
|
||||||
scheduler.setTimeout(clearLinesCleared, ANIMATION_DELAY)
|
scheduler.setTimeout(clearLinesCleared, DELAY.ANIMATION)
|
||||||
|
|
||||||
if (stats.goal <= 0)
|
if (stats.goal <= 0)
|
||||||
newLevel()
|
newLevel()
|
||||||
@ -637,16 +654,16 @@ function gameOver() {
|
|||||||
document.getElementById("leaderboardLink").style.display = "flex"
|
document.getElementById("leaderboardLink").style.display = "flex"
|
||||||
}
|
}
|
||||||
|
|
||||||
function autorepeat() {
|
function AUTOREPEAT_DELAY() {
|
||||||
if (actionsToRepeat.length) {
|
if (actionsToRepeat.length) {
|
||||||
actionsToRepeat[0]()
|
actionsToRepeat[0]()
|
||||||
if (scheduler.timeoutTasks.has(autorepeat)) {
|
if (scheduler.timeoutTasks.has(AUTOREPEAT_DELAY)) {
|
||||||
scheduler.clearTimeout(autorepeat)
|
scheduler.clearTimeout(AUTOREPEAT_DELAY)
|
||||||
scheduler.setInterval(autorepeat, autorepeatPeriod)
|
scheduler.setInterval(AUTOREPEAT_DELAY, autorepeatPeriod)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
scheduler.clearTimeout(autorepeat)
|
scheduler.clearTimeout(AUTOREPEAT_DELAY)
|
||||||
scheduler.clearInterval(autorepeat)
|
scheduler.clearInterval(AUTOREPEAT_DELAY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,12 +677,12 @@ function keyDownHandler(e) {
|
|||||||
action()
|
action()
|
||||||
if (REPEATABLE_ACTIONS.includes(action)) {
|
if (REPEATABLE_ACTIONS.includes(action)) {
|
||||||
actionsToRepeat.unshift(action)
|
actionsToRepeat.unshift(action)
|
||||||
scheduler.clearTimeout(autorepeat)
|
scheduler.clearTimeout(AUTOREPEAT_DELAY)
|
||||||
scheduler.clearInterval(autorepeat)
|
scheduler.clearInterval(AUTOREPEAT_DELAY)
|
||||||
if (action == softDrop)
|
if (action == softDrop)
|
||||||
scheduler.setInterval(autorepeat, stats.fallPeriod / 20)
|
scheduler.setInterval(AUTOREPEAT_DELAY, stats.fallPeriod / 20)
|
||||||
else
|
else
|
||||||
scheduler.setTimeout(autorepeat, autorepeatDelay)
|
scheduler.setTimeout(AUTOREPEAT_DELAY, autorepeatDelay)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -678,13 +695,14 @@ function keyUpHandler(e) {
|
|||||||
if (actionsToRepeat.includes(action)) {
|
if (actionsToRepeat.includes(action)) {
|
||||||
actionsToRepeat.splice(actionsToRepeat.indexOf(action), 1)
|
actionsToRepeat.splice(actionsToRepeat.indexOf(action), 1)
|
||||||
if (!actionsToRepeat.length) {
|
if (!actionsToRepeat.length) {
|
||||||
scheduler.clearTimeout(autorepeat)
|
scheduler.clearTimeout(AUTOREPEAT_DELAY)
|
||||||
scheduler.clearInterval(autorepeat)
|
scheduler.clearInterval(AUTOREPEAT_DELAY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// actions
|
||||||
function moveLeft() {
|
function moveLeft() {
|
||||||
move(MOVEMENT.LEFT);
|
move(MOVEMENT.LEFT);
|
||||||
}
|
}
|
||||||
@ -706,7 +724,7 @@ function hardDrop() {
|
|||||||
stats.score += 2 * matrix.trail.height
|
stats.score += 2 * matrix.trail.height
|
||||||
matrix.draw()
|
matrix.draw()
|
||||||
lockDown()
|
lockDown()
|
||||||
scheduler.setTimeout(clearTrail, ANIMATION_DELAY)
|
scheduler.setTimeout(clearTrail, DELAY.ANIMATION)
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearTrail() {
|
function clearTrail() {
|
||||||
@ -728,7 +746,7 @@ function hold() {
|
|||||||
scheduler.clearInterval(lockDown)
|
scheduler.clearInterval(lockDown)
|
||||||
var shape = matrix.piece.shape
|
var shape = matrix.piece.shape
|
||||||
matrix.piece = holdQueue.piece
|
matrix.piece = holdQueue.piece
|
||||||
holdQueue.piece = new Tetromino(HELD_PIECE_POSITION, shape)
|
holdQueue.piece = new Tetromino(POSITION.HELD_PIECE, shape)
|
||||||
holdQueue.piece.holdEnabled = false
|
holdQueue.piece.holdEnabled = false
|
||||||
holdQueue.draw()
|
holdQueue.draw()
|
||||||
generationPhase(matrix.piece)
|
generationPhase(matrix.piece)
|
||||||
@ -741,7 +759,7 @@ function pause() {
|
|||||||
actionsToRepeat = []
|
actionsToRepeat = []
|
||||||
scheduler.clearInterval(lockPhase)
|
scheduler.clearInterval(lockPhase)
|
||||||
scheduler.clearTimeout(lockDown)
|
scheduler.clearTimeout(lockDown)
|
||||||
scheduler.clearTimeout(autorepeat)
|
scheduler.clearTimeout(AUTOREPEAT_DELAY)
|
||||||
scheduler.clearInterval(clock)
|
scheduler.clearInterval(clock)
|
||||||
holdQueue.draw()
|
holdQueue.draw()
|
||||||
matrix.draw()
|
matrix.draw()
|
||||||
@ -765,7 +783,7 @@ function printTempTexts(text) {
|
|||||||
tempTexts.push(text)
|
tempTexts.push(text)
|
||||||
messageDiv.innerHTML = tempTexts[0]
|
messageDiv.innerHTML = tempTexts[0]
|
||||||
if (!scheduler.intervalTasks.has(delTempTexts))
|
if (!scheduler.intervalTasks.has(delTempTexts))
|
||||||
scheduler.setInterval(delTempTexts, TEMP_TEXTS_DELAY)
|
scheduler.setInterval(delTempTexts, DELAY.MESSAGE)
|
||||||
}
|
}
|
||||||
|
|
||||||
function delTempTexts(self) {
|
function delTempTexts(self) {
|
||||||
@ -802,8 +820,8 @@ function applySettings() {
|
|||||||
actions[STATE.PAUSED][getKeyName("pause")] = resume
|
actions[STATE.PAUSED][getKeyName("pause")] = resume
|
||||||
actions[STATE.GAME_OVER] = {}
|
actions[STATE.GAME_OVER] = {}
|
||||||
|
|
||||||
autorepeatDelay = localStorage.getItem("autorepeatDelay") || AUTOREPEAT_DELAY
|
autorepeatDelay = localStorage.getItem("autorepeatDelay") || DELAY.AUTOREPEAT_DELAY
|
||||||
autorepeatPeriod = localStorage.getItem("autorepeatPeriod") || AUTOREPEAT_PERIOD
|
autorepeatPeriod = localStorage.getItem("autorepeatPeriod") || DELAY.AUTOREPEAT_PERIOD
|
||||||
|
|
||||||
theme = localStorage.getItem("theme") || DEFAULT_THEME
|
theme = localStorage.getItem("theme") || DEFAULT_THEME
|
||||||
loadTheme()
|
loadTheme()
|
||||||
|
Reference in New Issue
Block a user