create-template-html-css 1.9.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/README.md +179 -2
- package/bin/cli.js +15 -3
- package/package.json +1 -1
- package/templates/blackjack/index.html +97 -0
- package/templates/blackjack/script.js +381 -0
- package/templates/blackjack/style.css +452 -0
- package/templates/breakout/index.html +56 -0
- package/templates/breakout/script.js +387 -0
- package/templates/breakout/style.css +239 -0
- package/templates/connect-four/index.html +78 -0
- package/templates/connect-four/script.js +413 -0
- package/templates/connect-four/style.css +426 -0
- package/templates/dice-game/index.html +99 -0
- package/templates/dice-game/script.js +291 -0
- package/templates/dice-game/style.css +403 -0
- package/templates/flappy-bird/index.html +47 -0
- package/templates/flappy-bird/script.js +394 -0
- package/templates/flappy-bird/style.css +243 -0
- package/templates/game-2048/index.html +59 -0
- package/templates/game-2048/script.js +269 -0
- package/templates/game-2048/style.css +281 -0
- package/templates/pong/index.html +90 -0
- package/templates/pong/script.js +364 -0
- package/templates/pong/style.css +371 -0
- package/templates/rock-paper-scissors/index.html +84 -0
- package/templates/rock-paper-scissors/script.js +199 -0
- package/templates/rock-paper-scissors/style.css +295 -0
- package/templates/simon-says/index.html +64 -0
- package/templates/simon-says/script.js +206 -0
- package/templates/simon-says/style.css +250 -0
- package/templates/slot-machine/index.html +112 -0
- package/templates/slot-machine/script.js +238 -0
- package/templates/slot-machine/style.css +464 -0
- package/templates/tetris/index.html +84 -0
- package/templates/tetris/script.js +447 -0
- package/templates/tetris/style.css +286 -0
- package/templates/whack-a-mole/index.html +85 -0
- package/templates/whack-a-mole/script.js +172 -0
- package/{demo-games/snake-game → templates/whack-a-mole}/style.css +114 -97
- package/COMPONENTS-GALLERY.html +0 -773
- package/PUBLISH-GUIDE.md +0 -112
- package/create-template-html-css-1.8.0.tgz +0 -0
- package/demo-games/guess-number/index.html +0 -71
- package/demo-games/guess-number/script.js +0 -216
- package/demo-games/guess-number/style.css +0 -337
- package/demo-games/memory-game/index.html +0 -50
- package/demo-games/memory-game/script.js +0 -216
- package/demo-games/memory-game/style.css +0 -288
- package/demo-games/snake-game/index.html +0 -61
- package/demo-games/snake-game/script.js +0 -360
- package/demo-games/tic-tac-toe/index.html +0 -57
- package/demo-games/tic-tac-toe/script.js +0 -156
- package/demo-games/tic-tac-toe/style.css +0 -244
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Tetris</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<div class="header">
|
|
12
|
+
<h1>🎮 Tetris</h1>
|
|
13
|
+
<p class="subtitle">The classic game!</p>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="game-container">
|
|
17
|
+
<div class="side-panel">
|
|
18
|
+
<div class="info-card">
|
|
19
|
+
<h3>📊 Score</h3>
|
|
20
|
+
<div class="score-value" id="score">0</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="info-card">
|
|
24
|
+
<h3>🏆 High Score</h3>
|
|
25
|
+
<div class="score-value" id="highScore">0</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div class="info-card">
|
|
29
|
+
<h3>📈 Level</h3>
|
|
30
|
+
<div class="score-value" id="level">1</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="info-card">
|
|
34
|
+
<h3>📏 Lines</h3>
|
|
35
|
+
<div class="score-value" id="lines">0</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<div class="info-card next-piece">
|
|
39
|
+
<h3>🔜 Next</h3>
|
|
40
|
+
<canvas id="nextCanvas"></canvas>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="game-board">
|
|
45
|
+
<canvas id="gameCanvas"></canvas>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div class="controls">
|
|
50
|
+
<button id="startBtn" class="btn btn-primary">Start Game</button>
|
|
51
|
+
<button id="pauseBtn" class="btn btn-secondary" disabled>Pause</button>
|
|
52
|
+
<button id="resetBtn" class="btn btn-danger">Reset</button>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div class="instructions">
|
|
56
|
+
<h3>⌨️ Controls:</h3>
|
|
57
|
+
<div class="key-instructions">
|
|
58
|
+
<div class="key-item">
|
|
59
|
+
<span class="key">←</span>
|
|
60
|
+
<span>Left</span>
|
|
61
|
+
</div>
|
|
62
|
+
<div class="key-item">
|
|
63
|
+
<span class="key">→</span>
|
|
64
|
+
<span>Right</span>
|
|
65
|
+
</div>
|
|
66
|
+
<div class="key-item">
|
|
67
|
+
<span class="key">↓</span>
|
|
68
|
+
<span>Fast Down</span>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="key-item">
|
|
71
|
+
<span class="key">↑</span>
|
|
72
|
+
<span>Rotate</span>
|
|
73
|
+
</div>
|
|
74
|
+
<div class="key-item">
|
|
75
|
+
<span class="key">Space</span>
|
|
76
|
+
<span>Hard Drop</span>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<script src="script.js"></script>
|
|
83
|
+
</body>
|
|
84
|
+
</html>
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
// Game constants
|
|
2
|
+
const canvas = document.getElementById('gameCanvas');
|
|
3
|
+
const ctx = canvas.getContext('2d');
|
|
4
|
+
const nextCanvas = document.getElementById('nextCanvas');
|
|
5
|
+
const nextCtx = nextCanvas.getContext('2d');
|
|
6
|
+
|
|
7
|
+
const BLOCK_SIZE = 30;
|
|
8
|
+
const ROWS = 20;
|
|
9
|
+
const COLS = 10;
|
|
10
|
+
|
|
11
|
+
canvas.width = COLS * BLOCK_SIZE;
|
|
12
|
+
canvas.height = ROWS * BLOCK_SIZE;
|
|
13
|
+
nextCanvas.width = 4 * BLOCK_SIZE;
|
|
14
|
+
nextCanvas.height = 4 * BLOCK_SIZE;
|
|
15
|
+
|
|
16
|
+
// Tetromino shapes
|
|
17
|
+
const SHAPES = {
|
|
18
|
+
I: [[1, 1, 1, 1]],
|
|
19
|
+
O: [[1, 1], [1, 1]],
|
|
20
|
+
T: [[0, 1, 0], [1, 1, 1]],
|
|
21
|
+
S: [[0, 1, 1], [1, 1, 0]],
|
|
22
|
+
Z: [[1, 1, 0], [0, 1, 1]],
|
|
23
|
+
J: [[1, 0, 0], [1, 1, 1]],
|
|
24
|
+
L: [[0, 0, 1], [1, 1, 1]]
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const COLORS = {
|
|
28
|
+
I: '#00f0f0',
|
|
29
|
+
O: '#f0f000',
|
|
30
|
+
T: '#a000f0',
|
|
31
|
+
S: '#00f000',
|
|
32
|
+
Z: '#f00000',
|
|
33
|
+
J: '#0000f0',
|
|
34
|
+
L: '#f0a000'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Game state
|
|
38
|
+
let gameState = {
|
|
39
|
+
running: false,
|
|
40
|
+
paused: false,
|
|
41
|
+
score: 0,
|
|
42
|
+
level: 1,
|
|
43
|
+
lines: 0,
|
|
44
|
+
highScore: parseInt(localStorage.getItem('tetrisHighScore')) || 0,
|
|
45
|
+
board: [],
|
|
46
|
+
currentPiece: null,
|
|
47
|
+
nextPiece: null,
|
|
48
|
+
dropCounter: 0,
|
|
49
|
+
dropInterval: 1000,
|
|
50
|
+
lastTime: 0
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Initialize board
|
|
54
|
+
function initBoard() {
|
|
55
|
+
gameState.board = Array(ROWS).fill().map(() => Array(COLS).fill(0));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Create new piece
|
|
59
|
+
function createPiece() {
|
|
60
|
+
const shapes = Object.keys(SHAPES);
|
|
61
|
+
const randomShape = shapes[Math.floor(Math.random() * shapes.length)];
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
shape: SHAPES[randomShape],
|
|
65
|
+
color: COLORS[randomShape],
|
|
66
|
+
type: randomShape,
|
|
67
|
+
x: Math.floor(COLS / 2) - Math.floor(SHAPES[randomShape][0].length / 2),
|
|
68
|
+
y: 0
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Draw block
|
|
73
|
+
function drawBlock(x, y, color, context = ctx) {
|
|
74
|
+
context.fillStyle = color;
|
|
75
|
+
context.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
|
|
76
|
+
|
|
77
|
+
context.strokeStyle = 'rgba(0, 0, 0, 0.3)';
|
|
78
|
+
context.lineWidth = 2;
|
|
79
|
+
context.strokeRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
|
|
80
|
+
|
|
81
|
+
// Add shine effect
|
|
82
|
+
context.fillStyle = 'rgba(255, 255, 255, 0.2)';
|
|
83
|
+
context.fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE / 2, BLOCK_SIZE / 2);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Draw board
|
|
87
|
+
function drawBoard() {
|
|
88
|
+
ctx.fillStyle = '#1a202c';
|
|
89
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
90
|
+
|
|
91
|
+
// Draw grid
|
|
92
|
+
ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
|
|
93
|
+
ctx.lineWidth = 1;
|
|
94
|
+
for (let i = 0; i <= ROWS; i++) {
|
|
95
|
+
ctx.beginPath();
|
|
96
|
+
ctx.moveTo(0, i * BLOCK_SIZE);
|
|
97
|
+
ctx.lineTo(COLS * BLOCK_SIZE, i * BLOCK_SIZE);
|
|
98
|
+
ctx.stroke();
|
|
99
|
+
}
|
|
100
|
+
for (let i = 0; i <= COLS; i++) {
|
|
101
|
+
ctx.beginPath();
|
|
102
|
+
ctx.moveTo(i * BLOCK_SIZE, 0);
|
|
103
|
+
ctx.lineTo(i * BLOCK_SIZE, ROWS * BLOCK_SIZE);
|
|
104
|
+
ctx.stroke();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Draw placed blocks
|
|
108
|
+
for (let y = 0; y < ROWS; y++) {
|
|
109
|
+
for (let x = 0; x < COLS; x++) {
|
|
110
|
+
if (gameState.board[y][x]) {
|
|
111
|
+
drawBlock(x, y, gameState.board[y][x]);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Draw piece
|
|
118
|
+
function drawPiece(piece, context = ctx, offsetX = 0, offsetY = 0) {
|
|
119
|
+
piece.shape.forEach((row, y) => {
|
|
120
|
+
row.forEach((value, x) => {
|
|
121
|
+
if (value) {
|
|
122
|
+
drawBlock(piece.x + x + offsetX, piece.y + y + offsetY, piece.color, context);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Draw next piece
|
|
129
|
+
function drawNextPiece() {
|
|
130
|
+
nextCtx.fillStyle = '#1a202c';
|
|
131
|
+
nextCtx.fillRect(0, 0, nextCanvas.width, nextCanvas.height);
|
|
132
|
+
|
|
133
|
+
if (gameState.nextPiece) {
|
|
134
|
+
const offsetX = Math.floor((4 - gameState.nextPiece.shape[0].length) / 2);
|
|
135
|
+
const offsetY = Math.floor((4 - gameState.nextPiece.shape.length) / 2);
|
|
136
|
+
|
|
137
|
+
gameState.nextPiece.shape.forEach((row, y) => {
|
|
138
|
+
row.forEach((value, x) => {
|
|
139
|
+
if (value) {
|
|
140
|
+
drawBlock(offsetX + x, offsetY + y, gameState.nextPiece.color, nextCtx);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Check collision
|
|
148
|
+
function checkCollision(piece, offsetX = 0, offsetY = 0) {
|
|
149
|
+
for (let y = 0; y < piece.shape.length; y++) {
|
|
150
|
+
for (let x = 0; x < piece.shape[y].length; x++) {
|
|
151
|
+
if (piece.shape[y][x]) {
|
|
152
|
+
const newX = piece.x + x + offsetX;
|
|
153
|
+
const newY = piece.y + y + offsetY;
|
|
154
|
+
|
|
155
|
+
if (newX < 0 || newX >= COLS || newY >= ROWS) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (newY >= 0 && gameState.board[newY][newX]) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Rotate piece
|
|
169
|
+
function rotatePiece(piece) {
|
|
170
|
+
const rotated = piece.shape[0].map((_, i) =>
|
|
171
|
+
piece.shape.map(row => row[i]).reverse()
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const newPiece = { ...piece, shape: rotated };
|
|
175
|
+
|
|
176
|
+
if (!checkCollision(newPiece)) {
|
|
177
|
+
piece.shape = rotated;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Move piece
|
|
182
|
+
function movePiece(dx, dy) {
|
|
183
|
+
if (!checkCollision(gameState.currentPiece, dx, dy)) {
|
|
184
|
+
gameState.currentPiece.x += dx;
|
|
185
|
+
gameState.currentPiece.y += dy;
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Drop piece
|
|
192
|
+
function dropPiece() {
|
|
193
|
+
while (movePiece(0, 1)) {}
|
|
194
|
+
lockPiece();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Lock piece
|
|
198
|
+
function lockPiece() {
|
|
199
|
+
gameState.currentPiece.shape.forEach((row, y) => {
|
|
200
|
+
row.forEach((value, x) => {
|
|
201
|
+
if (value) {
|
|
202
|
+
const boardY = gameState.currentPiece.y + y;
|
|
203
|
+
const boardX = gameState.currentPiece.x + x;
|
|
204
|
+
if (boardY >= 0) {
|
|
205
|
+
gameState.board[boardY][boardX] = gameState.currentPiece.color;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
clearLines();
|
|
212
|
+
spawnNewPiece();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Clear completed lines
|
|
216
|
+
function clearLines() {
|
|
217
|
+
let linesCleared = 0;
|
|
218
|
+
|
|
219
|
+
for (let y = ROWS - 1; y >= 0; y--) {
|
|
220
|
+
if (gameState.board[y].every(cell => cell !== 0)) {
|
|
221
|
+
gameState.board.splice(y, 1);
|
|
222
|
+
gameState.board.unshift(Array(COLS).fill(0));
|
|
223
|
+
linesCleared++;
|
|
224
|
+
y++; // Check the same row again
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (linesCleared > 0) {
|
|
229
|
+
gameState.lines += linesCleared;
|
|
230
|
+
|
|
231
|
+
// Score calculation (Tetris scoring system)
|
|
232
|
+
const points = [0, 40, 100, 300, 1200];
|
|
233
|
+
gameState.score += points[linesCleared] * gameState.level;
|
|
234
|
+
|
|
235
|
+
// Level up every 10 lines
|
|
236
|
+
const newLevel = Math.floor(gameState.lines / 10) + 1;
|
|
237
|
+
if (newLevel > gameState.level) {
|
|
238
|
+
gameState.level = newLevel;
|
|
239
|
+
gameState.dropInterval = Math.max(100, 1000 - (gameState.level - 1) * 100);
|
|
240
|
+
updateLevel();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
updateScore();
|
|
244
|
+
updateLines();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Spawn new piece
|
|
249
|
+
function spawnNewPiece() {
|
|
250
|
+
gameState.currentPiece = gameState.nextPiece || createPiece();
|
|
251
|
+
gameState.nextPiece = createPiece();
|
|
252
|
+
|
|
253
|
+
drawNextPiece();
|
|
254
|
+
|
|
255
|
+
if (checkCollision(gameState.currentPiece)) {
|
|
256
|
+
gameOver();
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Game over
|
|
261
|
+
function gameOver() {
|
|
262
|
+
gameState.running = false;
|
|
263
|
+
|
|
264
|
+
if (gameState.score > gameState.highScore) {
|
|
265
|
+
gameState.highScore = gameState.score;
|
|
266
|
+
localStorage.setItem('tetrisHighScore', gameState.highScore);
|
|
267
|
+
updateHighScore();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.8)';
|
|
271
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
272
|
+
|
|
273
|
+
ctx.fillStyle = '#FFFFFF';
|
|
274
|
+
ctx.font = 'bold 36px Arial';
|
|
275
|
+
ctx.textAlign = 'center';
|
|
276
|
+
ctx.fillText('משחק נגמר!', canvas.width / 2, canvas.height / 2 - 30);
|
|
277
|
+
|
|
278
|
+
ctx.font = '20px Arial';
|
|
279
|
+
ctx.fillText(`ניקוד: ${gameState.score}`, canvas.width / 2, canvas.height / 2 + 10);
|
|
280
|
+
|
|
281
|
+
document.getElementById('startBtn').disabled = false;
|
|
282
|
+
document.getElementById('pauseBtn').disabled = true;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Update UI
|
|
286
|
+
function updateScore() {
|
|
287
|
+
document.getElementById('score').textContent = gameState.score;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function updateLines() {
|
|
291
|
+
document.getElementById('lines').textContent = gameState.lines;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function updateLevel() {
|
|
295
|
+
document.getElementById('level').textContent = gameState.level;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function updateHighScore() {
|
|
299
|
+
document.getElementById('highScore').textContent = gameState.highScore;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Draw game
|
|
303
|
+
function draw() {
|
|
304
|
+
drawBoard();
|
|
305
|
+
|
|
306
|
+
if (gameState.currentPiece) {
|
|
307
|
+
// Draw ghost piece (shadow)
|
|
308
|
+
const ghostPiece = { ...gameState.currentPiece };
|
|
309
|
+
while (!checkCollision(ghostPiece, 0, 1)) {
|
|
310
|
+
ghostPiece.y++;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
ctx.globalAlpha = 0.2;
|
|
314
|
+
drawPiece(ghostPiece);
|
|
315
|
+
ctx.globalAlpha = 1.0;
|
|
316
|
+
|
|
317
|
+
// Draw current piece
|
|
318
|
+
drawPiece(gameState.currentPiece);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (gameState.paused) {
|
|
322
|
+
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
|
|
323
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
324
|
+
ctx.fillStyle = '#FFFFFF';
|
|
325
|
+
ctx.font = 'bold 28px Arial';
|
|
326
|
+
ctx.textAlign = 'center';
|
|
327
|
+
ctx.fillText('משחק מושהה', canvas.width / 2, canvas.height / 2);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Game loop
|
|
332
|
+
function gameLoop(time = 0) {
|
|
333
|
+
if (!gameState.running) return;
|
|
334
|
+
|
|
335
|
+
const deltaTime = time - gameState.lastTime;
|
|
336
|
+
gameState.lastTime = time;
|
|
337
|
+
|
|
338
|
+
if (!gameState.paused) {
|
|
339
|
+
gameState.dropCounter += deltaTime;
|
|
340
|
+
|
|
341
|
+
if (gameState.dropCounter > gameState.dropInterval) {
|
|
342
|
+
if (!movePiece(0, 1)) {
|
|
343
|
+
lockPiece();
|
|
344
|
+
}
|
|
345
|
+
gameState.dropCounter = 0;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
draw();
|
|
350
|
+
requestAnimationFrame(gameLoop);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Keyboard controls
|
|
354
|
+
document.addEventListener('keydown', (e) => {
|
|
355
|
+
if (!gameState.running || gameState.paused) return;
|
|
356
|
+
|
|
357
|
+
switch (e.key) {
|
|
358
|
+
case 'ArrowLeft':
|
|
359
|
+
movePiece(-1, 0);
|
|
360
|
+
break;
|
|
361
|
+
case 'ArrowRight':
|
|
362
|
+
movePiece(1, 0);
|
|
363
|
+
break;
|
|
364
|
+
case 'ArrowDown':
|
|
365
|
+
if (movePiece(0, 1)) {
|
|
366
|
+
gameState.score += 1;
|
|
367
|
+
updateScore();
|
|
368
|
+
}
|
|
369
|
+
gameState.dropCounter = 0;
|
|
370
|
+
break;
|
|
371
|
+
case 'ArrowUp':
|
|
372
|
+
rotatePiece(gameState.currentPiece);
|
|
373
|
+
break;
|
|
374
|
+
case ' ':
|
|
375
|
+
e.preventDefault();
|
|
376
|
+
dropPiece();
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
draw();
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
// Start game
|
|
384
|
+
function startGame() {
|
|
385
|
+
gameState.running = true;
|
|
386
|
+
gameState.paused = false;
|
|
387
|
+
gameState.score = 0;
|
|
388
|
+
gameState.level = 1;
|
|
389
|
+
gameState.lines = 0;
|
|
390
|
+
gameState.dropCounter = 0;
|
|
391
|
+
gameState.dropInterval = 1000;
|
|
392
|
+
gameState.lastTime = 0;
|
|
393
|
+
|
|
394
|
+
initBoard();
|
|
395
|
+
gameState.nextPiece = createPiece();
|
|
396
|
+
spawnNewPiece();
|
|
397
|
+
|
|
398
|
+
updateScore();
|
|
399
|
+
updateLines();
|
|
400
|
+
updateLevel();
|
|
401
|
+
|
|
402
|
+
document.getElementById('startBtn').disabled = true;
|
|
403
|
+
document.getElementById('pauseBtn').disabled = false;
|
|
404
|
+
|
|
405
|
+
gameLoop();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Pause game
|
|
409
|
+
function pauseGame() {
|
|
410
|
+
gameState.paused = !gameState.paused;
|
|
411
|
+
document.getElementById('pauseBtn').textContent = gameState.paused ? 'המשך' : 'השהה';
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Reset game
|
|
415
|
+
function resetGame() {
|
|
416
|
+
gameState.running = false;
|
|
417
|
+
gameState.paused = false;
|
|
418
|
+
gameState.score = 0;
|
|
419
|
+
gameState.level = 1;
|
|
420
|
+
gameState.lines = 0;
|
|
421
|
+
|
|
422
|
+
initBoard();
|
|
423
|
+
gameState.currentPiece = null;
|
|
424
|
+
gameState.nextPiece = null;
|
|
425
|
+
|
|
426
|
+
updateScore();
|
|
427
|
+
updateLines();
|
|
428
|
+
updateLevel();
|
|
429
|
+
|
|
430
|
+
draw();
|
|
431
|
+
drawNextPiece();
|
|
432
|
+
|
|
433
|
+
document.getElementById('startBtn').disabled = false;
|
|
434
|
+
document.getElementById('pauseBtn').disabled = true;
|
|
435
|
+
document.getElementById('pauseBtn').textContent = 'השהה';
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Event listeners
|
|
439
|
+
document.getElementById('startBtn').addEventListener('click', startGame);
|
|
440
|
+
document.getElementById('pauseBtn').addEventListener('click', pauseGame);
|
|
441
|
+
document.getElementById('resetBtn').addEventListener('click', resetGame);
|
|
442
|
+
|
|
443
|
+
// Initialize
|
|
444
|
+
updateHighScore();
|
|
445
|
+
initBoard();
|
|
446
|
+
draw();
|
|
447
|
+
drawNextPiece();
|