/**** * Classes ****/ var TetrominoBlock = Container.expand(function (type, i, j, blockSize) { var self = Container.call(this); var blockGraphic = self.createAsset('tetromino_' + type, type + ' Tetromino block', 0.5, 0.5); self.x = j * blockSize; self.y = i * blockSize; }); // Define Tetromino class var Tetromino = Container.expand(function () { var self = Container.call(this); Tetromino.prototype.checkCollision = function (dx, dy, board) { for (var i = 0; i < this.blocks.length; i++) { var block = this.blocks[i]; var newX = this.x + block.x + dx; var newY = this.y + block.y + dy; var gridX = Math.floor(newX / blockSize); var gridY = Math.floor(newY / blockSize); if (gridX < 0 || gridX >= boardWidth || gridY >= boardHeight) { return true; // Collision with board boundaries } if (gridY >= 0 && board.grid[gridY] && board.grid[gridY][gridX]) { return true; // Collision with another block } } return false; }; this.blocks = []; this.type = ''; this.rotationIndex = 0; this.initializeBlocks = function (layout) { this.initializeBlocks = function (layout, blockSize) { for (var i = 0; i < layout.length; i++) { for (var j = 0; j < layout[i].length; j++) { if (layout[i][j]) { var block = new TetrominoBlock(this.type, i, j, blockSize); this.blocks.push(block); this.addChild(block); } } } }; }; this.create = function (type, blockSize) { this.type = type; this.rotationIndex = 0; var layout = tetrominoLayouts[type][this.rotationIndex]; this.initializeBlocks(layout, blockSize); }; this.rotate = function () { var oldRotationIndex = this.rotationIndex; var newRotationIndex = (this.rotationIndex + 1) % tetrominoLayouts[this.type].length; this.rotationIndex = newRotationIndex; this.updateBlockPositions(); if (this.checkCollision(0, 0, board)) { this.rotationIndex = oldRotationIndex; this.updateBlockPositions(); } }; this.updateBlockPositions = function () { var layout = tetrominoLayouts[this.type][this.rotationIndex]; var k = 0; for (var i = 0; i < layout.length; i++) { for (var j = 0; j < layout[i].length; j++) { if (layout[i][j]) { this.blocks[k].x = j * this.blocks[k].width; this.blocks[k].y = i * this.blocks[k].height; k++; } } } }; this.move = function (dx, dy) { if (!this.checkCollision(dx, dy, board)) { this.x += dx; this.y += dy; } else if (dy > 0) { board.fixTetromino(this); spawnTetromino(); } }; }); // Define GameBoard class var GameBoard = Container.expand(function () { var self = Container.call(this); this.grid = []; this.init = function () { for (var i = 0; i < boardHeight; i++) { this.grid[i] = []; for (var j = 0; j < boardWidth; j++) { this.grid[i][j] = null; } } }; this.fixTetromino = function (tetromino) { var blocks = tetromino.blocks; for (var i = 0; i < blocks.length; i++) { var block = blocks[i]; var x = Math.round((block.x + tetromino.x - this.x) / blockSize); var y = Math.round((block.y + tetromino.y - this.y) / blockSize); if (y < boardHeight && x < boardWidth) { this.grid[y][x] = block; block.x = x * blockSize; block.y = y * blockSize; this.addChild(block); } else if (y < 0) { LK.showGameOver(); return false; } } this.checkLines(); return true; }; this.isGameOver = function () { for (var x = 0; x < boardWidth; x++) { if (this.grid[0][x] !== null) { return true; } } return false; }; this.checkLines = function () { for (var y = 0; y < boardHeight; y++) { var lineComplete = true; for (var x = 0; x < boardWidth; x++) { if (this.grid[y][x] === null) { lineComplete = false; break; } } if (lineComplete) { for (var row = y; row > 0; row--) { for (var x = 0; x < boardWidth; x++) { this.grid[row][x] = this.grid[row - 1][x]; if (this.grid[row][x]) { this.grid[row][x].y += blockSize; } } } for (var x = 0; x < boardWidth; x++) { this.grid[0][x] = null; } for (var x = 0; x < boardWidth; x++) { if (this.grid[y][x]) { this.grid[y][x].destroy(); this.grid[y][x] = null; } } var linesCleared = 0; for (var y = 0; y < boardHeight; y++) { var lineComplete = true; for (var x = 0; x < boardWidth; x++) { if (this.grid[y][x] === null) { lineComplete = false; break; } } if (lineComplete) { linesCleared++; for (var row = y; row > 0; row--) { for (var x = 0; x < boardWidth; x++) { this.grid[row][x] = this.grid[row - 1][x]; if (this.grid[row][x]) { this.grid[row][x].y += blockSize; } } } for (var x = 0; x < boardWidth; x++) { this.grid[0][x] = null; } for (var x = 0; x < boardWidth; x++) { if (this.grid[y][x]) { this.grid[y][x].destroy(); this.grid[y][x] = null; } } } } if (linesCleared > 0) { var scoreToAdd = linesCleared === 1 ? 100 : linesCleared === 2 ? 300 : linesCleared === 3 ? 500 : 800; LK.setScore(LK.getScore() + scoreToAdd); gameUI.updateScore(LK.getScore()); } } } }; this.init(); }); // Define LowerField class for collecting Tetrominoes var LowerField = Container.expand(function () { var self = Container.call(this); this.init = function () { this.grid = []; for (var i = 0; i < boardHeight; i++) { this.grid[i] = []; for (var j = 0; j < boardWidth; j++) { this.grid[i][j] = null; } } }; this.init(); this.addBlock = function (block, x, y) { if (y < boardHeight && x < boardWidth) { this.grid[y][x] = block; block.x = x * blockSize; block.y = y * blockSize; this.addChild(block); } }; }); // Define GameMenu class var GameMenu = Container.expand(function () { var self = Container.call(this); this.startButton = new Text2('Start', { size: 150, fill: "#ffffff", anchor: { x: 0.5, y: 0.5 } }); this.startButton.on('down', function () { GameState.initialize(); self.visible = false; }); this.pauseButton = new Text2('Pause', { size: 150, fill: "#ffffff", anchor: { x: 0.5, y: 0.5 } }); this.pauseButton.on('down', function () { GameState.state = 'paused'; }); this.resumeButton = new Text2('Resume', { size: 150, fill: "#ffffff", anchor: { x: 0.5, y: 0.5 } }); this.resumeButton.on('down', function () { GameState.state = 'active'; }); this.restartButton = new Text2('Restart', { size: 150, fill: "#ffffff", anchor: { x: 0.5, y: 0.5 } }); this.restartButton.on('down', function () { GameState.initialize(); }); LK.gui.bottom.addChild(this.startButton); LK.gui.bottom.addChild(this.pauseButton); LK.gui.bottom.addChild(this.resumeButton); LK.gui.bottom.addChild(this.restartButton); this.pauseButton.visible = false; this.resumeButton.visible = false; this.restartButton.visible = false; }); // Create a new GameMenu instance // Create score display var ScoreDisplay = Container.expand(function () { var self = Container.call(this); this.scoreText = new Text2('Score: 0', { size: 100, fill: "#ffffff", anchor: { x: 0.5, y: 0 } }); this.levelText = new Text2('Level: 1', { size: 100, fill: "#ffffff", anchor: { x: 0.5, y: 0 } }); this.linesText = new Text2('Lines: 0', { size: 100, fill: "#ffffff", anchor: { x: 0.5, y: 0 } }); LK.gui.top.addChild(this.scoreText); LK.gui.top.addChild(this.levelText); LK.gui.top.addChild(this.linesText); this.levelText.y = this.scoreText.height; this.linesText.y = this.scoreText.height * 2; this.updateScore = function (score) { this.scoreText.setText('Score: ' + score.toString()); }; this.updateLevel = function (level) { this.levelText.setText('Level: ' + level.toString()); }; this.updateLinesCleared = function (lines) { this.linesText.setText('Lines: ' + lines.toString()); }; }); // Create a new ScoreDisplay instance var GameUI = Container.expand(function () { var self = Container.call(this); this.scoreText = new Text2('Score: 0', { size: 100, fill: "#ffffff", anchor: { x: 0.5, y: 0 } }); this.levelText = new Text2('Level: 1', { size: 100, fill: "#ffffff", anchor: { x: 0.5, y: 0 } }); this.nextTetrominoPreview = new Container(); LK.gui.top.addChild(this.scoreText); LK.gui.top.addChild(this.levelText); LK.gui.topRight.addChild(this.nextTetrominoPreview); this.scoreText.y = 20; this.levelText.y = this.scoreText.height + 20; this.nextTetrominoPreview.y = 20; this.updateScore = function (score) { this.scoreText.setText('Score: ' + score.toString()); }; this.updateLevel = function (level) { this.levelText.setText('Level: ' + level.toString()); }; this.updateNextTetrominoPreview = function (type) { this.nextTetrominoPreview.removeChildren(); var layout = GameState.tetrominoLayouts[type][0]; for (var i = 0; i < layout.length; i++) { for (var j = 0; j < layout[i].length; j++) { if (layout[i][j]) { var block = LK.getAsset('tetromino_' + type + '_preview', 'Next Tetromino block', 0.5, 0.5); block.x = j * (blockSize * 0.5); block.y = i * (blockSize * 0.5); this.nextTetrominoPreview.addChild(block); } } } }; }); /**** * Initialize Game ****/ // Create a new GameUI instance var game = new LK.Game({ backgroundColor: 0x000000 // Init game with black background for better contrast }); /**** * Game Code ****/ // Create a new GameMenu instance var gameMenu = new GameMenu(); // Define game constants and variables var GameState = { boardWidth: 10, boardHeight: 20, blockSize: 2048 / 10, // Calculate block size based on viewable area width fallRate: 1000, // Time in milliseconds between each Tetromino fall step score: 0, // Initialize current score level: 1, // Initialize game level state: 'active', // Game state can be 'paused', 'active', or 'gameOver' grid: [], initializeGrid: function () { for (var i = 0; i < this.boardHeight; i++) { this.grid[i] = []; for (var j = 0; j < this.boardWidth; j++) { this.grid[i][j] = null; // Each cell starts as null (empty) } } } }; GameState.initializeGrid(); // Initialize the first Tetromino spawnTetromino(); // Define game constants and variables var TetrominoMovement = { calculateMove: function calculateMove(tetromino, dx, dy, board) { var newX = tetromino.x + dx; var newY = tetromino.y + dy; var canMove = true; for (var i = 0; i < tetromino.blocks.length; i++) { var block = tetromino.blocks[i]; var gridX = Math.round((block.x + newX - board.x) / blockSize); var gridY = Math.round((block.y + newY - board.y) / blockSize); if (gridX < 0 || gridX >= boardWidth || gridY < 0 || gridY >= boardHeight) { canMove = false; break; } else if (gridY >= 0 && board.grid[gridY] && board.grid[gridY][gridX]) { canMove = false; break; } } return { canMove: canMove, newX: newX, newY: newY }; } }; var boardWidth = 10; var boardHeight = 20; var blockSize = 2048 / boardWidth; // Calculate block size based on viewable area width var fallRate = 1000; // Time in milliseconds between each Tetromino fall step // GameBoard class is now encapsulated and instantiated separately var board = new GameBoard(); game.addChild(board); var GameState = { currentTetromino: null, nextTetrominoType: null, tetrominoTypes: ['I', 'J', 'L', 'O', 'S', 'T', 'Z'], tetrominoLayouts: { 'I': [[[1, 1, 1, 1]]], 'J': [[[1, 0, 0], [1, 1, 1]]], 'L': [[[0, 0, 1], [1, 1, 1]]], 'O': [[[1, 1], [1, 1]]], 'S': [[[0, 1, 1], [1, 1, 0]]], 'T': [[[0, 1, 0], [1, 1, 1]]], 'Z': [[[1, 1, 0], [0, 1, 1]]] }, scoreTxt: null, isGameOver: false, initialize: function () { this.currentTetromino = null; this.nextTetrominoType = this.tetrominoTypes[Math.floor(Math.random() * this.tetrominoTypes.length)]; this.linesCleared = 0; this.score = 0; this.level = 1; this.state = 'active'; this.spawnTetromino(); gameUI.updateScore(this.score); gameUI.updateLevel(this.level); gameUI.updateLinesCleared(this.linesCleared); gameMenu.startButton.visible = false; gameMenu.pauseButton.visible = true; gameMenu.resumeButton.visible = false; gameMenu.restartButton.visible = true; board.init(); game.addChild(board); board.x = (2048 - boardWidth * blockSize) / 2; board.y = (2732 - boardHeight * blockSize) / 2; game.addChild(gameUI); }, spawnTetromino: function () { GameState.linesCleared = 0; gameUI.updateScore(GameState.score); gameUI.updateLevel(GameState.level); gameUI.updateNextTetrominoPreview(GameState.nextTetrominoType); var type = this.nextTetrominoType != null ? this.nextTetrominoType : this.tetrominoTypes[Math.floor(Math.random() * this.tetrominoTypes.length)]; this.nextTetrominoType = this.tetrominoTypes[Math.floor(Math.random() * this.tetrominoTypes.length)]; this.currentTetromino = new Tetromino(); this.currentTetromino.create(type); this.currentTetromino.x = board.x + (boardWidth * blockSize - this.currentTetromino.width) / 2; this.currentTetromino.y = board.y - this.currentTetromino.height; game.addChild(this.currentTetromino); }, gameOver: function () { this.isGameOver = true; LK.showGameOver(); } }; GameState.initialize(); // Initialize board board.init(); // Center the board on the screen board.x = (2048 - boardWidth * blockSize) / 2; board.y = (2732 - boardHeight * blockSize) / 2; // Create a new GameUI instance var gameUI = new GameUI(); // Function to spawn a new tetromino function spawnTetromino(blockSize) { var type = GameState.nextTetrominoType != null ? GameState.nextTetrominoType : GameState.tetrominoTypes[Math.floor(Math.random() * GameState.tetrominoTypes.length)]; GameState.nextTetrominoType = GameState.tetrominoTypes[Math.floor(Math.random() * GameState.tetrominoTypes.length)]; GameState.currentTetromino = new Tetromino(); GameState.currentTetromino.create(type, blockSize); GameState.currentTetromino.x = board.x + boardWidth / 2 * blockSize - GameState.currentTetromino.width / 2; GameState.currentTetromino.y = board.y; game.addChild(GameState.currentTetromino); } // Start the game with a new tetromino spawnTetromino(); // Implement automatic Tetromino descent // Function to handle user input for moving and rotating the tetromino function handleUserInput() { game.on('down', function (obj) { var touchPos = obj.event.getLocalPosition(game); // Define touch areas for control var leftArea = { x: board.x, width: boardWidth * blockSize / 3 }; var rightArea = { x: board.x + boardWidth * blockSize * 2 / 3, width: boardWidth * blockSize / 3 }; var rotateArea = { x: board.x + boardWidth * blockSize / 3, width: boardWidth * blockSize / 3 }; // Move tetromino left if (touchPos.x >= leftArea.x && touchPos.x < leftArea.x + leftArea.width) { currentTetromino.move(-blockSize, 0); } // Move tetromino right else if (touchPos.x >= rightArea.x && touchPos.x < rightArea.x + rightArea.width) { currentTetromino.move(blockSize, 0); } // Rotate tetromino else if (touchPos.x >= rotateArea.x && touchPos.x < rotateArea.x + rotateArea.width) { currentTetromino.rotate(); } }); } // Game loop LK.on('tick', function () { if (GameState.state === 'paused') { return; } // Handle Tetromino descent if (GameState.currentTetromino && GameState.state === 'active') { if (LK.ticks % (fallRate / (1000 / 60)) === 0) { GameState.currentTetromino.move(0, blockSize); } } // Handle user input for moving and rotating the tetromino handleUserInput(); // Check for completed lines board.checkLines(); // Check for game over if (board.isGameOver()) { GameState.gameOver(); } }); // Function to handle user input for moving and rotating the tetromino function handleUserInput() { game.on('down', function (obj) { var touchPos = obj.event.getLocalPosition(game); var leftArea = { x: 0, width: board.x }; var rightArea = { x: board.x + boardWidth * blockSize, width: 2048 - (board.x + boardWidth * blockSize) }; var rotateArea = { x: board.x, y: board.y, width: boardWidth * blockSize, height: boardHeight * blockSize }; if (touchPos.x < leftArea.width) { GameState.currentTetromino.move(-blockSize, 0); } else if (touchPos.x > rightArea.x) { GameState.currentTetromino.move(blockSize, 0); } else if (touchPos.x >= rotateArea.x && touchPos.x < rotateArea.x + rotateArea.width && touchPos.y >= rotateArea.y && touchPos.y < rotateArea.y + rotateArea.height) { GameState.currentTetromino.rotate(); } }); }
/****
* Classes
****/
var TetrominoBlock = Container.expand(function (type, i, j, blockSize) {
var self = Container.call(this);
var blockGraphic = self.createAsset('tetromino_' + type, type + ' Tetromino block', 0.5, 0.5);
self.x = j * blockSize;
self.y = i * blockSize;
});
// Define Tetromino class
var Tetromino = Container.expand(function () {
var self = Container.call(this);
Tetromino.prototype.checkCollision = function (dx, dy, board) {
for (var i = 0; i < this.blocks.length; i++) {
var block = this.blocks[i];
var newX = this.x + block.x + dx;
var newY = this.y + block.y + dy;
var gridX = Math.floor(newX / blockSize);
var gridY = Math.floor(newY / blockSize);
if (gridX < 0 || gridX >= boardWidth || gridY >= boardHeight) {
return true; // Collision with board boundaries
}
if (gridY >= 0 && board.grid[gridY] && board.grid[gridY][gridX]) {
return true; // Collision with another block
}
}
return false;
};
this.blocks = [];
this.type = '';
this.rotationIndex = 0;
this.initializeBlocks = function (layout) {
this.initializeBlocks = function (layout, blockSize) {
for (var i = 0; i < layout.length; i++) {
for (var j = 0; j < layout[i].length; j++) {
if (layout[i][j]) {
var block = new TetrominoBlock(this.type, i, j, blockSize);
this.blocks.push(block);
this.addChild(block);
}
}
}
};
};
this.create = function (type, blockSize) {
this.type = type;
this.rotationIndex = 0;
var layout = tetrominoLayouts[type][this.rotationIndex];
this.initializeBlocks(layout, blockSize);
};
this.rotate = function () {
var oldRotationIndex = this.rotationIndex;
var newRotationIndex = (this.rotationIndex + 1) % tetrominoLayouts[this.type].length;
this.rotationIndex = newRotationIndex;
this.updateBlockPositions();
if (this.checkCollision(0, 0, board)) {
this.rotationIndex = oldRotationIndex;
this.updateBlockPositions();
}
};
this.updateBlockPositions = function () {
var layout = tetrominoLayouts[this.type][this.rotationIndex];
var k = 0;
for (var i = 0; i < layout.length; i++) {
for (var j = 0; j < layout[i].length; j++) {
if (layout[i][j]) {
this.blocks[k].x = j * this.blocks[k].width;
this.blocks[k].y = i * this.blocks[k].height;
k++;
}
}
}
};
this.move = function (dx, dy) {
if (!this.checkCollision(dx, dy, board)) {
this.x += dx;
this.y += dy;
} else if (dy > 0) {
board.fixTetromino(this);
spawnTetromino();
}
};
});
// Define GameBoard class
var GameBoard = Container.expand(function () {
var self = Container.call(this);
this.grid = [];
this.init = function () {
for (var i = 0; i < boardHeight; i++) {
this.grid[i] = [];
for (var j = 0; j < boardWidth; j++) {
this.grid[i][j] = null;
}
}
};
this.fixTetromino = function (tetromino) {
var blocks = tetromino.blocks;
for (var i = 0; i < blocks.length; i++) {
var block = blocks[i];
var x = Math.round((block.x + tetromino.x - this.x) / blockSize);
var y = Math.round((block.y + tetromino.y - this.y) / blockSize);
if (y < boardHeight && x < boardWidth) {
this.grid[y][x] = block;
block.x = x * blockSize;
block.y = y * blockSize;
this.addChild(block);
} else if (y < 0) {
LK.showGameOver();
return false;
}
}
this.checkLines();
return true;
};
this.isGameOver = function () {
for (var x = 0; x < boardWidth; x++) {
if (this.grid[0][x] !== null) {
return true;
}
}
return false;
};
this.checkLines = function () {
for (var y = 0; y < boardHeight; y++) {
var lineComplete = true;
for (var x = 0; x < boardWidth; x++) {
if (this.grid[y][x] === null) {
lineComplete = false;
break;
}
}
if (lineComplete) {
for (var row = y; row > 0; row--) {
for (var x = 0; x < boardWidth; x++) {
this.grid[row][x] = this.grid[row - 1][x];
if (this.grid[row][x]) {
this.grid[row][x].y += blockSize;
}
}
}
for (var x = 0; x < boardWidth; x++) {
this.grid[0][x] = null;
}
for (var x = 0; x < boardWidth; x++) {
if (this.grid[y][x]) {
this.grid[y][x].destroy();
this.grid[y][x] = null;
}
}
var linesCleared = 0;
for (var y = 0; y < boardHeight; y++) {
var lineComplete = true;
for (var x = 0; x < boardWidth; x++) {
if (this.grid[y][x] === null) {
lineComplete = false;
break;
}
}
if (lineComplete) {
linesCleared++;
for (var row = y; row > 0; row--) {
for (var x = 0; x < boardWidth; x++) {
this.grid[row][x] = this.grid[row - 1][x];
if (this.grid[row][x]) {
this.grid[row][x].y += blockSize;
}
}
}
for (var x = 0; x < boardWidth; x++) {
this.grid[0][x] = null;
}
for (var x = 0; x < boardWidth; x++) {
if (this.grid[y][x]) {
this.grid[y][x].destroy();
this.grid[y][x] = null;
}
}
}
}
if (linesCleared > 0) {
var scoreToAdd = linesCleared === 1 ? 100 : linesCleared === 2 ? 300 : linesCleared === 3 ? 500 : 800;
LK.setScore(LK.getScore() + scoreToAdd);
gameUI.updateScore(LK.getScore());
}
}
}
};
this.init();
});
// Define LowerField class for collecting Tetrominoes
var LowerField = Container.expand(function () {
var self = Container.call(this);
this.init = function () {
this.grid = [];
for (var i = 0; i < boardHeight; i++) {
this.grid[i] = [];
for (var j = 0; j < boardWidth; j++) {
this.grid[i][j] = null;
}
}
};
this.init();
this.addBlock = function (block, x, y) {
if (y < boardHeight && x < boardWidth) {
this.grid[y][x] = block;
block.x = x * blockSize;
block.y = y * blockSize;
this.addChild(block);
}
};
});
// Define GameMenu class
var GameMenu = Container.expand(function () {
var self = Container.call(this);
this.startButton = new Text2('Start', {
size: 150,
fill: "#ffffff",
anchor: {
x: 0.5,
y: 0.5
}
});
this.startButton.on('down', function () {
GameState.initialize();
self.visible = false;
});
this.pauseButton = new Text2('Pause', {
size: 150,
fill: "#ffffff",
anchor: {
x: 0.5,
y: 0.5
}
});
this.pauseButton.on('down', function () {
GameState.state = 'paused';
});
this.resumeButton = new Text2('Resume', {
size: 150,
fill: "#ffffff",
anchor: {
x: 0.5,
y: 0.5
}
});
this.resumeButton.on('down', function () {
GameState.state = 'active';
});
this.restartButton = new Text2('Restart', {
size: 150,
fill: "#ffffff",
anchor: {
x: 0.5,
y: 0.5
}
});
this.restartButton.on('down', function () {
GameState.initialize();
});
LK.gui.bottom.addChild(this.startButton);
LK.gui.bottom.addChild(this.pauseButton);
LK.gui.bottom.addChild(this.resumeButton);
LK.gui.bottom.addChild(this.restartButton);
this.pauseButton.visible = false;
this.resumeButton.visible = false;
this.restartButton.visible = false;
});
// Create a new GameMenu instance
// Create score display
var ScoreDisplay = Container.expand(function () {
var self = Container.call(this);
this.scoreText = new Text2('Score: 0', {
size: 100,
fill: "#ffffff",
anchor: {
x: 0.5,
y: 0
}
});
this.levelText = new Text2('Level: 1', {
size: 100,
fill: "#ffffff",
anchor: {
x: 0.5,
y: 0
}
});
this.linesText = new Text2('Lines: 0', {
size: 100,
fill: "#ffffff",
anchor: {
x: 0.5,
y: 0
}
});
LK.gui.top.addChild(this.scoreText);
LK.gui.top.addChild(this.levelText);
LK.gui.top.addChild(this.linesText);
this.levelText.y = this.scoreText.height;
this.linesText.y = this.scoreText.height * 2;
this.updateScore = function (score) {
this.scoreText.setText('Score: ' + score.toString());
};
this.updateLevel = function (level) {
this.levelText.setText('Level: ' + level.toString());
};
this.updateLinesCleared = function (lines) {
this.linesText.setText('Lines: ' + lines.toString());
};
});
// Create a new ScoreDisplay instance
var GameUI = Container.expand(function () {
var self = Container.call(this);
this.scoreText = new Text2('Score: 0', {
size: 100,
fill: "#ffffff",
anchor: {
x: 0.5,
y: 0
}
});
this.levelText = new Text2('Level: 1', {
size: 100,
fill: "#ffffff",
anchor: {
x: 0.5,
y: 0
}
});
this.nextTetrominoPreview = new Container();
LK.gui.top.addChild(this.scoreText);
LK.gui.top.addChild(this.levelText);
LK.gui.topRight.addChild(this.nextTetrominoPreview);
this.scoreText.y = 20;
this.levelText.y = this.scoreText.height + 20;
this.nextTetrominoPreview.y = 20;
this.updateScore = function (score) {
this.scoreText.setText('Score: ' + score.toString());
};
this.updateLevel = function (level) {
this.levelText.setText('Level: ' + level.toString());
};
this.updateNextTetrominoPreview = function (type) {
this.nextTetrominoPreview.removeChildren();
var layout = GameState.tetrominoLayouts[type][0];
for (var i = 0; i < layout.length; i++) {
for (var j = 0; j < layout[i].length; j++) {
if (layout[i][j]) {
var block = LK.getAsset('tetromino_' + type + '_preview', 'Next Tetromino block', 0.5, 0.5);
block.x = j * (blockSize * 0.5);
block.y = i * (blockSize * 0.5);
this.nextTetrominoPreview.addChild(block);
}
}
}
};
});
/****
* Initialize Game
****/
// Create a new GameUI instance
var game = new LK.Game({
backgroundColor: 0x000000 // Init game with black background for better contrast
});
/****
* Game Code
****/
// Create a new GameMenu instance
var gameMenu = new GameMenu();
// Define game constants and variables
var GameState = {
boardWidth: 10,
boardHeight: 20,
blockSize: 2048 / 10,
// Calculate block size based on viewable area width
fallRate: 1000,
// Time in milliseconds between each Tetromino fall step
score: 0,
// Initialize current score
level: 1,
// Initialize game level
state: 'active',
// Game state can be 'paused', 'active', or 'gameOver'
grid: [],
initializeGrid: function () {
for (var i = 0; i < this.boardHeight; i++) {
this.grid[i] = [];
for (var j = 0; j < this.boardWidth; j++) {
this.grid[i][j] = null; // Each cell starts as null (empty)
}
}
}
};
GameState.initializeGrid();
// Initialize the first Tetromino
spawnTetromino();
// Define game constants and variables
var TetrominoMovement = {
calculateMove: function calculateMove(tetromino, dx, dy, board) {
var newX = tetromino.x + dx;
var newY = tetromino.y + dy;
var canMove = true;
for (var i = 0; i < tetromino.blocks.length; i++) {
var block = tetromino.blocks[i];
var gridX = Math.round((block.x + newX - board.x) / blockSize);
var gridY = Math.round((block.y + newY - board.y) / blockSize);
if (gridX < 0 || gridX >= boardWidth || gridY < 0 || gridY >= boardHeight) {
canMove = false;
break;
} else if (gridY >= 0 && board.grid[gridY] && board.grid[gridY][gridX]) {
canMove = false;
break;
}
}
return {
canMove: canMove,
newX: newX,
newY: newY
};
}
};
var boardWidth = 10;
var boardHeight = 20;
var blockSize = 2048 / boardWidth; // Calculate block size based on viewable area width
var fallRate = 1000; // Time in milliseconds between each Tetromino fall step
// GameBoard class is now encapsulated and instantiated separately
var board = new GameBoard();
game.addChild(board);
var GameState = {
currentTetromino: null,
nextTetrominoType: null,
tetrominoTypes: ['I', 'J', 'L', 'O', 'S', 'T', 'Z'],
tetrominoLayouts: {
'I': [[[1, 1, 1, 1]]],
'J': [[[1, 0, 0], [1, 1, 1]]],
'L': [[[0, 0, 1], [1, 1, 1]]],
'O': [[[1, 1], [1, 1]]],
'S': [[[0, 1, 1], [1, 1, 0]]],
'T': [[[0, 1, 0], [1, 1, 1]]],
'Z': [[[1, 1, 0], [0, 1, 1]]]
},
scoreTxt: null,
isGameOver: false,
initialize: function () {
this.currentTetromino = null;
this.nextTetrominoType = this.tetrominoTypes[Math.floor(Math.random() * this.tetrominoTypes.length)];
this.linesCleared = 0;
this.score = 0;
this.level = 1;
this.state = 'active';
this.spawnTetromino();
gameUI.updateScore(this.score);
gameUI.updateLevel(this.level);
gameUI.updateLinesCleared(this.linesCleared);
gameMenu.startButton.visible = false;
gameMenu.pauseButton.visible = true;
gameMenu.resumeButton.visible = false;
gameMenu.restartButton.visible = true;
board.init();
game.addChild(board);
board.x = (2048 - boardWidth * blockSize) / 2;
board.y = (2732 - boardHeight * blockSize) / 2;
game.addChild(gameUI);
},
spawnTetromino: function () {
GameState.linesCleared = 0;
gameUI.updateScore(GameState.score);
gameUI.updateLevel(GameState.level);
gameUI.updateNextTetrominoPreview(GameState.nextTetrominoType);
var type = this.nextTetrominoType != null ? this.nextTetrominoType : this.tetrominoTypes[Math.floor(Math.random() * this.tetrominoTypes.length)];
this.nextTetrominoType = this.tetrominoTypes[Math.floor(Math.random() * this.tetrominoTypes.length)];
this.currentTetromino = new Tetromino();
this.currentTetromino.create(type);
this.currentTetromino.x = board.x + (boardWidth * blockSize - this.currentTetromino.width) / 2;
this.currentTetromino.y = board.y - this.currentTetromino.height;
game.addChild(this.currentTetromino);
},
gameOver: function () {
this.isGameOver = true;
LK.showGameOver();
}
};
GameState.initialize();
// Initialize board
board.init();
// Center the board on the screen
board.x = (2048 - boardWidth * blockSize) / 2;
board.y = (2732 - boardHeight * blockSize) / 2;
// Create a new GameUI instance
var gameUI = new GameUI();
// Function to spawn a new tetromino
function spawnTetromino(blockSize) {
var type = GameState.nextTetrominoType != null ? GameState.nextTetrominoType : GameState.tetrominoTypes[Math.floor(Math.random() * GameState.tetrominoTypes.length)];
GameState.nextTetrominoType = GameState.tetrominoTypes[Math.floor(Math.random() * GameState.tetrominoTypes.length)];
GameState.currentTetromino = new Tetromino();
GameState.currentTetromino.create(type, blockSize);
GameState.currentTetromino.x = board.x + boardWidth / 2 * blockSize - GameState.currentTetromino.width / 2;
GameState.currentTetromino.y = board.y;
game.addChild(GameState.currentTetromino);
}
// Start the game with a new tetromino
spawnTetromino();
// Implement automatic Tetromino descent
// Function to handle user input for moving and rotating the tetromino
function handleUserInput() {
game.on('down', function (obj) {
var touchPos = obj.event.getLocalPosition(game);
// Define touch areas for control
var leftArea = {
x: board.x,
width: boardWidth * blockSize / 3
};
var rightArea = {
x: board.x + boardWidth * blockSize * 2 / 3,
width: boardWidth * blockSize / 3
};
var rotateArea = {
x: board.x + boardWidth * blockSize / 3,
width: boardWidth * blockSize / 3
};
// Move tetromino left
if (touchPos.x >= leftArea.x && touchPos.x < leftArea.x + leftArea.width) {
currentTetromino.move(-blockSize, 0);
}
// Move tetromino right
else if (touchPos.x >= rightArea.x && touchPos.x < rightArea.x + rightArea.width) {
currentTetromino.move(blockSize, 0);
}
// Rotate tetromino
else if (touchPos.x >= rotateArea.x && touchPos.x < rotateArea.x + rotateArea.width) {
currentTetromino.rotate();
}
});
}
// Game loop
LK.on('tick', function () {
if (GameState.state === 'paused') {
return;
}
// Handle Tetromino descent
if (GameState.currentTetromino && GameState.state === 'active') {
if (LK.ticks % (fallRate / (1000 / 60)) === 0) {
GameState.currentTetromino.move(0, blockSize);
}
}
// Handle user input for moving and rotating the tetromino
handleUserInput();
// Check for completed lines
board.checkLines();
// Check for game over
if (board.isGameOver()) {
GameState.gameOver();
}
});
// Function to handle user input for moving and rotating the tetromino
function handleUserInput() {
game.on('down', function (obj) {
var touchPos = obj.event.getLocalPosition(game);
var leftArea = {
x: 0,
width: board.x
};
var rightArea = {
x: board.x + boardWidth * blockSize,
width: 2048 - (board.x + boardWidth * blockSize)
};
var rotateArea = {
x: board.x,
y: board.y,
width: boardWidth * blockSize,
height: boardHeight * blockSize
};
if (touchPos.x < leftArea.width) {
GameState.currentTetromino.move(-blockSize, 0);
} else if (touchPos.x > rightArea.x) {
GameState.currentTetromino.move(blockSize, 0);
} else if (touchPos.x >= rotateArea.x && touchPos.x < rotateArea.x + rotateArea.width && touchPos.y >= rotateArea.y && touchPos.y < rotateArea.y + rotateArea.height) {
GameState.currentTetromino.rotate();
}
});
}