/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Block = Container.expand(function (blockType) { var self = Container.call(this); self.blockType = blockType || 'block_I'; self.gridX = 0; self.gridY = 0; var blockGraphics = self.attachAsset(self.blockType, { anchorX: 0, anchorY: 0 }); self.setGridPosition = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = GRID_OFFSET_X + gridX * BLOCK_SIZE; self.y = GRID_OFFSET_Y + gridY * BLOCK_SIZE; }; return self; }); var Tetromino = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'I'; self.rotation = 0; self.gridX = 7; // Start in middle of board self.gridY = 0; self.blocks = []; self.fallTimer = 0; self.fallSpeed = 40; // Frames until next fall (faster falling speed) self.isPlaced = false; // Prevent multiple placement calls // Tetromino shapes and rotations self.shapes = { 'I': [[[1, 1, 1, 1]], [[1], [1], [1], [1]], [[1, 1, 1, 1]], [[1], [1], [1], [1]]], 'O': [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]], 'T': [[[0, 1, 0], [1, 1, 1]], [[1, 0], [1, 1], [1, 0]], [[1, 1, 1], [0, 1, 0]], [[0, 1], [1, 1], [0, 1]]], 'S': [[[0, 1, 1], [1, 1, 0]], [[1, 0], [1, 1], [0, 1]], [[0, 1, 1], [1, 1, 0]], [[1, 0], [1, 1], [0, 1]]], 'Z': [[[1, 1, 0], [0, 1, 1]], [[0, 1], [1, 1], [1, 0]], [[1, 1, 0], [0, 1, 1]], [[0, 1], [1, 1], [1, 0]]], 'J': [[[1, 0, 0], [1, 1, 1]], [[1, 1], [1, 0], [1, 0]], [[1, 1, 1], [0, 0, 1]], [[0, 1], [0, 1], [1, 1]]], 'L': [[[0, 0, 1], [1, 1, 1]], [[1, 0], [1, 0], [1, 1]], [[1, 1, 1], [1, 0, 0]], [[1, 1], [0, 1], [0, 1]]] }; self.getBlockType = function () { // Map each tetromino type to its corresponding block asset var blockTypeMap = { 'I': 'block_I', 'O': 'block_O', 'T': 'block_T', 'S': 'block_S', 'Z': 'block_Z', 'J': 'block_J', 'L': 'block_L' }; return blockTypeMap[self.type] || 'block_I'; }; self.createBlocks = function () { // Clear existing blocks for (var i = 0; i < self.blocks.length; i++) { self.blocks[i].destroy(); } self.blocks = []; // Ensure we have a valid shape for this type if (!self.shapes[self.type]) { console.error('Invalid tetromino type:', self.type); return; } var shape = self.shapes[self.type][self.rotation]; var blockType = self.getBlockType(); for (var y = 0; y < shape.length; y++) { for (var x = 0; x < shape[y].length; x++) { if (shape[y][x] === 1) { var block = new Block(blockType); block.setGridPosition(self.gridX + x, self.gridY + y); self.blocks.push(block); game.addChild(block); } } } }; self.updatePosition = function () { var shape = self.shapes[self.type][self.rotation]; var blockIndex = 0; for (var y = 0; y < shape.length; y++) { for (var x = 0; x < shape[y].length; x++) { if (shape[y][x] === 1) { if (blockIndex < self.blocks.length) { self.blocks[blockIndex].setGridPosition(self.gridX + x, self.gridY + y); } blockIndex++; } } } }; self.canMoveTo = function (newX, newY, newRotation) { var testRotation = newRotation !== undefined ? newRotation : self.rotation; var shape = self.shapes[self.type][testRotation]; for (var y = 0; y < shape.length; y++) { for (var x = 0; x < shape[y].length; x++) { if (shape[y][x] === 1) { var checkX = newX + x; var checkY = newY + y; // Check bounds if (checkX < 0 || checkX >= GRID_WIDTH || checkY >= GRID_HEIGHT) { return false; } // Check collision with placed blocks if (checkY >= 0 && gameGrid[checkY] && gameGrid[checkY][checkX]) { return false; } } } } return true; }; self.move = function (deltaX, deltaY) { if (self.canMoveTo(self.gridX + deltaX, self.gridY + deltaY)) { self.gridX += deltaX; self.gridY += deltaY; self.updatePosition(); return true; } return false; }; self.rotate = function () { var newRotation = (self.rotation + 1) % 4; if (self.canMoveTo(self.gridX, self.gridY, newRotation)) { self.rotation = newRotation; self.updatePosition(); // Create sparkle effect for rotation for (var i = 0; i < self.blocks.length; i++) { var block = self.blocks[i]; var sparkleCount = 8; for (var j = 0; j < sparkleCount; j++) { var sparkle = LK.getAsset('grid_border', { x: block.x + BLOCK_SIZE / 2, y: block.y + BLOCK_SIZE / 2, scaleX: 5, scaleY: 5, alpha: 1.5, tint: 0xFFFFFF }); game.addChild(sparkle); // Random sparkle position around block var angle = j / sparkleCount * Math.PI * 2; var distance = BLOCK_SIZE * 1.0; var targetX = sparkle.x + Math.cos(angle) * distance; var targetY = sparkle.y + Math.sin(angle) * distance; // Animate sparkle outward and fade tween(sparkle, { x: targetX, y: targetY, alpha: 0.3, scaleX: 0.3, scaleY: 0.3 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { if (sparkle && sparkle.destroy) { sparkle.destroy(); } } }); } } LK.getSound('sparkle_effect').play(); LK.getSound('piece_rotate').play(); return true; } return false; }; self.update = function () { self.fallTimer++; if (self.fallTimer >= self.fallSpeed) { self.fallTimer = 0; if (!self.move(0, 1)) { // Piece has landed self.placePiece(); } } }; self.placePiece = function () { // Prevent multiple calls if (self.isPlaced) return; self.isPlaced = true; // Add blocks to grid and create sparkle effects for (var i = 0; i < self.blocks.length; i++) { var block = self.blocks[i]; if (block.gridY >= 0) { if (!gameGrid[block.gridY]) gameGrid[block.gridY] = []; gameGrid[block.gridY][block.gridX] = block; // Create sparkle effect around the block var sparkleCount = 8; for (var j = 0; j < sparkleCount; j++) { var sparkle = LK.getAsset('grid_border', { x: block.x + BLOCK_SIZE / 2, y: block.y + BLOCK_SIZE / 2, scaleX: 8, scaleY: 8, alpha: 1.5, tint: 0xFFFFFF }); game.addChild(sparkle); // Random sparkle position around block var angle = j / sparkleCount * Math.PI * 2; var distance = BLOCK_SIZE * 1.0; var targetX = sparkle.x + Math.cos(angle) * distance; var targetY = sparkle.y + Math.sin(angle) * distance; // Animate sparkle outward and fade tween(sparkle, { x: targetX, y: targetY, alpha: 0.3, scaleX: 0.3, scaleY: 0.3 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { if (sparkle && sparkle.destroy) { sparkle.destroy(); } } }); } } } // Remove blocks from this tetromino since they're now part of the grid self.blocks = []; LK.getSound('sparkle_effect').play(); LK.getSound('piece_drop').play(); checkCompleteLines(); // Clear current piece reference first currentPiece = null; // Spawn new piece with a small delay to ensure proper sequencing LK.setTimeout(function () { spawnNewPiece(); }, 50); }; self.destroy = function () { // Clean up all blocks when tetromino is destroyed for (var i = 0; i < self.blocks.length; i++) { if (self.blocks[i] && self.blocks[i].destroy) { self.blocks[i].destroy(); } } self.blocks = []; // Call parent destroy Container.prototype.destroy.call(self); }; return self; }); /**** * Initialize Game ****/ // Game constants var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Orange // Blue // Red // Green // Purple // Yellow // Cyan // Tetromino blocks - using different colors for each piece type // Game constants var GRID_WIDTH = 14; var GRID_HEIGHT = 24; var BLOCK_SIZE = 100; var GRID_OFFSET_X = (2048 - GRID_WIDTH * BLOCK_SIZE) / 2; var GRID_OFFSET_Y = 100; // Game variables var gameGrid = []; var currentPiece = null; var nextPieceType = null; var gameLevel = 1; var linesCleared = 0; var lastMoveTime = 0; var moveDelay = 100; // Milliseconds between moves // Initialize grid for (var y = 0; y < GRID_HEIGHT; y++) { gameGrid[y] = []; for (var x = 0; x < GRID_WIDTH; x++) { gameGrid[y][x] = null; } } // Tetromino types var pieceTypes = ['I', 'O', 'T', 'S', 'Z', 'J', 'L']; function getRandomPieceType() { return pieceTypes[Math.floor(Math.random() * pieceTypes.length)]; } function spawnNewPiece() { // Don't spawn if there's already an active piece or if one is being placed if (currentPiece && !currentPiece.isPlaced) { return; } if (!nextPieceType) { nextPieceType = getRandomPieceType(); } // Create new piece and set as current immediately to prevent double spawning var newPiece = new Tetromino(nextPieceType); currentPiece = newPiece; game.addChild(currentPiece); currentPiece.createBlocks(); nextPieceType = getRandomPieceType(); // Check game over if (!currentPiece.canMoveTo(currentPiece.gridX, currentPiece.gridY)) { LK.showGameOver(); return; } } function checkCompleteLines() { var completedLines = []; // Find completed lines for (var y = 0; y < GRID_HEIGHT; y++) { var lineComplete = true; for (var x = 0; x < GRID_WIDTH; x++) { if (!gameGrid[y][x]) { lineComplete = false; break; } } if (lineComplete) { completedLines.push(y); } } if (completedLines.length > 0) { // Sort completed lines from bottom to top (highest Y to lowest Y) // This ensures we process lines from bottom up, keeping indices valid completedLines.sort(function (a, b) { return b - a; }); // Remove completed lines for (var i = 0; i < completedLines.length; i++) { var lineY = completedLines[i]; // Create particle explosion effect for this line for (var x = 0; x < GRID_WIDTH; x++) { if (gameGrid[lineY][x]) { var block = gameGrid[lineY][x]; // Create sparkle effect for cleared line blocks var sparkleCount = 8; //{2X_new} for (var j = 0; j < sparkleCount; j++) { //{2X_new2} var sparkle = LK.getAsset('grid_border', { //{2X_new3} x: block.x + BLOCK_SIZE / 2, //{2X_new4} y: block.y + BLOCK_SIZE / 2, //{2X_new5} scaleX: 5, //{2X_new6} scaleY: 5, //{2X_new7} alpha: 1.5, //{2X_new8} tint: 0xFFFFFF //{2X_new9} }); //{2X_new10} game.addChild(sparkle); //{2X_new11} // Random sparkle position around block//{2X_new12} var angle = j / sparkleCount * Math.PI * 2; //{2X_new13} var distance = BLOCK_SIZE * 1.0; //{2X_new14} var targetX = sparkle.x + Math.cos(angle) * distance; //{2X_new15} var targetY = sparkle.y + Math.sin(angle) * distance; //{2X_new16} // Animate sparkle outward and fade//{2X_new17} tween(sparkle, { //{2X_new18} x: targetX, //{2X_new19} y: targetY, //{2X_new20} alpha: 0.3, //{2X_new21} scaleX: 0.3, //{2X_new22} scaleY: 0.3 //{2X_new23} }, { //{2X_new24} duration: 200, //{2X_new25} easing: tween.easeOut, //{2X_new26} onFinish: function onFinish() { //{2X_new27} if (sparkle && sparkle.destroy) { //{2X_new28} sparkle.destroy(); //{2X_new29} } //{2X_new30} } //{2X_new31} }); //{2X_new32} } //{2X_new33} // Create explosion effect before destroying var originalX = block.x; var originalY = block.y; var originalScaleX = block.scaleX; var originalScaleY = block.scaleY; var originalAlpha = block.alpha; // Random direction for explosion var randomX = originalX + (Math.random() - 0.5) * 400; var randomY = originalY + (Math.random() - 0.5) * 200; // Animate block flying away and fading tween(block, { x: randomX, y: randomY, scaleX: originalScaleX * 0.2, scaleY: originalScaleY * 0.2, alpha: 0, rotation: (Math.random() - 0.5) * Math.PI * 2 }, { duration: 30, easing: tween.easeOut, onFinish: function onFinish() { if (block && block.destroy) { block.destroy(); } } }); gameGrid[lineY][x] = null; } } // Move lines down for (var y = lineY; y > 0; y--) { for (var x = 0; x < GRID_WIDTH; x++) { gameGrid[y][x] = gameGrid[y - 1][x]; if (gameGrid[y][x]) { gameGrid[y][x].setGridPosition(x, y); } } } // Clear top line for (var x = 0; x < GRID_WIDTH; x++) { gameGrid[0][x] = null; } } // Update score linesCleared += completedLines.length; var points = 0; switch (completedLines.length) { case 1: points = 100; break; case 2: points = 300; break; case 3: points = 500; break; case 4: points = 800; break; } LK.setScore(LK.getScore() + points * gameLevel); // Level up every 10 lines var newLevel = Math.floor(linesCleared / 10) + 1; if (newLevel > gameLevel) { gameLevel = newLevel; } // Create border sparkle effect using line clear sparkles when lines are cleared var borderSparkles = []; // Left border sparkles for (var i = 0; i < 10; i++) { var sparkle = LK.getAsset('grid_border', { x: GRID_OFFSET_X - 20, y: GRID_OFFSET_Y + i * (GRID_HEIGHT * BLOCK_SIZE / 10), scaleX: 5, scaleY: 5, alpha: 1.5, tint: 0xFFFFFF }); game.addChild(sparkle); borderSparkles.push(sparkle); // Random sparkle position for border effect//{3K_new} var angle = i / 10 * Math.PI * 2; //{3K_new2} var distance = 50; //{3K_new3} var targetX = sparkle.x + Math.cos(angle) * distance; //{3K_new4} var targetY = sparkle.y + Math.sin(angle) * distance; //{3K_new5} // Animate sparkle outward and fade//{3K_new6} tween(sparkle, { //{3K_new7} x: targetX, //{3K_new8} y: targetY, //{3K_new9} alpha: 0.3, //{3K_new10} scaleX: 0.3, //{3K_new11} scaleY: 0.3 //{3K_new12} }, { //{3K_new13} duration: 200, //{3K_new14} easing: tween.easeOut, //{3K_new15} onFinish: function onFinish() { //{3K_new16} if (sparkle && sparkle.destroy) { //{3K_new17} sparkle.destroy(); //{3K_new18} } //{3K_new19} } //{3K_new20} }); //{3K_new21} } // Right border sparkles for (var i = 0; i < 10; i++) { var sparkle = LK.getAsset('grid_border', { x: GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 20, y: GRID_OFFSET_Y + i * (GRID_HEIGHT * BLOCK_SIZE / 10), scaleX: 5, scaleY: 5, alpha: 1.5, tint: 0xFFFFFF }); game.addChild(sparkle); borderSparkles.push(sparkle); // Random sparkle position for border effect//{3V_new} var angle = i / 10 * Math.PI * 2; //{3V_new2} var distance = 50; //{3V_new3} var targetX = sparkle.x + Math.cos(angle) * distance; //{3V_new4} var targetY = sparkle.y + Math.sin(angle) * distance; //{3V_new5} // Animate sparkle outward and fade//{3V_new6} tween(sparkle, { //{3V_new7} x: targetX, //{3V_new8} y: targetY, //{3V_new9} alpha: 0.3, //{3V_new10} scaleX: 0.3, //{3V_new11} scaleY: 0.3 //{3V_new12} }, { //{3V_new13} duration: 200, //{3V_new14} easing: tween.easeOut, //{3V_new15} onFinish: function onFinish() { //{3V_new16} if (sparkle && sparkle.destroy) { //{3V_new17} sparkle.destroy(); //{3V_new18} } //{3V_new19} } //{3V_new20} }); //{3V_new21} } LK.getSound('sparkle_effect').play(); //{3j_new} LK.getSound('line_clear').play(); } } // Create score display var scoreText = new Text2('Score: 0', { size: 40, fill: 0xFFFFFF }); scoreText.anchor.set(1, 0); scoreText.x = -50; LK.gui.topRight.addChild(scoreText); var levelText = new Text2('Level: 1', { size: 40, fill: 0xFFFFFF }); levelText.anchor.set(1, 0); levelText.x = -50; levelText.y = 60; LK.gui.topRight.addChild(levelText); var linesText = new Text2('Lines: 0', { size: 40, fill: 0xFFFFFF }); linesText.anchor.set(1, 0); linesText.x = -50; linesText.y = 120; LK.gui.topRight.addChild(linesText); // Add background color asset var backgroundColorAsset = LK.getAsset('background_color', { x: 0, y: 0 }); game.addChild(backgroundColorAsset); // Draw grid borders - vertical lines only for game area height for (var x = 0; x <= GRID_WIDTH; x++) { if (x === 0 || x === GRID_WIDTH) { var border = LK.getAsset('grid_border', { x: GRID_OFFSET_X + x * BLOCK_SIZE - 4, y: GRID_OFFSET_Y, scaleX: 1, scaleY: GRID_HEIGHT * BLOCK_SIZE / 8 }); game.addChild(border); } } for (var y = GRID_HEIGHT; y <= GRID_HEIGHT; y++) { for (var x = 0; x < GRID_WIDTH; x++) { var border = LK.getAsset('grid_border', { x: GRID_OFFSET_X + x * BLOCK_SIZE, y: GRID_OFFSET_Y + y * BLOCK_SIZE, scaleX: BLOCK_SIZE / 8, scaleY: 1 }); game.addChild(border); } } // Spawn first piece spawnNewPiece(); // Start background music LK.playMusic('tetris_theme'); game.down = function (x, y, obj) { if (!currentPiece) return; var now = Date.now(); if (now - lastMoveTime < moveDelay) return; lastMoveTime = now; // Divide screen into zones for controls var leftZone = 2048 * 0.25; var rightZone = 2048 * 0.75; var rotateZone = 2732 * 0.7; if (x < leftZone) { // Left movement currentPiece.move(-1, 0); } else if (x > rightZone) { // Right movement currentPiece.move(1, 0); } else if (y > rotateZone) { // Drop piece faster currentPiece.move(0, 1); } else { // Rotate piece currentPiece.rotate(); } }; game.update = function () { if (currentPiece) { // Update fall speed based on level currentPiece.fallSpeed = Math.max(10, 40 - (gameLevel - 1) * 5); } // Update UI scoreText.setText('Score: ' + LK.getScore()); levelText.setText('Level: ' + gameLevel); linesText.setText('Lines: ' + linesCleared); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Block = Container.expand(function (blockType) {
var self = Container.call(this);
self.blockType = blockType || 'block_I';
self.gridX = 0;
self.gridY = 0;
var blockGraphics = self.attachAsset(self.blockType, {
anchorX: 0,
anchorY: 0
});
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = GRID_OFFSET_X + gridX * BLOCK_SIZE;
self.y = GRID_OFFSET_Y + gridY * BLOCK_SIZE;
};
return self;
});
var Tetromino = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'I';
self.rotation = 0;
self.gridX = 7; // Start in middle of board
self.gridY = 0;
self.blocks = [];
self.fallTimer = 0;
self.fallSpeed = 40; // Frames until next fall (faster falling speed)
self.isPlaced = false; // Prevent multiple placement calls
// Tetromino shapes and rotations
self.shapes = {
'I': [[[1, 1, 1, 1]], [[1], [1], [1], [1]], [[1, 1, 1, 1]], [[1], [1], [1], [1]]],
'O': [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1, 1]]],
'T': [[[0, 1, 0], [1, 1, 1]], [[1, 0], [1, 1], [1, 0]], [[1, 1, 1], [0, 1, 0]], [[0, 1], [1, 1], [0, 1]]],
'S': [[[0, 1, 1], [1, 1, 0]], [[1, 0], [1, 1], [0, 1]], [[0, 1, 1], [1, 1, 0]], [[1, 0], [1, 1], [0, 1]]],
'Z': [[[1, 1, 0], [0, 1, 1]], [[0, 1], [1, 1], [1, 0]], [[1, 1, 0], [0, 1, 1]], [[0, 1], [1, 1], [1, 0]]],
'J': [[[1, 0, 0], [1, 1, 1]], [[1, 1], [1, 0], [1, 0]], [[1, 1, 1], [0, 0, 1]], [[0, 1], [0, 1], [1, 1]]],
'L': [[[0, 0, 1], [1, 1, 1]], [[1, 0], [1, 0], [1, 1]], [[1, 1, 1], [1, 0, 0]], [[1, 1], [0, 1], [0, 1]]]
};
self.getBlockType = function () {
// Map each tetromino type to its corresponding block asset
var blockTypeMap = {
'I': 'block_I',
'O': 'block_O',
'T': 'block_T',
'S': 'block_S',
'Z': 'block_Z',
'J': 'block_J',
'L': 'block_L'
};
return blockTypeMap[self.type] || 'block_I';
};
self.createBlocks = function () {
// Clear existing blocks
for (var i = 0; i < self.blocks.length; i++) {
self.blocks[i].destroy();
}
self.blocks = [];
// Ensure we have a valid shape for this type
if (!self.shapes[self.type]) {
console.error('Invalid tetromino type:', self.type);
return;
}
var shape = self.shapes[self.type][self.rotation];
var blockType = self.getBlockType();
for (var y = 0; y < shape.length; y++) {
for (var x = 0; x < shape[y].length; x++) {
if (shape[y][x] === 1) {
var block = new Block(blockType);
block.setGridPosition(self.gridX + x, self.gridY + y);
self.blocks.push(block);
game.addChild(block);
}
}
}
};
self.updatePosition = function () {
var shape = self.shapes[self.type][self.rotation];
var blockIndex = 0;
for (var y = 0; y < shape.length; y++) {
for (var x = 0; x < shape[y].length; x++) {
if (shape[y][x] === 1) {
if (blockIndex < self.blocks.length) {
self.blocks[blockIndex].setGridPosition(self.gridX + x, self.gridY + y);
}
blockIndex++;
}
}
}
};
self.canMoveTo = function (newX, newY, newRotation) {
var testRotation = newRotation !== undefined ? newRotation : self.rotation;
var shape = self.shapes[self.type][testRotation];
for (var y = 0; y < shape.length; y++) {
for (var x = 0; x < shape[y].length; x++) {
if (shape[y][x] === 1) {
var checkX = newX + x;
var checkY = newY + y;
// Check bounds
if (checkX < 0 || checkX >= GRID_WIDTH || checkY >= GRID_HEIGHT) {
return false;
}
// Check collision with placed blocks
if (checkY >= 0 && gameGrid[checkY] && gameGrid[checkY][checkX]) {
return false;
}
}
}
}
return true;
};
self.move = function (deltaX, deltaY) {
if (self.canMoveTo(self.gridX + deltaX, self.gridY + deltaY)) {
self.gridX += deltaX;
self.gridY += deltaY;
self.updatePosition();
return true;
}
return false;
};
self.rotate = function () {
var newRotation = (self.rotation + 1) % 4;
if (self.canMoveTo(self.gridX, self.gridY, newRotation)) {
self.rotation = newRotation;
self.updatePosition();
// Create sparkle effect for rotation
for (var i = 0; i < self.blocks.length; i++) {
var block = self.blocks[i];
var sparkleCount = 8;
for (var j = 0; j < sparkleCount; j++) {
var sparkle = LK.getAsset('grid_border', {
x: block.x + BLOCK_SIZE / 2,
y: block.y + BLOCK_SIZE / 2,
scaleX: 5,
scaleY: 5,
alpha: 1.5,
tint: 0xFFFFFF
});
game.addChild(sparkle);
// Random sparkle position around block
var angle = j / sparkleCount * Math.PI * 2;
var distance = BLOCK_SIZE * 1.0;
var targetX = sparkle.x + Math.cos(angle) * distance;
var targetY = sparkle.y + Math.sin(angle) * distance;
// Animate sparkle outward and fade
tween(sparkle, {
x: targetX,
y: targetY,
alpha: 0.3,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
if (sparkle && sparkle.destroy) {
sparkle.destroy();
}
}
});
}
}
LK.getSound('sparkle_effect').play();
LK.getSound('piece_rotate').play();
return true;
}
return false;
};
self.update = function () {
self.fallTimer++;
if (self.fallTimer >= self.fallSpeed) {
self.fallTimer = 0;
if (!self.move(0, 1)) {
// Piece has landed
self.placePiece();
}
}
};
self.placePiece = function () {
// Prevent multiple calls
if (self.isPlaced) return;
self.isPlaced = true;
// Add blocks to grid and create sparkle effects
for (var i = 0; i < self.blocks.length; i++) {
var block = self.blocks[i];
if (block.gridY >= 0) {
if (!gameGrid[block.gridY]) gameGrid[block.gridY] = [];
gameGrid[block.gridY][block.gridX] = block;
// Create sparkle effect around the block
var sparkleCount = 8;
for (var j = 0; j < sparkleCount; j++) {
var sparkle = LK.getAsset('grid_border', {
x: block.x + BLOCK_SIZE / 2,
y: block.y + BLOCK_SIZE / 2,
scaleX: 8,
scaleY: 8,
alpha: 1.5,
tint: 0xFFFFFF
});
game.addChild(sparkle);
// Random sparkle position around block
var angle = j / sparkleCount * Math.PI * 2;
var distance = BLOCK_SIZE * 1.0;
var targetX = sparkle.x + Math.cos(angle) * distance;
var targetY = sparkle.y + Math.sin(angle) * distance;
// Animate sparkle outward and fade
tween(sparkle, {
x: targetX,
y: targetY,
alpha: 0.3,
scaleX: 0.3,
scaleY: 0.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
if (sparkle && sparkle.destroy) {
sparkle.destroy();
}
}
});
}
}
}
// Remove blocks from this tetromino since they're now part of the grid
self.blocks = [];
LK.getSound('sparkle_effect').play();
LK.getSound('piece_drop').play();
checkCompleteLines();
// Clear current piece reference first
currentPiece = null;
// Spawn new piece with a small delay to ensure proper sequencing
LK.setTimeout(function () {
spawnNewPiece();
}, 50);
};
self.destroy = function () {
// Clean up all blocks when tetromino is destroyed
for (var i = 0; i < self.blocks.length; i++) {
if (self.blocks[i] && self.blocks[i].destroy) {
self.blocks[i].destroy();
}
}
self.blocks = [];
// Call parent destroy
Container.prototype.destroy.call(self);
};
return self;
});
/****
* Initialize Game
****/
// Game constants
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Orange
// Blue
// Red
// Green
// Purple
// Yellow
// Cyan
// Tetromino blocks - using different colors for each piece type
// Game constants
var GRID_WIDTH = 14;
var GRID_HEIGHT = 24;
var BLOCK_SIZE = 100;
var GRID_OFFSET_X = (2048 - GRID_WIDTH * BLOCK_SIZE) / 2;
var GRID_OFFSET_Y = 100;
// Game variables
var gameGrid = [];
var currentPiece = null;
var nextPieceType = null;
var gameLevel = 1;
var linesCleared = 0;
var lastMoveTime = 0;
var moveDelay = 100; // Milliseconds between moves
// Initialize grid
for (var y = 0; y < GRID_HEIGHT; y++) {
gameGrid[y] = [];
for (var x = 0; x < GRID_WIDTH; x++) {
gameGrid[y][x] = null;
}
}
// Tetromino types
var pieceTypes = ['I', 'O', 'T', 'S', 'Z', 'J', 'L'];
function getRandomPieceType() {
return pieceTypes[Math.floor(Math.random() * pieceTypes.length)];
}
function spawnNewPiece() {
// Don't spawn if there's already an active piece or if one is being placed
if (currentPiece && !currentPiece.isPlaced) {
return;
}
if (!nextPieceType) {
nextPieceType = getRandomPieceType();
}
// Create new piece and set as current immediately to prevent double spawning
var newPiece = new Tetromino(nextPieceType);
currentPiece = newPiece;
game.addChild(currentPiece);
currentPiece.createBlocks();
nextPieceType = getRandomPieceType();
// Check game over
if (!currentPiece.canMoveTo(currentPiece.gridX, currentPiece.gridY)) {
LK.showGameOver();
return;
}
}
function checkCompleteLines() {
var completedLines = [];
// Find completed lines
for (var y = 0; y < GRID_HEIGHT; y++) {
var lineComplete = true;
for (var x = 0; x < GRID_WIDTH; x++) {
if (!gameGrid[y][x]) {
lineComplete = false;
break;
}
}
if (lineComplete) {
completedLines.push(y);
}
}
if (completedLines.length > 0) {
// Sort completed lines from bottom to top (highest Y to lowest Y)
// This ensures we process lines from bottom up, keeping indices valid
completedLines.sort(function (a, b) {
return b - a;
});
// Remove completed lines
for (var i = 0; i < completedLines.length; i++) {
var lineY = completedLines[i];
// Create particle explosion effect for this line
for (var x = 0; x < GRID_WIDTH; x++) {
if (gameGrid[lineY][x]) {
var block = gameGrid[lineY][x];
// Create sparkle effect for cleared line blocks
var sparkleCount = 8; //{2X_new}
for (var j = 0; j < sparkleCount; j++) {
//{2X_new2}
var sparkle = LK.getAsset('grid_border', {
//{2X_new3}
x: block.x + BLOCK_SIZE / 2,
//{2X_new4}
y: block.y + BLOCK_SIZE / 2,
//{2X_new5}
scaleX: 5,
//{2X_new6}
scaleY: 5,
//{2X_new7}
alpha: 1.5,
//{2X_new8}
tint: 0xFFFFFF //{2X_new9}
}); //{2X_new10}
game.addChild(sparkle); //{2X_new11}
// Random sparkle position around block//{2X_new12}
var angle = j / sparkleCount * Math.PI * 2; //{2X_new13}
var distance = BLOCK_SIZE * 1.0; //{2X_new14}
var targetX = sparkle.x + Math.cos(angle) * distance; //{2X_new15}
var targetY = sparkle.y + Math.sin(angle) * distance; //{2X_new16}
// Animate sparkle outward and fade//{2X_new17}
tween(sparkle, {
//{2X_new18}
x: targetX,
//{2X_new19}
y: targetY,
//{2X_new20}
alpha: 0.3,
//{2X_new21}
scaleX: 0.3,
//{2X_new22}
scaleY: 0.3 //{2X_new23}
}, {
//{2X_new24}
duration: 200,
//{2X_new25}
easing: tween.easeOut,
//{2X_new26}
onFinish: function onFinish() {
//{2X_new27}
if (sparkle && sparkle.destroy) {
//{2X_new28}
sparkle.destroy(); //{2X_new29}
} //{2X_new30}
} //{2X_new31}
}); //{2X_new32}
} //{2X_new33}
// Create explosion effect before destroying
var originalX = block.x;
var originalY = block.y;
var originalScaleX = block.scaleX;
var originalScaleY = block.scaleY;
var originalAlpha = block.alpha;
// Random direction for explosion
var randomX = originalX + (Math.random() - 0.5) * 400;
var randomY = originalY + (Math.random() - 0.5) * 200;
// Animate block flying away and fading
tween(block, {
x: randomX,
y: randomY,
scaleX: originalScaleX * 0.2,
scaleY: originalScaleY * 0.2,
alpha: 0,
rotation: (Math.random() - 0.5) * Math.PI * 2
}, {
duration: 30,
easing: tween.easeOut,
onFinish: function onFinish() {
if (block && block.destroy) {
block.destroy();
}
}
});
gameGrid[lineY][x] = null;
}
}
// Move lines down
for (var y = lineY; y > 0; y--) {
for (var x = 0; x < GRID_WIDTH; x++) {
gameGrid[y][x] = gameGrid[y - 1][x];
if (gameGrid[y][x]) {
gameGrid[y][x].setGridPosition(x, y);
}
}
}
// Clear top line
for (var x = 0; x < GRID_WIDTH; x++) {
gameGrid[0][x] = null;
}
}
// Update score
linesCleared += completedLines.length;
var points = 0;
switch (completedLines.length) {
case 1:
points = 100;
break;
case 2:
points = 300;
break;
case 3:
points = 500;
break;
case 4:
points = 800;
break;
}
LK.setScore(LK.getScore() + points * gameLevel);
// Level up every 10 lines
var newLevel = Math.floor(linesCleared / 10) + 1;
if (newLevel > gameLevel) {
gameLevel = newLevel;
}
// Create border sparkle effect using line clear sparkles when lines are cleared
var borderSparkles = [];
// Left border sparkles
for (var i = 0; i < 10; i++) {
var sparkle = LK.getAsset('grid_border', {
x: GRID_OFFSET_X - 20,
y: GRID_OFFSET_Y + i * (GRID_HEIGHT * BLOCK_SIZE / 10),
scaleX: 5,
scaleY: 5,
alpha: 1.5,
tint: 0xFFFFFF
});
game.addChild(sparkle);
borderSparkles.push(sparkle);
// Random sparkle position for border effect//{3K_new}
var angle = i / 10 * Math.PI * 2; //{3K_new2}
var distance = 50; //{3K_new3}
var targetX = sparkle.x + Math.cos(angle) * distance; //{3K_new4}
var targetY = sparkle.y + Math.sin(angle) * distance; //{3K_new5}
// Animate sparkle outward and fade//{3K_new6}
tween(sparkle, {
//{3K_new7}
x: targetX,
//{3K_new8}
y: targetY,
//{3K_new9}
alpha: 0.3,
//{3K_new10}
scaleX: 0.3,
//{3K_new11}
scaleY: 0.3 //{3K_new12}
}, {
//{3K_new13}
duration: 200,
//{3K_new14}
easing: tween.easeOut,
//{3K_new15}
onFinish: function onFinish() {
//{3K_new16}
if (sparkle && sparkle.destroy) {
//{3K_new17}
sparkle.destroy(); //{3K_new18}
} //{3K_new19}
} //{3K_new20}
}); //{3K_new21}
}
// Right border sparkles
for (var i = 0; i < 10; i++) {
var sparkle = LK.getAsset('grid_border', {
x: GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 20,
y: GRID_OFFSET_Y + i * (GRID_HEIGHT * BLOCK_SIZE / 10),
scaleX: 5,
scaleY: 5,
alpha: 1.5,
tint: 0xFFFFFF
});
game.addChild(sparkle);
borderSparkles.push(sparkle);
// Random sparkle position for border effect//{3V_new}
var angle = i / 10 * Math.PI * 2; //{3V_new2}
var distance = 50; //{3V_new3}
var targetX = sparkle.x + Math.cos(angle) * distance; //{3V_new4}
var targetY = sparkle.y + Math.sin(angle) * distance; //{3V_new5}
// Animate sparkle outward and fade//{3V_new6}
tween(sparkle, {
//{3V_new7}
x: targetX,
//{3V_new8}
y: targetY,
//{3V_new9}
alpha: 0.3,
//{3V_new10}
scaleX: 0.3,
//{3V_new11}
scaleY: 0.3 //{3V_new12}
}, {
//{3V_new13}
duration: 200,
//{3V_new14}
easing: tween.easeOut,
//{3V_new15}
onFinish: function onFinish() {
//{3V_new16}
if (sparkle && sparkle.destroy) {
//{3V_new17}
sparkle.destroy(); //{3V_new18}
} //{3V_new19}
} //{3V_new20}
}); //{3V_new21}
}
LK.getSound('sparkle_effect').play(); //{3j_new}
LK.getSound('line_clear').play();
}
}
// Create score display
var scoreText = new Text2('Score: 0', {
size: 40,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
scoreText.x = -50;
LK.gui.topRight.addChild(scoreText);
var levelText = new Text2('Level: 1', {
size: 40,
fill: 0xFFFFFF
});
levelText.anchor.set(1, 0);
levelText.x = -50;
levelText.y = 60;
LK.gui.topRight.addChild(levelText);
var linesText = new Text2('Lines: 0', {
size: 40,
fill: 0xFFFFFF
});
linesText.anchor.set(1, 0);
linesText.x = -50;
linesText.y = 120;
LK.gui.topRight.addChild(linesText);
// Add background color asset
var backgroundColorAsset = LK.getAsset('background_color', {
x: 0,
y: 0
});
game.addChild(backgroundColorAsset);
// Draw grid borders - vertical lines only for game area height
for (var x = 0; x <= GRID_WIDTH; x++) {
if (x === 0 || x === GRID_WIDTH) {
var border = LK.getAsset('grid_border', {
x: GRID_OFFSET_X + x * BLOCK_SIZE - 4,
y: GRID_OFFSET_Y,
scaleX: 1,
scaleY: GRID_HEIGHT * BLOCK_SIZE / 8
});
game.addChild(border);
}
}
for (var y = GRID_HEIGHT; y <= GRID_HEIGHT; y++) {
for (var x = 0; x < GRID_WIDTH; x++) {
var border = LK.getAsset('grid_border', {
x: GRID_OFFSET_X + x * BLOCK_SIZE,
y: GRID_OFFSET_Y + y * BLOCK_SIZE,
scaleX: BLOCK_SIZE / 8,
scaleY: 1
});
game.addChild(border);
}
}
// Spawn first piece
spawnNewPiece();
// Start background music
LK.playMusic('tetris_theme');
game.down = function (x, y, obj) {
if (!currentPiece) return;
var now = Date.now();
if (now - lastMoveTime < moveDelay) return;
lastMoveTime = now;
// Divide screen into zones for controls
var leftZone = 2048 * 0.25;
var rightZone = 2048 * 0.75;
var rotateZone = 2732 * 0.7;
if (x < leftZone) {
// Left movement
currentPiece.move(-1, 0);
} else if (x > rightZone) {
// Right movement
currentPiece.move(1, 0);
} else if (y > rotateZone) {
// Drop piece faster
currentPiece.move(0, 1);
} else {
// Rotate piece
currentPiece.rotate();
}
};
game.update = function () {
if (currentPiece) {
// Update fall speed based on level
currentPiece.fallSpeed = Math.max(10, 40 - (gameLevel - 1) * 5);
}
// Update UI
scoreText.setText('Score: ' + LK.getScore());
levelText.setText('Level: ' + gameLevel);
linesText.setText('Lines: ' + linesCleared);
};