Compare commits
1 Commits
master
...
two-player
Author | SHA1 | Date | |
---|---|---|---|
|
06a2e582fd |
30
index.html
30
index.html
@@ -7,7 +7,7 @@
|
|||||||
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
|
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
<svg style="position: absolute;" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<filter id="f1" x="0" y="0">
|
<filter id="f1" x="0" y="0">
|
||||||
<feGaussianBlur in="SourceGraphic" stdDeviation="15"></feGaussianBlur>
|
<feGaussianBlur in="SourceGraphic" stdDeviation="15"></feGaussianBlur>
|
||||||
@@ -70,16 +70,24 @@
|
|||||||
<span class="bar"></span>
|
<span class="bar"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="background" class="blurred">
|
|
||||||
<div class="game-stats" id="score" data-string="counterScore" data-string-type="prefix">0</div>
|
<template id="player-container">
|
||||||
<div class="game-stats" id="time" data-string="counterTime" data-string-type="prefix">00:00</div>
|
<div class="blurred" id="background">
|
||||||
|
<div class="game-stats" data-string="counterScore" data-string-type="prefix" id="score">0</div>
|
||||||
|
<div class="game-stats" data-string="counterTime" data-string-type="prefix" id="time">00:00</div>
|
||||||
<div id="canvas-container">
|
<div id="canvas-container">
|
||||||
<canvas id="tetris-hold" width="100" height="100"></canvas>
|
<canvas height="100" id="tetris-hold" width="100"></canvas>
|
||||||
<canvas id="tetris-background" width="240" height="400"></canvas>
|
<canvas height="400" id="tetris-background" width="240"></canvas>
|
||||||
<canvas id="tetris" width="240" height="400"></canvas>
|
<canvas height="400" id="tetris" width="240"></canvas>
|
||||||
<canvas id="tetris-upcoming" width="100" height="300"></canvas>
|
<canvas height="300" id="tetris-upcoming" width="100"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="game-container blurred">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<h1 id="game-title" data-string="titlePaused" data-string-first-run="titleGame">Tetris.js</h1>
|
<h1 id="game-title" data-string="titlePaused" data-string-first-run="titleGame">Tetris.js</h1>
|
||||||
<button id="game-play" data-string="btnResume" data-string-first-run="btnPlay">Play!</button>
|
<button id="game-play" data-string="btnResume" data-string-first-run="btnPlay">Play!</button>
|
||||||
@@ -93,12 +101,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<script src="https://hammerjs.github.io/dist/hammer.js"></script>
|
<script src="https://hammerjs.github.io/dist/hammer.js"></script>
|
||||||
<script src="js/language.js"></script>
|
<script src="js/language.js"></script>
|
||||||
|
<script src="js/helper-functions.js"></script>
|
||||||
|
<script src="js/theme.js"></script>
|
||||||
<script src="js/game-info.js"></script>
|
<script src="js/game-info.js"></script>
|
||||||
<script src="js/game.js"></script>
|
<script src="js/game.js"></script>
|
||||||
<script src="js/arena.js"></script>
|
<script src="js/arena.js"></script>
|
||||||
<script src="js/player.js"></script>
|
<script src="js/player.js"></script>
|
||||||
|
<script src="js/local-player.js"></script>
|
||||||
|
<script src="js/tetris-manager.js"></script>
|
||||||
<script src="js/tetris.js"></script>
|
<script src="js/tetris.js"></script>
|
||||||
<script src="js/menu.js"></script>
|
<script src="js/menu.js"></script>
|
||||||
<script src="js/touch-control.js"></script>
|
<!--<script src="js/touch-control.js"></script>-->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@@ -23,14 +23,9 @@ class Arena {
|
|||||||
this.field.unshift(row);
|
this.field.unshift(row);
|
||||||
++y;
|
++y;
|
||||||
|
|
||||||
this.p.score += rowCount * 10;
|
this.p.score += rowCount * 20;
|
||||||
rowCount *= 2;
|
rowCount *= 2;
|
||||||
}
|
}
|
||||||
if (this.p.score - this.g.prevUpdateScore > 50) {
|
|
||||||
this.g.dropInterval -= 20;
|
|
||||||
this.g.dropInterval = this.g.dropInterval > 100 ? this.g.dropInterval : 100;
|
|
||||||
this.g.prevUpdateScore = this.p.score;
|
|
||||||
}
|
|
||||||
this.game.drawArena();
|
this.game.drawArena();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -3,19 +3,43 @@ class GameInfo {
|
|||||||
this.fieldSize = {x: 12, y: 20};
|
this.fieldSize = {x: 12, y: 20};
|
||||||
this.arena = new Arena(this, game);
|
this.arena = new Arena(this, game);
|
||||||
|
|
||||||
this.player = new Player(this, game);
|
this.player = new LocalPlayer(this, game);
|
||||||
|
|
||||||
this.canvas = document.getElementById('tetris');
|
const container = document.createElement('div');
|
||||||
this.context = this.canvas.getContext('2d');
|
container.className = 'game-instance';
|
||||||
|
manager.container.appendChild(container);
|
||||||
|
|
||||||
this.canvasBg = document.getElementById('tetris-background');
|
this.score = document.createElement('div');
|
||||||
this.contextBg = this.canvasBg.getContext('2d');
|
this.score.classList.add('game-stats', 'score');
|
||||||
|
container.appendChild(this.score);
|
||||||
|
|
||||||
this.canvasHold = document.getElementById('tetris-hold');
|
this.canvasContainer = document.createElement('div');
|
||||||
|
this.canvasContainer.className = 'canvas-container';
|
||||||
|
container.appendChild(this.canvasContainer);
|
||||||
|
|
||||||
|
this.canvasHold = document.createElement('canvas');
|
||||||
|
this.canvasHold.className = 'tetris-hold';
|
||||||
this.contextHold = this.canvasHold.getContext('2d');
|
this.contextHold = this.canvasHold.getContext('2d');
|
||||||
|
this.canvasContainer.appendChild(this.canvasHold);
|
||||||
|
|
||||||
this.canvasUpcoming = document.getElementById('tetris-upcoming');
|
this.canvasBg = document.createElement('canvas');
|
||||||
|
this.canvasBg.className = 'tetris-background';
|
||||||
|
this.contextBg = this.canvasBg.getContext('2d');
|
||||||
|
this.canvasContainer.appendChild(this.canvasBg);
|
||||||
|
|
||||||
|
this.canvas = document.createElement('canvas');
|
||||||
|
this.canvas.className = 'tetris-arena';
|
||||||
|
this.context = this.canvas.getContext('2d');
|
||||||
|
this.canvasContainer.appendChild(this.canvas);
|
||||||
|
|
||||||
|
this.canvasUpcoming = document.createElement('canvas');
|
||||||
|
this.canvasUpcoming.className = 'tetris-upcoming';
|
||||||
this.contextUpcoming = this.canvasUpcoming.getContext('2d');
|
this.contextUpcoming = this.canvasUpcoming.getContext('2d');
|
||||||
|
this.canvasContainer.appendChild(this.canvasUpcoming);
|
||||||
|
|
||||||
|
this.time = document.createElement('div');
|
||||||
|
this.time.classList.add('game-stats', 'time');
|
||||||
|
container.appendChild(this.time);
|
||||||
|
|
||||||
this.isPaused = true;
|
this.isPaused = true;
|
||||||
|
|
||||||
@@ -25,28 +49,32 @@ class GameInfo {
|
|||||||
this.keys = {
|
this.keys = {
|
||||||
down: {
|
down: {
|
||||||
keys: [40, 83],
|
keys: [40, 83],
|
||||||
action: () => this.player.drop()
|
action: () => {
|
||||||
|
this.player.drop();
|
||||||
|
this.player.score++;
|
||||||
|
this.player.game.updateScore(false);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
left: {
|
left: {
|
||||||
keys: [37, 65],
|
keys: [37, 65],
|
||||||
action: () => this.player.move(-1)
|
action: () => this.player.move(-1),
|
||||||
},
|
},
|
||||||
right: {
|
right: {
|
||||||
keys: [39, 68],
|
keys: [39, 68],
|
||||||
action: () => this.player.move(1)
|
action: () => this.player.move(1),
|
||||||
},
|
},
|
||||||
rotateLeft: {
|
rotateLeft: {
|
||||||
keys: [81],
|
keys: [81],
|
||||||
action: () => this.player.rotate(-1)
|
action: () => this.player.rotate(-1),
|
||||||
},
|
},
|
||||||
rotateRight: {
|
rotateRight: {
|
||||||
keys: [69],
|
keys: [69],
|
||||||
action: () => this.player.rotate(1)
|
action: () => this.player.rotate(1),
|
||||||
},
|
},
|
||||||
holdTile: {
|
holdTile: {
|
||||||
keys: [38, 87],
|
keys: [38, 87],
|
||||||
action: () => this.player.hold()
|
action: () => this.player.hold(),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.prevUpdateScore = 0;
|
this.prevUpdateScore = 0;
|
||||||
@@ -65,6 +93,16 @@ class GameInfo {
|
|||||||
modern -> rounded corners
|
modern -> rounded corners
|
||||||
snake -> all tiles are connected
|
snake -> all tiles are connected
|
||||||
*/
|
*/
|
||||||
this.theme = 'default';
|
this.theme = new DefaultTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScore(animate) {
|
||||||
|
this.score.innerText = this.lastScore;
|
||||||
|
if (animate) {
|
||||||
|
this.score.classList.add('update');
|
||||||
|
setTimeout(() => {
|
||||||
|
this.score.classList.remove('update');
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
103
js/game.js
103
js/game.js
@@ -38,54 +38,14 @@ class Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
drawTile(x, y, offset, color, matrix, ctx = this.g.context) {
|
drawTile(x, y, offset, color, matrix, ctx = this.g.context) {
|
||||||
ctx.fillStyle = color;
|
|
||||||
x += offset.x;
|
x += offset.x;
|
||||||
y += offset.y;
|
y += offset.y;
|
||||||
switch (this.g.theme) {
|
|
||||||
case "default":
|
ctx.save();
|
||||||
ctx.fillRect(x + tileGap / 2, y + tileGap / 2, 1 - tileGap, 1 - tileGap);
|
ctx.translate(x, y);
|
||||||
break;
|
|
||||||
case "clean":
|
this.g.theme.drawTile(color, matrix, ctx, x, y);
|
||||||
ctx.fillRect(x, y, 1, 1);
|
ctx.restore();
|
||||||
break;
|
|
||||||
case "modern":
|
|
||||||
drawRoundRect(ctx, x + tileGap / 2, y + tileGap / 2, 1 - tileGap, 1 - tileGap, .15);
|
|
||||||
break;
|
|
||||||
case "snakes":
|
|
||||||
let r1 = .15, // top right
|
|
||||||
r2 = .15, // bottom right
|
|
||||||
r3 = .15, // bottom left
|
|
||||||
r4 = .15; // top left
|
|
||||||
// Is there a tile to the left?
|
|
||||||
if (matrix[y][x - 1] > 0) {
|
|
||||||
r3 = 0;
|
|
||||||
r4 = 0;
|
|
||||||
}
|
|
||||||
// Is there a tile to the right?
|
|
||||||
if (matrix[y][x + 1] > 0) {
|
|
||||||
r1 = 0;
|
|
||||||
r2 = 0;
|
|
||||||
}
|
|
||||||
// Is there a tile to the top?
|
|
||||||
if (matrix[y - 1] !== undefined && matrix[y - 1][x] > 0) {
|
|
||||||
r1 = 0;
|
|
||||||
r4 = 0;
|
|
||||||
}
|
|
||||||
// Is there a tile to the bottom?
|
|
||||||
if (matrix[y + 1] !== undefined && matrix[y + 1][x] > 0) {
|
|
||||||
r2 = 0;
|
|
||||||
r3 = 0;
|
|
||||||
}
|
|
||||||
drawRoundRect(ctx, x, y, 1, 1, [r1, r2, r3, r4]);
|
|
||||||
break;
|
|
||||||
case "retro":
|
|
||||||
drawReliefRect(ctx, x, y, 1, 1, .15, color);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.g.theme = "default";
|
|
||||||
this.drawTile(x, y, offset, color, matrix, ctx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drawUpcoming() {
|
drawUpcoming() {
|
||||||
@@ -141,9 +101,42 @@ class Game {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rescale() {
|
||||||
|
let conWidth = manager.container.clientWidth / manager.instances.size;
|
||||||
|
let conHeight = manager.container.clientHeight - 78;
|
||||||
|
|
||||||
|
if (conHeight < conWidth) {
|
||||||
|
conWidth = conHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
conWidth = Math.floor(conWidth);
|
||||||
|
|
||||||
|
const canvasScale = Math.floor(conWidth * .6 / this.g.fieldSize.x);
|
||||||
|
const canvasHoldScale = Math.floor(conWidth * .2 / 6);
|
||||||
|
|
||||||
|
const realWidth = canvasScale * this.g.fieldSize.x + 2 * canvasHoldScale * 6;
|
||||||
|
const realHeight = canvasScale * this.g.fieldSize.y;
|
||||||
|
|
||||||
|
this.g.canvasContainer.style.width = realWidth + 'px';
|
||||||
|
this.g.canvasContainer.style.height = realHeight + 'px';
|
||||||
|
|
||||||
|
this.g.canvasUpcoming.style.height = this.g.canvasUpcoming.clientWidth * 3 + 'px';
|
||||||
|
this.g.canvasHold.style.height = this.g.canvasHold.clientWidth + 'px';
|
||||||
|
|
||||||
|
this.g.canvasBg.adjustResolution(this.g.contextBg, canvasScale);
|
||||||
|
this.g.canvas.adjustResolution(this.g.context, canvasScale);
|
||||||
|
this.g.canvasUpcoming.adjustResolution(this.g.contextUpcoming, canvasHoldScale);
|
||||||
|
this.g.canvasHold.adjustResolution(this.g.contextHold, canvasHoldScale);
|
||||||
|
|
||||||
|
if (!firstRun && this.g.isPaused) {
|
||||||
|
this.draw();
|
||||||
|
}
|
||||||
|
this.redrawScreen();
|
||||||
|
}
|
||||||
|
|
||||||
saveHighscore() {
|
saveHighscore() {
|
||||||
if (getCookie("highscore").value < this.p.score) {
|
if (getCookie('highscore') && getCookie('highscore').value < this.p.score) {
|
||||||
document.cookie = "highscore=" + this.p.score + "; max-age=" + 60 * 60 * 24 * 365 * 1000 + "; path=/;";
|
document.cookie = 'highscore=' + this.p.score + '; max-age=' + 60 * 60 * 24 * 365 * 1000 + '; path=/;';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,18 +168,24 @@ class Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateScore() {
|
updateScore(animate = true) {
|
||||||
if (this.g.lastScore !== this.p.score) {
|
if (this.g.lastScore !== this.p.score) {
|
||||||
scoreUpdateAni();
|
this.g.updateScore(animate);
|
||||||
this.g.lastScore = this.p.score;
|
this.g.lastScore = this.p.score;
|
||||||
this.saveHighscore();
|
this.saveHighscore();
|
||||||
|
|
||||||
|
if (this.p.score - this.p.lastLevelScore > 500) {
|
||||||
|
this.p.lastLevelScore = this.p.score;
|
||||||
|
this.p.level++;
|
||||||
|
this.g.dropInterval *= .9;
|
||||||
}
|
}
|
||||||
document.getElementById('score').innerText = this.p.score.toString();
|
}
|
||||||
|
this.g.updateScore(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTime() {
|
updateTime() {
|
||||||
timePassed += Date.now() - this.g.lastTimeUpdate;
|
timePassed += Date.now() - this.g.lastTimeUpdate;
|
||||||
timeElement.innerHTML = formatMillis(timePassed);
|
this.g.time.innerText = formatMillis(timePassed);
|
||||||
this.g.lastTimeUpdate = Date.now();
|
this.g.lastTimeUpdate = Date.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
6
js/helper-functions.js
Normal file
6
js/helper-functions.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
HTMLCanvasElement.prototype.adjustResolution = function (ctx, scale) {
|
||||||
|
this.width = this.clientWidth;
|
||||||
|
this.height = this.clientHeight;
|
||||||
|
|
||||||
|
ctx.scale(scale, scale);
|
||||||
|
};
|
@@ -53,6 +53,17 @@ let firstRun = true;
|
|||||||
class Language {
|
class Language {
|
||||||
|
|
||||||
constructor(lang) {
|
constructor(lang) {
|
||||||
|
const xmlhttp = new XMLHttpRequest();
|
||||||
|
xmlhttp.onreadystatechange = function () {
|
||||||
|
if (this.readyState === 4 && this.status === 200) {
|
||||||
|
const obj = JSON.parse(this.responseText);
|
||||||
|
console.log(JSON.stringify(obj));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xmlhttp.open("GET", "/lang.json", true);
|
||||||
|
xmlhttp.send();
|
||||||
|
|
||||||
this.lang = lang;
|
this.lang = lang;
|
||||||
if(eval('typeof ' + this.lang) === 'undefined')
|
if(eval('typeof ' + this.lang) === 'undefined')
|
||||||
this.lang = "en";
|
this.lang = "en";
|
||||||
|
21
js/local-player.js
Normal file
21
js/local-player.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
class LocalPlayer extends Player {
|
||||||
|
constructor(gameInfo, game) {
|
||||||
|
super(gameInfo, game);
|
||||||
|
|
||||||
|
this.isHolding = false;
|
||||||
|
this.holdingTile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
hold() {
|
||||||
|
if (this.isHolding)
|
||||||
|
return;
|
||||||
|
if (this.holdingTile === null) {
|
||||||
|
this.holdingTile = this.matrix;
|
||||||
|
this.reset(true);
|
||||||
|
} else {
|
||||||
|
this.holdingTile = [this.matrix, this.matrix = this.holdingTile][0];
|
||||||
|
this.reset(true, false);
|
||||||
|
}
|
||||||
|
this.game.drawHolding();
|
||||||
|
}
|
||||||
|
}
|
159
js/menu.js
159
js/menu.js
@@ -6,67 +6,17 @@
|
|||||||
let escState = 1;
|
let escState = 1;
|
||||||
|
|
||||||
window.onresize = () => {
|
window.onresize = () => {
|
||||||
scaleWindow();
|
manager.callAll('rescale', []);
|
||||||
game.redrawScreen();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function scaleWindow() {
|
document.addEventListener('keyup', (event) => {
|
||||||
const canvasContainer = document.getElementById("canvas-container");
|
console.log(event.key);
|
||||||
let height = .8 * window.innerHeight - 40;
|
if (event.key === ' ') {
|
||||||
let width = height / (5 / 3);
|
|
||||||
let conWidth = width + (2 * (height / game.g.fieldSize.y * 5));
|
|
||||||
const ratio = width / conWidth;
|
|
||||||
|
|
||||||
if (conWidth > window.innerWidth * .8) {
|
|
||||||
conWidth = window.innerWidth * .8;
|
|
||||||
width = conWidth * ratio;
|
|
||||||
height = width * (5 / 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
width = Math.floor(width);
|
|
||||||
height = Math.floor(height);
|
|
||||||
|
|
||||||
canvasContainer.style.height = height + "px";
|
|
||||||
canvasContainer.style.width = conWidth + "px";
|
|
||||||
|
|
||||||
const canvasScale = width / game.g.fieldSize.x;
|
|
||||||
|
|
||||||
game.g.canvasBg.height = height;
|
|
||||||
game.g.canvasBg.width = width;
|
|
||||||
game.g.contextBg.scale(canvasScale, canvasScale);
|
|
||||||
|
|
||||||
game.g.canvas.height = height;
|
|
||||||
game.g.canvas.width = width;
|
|
||||||
game.g.context.scale(canvasScale, canvasScale);
|
|
||||||
|
|
||||||
game.g.canvasHold.height = game.g.canvasHold.width = height / game.g.fieldSize.y * 5;
|
|
||||||
game.g.canvasHold.style.transform = "translate(-100%, -.2em) translateX(-" + width / 2 + "px)";
|
|
||||||
const contextHoldScale = Math.floor(game.g.canvasHold.width / 6);
|
|
||||||
game.g.contextHold.scale(contextHoldScale, contextHoldScale);
|
|
||||||
|
|
||||||
game.g.canvasUpcoming.width = height / game.g.fieldSize.y * 5;
|
|
||||||
game.g.canvasUpcoming.height = game.g.canvasUpcoming.width * 3;
|
|
||||||
game.g.canvasUpcoming.style.transform = "translate(100%, -.2em) translateX(" + width / 2 + "px)";
|
|
||||||
const contextUpcomingScale = Math.floor(game.g.canvasUpcoming.width / 6);
|
|
||||||
game.g.contextUpcoming.scale(contextUpcomingScale, contextUpcomingScale);
|
|
||||||
|
|
||||||
if (!firstRun && game.g.isPaused) {
|
|
||||||
game.draw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scaleWindow();
|
|
||||||
|
|
||||||
document.addEventListener("keydown", (event) => {
|
|
||||||
if(event.keyCode === 32) {
|
|
||||||
if (firstRun) {
|
if (firstRun) {
|
||||||
initGame();
|
initGame();
|
||||||
} else {
|
} else {
|
||||||
if (!game.g.isPaused) {
|
escState = escState === 0 ? 1 : 0;
|
||||||
showMenu();
|
toggleMenu();
|
||||||
} else {
|
|
||||||
hideMenu();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (event.keyCode === 27) {
|
} else if (event.keyCode === 27) {
|
||||||
escState++;
|
escState++;
|
||||||
@@ -78,7 +28,7 @@ document.addEventListener("keydown", (event) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("game-play").addEventListener("click", () => {
|
document.getElementById('game-play').addEventListener('click', () => {
|
||||||
if (firstRun) {
|
if (firstRun) {
|
||||||
initGame();
|
initGame();
|
||||||
} else {
|
} else {
|
||||||
@@ -86,7 +36,7 @@ document.getElementById("game-play").addEventListener("click", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("game-reset").addEventListener("click", () => {
|
document.getElementById('game-reset').addEventListener('click', () => {
|
||||||
firstRun = true;
|
firstRun = true;
|
||||||
game.clearScreen();
|
game.clearScreen();
|
||||||
hideMenu();
|
hideMenu();
|
||||||
@@ -94,17 +44,21 @@ document.getElementById("game-reset").addEventListener("click", () => {
|
|||||||
showMenu();
|
showMenu();
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementsByName("theme").forEach((el) => {
|
document.getElementsByName('theme').forEach((el) => {
|
||||||
el.addEventListener("change", (e) => {
|
el.addEventListener('change', (e) => {
|
||||||
game.g.theme = e.target.getAttribute("data-theme");
|
const themeName = e.target.getAttribute('data-theme');
|
||||||
game.redrawScreen();
|
const theme = themes[themeName];
|
||||||
|
manager.callAll(instance => {
|
||||||
|
instance.g.theme = theme;
|
||||||
|
instance.redrawScreen();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let isActive = false;
|
let isActive = false;
|
||||||
const menuButton = document.getElementById("menu-opener");
|
const menuButton = document.getElementById('menu-opener');
|
||||||
|
|
||||||
menuButton.addEventListener("click", () => {
|
menuButton.addEventListener('click', () => {
|
||||||
toggleSettings();
|
toggleSettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -112,120 +66,109 @@ function toggleSettings() {
|
|||||||
if (isActive) {
|
if (isActive) {
|
||||||
escState = 1;
|
escState = 1;
|
||||||
menuButton.classList.remove('active');
|
menuButton.classList.remove('active');
|
||||||
document.getElementsByTagName('body')[0].classList.remove('menu-open');
|
document.body.classList.remove('menu-open');
|
||||||
} else {
|
} else {
|
||||||
escState = 2;
|
escState = 2;
|
||||||
menuButton.classList.add('active');
|
menuButton.classList.add('active');
|
||||||
document.getElementsByTagName('body')[0].classList.add('menu-open');
|
document.body.classList.add('menu-open');
|
||||||
}
|
}
|
||||||
isActive = !isActive;
|
isActive = !isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleMenu() {
|
function toggleMenu() {
|
||||||
if (escState === 0) {
|
if (escState === 0) {
|
||||||
document.getElementsByTagName("body")[0].classList.remove("menu-open");
|
document.body.classList.remove('menu-open');
|
||||||
menuButton.classList.remove('active');
|
menuButton.classList.remove('active');
|
||||||
hideMenu();
|
hideMenu();
|
||||||
} else if (escState === 1) {
|
} else if (escState === 1) {
|
||||||
document.getElementsByTagName("body")[0].classList.remove("menu-open");
|
document.body.classList.remove('menu-open');
|
||||||
menuButton.classList.remove('active');
|
menuButton.classList.remove('active');
|
||||||
showMenu();
|
showMenu();
|
||||||
} else {
|
} else {
|
||||||
document.getElementsByTagName("body")[0].classList.add("menu-open");
|
document.body.classList.add('menu-open');
|
||||||
menuButton.classList.add('active');
|
menuButton.classList.add('active');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fadeBlurIn() {
|
function fadeBlurIn() {
|
||||||
const blurEl = document.getElementById("f1").children[0];
|
const blurEl = document.getElementById('f1').children[0];
|
||||||
const finalVal = 15;
|
const finalVal = 15;
|
||||||
let currentVal = 0;
|
let currentVal = 0;
|
||||||
|
|
||||||
const id = setInterval(frame, 16);
|
const interval = 1000 / 60;
|
||||||
|
const id = setInterval(frame, interval);
|
||||||
|
|
||||||
function frame() {
|
function frame() {
|
||||||
if (currentVal >= finalVal) {
|
if (currentVal >= finalVal) {
|
||||||
clearInterval(id);
|
clearInterval(id);
|
||||||
} else {
|
} else {
|
||||||
currentVal += 1.6;
|
currentVal += 0.5;
|
||||||
blurEl.setAttribute("stdDeviation", currentVal);
|
blurEl.setAttribute('stdDeviation', currentVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (currentVal < finalVal) {
|
if (currentVal < finalVal) {
|
||||||
blurEl.setAttribute("stdDeviation", finalVal);
|
blurEl.setAttribute('stdDeviation', finalVal);
|
||||||
clearInterval(id);
|
clearInterval(id);
|
||||||
console.log("Performance Issues: system couldn't hold up");
|
console.log('Performance Issues: system couldn\'t hold up');
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fadeBlurOut() {
|
function fadeBlurOut() {
|
||||||
const blurEl = document.getElementById("f1").children[0];
|
const blurEl = document.getElementById('f1').children[0];
|
||||||
const finalVal = 0;
|
const finalVal = 0;
|
||||||
let currentVal = 15;
|
let currentVal = 15;
|
||||||
|
|
||||||
const id = setInterval(frame, 16);
|
const interval = 1000 / 60;
|
||||||
|
const id = setInterval(frame, interval);
|
||||||
|
|
||||||
function frame() {
|
function frame() {
|
||||||
if (currentVal <= finalVal) {
|
if (currentVal <= finalVal) {
|
||||||
clearInterval(id);
|
clearInterval(id);
|
||||||
} else {
|
} else {
|
||||||
currentVal -= 1.6;
|
currentVal -= 0.5;
|
||||||
blurEl.setAttribute("stdDeviation", currentVal);
|
blurEl.setAttribute('stdDeviation', currentVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
blurEl.setAttribute("stdDeviation", finalVal);
|
blurEl.setAttribute('stdDeviation', finalVal);
|
||||||
clearInterval(id);
|
clearInterval(id);
|
||||||
if (currentVal < finalVal) {
|
if (currentVal < finalVal) {
|
||||||
console.log("Performance Issues: system couldn't hold up");
|
console.log('Performance Issues: system couldn\'t hold up');
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
const scoreEl = document.getElementById("score");
|
|
||||||
const nativeTransform = getComputedStyle(scoreEl).transform;
|
|
||||||
function scoreUpdateAni() {
|
|
||||||
scoreEl.classList.add("update");
|
|
||||||
setTimeout(() => {
|
|
||||||
scoreEl.classList.remove("update");
|
|
||||||
}, 1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showMenu() {
|
function showMenu() {
|
||||||
game.g.isPaused = true;
|
manager.pause();
|
||||||
escState = 1;
|
escState = 1;
|
||||||
document.getElementById("game-title").style.display = "block";
|
document.getElementById('game-title').style.display = 'block';
|
||||||
document.getElementById("game-play").style.display = "block";
|
document.getElementById('game-play').style.display = 'block';
|
||||||
document.getElementById("game-reset").style.display = "block";
|
document.getElementById('game-reset').style.display = 'block';
|
||||||
|
|
||||||
document.getElementById("game-title").style.opacity = "1";
|
document.getElementById('game-title').style.opacity = '1';
|
||||||
document.getElementById("game-play").style.opacity = "1";
|
document.getElementById('game-play').style.opacity = '1';
|
||||||
fadeBlurIn();
|
fadeBlurIn();
|
||||||
if (!firstRun) {
|
if (!firstRun) {
|
||||||
document.getElementById("game-reset").style.opacity = "1";
|
document.getElementById('game-reset').style.opacity = '1';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideMenu() {
|
function hideMenu() {
|
||||||
game.g.isPaused = false;
|
manager.resume();
|
||||||
escState = 0;
|
escState = 0;
|
||||||
document.getElementById("game-title").style.opacity = "0";
|
document.getElementById('game-title').style.opacity = '0';
|
||||||
document.getElementById("game-play").style.opacity = "0";
|
document.getElementById('game-play').style.opacity = '0';
|
||||||
document.getElementById("game-reset").style.opacity = "0";
|
document.getElementById('game-reset').style.opacity = '0';
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.getElementById("game-title").style.display = "none";
|
document.getElementById('game-title').style.display = 'none';
|
||||||
document.getElementById("game-play").style.display = "none";
|
document.getElementById('game-play').style.display = 'none';
|
||||||
document.getElementById("game-reset").style.display = "none";
|
document.getElementById('game-reset').style.display = 'none';
|
||||||
}, 500);
|
}, 500);
|
||||||
game.g.lastTimeUpdate = Date.now();
|
|
||||||
fadeBlurOut();
|
fadeBlurOut();
|
||||||
if(!firstRun) {
|
|
||||||
game.update(game.g.lastTime);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initGame() {
|
function initGame() {
|
||||||
|
18
js/player.js
18
js/player.js
@@ -6,9 +6,8 @@ class Player {
|
|||||||
this.pos = {x: 0, y: 0};
|
this.pos = {x: 0, y: 0};
|
||||||
this.matrix = null;
|
this.matrix = null;
|
||||||
this.score = 0;
|
this.score = 0;
|
||||||
|
this.level = 1;
|
||||||
this.isHolding = false;
|
this.lastLevelScore = 0;
|
||||||
this.holdingTile = null;
|
|
||||||
|
|
||||||
this.a.p = this;
|
this.a.p = this;
|
||||||
}
|
}
|
||||||
@@ -29,19 +28,6 @@ class Player {
|
|||||||
this.g.dropCounter = 0;
|
this.g.dropCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
hold() {
|
|
||||||
if (this.isHolding)
|
|
||||||
return;
|
|
||||||
if (this.holdingTile === null) {
|
|
||||||
this.holdingTile = this.matrix;
|
|
||||||
this.reset(true);
|
|
||||||
} else {
|
|
||||||
this.holdingTile = [this.matrix, this.matrix = this.holdingTile][0];
|
|
||||||
this.reset(true, false);
|
|
||||||
}
|
|
||||||
this.game.drawHolding();
|
|
||||||
}
|
|
||||||
|
|
||||||
move(dir) {
|
move(dir) {
|
||||||
this.pos.x += dir;
|
this.pos.x += dir;
|
||||||
if (collide(this.a.field, this)) {
|
if (collide(this.a.field, this)) {
|
||||||
|
56
js/tetris-manager.js
Normal file
56
js/tetris-manager.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
class TetrisManager {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.container = document.querySelector('.game-container');
|
||||||
|
|
||||||
|
this.instances = new Set;
|
||||||
|
}
|
||||||
|
|
||||||
|
createPlayer() {
|
||||||
|
const game = new Game();
|
||||||
|
this.instances.add(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.callAll('rescale', []);
|
||||||
|
}
|
||||||
|
|
||||||
|
removePlayer(tetris) {
|
||||||
|
this.instances.delete(tetris);
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
this.instances.forEach(instance => {
|
||||||
|
instance.start();
|
||||||
|
});
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
resume() {
|
||||||
|
this.instances.forEach(instance => {
|
||||||
|
instance.g.isPaused = false;
|
||||||
|
instance.g.lastTimeUpdate = Date.now();
|
||||||
|
if (!firstRun) {
|
||||||
|
instance.update(instance.g.lastTime);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
this.instances.forEach(instance => {
|
||||||
|
instance.g.isPaused = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
callAll(method, args) {
|
||||||
|
if (typeof method === 'string') {
|
||||||
|
this.instances.forEach(instance => {
|
||||||
|
instance[method](...args);
|
||||||
|
});
|
||||||
|
} else if (typeof method === 'function') {
|
||||||
|
this.instances.forEach(instance => {
|
||||||
|
method(instance);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
js/tetris.js
50
js/tetris.js
@@ -25,7 +25,7 @@ function centerOffset(matrix) {
|
|||||||
let offsetY = 0;
|
let offsetY = 0;
|
||||||
matrix.forEach((row, y) => {
|
matrix.forEach((row, y) => {
|
||||||
let onlyZeroesY = true;
|
let onlyZeroesY = true;
|
||||||
row.forEach((value, x) => {
|
row.forEach((value) => {
|
||||||
if (value > 0) {
|
if (value > 0) {
|
||||||
onlyZeroesY = false;
|
onlyZeroesY = false;
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ function centerOffset(matrix) {
|
|||||||
});
|
});
|
||||||
for (let x = 0; x < matrix[0].length; x++) {
|
for (let x = 0; x < matrix[0].length; x++) {
|
||||||
let onlyZeroesX = true;
|
let onlyZeroesX = true;
|
||||||
matrix.forEach((row, y) => {
|
matrix.forEach((row) => {
|
||||||
if (row[x] > 0)
|
if (row[x] > 0)
|
||||||
onlyZeroesX = false;
|
onlyZeroesX = false;
|
||||||
});
|
});
|
||||||
@@ -165,44 +165,6 @@ function drawRoundRect(ctx, x, y, w, h, r) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function drawReliefRect(ctx, x, y, w, h, l, clr) {
|
function drawReliefRect(ctx, x, y, w, h, l, clr) {
|
||||||
ctx.fillStyle = clr;
|
|
||||||
ctx.fillRect(x + l, y + l, w - (2 * l), h - (2 * l));
|
|
||||||
|
|
||||||
ctx.fillStyle = colorLuminance(clr, .6);
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(x, y);
|
|
||||||
ctx.lineTo(x + w, y);
|
|
||||||
ctx.lineTo(x + w - l, y + l);
|
|
||||||
ctx.lineTo(x + l, y + l);
|
|
||||||
ctx.fill();
|
|
||||||
ctx.closePath();
|
|
||||||
|
|
||||||
ctx.fillStyle = colorLuminance(clr, .3);
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(x, y);
|
|
||||||
ctx.lineTo(x, y + h);
|
|
||||||
ctx.lineTo(x + l, y + h - l);
|
|
||||||
ctx.lineTo(x + l, y + l);
|
|
||||||
ctx.fill();
|
|
||||||
ctx.closePath();
|
|
||||||
|
|
||||||
ctx.fillStyle = colorLuminance(clr, -.6);
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(x, y + h);
|
|
||||||
ctx.lineTo(x + w, y + h);
|
|
||||||
ctx.lineTo(x + w - l, y + h - l);
|
|
||||||
ctx.lineTo(x + l, y + h - l);
|
|
||||||
ctx.fill();
|
|
||||||
ctx.closePath();
|
|
||||||
|
|
||||||
ctx.fillStyle = colorLuminance(clr, -.3);
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(x + w, y);
|
|
||||||
ctx.lineTo(x + w, y + h);
|
|
||||||
ctx.lineTo(x + w - l, y + h - l);
|
|
||||||
ctx.lineTo(x + w - l, y + l);
|
|
||||||
ctx.fill();
|
|
||||||
ctx.closePath();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatMillis(millis) {
|
function formatMillis(millis) {
|
||||||
@@ -279,10 +241,14 @@ function rotate(matrix, dir) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const game = new Game();
|
const manager = new TetrisManager();
|
||||||
|
manager.createPlayer();
|
||||||
|
|
||||||
|
// const game = new Game();
|
||||||
|
|
||||||
function startGame() {
|
function startGame() {
|
||||||
game.start();
|
// game.start();
|
||||||
|
manager.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
110
js/theme.js
Normal file
110
js/theme.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
class Theme {
|
||||||
|
drawTile(color, matrix, ctx) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DefaultTheme extends Theme {
|
||||||
|
drawTile(color, matrix, ctx) {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillRect(tileGap / 2, tileGap / 2, 1 - tileGap, 1 - tileGap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CleanTheme extends Theme {
|
||||||
|
drawTile(color, matrix, ctx) {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillRect(0, 0, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModernTheme extends Theme {
|
||||||
|
drawTile(color, matrix, ctx) {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
drawRoundRect(ctx, tileGap / 2, tileGap / 2, 1 - tileGap, 1 - tileGap, .15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SnakesTheme extends Theme {
|
||||||
|
drawTile(color, matrix, ctx, x, y) {
|
||||||
|
let r1 = .15, // top right
|
||||||
|
r2 = .15, // bottom right
|
||||||
|
r3 = .15, // bottom left
|
||||||
|
r4 = .15; // top left
|
||||||
|
// Is there a tile to the left?
|
||||||
|
if (matrix[y][x - 1] > 0) {
|
||||||
|
r3 = 0;
|
||||||
|
r4 = 0;
|
||||||
|
}
|
||||||
|
// Is there a tile to the right?
|
||||||
|
if (matrix[y][x + 1] > 0) {
|
||||||
|
r1 = 0;
|
||||||
|
r2 = 0;
|
||||||
|
}
|
||||||
|
// Is there a tile to the top?
|
||||||
|
if (matrix[y - 1] !== undefined && matrix[y - 1][x] > 0) {
|
||||||
|
r1 = 0;
|
||||||
|
r4 = 0;
|
||||||
|
}
|
||||||
|
// Is there a tile to the bottom?
|
||||||
|
if (matrix[y + 1] !== undefined && matrix[y + 1][x] > 0) {
|
||||||
|
r2 = 0;
|
||||||
|
r3 = 0;
|
||||||
|
}
|
||||||
|
drawRoundRect(ctx, x, y, 1, 1, [r1, r2, r3, r4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RetroTheme extends Theme {
|
||||||
|
static drawReliefRect(ctx, x, y, width, height, elevation, color) {
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillRect(x + elevation, y + elevation, width - (2 * elevation), height - (2 * elevation));
|
||||||
|
|
||||||
|
ctx.fillStyle = colorLuminance(color, .6);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y);
|
||||||
|
ctx.lineTo(x + width, y);
|
||||||
|
ctx.lineTo(x + width - elevation, y + elevation);
|
||||||
|
ctx.lineTo(x + elevation, y + elevation);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.closePath();
|
||||||
|
|
||||||
|
ctx.fillStyle = colorLuminance(color, .3);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y);
|
||||||
|
ctx.lineTo(x, y + height);
|
||||||
|
ctx.lineTo(x + elevation, y + height - elevation);
|
||||||
|
ctx.lineTo(x + elevation, y + elevation);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.closePath();
|
||||||
|
|
||||||
|
ctx.fillStyle = colorLuminance(color, -.6);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y + height);
|
||||||
|
ctx.lineTo(x + width, y + height);
|
||||||
|
ctx.lineTo(x + width - elevation, y + height - elevation);
|
||||||
|
ctx.lineTo(x + elevation, y + height - elevation);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.closePath();
|
||||||
|
|
||||||
|
ctx.fillStyle = colorLuminance(color, -.3);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + width, y);
|
||||||
|
ctx.lineTo(x + width, y + height);
|
||||||
|
ctx.lineTo(x + width - elevation, y + height - elevation);
|
||||||
|
ctx.lineTo(x + width - elevation, y + elevation);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.closePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
drawTile(color, matrix, ctx) {
|
||||||
|
RetroTheme.drawReliefRect(ctx, 0, 0, 1, 1, .15, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const themes = {
|
||||||
|
default: new DefaultTheme(),
|
||||||
|
clean: new CleanTheme(),
|
||||||
|
modern: new ModernTheme(),
|
||||||
|
snakes: new SnakesTheme(),
|
||||||
|
retro: new RetroTheme()
|
||||||
|
};
|
70
lang.json
Normal file
70
lang.json
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"en": {
|
||||||
|
"button": {
|
||||||
|
"play": "Play!",
|
||||||
|
"reset": "Reset",
|
||||||
|
"resume": "Resume"
|
||||||
|
},
|
||||||
|
"controls": {
|
||||||
|
"down": "Accelerate falling",
|
||||||
|
"hold": "Hold",
|
||||||
|
"left": "Move left",
|
||||||
|
"pause": "Pause",
|
||||||
|
"right": "Move right",
|
||||||
|
"tLeft": "Rotate to the left",
|
||||||
|
"tRight": "Rotate to the right"
|
||||||
|
},
|
||||||
|
"counter": {
|
||||||
|
"score": "Score: ",
|
||||||
|
"time": "Time: "
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"default": "Default",
|
||||||
|
"clean": "Clean",
|
||||||
|
"modern": "Modern",
|
||||||
|
"retro": "Retro",
|
||||||
|
"snakes": "Snakes"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"appearance": "Appearance",
|
||||||
|
"controls": "Controls",
|
||||||
|
"game": "Tetris.js",
|
||||||
|
"language": "Language",
|
||||||
|
"paused": "Paused"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"de": {
|
||||||
|
"button": {
|
||||||
|
"play": "Spielen!",
|
||||||
|
"reset": "Zurücksetzen",
|
||||||
|
"resume": "Fortsetzen"
|
||||||
|
},
|
||||||
|
"controls": {
|
||||||
|
"down": "Fallen beschleunigen",
|
||||||
|
"hold": "Halten",
|
||||||
|
"left": "Nach links bewegen",
|
||||||
|
"pause": "Pausieren",
|
||||||
|
"right": "Nach rechts bewegen",
|
||||||
|
"tLeft": "Nach rechts drehen",
|
||||||
|
"tRight": "Nach links drehen"
|
||||||
|
},
|
||||||
|
"counter": {
|
||||||
|
"score": "Punkte: ",
|
||||||
|
"time": "Zeit: "
|
||||||
|
},
|
||||||
|
"theme": {
|
||||||
|
"default": "Standard",
|
||||||
|
"clean": "Lückenlos",
|
||||||
|
"modern": "Futuristisch",
|
||||||
|
"retro": "Retro",
|
||||||
|
"snakes": "Schlangen"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"appearance": "Erscheinungsbild",
|
||||||
|
"controls": "Steuerung",
|
||||||
|
"game": "Tetris.js",
|
||||||
|
"language": "Sprache",
|
||||||
|
"paused": "Pausiert"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
247
style.css
247
style.css
@@ -1,59 +1,59 @@
|
|||||||
body {
|
body {
|
||||||
|
overflow: hidden;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
|
||||||
background: #202028;
|
background: #202028;
|
||||||
|
color: #fff;
|
||||||
font-family: Roboto, Helvetica, Arial, sans-serif;
|
font-family: Roboto, Helvetica, Arial, sans-serif;
|
||||||
font-size: 2.5em;
|
font-size: 2.5em;
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
|
position: absolute;
|
||||||
|
-moz-transform: scale(.5);
|
||||||
|
-ms-transform: scale(.5);
|
||||||
|
-o-transform: scale(.5);
|
||||||
|
-webkit-transform: scale(.5);
|
||||||
|
transform: scale(.5);
|
||||||
|
z-index: 1;
|
||||||
|
width: 3.5rem;
|
||||||
|
height: 2.7rem;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
background: 0;
|
||||||
|
outline: 0;
|
||||||
-webkit-transition: 0.1s -webkit-transform linear;
|
-webkit-transition: 0.1s -webkit-transform linear;
|
||||||
transition: 0.1s -webkit-transform linear;
|
transition: 0.1s -webkit-transform linear;
|
||||||
transition: 0.1s transform linear;
|
transition: 0.1s transform linear;
|
||||||
transition: 0.1s transform linear, 0.1s -webkit-transform linear;
|
transition: 0.1s transform linear, 0.1s -webkit-transform linear;
|
||||||
position: absolute;
|
|
||||||
background: 0;
|
|
||||||
float: left;
|
float: left;
|
||||||
height: 2.7rem;
|
|
||||||
width: 3.5rem;
|
|
||||||
z-index: 1;
|
|
||||||
outline: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
-webkit-transform: scale(.5);
|
|
||||||
-moz-transform: scale(.5);
|
|
||||||
-ms-transform: scale(.5);
|
|
||||||
-o-transform: scale(.5);
|
|
||||||
transform: scale(.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu {
|
#menu {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
transform: translateX(-100%);
|
||||||
|
z-index: 100;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgba(0, 0, 0, .75);
|
background: rgba(0, 0, 0, .75);
|
||||||
transform: translateX(-100%);
|
|
||||||
-webkit-transition: transform .5s;
|
|
||||||
-moz-transition: transform .5s;
|
-moz-transition: transform .5s;
|
||||||
-ms-transition: transform .5s;
|
-ms-transition: transform .5s;
|
||||||
-o-transition: transform .5s;
|
-o-transition: transform .5s;
|
||||||
|
-webkit-transition: transform .5s;
|
||||||
transition: transform .5s;
|
transition: transform .5s;
|
||||||
z-index: 100;
|
|
||||||
top: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu-content {
|
#menu-content {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
font-size: .6em;
|
overflow: auto;
|
||||||
width: 375px;
|
width: 375px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
max-height: 90%;
|
max-height: 90%;
|
||||||
overflow: auto;
|
font-size: .6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu-content > * {
|
#menu-content > * {
|
||||||
@@ -75,22 +75,22 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#help-controls dt {
|
#help-controls dt {
|
||||||
float: left;
|
|
||||||
width: 30%;
|
width: 30%;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
#help-controls dt code {
|
#help-controls dt code {
|
||||||
background: rgba(255, 255, 255, 1);
|
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
background: rgba(255, 255, 255, 1);
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#help-controls dd {
|
#help-controls dd {
|
||||||
float: left;
|
|
||||||
width: 70%;
|
width: 70%;
|
||||||
margin: 0 0 10px;
|
margin: 0 0 10px;
|
||||||
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-open #menu {
|
.menu-open #menu {
|
||||||
@@ -99,16 +99,16 @@ body {
|
|||||||
|
|
||||||
#menu-opener {
|
#menu-opener {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 10px;
|
|
||||||
top: 10px;
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
z-index: 101;
|
z-index: 101;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lang {
|
.lang {
|
||||||
display: inline;
|
display: inline;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
border-radius: 2px;
|
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
|
border-radius: 2px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +118,8 @@ body {
|
|||||||
|
|
||||||
#game-title {
|
#game-title {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
transform: translate(-50%, -25%) translateY(-200px);
|
transform: translate(-50%, -25%) translateY(-200px);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity .5s;
|
transition: opacity .5s;
|
||||||
@@ -132,69 +132,73 @@ body {
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
width: 300px;
|
width: 300px;
|
||||||
height: 75px;
|
height: 75px;
|
||||||
font-size: 30px;
|
|
||||||
box-shadow: none;
|
|
||||||
-webkit-text-fill-color: #fff;
|
|
||||||
color: #ffff !important;
|
|
||||||
background: #3877FF !important;
|
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
transition: box-shadow .2s, opacity .5s;
|
background: #3877FF !important;
|
||||||
cursor: pointer;
|
color: #ffff !important;
|
||||||
|
box-shadow: none;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
font-size: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: box-shadow .2s, opacity .5s;
|
||||||
|
-webkit-text-fill-color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#game-reset {
|
#game-reset {
|
||||||
|
transform: translate(-50%, -50%) translateY(85px);
|
||||||
display: none;
|
display: none;
|
||||||
background-color: #FF0D72 !important;
|
background-color: #FF0D72 !important;
|
||||||
transform: translate(-50%, -50%) translateY(85px);
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#game-play {
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
#game-play:hover, #game-play:active, #game-play:focus {
|
#game-play:hover, #game-play:active, #game-play:focus {
|
||||||
outline: none !important;
|
|
||||||
box-shadow: 3px 4px 0 3px rgba(0, 0, 0, 0.2) !important;
|
box-shadow: 3px 4px 0 3px rgba(0, 0, 0, 0.2) !important;
|
||||||
|
outline: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#background {
|
#background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
filter: url(#f2);
|
filter: url(#f2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#background.blurred {
|
.blurred {
|
||||||
filter: url(#f1);
|
filter: url(#f1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#canvas-container {
|
#canvas-container {
|
||||||
height: 936px;
|
|
||||||
width: 761px;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
|
width: 761px;
|
||||||
|
height: 936px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tetris, #tetris-background {
|
#tetris, #tetris-background {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
top: 50%;
|
||||||
-webkit-transform: translate(-50%, -50%);
|
left: 50%;
|
||||||
-moz-transform: translate(-50%, -50%);
|
-moz-transform: translate(-50%, -50%);
|
||||||
-ms-transform: translate(-50%, -50%);
|
-ms-transform: translate(-50%, -50%);
|
||||||
-o-transform: translate(-50%, -50%);
|
-o-transform: translate(-50%, -50%);
|
||||||
|
-webkit-transform: translate(-50%, -50%);
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
border: solid .2em #fff;
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
border: solid .2em #fff;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tetris-background {
|
#tetris-background {
|
||||||
border: none;
|
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#tetris-hold {
|
#tetris-hold {
|
||||||
@@ -209,36 +213,36 @@ body {
|
|||||||
border: solid .2em #fff;
|
border: solid .2em #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-stats {
|
/*.game-stats {*/
|
||||||
position: absolute;
|
/* position: absolute;*/
|
||||||
left: 50%;
|
/* bottom: 3.75vh;*/
|
||||||
bottom: 3.75vh;
|
/* left: 50%;*/
|
||||||
transform: translateX(-50%);
|
/* transform: translateX(-50%);*/
|
||||||
font-size: 2.5vh;
|
/* font-size: 2.5vh;*/
|
||||||
}
|
/*}*/
|
||||||
|
/**/
|
||||||
#score {
|
/*#score {*/
|
||||||
font-weight: 900;
|
/* top: 2.5vh;*/
|
||||||
bottom: initial;
|
/* bottom: initial;*/
|
||||||
top: 2.5vh;
|
/* font-size: 5vh;*/
|
||||||
font-size: 5vh;
|
/* font-weight: 900;*/
|
||||||
}
|
/*}*/
|
||||||
|
/**/
|
||||||
#score.update {
|
#score.update {
|
||||||
animation: scoreUpdate .5s;
|
animation: scoreUpdate .5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes scoreUpdate {
|
@keyframes scoreUpdate {
|
||||||
0% {
|
0% {
|
||||||
transform: translateX(-50%) scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
transform: translateX(-50%) scale(1.5);
|
transform: scale(1.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: translateX(-50%) scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,8 +255,8 @@ body {
|
|||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%) translateX(30vw);
|
transform: translate(-50%, -50%) translateX(30vw);
|
||||||
font-size: 20px;
|
|
||||||
max-width: 20vw;
|
max-width: 20vw;
|
||||||
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-over #score {
|
.game-over #score {
|
||||||
@@ -261,12 +265,12 @@ body {
|
|||||||
|
|
||||||
#footer {
|
#footer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 10px;
|
|
||||||
width: 95%;
|
|
||||||
left: 2.5%;
|
|
||||||
right: 2.5%;
|
right: 2.5%;
|
||||||
font-size: 12px;
|
bottom: 10px;
|
||||||
|
left: 2.5%;
|
||||||
|
width: 95%;
|
||||||
color: rgba(255, 255, 255, .5);
|
color: rgba(255, 255, 255, .5);
|
||||||
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#version-author {
|
#version-author {
|
||||||
@@ -274,21 +278,21 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#corner-buttons {
|
#corner-buttons {
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#corner-buttons > a {
|
#corner-buttons > a {
|
||||||
text-decoration: none;
|
|
||||||
display: inline-block;
|
|
||||||
left: 10px;
|
left: 10px;
|
||||||
|
display: inline-block;
|
||||||
color: rgba(255, 255, 255, .5);
|
color: rgba(255, 255, 255, .5);
|
||||||
-webkit-transition: color .2s;
|
|
||||||
-moz-transition: color .2s;
|
-moz-transition: color .2s;
|
||||||
-ms-transition: color .2s;
|
-ms-transition: color .2s;
|
||||||
-o-transition: color .2s;
|
-o-transition: color .2s;
|
||||||
|
-webkit-transition: color .2s;
|
||||||
transition: color .2s;
|
transition: color .2s;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#corner-buttons > a:hover, #corner-buttons > a:active {
|
#corner-buttons > a:hover, #corner-buttons > a:active {
|
||||||
@@ -296,18 +300,16 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bar, .bar::before, .bar::after {
|
.bar, .bar::before, .bar::after {
|
||||||
-webkit-transition: 0.2s background linear 0.1s, 0.2s top linear 0.2s, 0.2s -webkit-transform linear;
|
|
||||||
transition: 0.2s background linear 0.1s, 0.2s top linear 0.2s, 0.2s -webkit-transform linear;
|
|
||||||
transition: 0.2s background linear 0.1s, 0.2s top linear 0.2s, 0.2s transform linear;
|
|
||||||
transition: 0.2s background linear 0.1s, 0.2s top linear 0.2s, 0.2s transform linear, 0.2s -webkit-transform linear;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: #fff;
|
|
||||||
margin: auto;
|
|
||||||
width: 100%;
|
|
||||||
height: 0.3rem;
|
|
||||||
content: '';
|
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 0.3rem;
|
||||||
|
margin: auto;
|
||||||
|
background: #fff;
|
||||||
|
content: '';
|
||||||
|
-webkit-transition: 0.2s background linear 0.1s, 0.2s top linear 0.2s, 0.2s -webkit-transform linear;
|
||||||
|
transition: 0.2s background linear 0.1s, 0.2s top linear 0.2s, 0.2s transform linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bar {
|
.bar {
|
||||||
@@ -366,21 +368,21 @@ Radio Buttons
|
|||||||
}
|
}
|
||||||
|
|
||||||
.radio input[type="radio"] + .radio-label:before {
|
.radio input[type="radio"] + .radio-label:before {
|
||||||
content: '';
|
position: relative;
|
||||||
background: #f4f4f4;
|
top: -0.2em;
|
||||||
border-radius: 100%;
|
|
||||||
border: 1px solid #b4b4b4;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1.4em;
|
width: 1.4em;
|
||||||
height: 1.4em;
|
height: 1.4em;
|
||||||
position: relative;
|
|
||||||
top: -0.2em;
|
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
vertical-align: top;
|
border: 1px solid #b4b4b4;
|
||||||
cursor: pointer;
|
border-radius: 100%;
|
||||||
|
background: #f4f4f4;
|
||||||
|
content: '';
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
-webkit-transition: all 250ms ease;
|
-webkit-transition: all 250ms ease;
|
||||||
transition: all 250ms ease;
|
transition: all 250ms ease;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio input[type="radio"]:checked + .radio-label:before {
|
.radio input[type="radio"]:checked + .radio-label:before {
|
||||||
@@ -390,17 +392,72 @@ Radio Buttons
|
|||||||
}
|
}
|
||||||
|
|
||||||
.radio input[type="radio"]:focus + .radio-label:before {
|
.radio input[type="radio"]:focus + .radio-label:before {
|
||||||
outline: none;
|
|
||||||
border-color: #3197EE;
|
border-color: #3197EE;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio input[type="radio"]:disabled + .radio-label:before {
|
.radio input[type="radio"]:disabled + .radio-label:before {
|
||||||
-webkit-box-shadow: inset 0 0 0 4px #f4f4f4;
|
|
||||||
box-shadow: inset 0 0 0 4px #f4f4f4;
|
|
||||||
border-color: #b4b4b4;
|
border-color: #b4b4b4;
|
||||||
background: #b4b4b4;
|
background: #b4b4b4;
|
||||||
|
-webkit-box-shadow: inset 0 0 0 4px #f4f4f4;
|
||||||
|
box-shadow: inset 0 0 0 4px #f4f4f4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio input[type="radio"] + .radio-label:empty:before {
|
.radio input[type="radio"] + .radio-label:empty:before {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Game */
|
||||||
|
.game-container {
|
||||||
|
display: flex;
|
||||||
|
height: calc(100vh - 40px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-instance {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score {
|
||||||
|
margin: 10px 0;
|
||||||
|
font-size: 40px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.score.update {
|
||||||
|
animation: scoreUpdate .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-size: 25px;
|
||||||
|
text-align: center;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canvas-container {
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 80%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tetris-hold {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tetris-arena, .tetris-background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 20%;
|
||||||
|
width: 60%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tetris-upcoming {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user