use table instead of canvas
This commit is contained in:
parent
826e71d0a0
commit
ecbb8552cd
@ -17,7 +17,7 @@
|
|||||||
* {
|
* {
|
||||||
color: white;
|
color: white;
|
||||||
font-family: 'Share Tech';
|
font-family: 'Share Tech';
|
||||||
font-size: 1.05em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@ -28,7 +28,7 @@ body {
|
|||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 50px;
|
font-size: 50px;
|
||||||
margin: 40px;
|
margin: 20px;
|
||||||
text-shadow: 3px 2px rgb(153, 145, 175);
|
text-shadow: 3px 2px rgb(153, 145, 175);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@ -42,10 +42,11 @@ a {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
border-style: outset;
|
border-style: outset;
|
||||||
border-width: 5px;
|
border-width: 2px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
color: black;
|
color: black;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:active {
|
a:active {
|
||||||
@ -69,38 +70,56 @@ a:active {
|
|||||||
|
|
||||||
#grid {
|
#grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 2fr 3fr 10fr 6fr;
|
grid-template-columns: 6fr 10fr 6fr;
|
||||||
grid-gap: 20px;
|
grid-gap: 20px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
width: 550px;
|
width: 550px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#hold {
|
table {
|
||||||
grid-column: 1 / 3;
|
table-layout: fixed;
|
||||||
grid-row: 1;
|
border-spacing: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#stats-names {
|
td {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#hold {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#matrix {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1 / 3;
|
||||||
|
width: 200px;
|
||||||
|
height: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#next {
|
||||||
|
grid-column: 3;
|
||||||
|
grid-row: 1 / 3;
|
||||||
|
width: 120px;
|
||||||
|
height: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stats {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
grid-row: 2;
|
grid-row: 2;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
table-layout: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-label {
|
||||||
font-family: 'Share Tech';
|
font-family: 'Share Tech';
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
#stats-values {
|
.stat-value {
|
||||||
grid-column: 2;
|
|
||||||
grid-row: 2;
|
|
||||||
font-family: 'Share Tech Mono';
|
font-family: 'Share Tech Mono';
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
|
||||||
|
|
||||||
#matrix {
|
|
||||||
grid-column: 3;
|
|
||||||
grid-row: 1 / 3;
|
|
||||||
border: 0.5px solid grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
#next {
|
|
||||||
grid-column: 4;
|
|
||||||
grid-row: 1 / 3;
|
|
||||||
}
|
}
|
429
js/webtris.js
429
js/webtris.js
@ -20,30 +20,37 @@ Array.prototype.pick = function() {
|
|||||||
|
|
||||||
|
|
||||||
const MINO_SIZE = 20
|
const MINO_SIZE = 20
|
||||||
const NEXT_PIECES = 5
|
const NEXT_PIECES = 6
|
||||||
const HOLD_ROWS = 6
|
const HOLD_ROWS = 6
|
||||||
const HOLD_COLUMNS = 6
|
const HOLD_COLUMNS = 6
|
||||||
const MATRIX_ROWS = 20
|
const MATRIX_ROWS = 24
|
||||||
const MATRIX_COLUMNS = 10
|
const MATRIX_COLUMNS = 10
|
||||||
const NEXT_ROWS = 20
|
const NEXT_ROWS = 24
|
||||||
const NEXT_COLUMNS = 6
|
const NEXT_COLUMNS = 6
|
||||||
const HELD_PIECE_POSITION = [2, 2]
|
const HOLD_BG_COLOR = ""
|
||||||
const FALLING_PIECE_POSITION = [4, 0]
|
const HOLD_BORDER_COLOR = ""
|
||||||
const NEXT_PIECES_POSITIONS = Array.from({length: NEXT_PIECES}, (v, k) => [2, k*4+2])
|
const MATRIX_BG_COLOR = ""
|
||||||
const LOCK_DELAY = 500
|
const MATRIX_BORDER_COLOR = "#333"
|
||||||
const FALL_DELAY = 1000
|
const NEXT_BG_COLOR = ""
|
||||||
const AUTOREPEAT_DELAY = 250
|
const NEXT_BORDER_COLOR = ""
|
||||||
const AUTOREPEAT_PERIOD = 10
|
const MINO_BORDER_COLOR = "white"
|
||||||
const ANIMATION_DELAY = 100
|
const HELD_PIECE_POSITION = [2, 3]
|
||||||
const TEMP_TEXTS_DELAY = 700
|
const FALLING_PIECE_POSITION = [4, 3]
|
||||||
|
const NEXT_PIECES_POSITIONS = Array.from({length: NEXT_PIECES}, (v, k) => [2, k*4+3])
|
||||||
|
const LOCK_DELAY = 500
|
||||||
|
const FALL_PERIOD = 1000
|
||||||
|
const AUTOREPEAT_DELAY = 300
|
||||||
|
const AUTOREPEAT_PERIOD = 10
|
||||||
|
const ANIMATION_DELAY = 100
|
||||||
|
const TEMP_TEXTS_DELAY = 700
|
||||||
const MOVEMENT = {
|
const MOVEMENT = {
|
||||||
LEFT: [-1, 0],
|
LEFT: [-1, 0],
|
||||||
RIGHT: [ 1, 0],
|
RIGHT: [ 1, 0],
|
||||||
DOWN: [ 0, 1]
|
DOWN: [ 0, 1]
|
||||||
}
|
}
|
||||||
const SPIN = {
|
const SPIN = {
|
||||||
CW: 1,
|
CW: 1, // ClockWise
|
||||||
CCW: -1
|
CCW: -1 // CounterClockWise
|
||||||
}
|
}
|
||||||
const T_SPIN = {
|
const T_SPIN = {
|
||||||
NONE: "",
|
NONE: "",
|
||||||
@ -113,7 +120,7 @@ class Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
shapes = []
|
randomBag = []
|
||||||
class Tetromino {
|
class Tetromino {
|
||||||
constructor(position=null, shape=null) {
|
constructor(position=null, shape=null) {
|
||||||
this.pos = position
|
this.pos = position
|
||||||
@ -122,7 +129,7 @@ class Tetromino {
|
|||||||
this.rotationPoint5Used = false
|
this.rotationPoint5Used = false
|
||||||
this.holdEnabled = true
|
this.holdEnabled = true
|
||||||
this.locked = false
|
this.locked = false
|
||||||
this.srs = {}
|
this.srs = {} // Super Rotation System
|
||||||
this.srs[SPIN.CW] = [
|
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]],
|
||||||
@ -138,15 +145,15 @@ class Tetromino {
|
|||||||
if (shape)
|
if (shape)
|
||||||
this.shape = shape
|
this.shape = shape
|
||||||
else {
|
else {
|
||||||
if (!shapes.length)
|
if (!randomBag.length)
|
||||||
shapes = ['I', 'J', 'L', 'O', 'S', 'T', 'Z']
|
randomBag = ['I', 'J', 'L', 'O', 'S', 'T', 'Z']
|
||||||
this.shape = shapes.pick()
|
this.shape = randomBag.pick()
|
||||||
}
|
}
|
||||||
switch(this.shape) {
|
switch(this.shape) {
|
||||||
case 'I':
|
case 'I':
|
||||||
this.color = "rgb(153, 179, 255)"
|
this.color = "rgb(153, 179, 255)"
|
||||||
this.lightColor = "rgb(234, 250, 250)"
|
this.lightColor = "rgb(234, 250, 250)"
|
||||||
this.ghostColor = "rgba(234, 250, 250, 0.5)"
|
this.transparentColor = "rgba(234, 250, 250, 0.5)"
|
||||||
this.minoesPos = [[-1, 0], [0, 0], [1, 0], [2, 0]]
|
this.minoesPos = [[-1, 0], [0, 0], [1, 0], [2, 0]]
|
||||||
this.srs[SPIN.CW] = [
|
this.srs[SPIN.CW] = [
|
||||||
[[ 1, 0], [-1, 0], [ 2, 0], [-1, 1], [ 2, -2]],
|
[[ 1, 0], [-1, 0], [ 2, 0], [-1, 1], [ 2, -2]],
|
||||||
@ -164,19 +171,19 @@ class Tetromino {
|
|||||||
case 'J':
|
case 'J':
|
||||||
this.color = "rgb(153, 255, 255)"
|
this.color = "rgb(153, 255, 255)"
|
||||||
this.lightColor = "rgb(230, 240, 255)"
|
this.lightColor = "rgb(230, 240, 255)"
|
||||||
this.ghostColor = "rgba(230, 240, 255, 0.5)"
|
this.transparentColor = "rgba(230, 240, 255, 0.5)"
|
||||||
this.minoesPos = [[-1, -1], [-1, 0], [0, 0], [1, 0]]
|
this.minoesPos = [[-1, -1], [-1, 0], [0, 0], [1, 0]]
|
||||||
break
|
break
|
||||||
case 'L':
|
case 'L':
|
||||||
this.color = "rgb(255, 204, 153)"
|
this.color = "rgb(255, 204, 153)"
|
||||||
this.lightColor = "rgb(255, 224, 204)"
|
this.lightColor = "rgb(255, 224, 204)"
|
||||||
this.ghostColor = "rgba(255, 224, 204, 0.5)"
|
this.transparentColor = "rgba(255, 224, 204, 0.5)"
|
||||||
this.minoesPos = [[-1, 0], [0, 0], [1, 0], [1, -1]]
|
this.minoesPos = [[-1, 0], [0, 0], [1, 0], [1, -1]]
|
||||||
break
|
break
|
||||||
case 'O':
|
case 'O':
|
||||||
this.color = " rgb(255, 255, 153)"
|
this.color = " rgb(255, 255, 153)"
|
||||||
this.lightColor = "rgb(255, 255, 230)"
|
this.lightColor = "rgb(255, 255, 230)"
|
||||||
this.ghostColor = "rgba(255, 255, 230, 0.5)"
|
this.transparentColor = "rgba(255, 255, 230, 0.5)"
|
||||||
this.minoesPos = [[0, 0], [1, 0], [0, -1], [1, -1]]
|
this.minoesPos = [[0, 0], [1, 0], [0, -1], [1, -1]]
|
||||||
this.srs[SPIN.CW] = [[]]
|
this.srs[SPIN.CW] = [[]]
|
||||||
this.srs[SPIN.CCW] = [[]]
|
this.srs[SPIN.CCW] = [[]]
|
||||||
@ -184,19 +191,19 @@ class Tetromino {
|
|||||||
case 'S':
|
case 'S':
|
||||||
this.color = "rgb(153, 255, 153)"
|
this.color = "rgb(153, 255, 153)"
|
||||||
this.lightColor = "rgb(236, 255, 230)"
|
this.lightColor = "rgb(236, 255, 230)"
|
||||||
this.ghostColor = "rgba(236, 255, 230, 0.5)"
|
this.transparentColor = "rgba(236, 255, 230, 0.5)"
|
||||||
this.minoesPos = [[-1, 0], [0, 0], [0, -1], [1, -1]]
|
this.minoesPos = [[-1, 0], [0, 0], [0, -1], [1, -1]]
|
||||||
break
|
break
|
||||||
case 'T':
|
case 'T':
|
||||||
this.color = "rgb(204, 153, 255)"
|
this.color = "rgb(204, 153, 255)"
|
||||||
this.lightColor= "rgb(242, 230, 255)"
|
this.lightColor= "rgb(242, 230, 255)"
|
||||||
this.ghostColor = "rgba(242, 230, 255, 0.5)"
|
this.transparentColor = "rgba(242, 230, 255, 0.5)"
|
||||||
this.minoesPos = [[-1, 0], [0, 0], [1, 0], [0, -1]]
|
this.minoesPos = [[-1, 0], [0, 0], [1, 0], [0, -1]]
|
||||||
break
|
break
|
||||||
case 'Z':
|
case 'Z':
|
||||||
this.color = "rgb(255, 153, 153)"
|
this.color = "rgb(255, 153, 153)"
|
||||||
this.lightColor = "rgb(255, 230, 230)"
|
this.lightColor = "rgb(255, 230, 230)"
|
||||||
this.ghostColor = "rgba(255, 230, 230, 0.5)"
|
this.transparentColor = "rgba(255, 230, 230, 0.5)"
|
||||||
this.minoesPos = [[-1, -1], [0, -1], [0, 0], [1, 0]]
|
this.minoesPos = [[-1, -1], [0, -1], [0, 0], [1, 0]]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -206,44 +213,164 @@ class Tetromino {
|
|||||||
return this.minoesPos.translate(this.pos)
|
return this.minoesPos.translate(this.pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(context, ghostYOffset=0) {
|
drawIn(table) {
|
||||||
const color = this.locked ? this.lightColor : this.color
|
var bgColor = this.locked ? this.lightColor : this.color
|
||||||
if (ghostYOffset) {
|
this.minoesAbsPos.forEach( pos => {
|
||||||
context.save()
|
drawMino(table.rows[pos[1]].cells[pos[0]], bgColor, MINO_BORDER_COLOR)})
|
||||||
context.shadowColor = this.ghostColor
|
|
||||||
context.shadowOffsetX = 0
|
|
||||||
context.shadowOffsetY = ghostYOffset * MINO_SIZE
|
|
||||||
context.shadowBlur = 4
|
|
||||||
this.minoesAbsPos.forEach(pos => drawMino(context, pos, color))
|
|
||||||
context.restore()
|
|
||||||
}
|
|
||||||
this.minoesAbsPos.forEach(pos => drawMino(context, pos, color))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawMino(context, pos, color) {
|
|
||||||
var topLeft = pos.mul(MINO_SIZE)
|
function drawMino(cell, backgroundColor, borderColor) {
|
||||||
context.fillStyle = color
|
cell.style.backgroundColor = backgroundColor
|
||||||
context.fillRect(...topLeft, MINO_SIZE, MINO_SIZE)
|
cell.style.borderColor = borderColor
|
||||||
context.lineWidth = 0.5
|
|
||||||
context.strokeStyle = "white"
|
|
||||||
context.strokeRect(...topLeft, MINO_SIZE, MINO_SIZE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class HoldQueue {
|
class MinoesTable {
|
||||||
constructor() {
|
constructor(id, rows, columns, defaultBgColor, defaultBorderColor) {
|
||||||
this.context = document.getElementById("hold").getContext("2d")
|
this.table = document.getElementById(id)
|
||||||
|
this.rows = rows
|
||||||
|
this.columns = columns
|
||||||
|
this.defaultBgColor = defaultBgColor
|
||||||
|
this.defaultBorderColor = defaultBorderColor
|
||||||
|
this.width = columns * MINO_SIZE
|
||||||
|
this.height = rows * MINO_SIZE
|
||||||
this.piece = null
|
this.piece = null
|
||||||
this.width = HOLD_COLUMNS*MINO_SIZE
|
for (var y=0; y < rows; y++) {
|
||||||
this.height = HOLD_ROWS*MINO_SIZE
|
var row = this.table.insertRow()
|
||||||
|
for (var x=0; x < columns; x++) {
|
||||||
|
row.insertCell()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearTable() {
|
||||||
|
for(var y=0; y < this.rows; y++) {
|
||||||
|
for (var x=0; x < this.columns; x++) {
|
||||||
|
var cell = this.table.rows[y].cells[x]
|
||||||
|
drawMino(cell, this.defaultBgColor, this.defaultBorderColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HoldQueue extends MinoesTable {
|
||||||
|
constructor() {
|
||||||
|
super("hold", HOLD_ROWS, HOLD_COLUMNS, HOLD_BG_COLOR, HOLD_BORDER_COLOR)
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
this.context.clearRect(0, 0, this.width, this.height)
|
this.clearTable()
|
||||||
|
if (this.piece && state != STATE.PAUSED)
|
||||||
|
this.piece.drawIn(this.table)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Matrix extends MinoesTable {
|
||||||
|
constructor() {
|
||||||
|
super("matrix", MATRIX_ROWS, MATRIX_COLUMNS, MATRIX_BG_COLOR, MATRIX_BORDER_COLOR)
|
||||||
|
this.lockedMinoes = Array.from(Array(MATRIX_ROWS+3), row => Array(MATRIX_COLUMNS))
|
||||||
|
this.piece = null
|
||||||
|
/*this.context.textAlign = "center"
|
||||||
|
this.context.textBaseline = "center"
|
||||||
|
this.context.font = "27px 'Share Tech', sans-serif"
|
||||||
|
this.centerX = this.width / 2
|
||||||
|
this.centerY = this.height / 2
|
||||||
|
this.linesCleared = []
|
||||||
|
this.trail = {
|
||||||
|
minoesPos: [],
|
||||||
|
height: 0,
|
||||||
|
gradient: null
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
cellIsOccupied(x, y) {
|
||||||
|
return 0 <= x && x < MATRIX_COLUMNS && y < MATRIX_ROWS ? this.lockedMinoes[y][x] : true
|
||||||
|
}
|
||||||
|
|
||||||
|
spaceToMove(minoesAbsPos) {
|
||||||
|
return !minoesAbsPos.some(pos => this.cellIsOccupied(...pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
if (state == STATE.PAUSED) {
|
||||||
|
this.clearTable()
|
||||||
|
} else {
|
||||||
|
// locked minoes
|
||||||
|
for(var y=0; y < this.rows; y++) {
|
||||||
|
for (var x=0; x < this.columns; x++) {
|
||||||
|
var cell = this.table.rows[y].cells[x]
|
||||||
|
var bgColor = this.lockedMinoes[y][x]
|
||||||
|
if (bgColor) drawMino(cell, bgColor, MINO_BORDER_COLOR)
|
||||||
|
else {
|
||||||
|
if (y < 4) drawMino(cell, this.defaultBgColor, "transparent")
|
||||||
|
else drawMino(cell, this.defaultBgColor, MATRIX_BORDER_COLOR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trail
|
||||||
|
/*if (this.trail.height) {
|
||||||
|
this.context.fillStyle = this.trail.gradient
|
||||||
|
this.trail.minoesPos.forEach(topLeft => {
|
||||||
|
this.context.fillRect(...topLeft, MINO_SIZE, this.trail.height)
|
||||||
|
})
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// falling piece
|
||||||
|
/*for (var ghostYOffset = 1; this.spaceToMove(this.piece.minoesAbsPos.translate([0, ghostYOffset])); ghostYOffset++) {}
|
||||||
|
ghostYOffset--*/
|
||||||
|
this.piece.drawIn(this.table)
|
||||||
|
|
||||||
|
// Lines cleared
|
||||||
|
/*this.context.fillStyle = "rgba(255, 255, 255, 0.5)"
|
||||||
|
this.linesCleared.forEach(y => this.context.fillRect(0, y, this.width, MINO_SIZE))*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// text
|
||||||
|
/*var texts = []
|
||||||
|
switch(state) {
|
||||||
|
case STATE.PLAYING:
|
||||||
|
if (tempTexts.length)
|
||||||
|
texts = tempTexts[0]
|
||||||
|
break
|
||||||
|
case STATE.PAUSED:
|
||||||
|
texts = ["PAUSED"]
|
||||||
|
break
|
||||||
|
case STATE.GAME_OVER:
|
||||||
|
texts = ["GAME", "OVER"]
|
||||||
|
}
|
||||||
|
if (texts.length) {
|
||||||
|
this.context.save()
|
||||||
|
this.context.shadowColor = "black"
|
||||||
|
this.context.shadowOffsetX = 1
|
||||||
|
this.context.shadowOffsetY = 1
|
||||||
|
this.context.shadowBlur = 2
|
||||||
|
this.context.fillStyle = "white"
|
||||||
|
if (texts.length == 1)
|
||||||
|
this.context.fillText(texts[0], this.centerX, this.centerY)
|
||||||
|
else {
|
||||||
|
this.context.fillText(texts[0], this.centerX, this.centerY - 20)
|
||||||
|
this.context.fillText(texts[1], this.centerX, this.centerY + 20)
|
||||||
|
}
|
||||||
|
this.context.restore()
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class NextQueue extends MinoesTable {
|
||||||
|
constructor() {
|
||||||
|
super("next", NEXT_ROWS, NEXT_COLUMNS, NEXT_BG_COLOR, NEXT_BORDER_COLOR)
|
||||||
|
this.pieces = Array.from({length: NEXT_PIECES}, (v, k) => new Tetromino(NEXT_PIECES_POSITIONS[k]))
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
this.clearTable()
|
||||||
if (state != STATE.PAUSED) {
|
if (state != STATE.PAUSED) {
|
||||||
if (this.piece)
|
this.pieces.forEach(piece => piece.drawIn(this.table))
|
||||||
this.piece.draw(this.context)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,7 +392,7 @@ class Stats {
|
|||||||
this.pauseTime = 0
|
this.pauseTime = 0
|
||||||
this.combo = -1
|
this.combo = -1
|
||||||
this.lockDelay = LOCK_DELAY
|
this.lockDelay = LOCK_DELAY
|
||||||
this.fallDelay = FALL_DELAY
|
this.fallPeriod = FALL_PERIOD
|
||||||
}
|
}
|
||||||
|
|
||||||
get score() {
|
get score() {
|
||||||
@ -286,12 +413,12 @@ class Stats {
|
|||||||
printTempTexts(["LEVEL", this.level])
|
printTempTexts(["LEVEL", this.level])
|
||||||
this.goal += 5 * this.level
|
this.goal += 5 * this.level
|
||||||
if (this.level <= 20)
|
if (this.level <= 20)
|
||||||
this.fallDelay = 1000 * Math.pow(0.8 - ((this.level - 1) * 0.007), this.level - 1)
|
this.fallPeriod = 1000 * Math.pow(0.8 - ((this.level - 1) * 0.007), this.level - 1)
|
||||||
if (this.level > 15)
|
if (this.level > 15)
|
||||||
this.lockDelay = 500 * Math.pow(0.9, this.level - 15)
|
this.lockDelay = 500 * Math.pow(0.9, this.level - 15)
|
||||||
}
|
}
|
||||||
|
|
||||||
locksDown(tSpin, linesCleared) {
|
lockDown(tSpin, linesCleared) {
|
||||||
var patternName = []
|
var patternName = []
|
||||||
var patternScore = 0
|
var patternScore = 0
|
||||||
var combo_score = 0
|
var combo_score = 0
|
||||||
@ -334,125 +461,6 @@ class Stats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class Matrix {
|
|
||||||
constructor() {
|
|
||||||
this.context = document.getElementById("matrix").getContext("2d")
|
|
||||||
this.context.textAlign = "center"
|
|
||||||
this.context.textBaseline = "center"
|
|
||||||
this.context.font = "27px 'Share Tech', sans-serif"
|
|
||||||
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.centerX = this.width / 2
|
|
||||||
this.centerY = this.height / 2
|
|
||||||
this.piece = null
|
|
||||||
this.trail = {
|
|
||||||
minoesPos: [],
|
|
||||||
height: 0,
|
|
||||||
gradient: null
|
|
||||||
}
|
|
||||||
this.linesCleared = []
|
|
||||||
}
|
|
||||||
|
|
||||||
cellIsOccupied(x, y) {
|
|
||||||
return 0 <= x && x < MATRIX_COLUMNS && y < MATRIX_ROWS ? this.cells[y+3][x] : 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
|
|
||||||
this.context.beginPath()
|
|
||||||
for (var x = 0; x <= this.width; x += MINO_SIZE) {
|
|
||||||
this.context.moveTo(x, 0);
|
|
||||||
this.context.lineTo(x, this.height);
|
|
||||||
}
|
|
||||||
for (var y = 0; y <= this.height; y += MINO_SIZE) {
|
|
||||||
this.context.moveTo(0, y);
|
|
||||||
this.context.lineTo(this.width, y);
|
|
||||||
}
|
|
||||||
this.context.stroke()
|
|
||||||
|
|
||||||
if (state != STATE.PAUSED) {
|
|
||||||
// locked minoes
|
|
||||||
this.cells.slice(3).forEach((row, y) => row.forEach((color, x) => {
|
|
||||||
if (color) drawMino(this.context, [x, y], color)
|
|
||||||
}))
|
|
||||||
|
|
||||||
// trail
|
|
||||||
if (this.trail.height) {
|
|
||||||
this.context.fillStyle = this.trail.gradient
|
|
||||||
this.trail.minoesPos.forEach(topLeft => {
|
|
||||||
this.context.fillRect(...topLeft, MINO_SIZE, this.trail.height)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// falling piece
|
|
||||||
for (var ghostYOffset = 1; this.spaceToMove(this.piece.minoesAbsPos.translate([0, ghostYOffset])); ghostYOffset++) {}
|
|
||||||
ghostYOffset--
|
|
||||||
if (this.piece)
|
|
||||||
this.piece.draw(this.context, ghostYOffset)
|
|
||||||
|
|
||||||
// Lines cleared
|
|
||||||
this.context.fillStyle = "rgba(255, 255, 255, 0.5)"
|
|
||||||
this.linesCleared.forEach(y => this.context.fillRect(0, y, this.width, MINO_SIZE))
|
|
||||||
}
|
|
||||||
|
|
||||||
// text
|
|
||||||
var texts = []
|
|
||||||
switch(state) {
|
|
||||||
case STATE.PLAYING:
|
|
||||||
if (tempTexts.length)
|
|
||||||
texts = tempTexts[0]
|
|
||||||
break
|
|
||||||
case STATE.PAUSED:
|
|
||||||
texts = ["PAUSED"]
|
|
||||||
break
|
|
||||||
case STATE.GAME_OVER:
|
|
||||||
texts = ["GAME", "OVER"]
|
|
||||||
}
|
|
||||||
if (texts.length) {
|
|
||||||
this.context.save()
|
|
||||||
this.context.shadowColor = "black"
|
|
||||||
this.context.shadowOffsetX = 1
|
|
||||||
this.context.shadowOffsetY = 1
|
|
||||||
this.context.shadowBlur = 2
|
|
||||||
this.context.fillStyle = "white"
|
|
||||||
if (texts.length == 1)
|
|
||||||
this.context.fillText(texts[0], this.centerX, this.centerY)
|
|
||||||
else {
|
|
||||||
this.context.fillText(texts[0], this.centerX, this.centerY - 20)
|
|
||||||
this.context.fillText(texts[1], this.centerX, this.centerY + 20)
|
|
||||||
}
|
|
||||||
this.context.restore()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class NextQueue {
|
|
||||||
constructor() {
|
|
||||||
this.context = document.getElementById("next").getContext("2d")
|
|
||||||
this.pieces = Array.from({length: NEXT_PIECES}, (v, k) => new Tetromino(NEXT_PIECES_POSITIONS[k]))
|
|
||||||
this.width = NEXT_COLUMNS*MINO_SIZE
|
|
||||||
this.height = NEXT_ROWS*MINO_SIZE
|
|
||||||
}
|
|
||||||
|
|
||||||
draw() {
|
|
||||||
this.context.clearRect(0, 0, this.width, this.height)
|
|
||||||
if (state != STATE.PAUSED) {
|
|
||||||
this.pieces.forEach(piece => piece.draw(this.context))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function newLevel(startLevel) {
|
function newLevel(startLevel) {
|
||||||
stats.newLevel(startLevel)
|
stats.newLevel(startLevel)
|
||||||
generationPhase()
|
generationPhase()
|
||||||
@ -465,24 +473,24 @@ function generationPhase(held_piece=null) {
|
|||||||
nextQueue.pieces.forEach((piece, i) => piece.pos = NEXT_PIECES_POSITIONS[i])
|
nextQueue.pieces.forEach((piece, i) => piece.pos = NEXT_PIECES_POSITIONS[i])
|
||||||
}
|
}
|
||||||
matrix.piece.pos = FALLING_PIECE_POSITION
|
matrix.piece.pos = FALLING_PIECE_POSITION
|
||||||
if (matrix.spaceToMove(matrix.piece.minoesPos.translate(matrix.piece.pos)))
|
if (matrix.spaceToMove(matrix.piece.minoesPos.translate(matrix.piece.pos))){
|
||||||
|
scheduler.clearInterval(lockPhase)
|
||||||
|
scheduler.setInterval(lockPhase, stats.fallPeriod)
|
||||||
fallingPhase()
|
fallingPhase()
|
||||||
else
|
} else
|
||||||
gameOver()
|
gameOver()
|
||||||
}
|
}
|
||||||
|
|
||||||
function fallingPhase() {
|
function fallingPhase() {
|
||||||
scheduler.clearTimeout(lockPhase)
|
scheduler.clearTimeout(lockDown)
|
||||||
scheduler.clearTimeout(locksDown)
|
|
||||||
matrix.piece.locked = false
|
matrix.piece.locked = false
|
||||||
scheduler.setTimeout(lockPhase, stats.fallDelay)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function lockPhase() {
|
function lockPhase() {
|
||||||
if (!move(MOVEMENT.DOWN)) {
|
if (!move(MOVEMENT.DOWN)) {
|
||||||
matrix.piece.locked = true
|
matrix.piece.locked = true
|
||||||
if (!scheduler.timeoutTasks.has(locksDown))
|
if (!scheduler.timeoutTasks.has(lockDown))
|
||||||
scheduler.setTimeout(locksDown, stats.lockDelay)
|
scheduler.setTimeout(lockDown, stats.lockDelay)
|
||||||
}
|
}
|
||||||
requestAnimationFrame(draw)
|
requestAnimationFrame(draw)
|
||||||
}
|
}
|
||||||
@ -498,8 +506,8 @@ function move(movement, testMinoesPos=matrix.piece.minoesPos) {
|
|||||||
fallingPhase()
|
fallingPhase()
|
||||||
else {
|
else {
|
||||||
matrix.piece.locked = true
|
matrix.piece.locked = true
|
||||||
scheduler.clearTimeout(locksDown)
|
scheduler.clearTimeout(lockDown)
|
||||||
scheduler.setTimeout(locksDown, stats.lockDelay)
|
scheduler.setTimeout(lockDown, stats.lockDelay)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
@ -523,12 +531,12 @@ function rotate(spin) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
function locksDown(){
|
function lockDown(){
|
||||||
scheduler.clearInterval(move)
|
scheduler.clearInterval(lockPhase)
|
||||||
if (matrix.piece.minoesAbsPos.every(pos => pos.y < 0))
|
if (matrix.piece.minoesAbsPos.every(pos => pos.y < 4))
|
||||||
game_over()
|
game_over()
|
||||||
else {
|
else {
|
||||||
matrix.piece.minoesAbsPos.forEach(pos => matrix.cells[pos[1]+3][pos[0]] = matrix.piece.color)
|
matrix.piece.minoesAbsPos.forEach(pos => matrix.lockedMinoes[pos[1]][pos[0]] = matrix.piece.color)
|
||||||
|
|
||||||
// T-Spin detection
|
// T-Spin detection
|
||||||
var tSpin = T_SPIN.NONE
|
var tSpin = T_SPIN.NONE
|
||||||
@ -546,15 +554,15 @@ function locksDown(){
|
|||||||
|
|
||||||
// Complete lines
|
// Complete lines
|
||||||
matrix.linesCleared = []
|
matrix.linesCleared = []
|
||||||
matrix.cells.forEach((row, y) => {
|
matrix.lockedMinoes.forEach((row, y) => {
|
||||||
if (row.filter(mino => mino.length).length == MATRIX_COLUMNS) {
|
if (row.filter(mino => mino.length).length == MATRIX_COLUMNS) {
|
||||||
matrix.cells.splice(y, 1)
|
matrix.lockedMinoes.splice(y, 1)
|
||||||
matrix.cells.unshift(Array(MATRIX_COLUMNS))
|
matrix.lockedMinoes.unshift(Array(MATRIX_COLUMNS))
|
||||||
matrix.linesCleared.push((y-3) * MINO_SIZE)
|
matrix.linesCleared.push((y-3) * MINO_SIZE)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
stats.locksDown(tSpin, matrix.linesCleared.length)
|
stats.lockDown(tSpin, matrix.linesCleared.length)
|
||||||
requestAnimationFrame(draw)
|
requestAnimationFrame(draw)
|
||||||
scheduler.setTimeout(clearLinesCleared, ANIMATION_DELAY)
|
scheduler.setTimeout(clearLinesCleared, ANIMATION_DELAY)
|
||||||
|
|
||||||
@ -572,8 +580,8 @@ function clearLinesCleared() {
|
|||||||
|
|
||||||
function gameOver() {
|
function gameOver() {
|
||||||
state = STATE.GAME_OVER
|
state = STATE.GAME_OVER
|
||||||
scheduler.clearTimeout(lockPhase)
|
scheduler.clearInterval(lockPhase)
|
||||||
scheduler.clearTimeout(locksDown)
|
scheduler.clearTimeout(lockDown)
|
||||||
scheduler.clearInterval(clock)
|
scheduler.clearInterval(clock)
|
||||||
requestAnimationFrame(draw)
|
requestAnimationFrame(draw)
|
||||||
|
|
||||||
@ -611,7 +619,7 @@ function keyDownHandler(e) {
|
|||||||
scheduler.clearTimeout(autorepeat)
|
scheduler.clearTimeout(autorepeat)
|
||||||
scheduler.clearInterval(autorepeat)
|
scheduler.clearInterval(autorepeat)
|
||||||
if (action == softDrop)
|
if (action == softDrop)
|
||||||
scheduler.setInterval(autorepeat, stats.fallDelay / 20)
|
scheduler.setInterval(autorepeat, stats.fallPeriod / 20)
|
||||||
else
|
else
|
||||||
scheduler.setTimeout(autorepeat, AUTOREPEAT_DELAY)
|
scheduler.setTimeout(autorepeat, AUTOREPEAT_DELAY)
|
||||||
}
|
}
|
||||||
@ -647,17 +655,18 @@ function softDrop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function hardDrop() {
|
function hardDrop() {
|
||||||
scheduler.clearTimeout(lockPhase)
|
scheduler.clearInterval(lockPhase)
|
||||||
scheduler.clearTimeout(locksDown)
|
scheduler.clearTimeout(lockDown)
|
||||||
matrix.trail.minoesPos = Array.from(matrix.piece.minoesAbsPos).map(pos => pos.mul(MINO_SIZE))
|
/*matrix.trail.minoesPos = Array.from(matrix.piece.minoesAbsPos).map(pos => pos.mul(MINO_SIZE))
|
||||||
for (matrix.trail.height=0; move(MOVEMENT.DOWN); matrix.trail.height += MINO_SIZE) {
|
for (matrix.trail.height=0; move(MOVEMENT.DOWN); matrix.trail.height += MINO_SIZE) {
|
||||||
stats.score += 2
|
stats.score += 2
|
||||||
}
|
}*/
|
||||||
locksDown()
|
while (move(MOVEMENT.DOWN)) {}
|
||||||
matrix.trail.gradient = matrix.context.createLinearGradient(0, 0, 0, matrix.trail.height)
|
lockDown()
|
||||||
|
/*matrix.trail.gradient = matrix.context.createLinearGradient(0, 0, 0, matrix.trail.height)
|
||||||
matrix.trail.gradient.addColorStop(0,"rgba(255, 255, 255, 0)")
|
matrix.trail.gradient.addColorStop(0,"rgba(255, 255, 255, 0)")
|
||||||
matrix.trail.gradient.addColorStop(1, matrix.piece.ghostColor)
|
matrix.trail.gradient.addColorStop(1, matrix.piece.transparentColor)
|
||||||
scheduler.setTimeout(clearTrail, ANIMATION_DELAY)
|
scheduler.setTimeout(clearTrail, ANIMATION_DELAY)*/
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearTrail() {
|
function clearTrail() {
|
||||||
@ -676,7 +685,7 @@ function rotateCCW() {
|
|||||||
function hold() {
|
function hold() {
|
||||||
if (this.matrix.piece.holdEnabled) {
|
if (this.matrix.piece.holdEnabled) {
|
||||||
scheduler.clearInterval(move)
|
scheduler.clearInterval(move)
|
||||||
scheduler.clearInterval(locksDown)
|
scheduler.clearInterval(lockDown)
|
||||||
var shape = this.matrix.piece.shape
|
var shape = this.matrix.piece.shape
|
||||||
this.matrix.piece = this.holdQueue.piece
|
this.matrix.piece = this.holdQueue.piece
|
||||||
this.holdQueue.piece = new Tetromino(HELD_PIECE_POSITION, shape)
|
this.holdQueue.piece = new Tetromino(HELD_PIECE_POSITION, shape)
|
||||||
@ -688,8 +697,8 @@ function hold() {
|
|||||||
function pause() {
|
function pause() {
|
||||||
state = STATE.PAUSED
|
state = STATE.PAUSED
|
||||||
stats.pauseTime = Date.now() - stats.startTime
|
stats.pauseTime = Date.now() - stats.startTime
|
||||||
scheduler.clearTimeout(lockPhase)
|
scheduler.clearInterval(lockPhase)
|
||||||
scheduler.clearTimeout(locksDown)
|
scheduler.clearTimeout(lockDown)
|
||||||
scheduler.clearTimeout(autorepeat)
|
scheduler.clearTimeout(autorepeat)
|
||||||
scheduler.clearInterval(clock)
|
scheduler.clearInterval(clock)
|
||||||
}
|
}
|
||||||
@ -697,9 +706,9 @@ function pause() {
|
|||||||
function resume() {
|
function resume() {
|
||||||
state = STATE.PLAYING
|
state = STATE.PLAYING
|
||||||
stats.startTime = Date.now() - stats.pauseTime
|
stats.startTime = Date.now() - stats.pauseTime
|
||||||
scheduler.setTimeout(lockPhase, stats.fallDelay)
|
scheduler.setTimeout(lockPhase, stats.fallPeriod)
|
||||||
if (matrix.piece.locked)
|
if (matrix.piece.locked)
|
||||||
scheduler.setTimeout(locksDown, stats.lockDelay)
|
scheduler.setTimeout(lockDown, stats.lockDelay)
|
||||||
requestAnimationFrame(draw)
|
requestAnimationFrame(draw)
|
||||||
scheduler.setInterval(clock, 1000)
|
scheduler.setInterval(clock, 1000)
|
||||||
}
|
}
|
||||||
@ -718,14 +727,14 @@ function delTempTexts(self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clock() {
|
function clock() {
|
||||||
stats.print()
|
//stats.print()
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
holdQueue.draw()
|
holdQueue.draw()
|
||||||
stats.print()
|
|
||||||
matrix.draw()
|
matrix.draw()
|
||||||
nextQueue.draw()
|
nextQueue.draw()
|
||||||
|
//stats.print()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getKey(action) {
|
function getKey(action) {
|
||||||
|
0
leaderboard.php
Normal file
0
leaderboard.php
Normal file
35
webtris.html
35
webtris.html
@ -5,33 +5,24 @@
|
|||||||
<title>Webtris</title>
|
<title>Webtris</title>
|
||||||
<link rel="icon" type="image/png" href="favicon.png">
|
<link rel="icon" type="image/png" href="favicon.png">
|
||||||
<link rel="stylesheet" type="text/css" href="css/style.css" />
|
<link rel="stylesheet" type="text/css" href="css/style.css" />
|
||||||
<!--[if lt IE 9]><script type="text/javascript" src="js/excanvas.js"></script><![endif]-->
|
|
||||||
<script type="text/javascript" src="js/webtris.js"></script>
|
<script type="text/javascript" src="js/webtris.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>WEBTRIS</h1>
|
<h1>WEBTRIS</h1>
|
||||||
<div id="grid">
|
<div id="grid">
|
||||||
<canvas id="hold" width="120" height="120"></canvas>
|
<table id="hold"><table>
|
||||||
<div id="stats-names">
|
<table id="matrix"><table>
|
||||||
SCORE<br/>
|
<table id="next"><table>
|
||||||
RECORD<br/>
|
<table id="stats">
|
||||||
TEMPS<br/>
|
<tbody>
|
||||||
<br />
|
<tr><td class="stat-label">SCORE</td><td class="stat-value" id="score">0</td></tr>
|
||||||
NIVEAU<br/>
|
<tr><td class="stat-label">RECORD</td><td class="stat-value" id="highScore">0</td></tr>
|
||||||
OBJECTIF<br/>
|
<tr><td class="stat-label">TEMPS</td><td class="stat-value" id="time">00:00</td></tr>
|
||||||
LIGNES
|
<tr><td class="stat-label">NIVEAU</td><td class="stat-value" id="level">0</td></tr>
|
||||||
</div>
|
<tr><td class="stat-label">OBJECTIF</td><td class="stat-value" id="goal">0</td></tr>
|
||||||
<div id="stats-values">
|
<tr><td class="stat-label">LIGNES</td><td class="stat-value" id="clearedLines">0</td></tr>
|
||||||
0<br/>
|
</tbody>
|
||||||
0<br/>
|
</table>
|
||||||
00:00<br/>
|
|
||||||
<br/>
|
|
||||||
0<br/>
|
|
||||||
0<br/>
|
|
||||||
0
|
|
||||||
</div>
|
|
||||||
<canvas id="matrix" width="200" height="400">Votre navigateur ne supporte pas HTML5, veuillez le mettre à jour pour jouer.</canvas>
|
|
||||||
<canvas id="next"width="120" height="400"></canvas>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="play">
|
<div id="play">
|
||||||
<a href="webtris.html">REJOUER</a>
|
<a href="webtris.html">REJOUER</a>
|
||||||
|
Reference in New Issue
Block a user