/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0, level: 1 }); /**** * Classes ****/ var Block = Container.expand(function () { var self = Container.call(this); self.blockType = ''; self.init = function (type) { self.blockType = type; var color = 'block_' + type; self.graphics = self.attachAsset(color, { anchorX: 0, anchorY: 0, alpha: 0.9 }); return self; }; return self; }); var Button = Container.expand(function () { var self = Container.call(this); self.init = function (label, iconType) { var buttonGraphics = self.attachAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 2.0, shape: 'ellipse' // Change shape to ellipse to make it circular }); var text = new Text2(label, { size: 100, fill: 0xFFFFFF // White color for icon }); text.anchor.set(0.5, 0.5); self.addChild(text); self.down = function (x, y, obj) { buttonGraphics.alpha = 0.7; }; self.up = function (x, y, obj) { buttonGraphics.alpha = 1.0; }; return self; }; return self; }); var Tetromino = Container.expand(function () { var self = Container.call(this); self.blocks = []; self.shape = []; self.type = ''; self.rotation = 0; self.gridX = 0; self.gridY = 0; self.init = function (type) { self.type = type; self.createShape(type); return self; }; self.createShape = function (type) { // Clear existing blocks for (var i = 0; i < self.blocks.length; i++) { self.removeChild(self.blocks[i]); } self.blocks = []; // Create new shape based on type switch (type) { case 'i': self.shape = [[0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]]; break; case 'j': self.shape = [[1, 0, 0], [1, 1, 1], [0, 0, 0]]; break; case 'l': self.shape = [[0, 0, 1], [1, 1, 1], [0, 0, 0]]; break; case 'o': self.shape = [[1, 1], [1, 1]]; break; case 's': self.shape = [[0, 1, 1], [1, 1, 0], [0, 0, 0]]; break; case 't': self.shape = [[0, 1, 0], [1, 1, 1], [0, 0, 0]]; break; case 'z': self.shape = [[1, 1, 0], [0, 1, 1], [0, 0, 0]]; break; } // Create blocks based on shape for (var y = 0; y < self.shape.length; y++) { for (var x = 0; x < self.shape[y].length; x++) { if (self.shape[y][x]) { var block = new Block().init(type); block.x = x * BLOCK_SIZE; block.y = y * BLOCK_SIZE; self.addChild(block); self.blocks.push(block); } } } }; self.rotate = function () { if (self.type === 'o') { return; } // O pieces don't rotate // Create a new rotated matrix var newShape = []; var size = self.shape.length; for (var y = 0; y < size; y++) { newShape[y] = []; for (var x = 0; x < size; x++) { newShape[y][x] = self.shape[size - 1 - x][y]; } } // Save original shape in case rotation is invalid var originalShape = self.shape; self.shape = newShape; // Update block positions self.updateBlocks(); // Check if the rotation is valid if (!isValidPosition(self)) { // Revert to original shape self.shape = originalShape; self.updateBlocks(); return false; } if (LK.getSound('tetromino_rotate')) { LK.getSound('tetromino_rotate').play(); } else { console.error("Sound 'tetromino_rotate' not found"); } return true; }; self.updateBlocks = function () { var blockIndex = 0; for (var y = 0; y < self.shape.length; y++) { for (var x = 0; x < self.shape[y].length; x++) { if (self.shape[y][x]) { self.blocks[blockIndex].x = x * BLOCK_SIZE; self.blocks[blockIndex].y = y * BLOCK_SIZE; blockIndex++; } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x9bbc0f }); /**** * Game Code ****/ // Constants var BLOCK_SIZE = 100; // Block size in pixels var GRID_WIDTH = 10; // Grid width in blocks var GRID_HEIGHT = 20; // Grid height in blocks var GRID_OFFSET_X = (2048 - GRID_WIDTH * BLOCK_SIZE) / 2; // Center grid x position var GRID_OFFSET_Y = 100; // Grid y position var DROP_INTERVAL = 1000; // Initial interval between block drops in ms var DROP_SPEED_INCREASE = 0.8; // Speed multiplier per level // Game variables var grid = []; // Grid state var currentTetromino = null; // Current active tetromino var nextTetromino = null; // Next tetromino to drop var score = 0; var level = storage.level || 1; var lines = 0; var isGameOver = false; var dropTimer = 0; var lastDropTime = 0; var inputDelay = 0; // Delay for input controls to prevent too fast movement // Initialize grid for (var y = 0; y < GRID_HEIGHT; y++) { grid[y] = []; for (var x = 0; x < GRID_WIDTH; x++) { grid[y][x] = null; } } // Create grid background with lines var gridBackground = new Container(); for (var y = 0; y < GRID_HEIGHT; y++) { for (var x = 0; x < GRID_WIDTH; x++) { var cell = LK.getAsset('grid_cell', { anchorX: 0, anchorY: 0, alpha: 0.5 }); cell.x = x * BLOCK_SIZE; cell.y = y * BLOCK_SIZE; gridBackground.addChild(cell); // Add vertical line if (x < GRID_WIDTH - 1) { var verticalLine = LK.getAsset('block', { anchorX: 0, anchorY: 0, alpha: 0.2, width: 20, // Increased thickness height: BLOCK_SIZE, color: 0x000000 // Black color }); verticalLine.x = (x + 1) * BLOCK_SIZE - 1; verticalLine.y = y * BLOCK_SIZE; gridBackground.addChild(verticalLine); } // Add horizontal line if (y < GRID_HEIGHT - 1) { var horizontalLine = LK.getAsset('block', { anchorX: 0, anchorY: 0, alpha: 0.2, width: BLOCK_SIZE, height: 20, // Increased thickness color: 0x000000 // Black color }); horizontalLine.x = x * BLOCK_SIZE; horizontalLine.y = (y + 1) * BLOCK_SIZE - 1; gridBackground.addChild(horizontalLine); } } } gridBackground.x = GRID_OFFSET_X; gridBackground.y = GRID_OFFSET_Y; game.addChild(gridBackground); // Create game board container var gameBoard = new Container(); gameBoard.x = GRID_OFFSET_X; gameBoard.y = GRID_OFFSET_Y; game.addChild(gameBoard); // Next piece preview var nextPieceContainer = new Container(); nextPieceContainer.x = GRID_OFFSET_X - 200; nextPieceContainer.y = GRID_OFFSET_Y + 100; game.addChild(nextPieceContainer); // Create UI text elements var scoreText = new Text2('Score: 0', { size: 80, fill: 0x000000 }); scoreText.anchor.set(0, 0); scoreText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 50; scoreText.y = GRID_OFFSET_Y; game.addChild(scoreText); var levelText = new Text2('Level: 1', { size: 80, fill: 0x000000 }); levelText.anchor.set(0, 0); levelText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 50; levelText.y = GRID_OFFSET_Y + 120; game.addChild(levelText); var linesText = new Text2('Lines: 0', { size: 80, fill: 0x000000 }); linesText.anchor.set(0, 0); linesText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 50; linesText.y = GRID_OFFSET_Y + 250; game.addChild(linesText); // Create control buttons var leftButton = new Button().init('←'); leftButton.x = 300; leftButton.y = 2432; // Move up by 200 pixels leftButton.down = function () { moveTetrominoLeft(); this.alpha = 0.7; }; leftButton.up = function () { this.alpha = 1.0; }; game.addChild(leftButton); var rightButton = new Button().init('→'); rightButton.x = 750; rightButton.y = 2432; // Move up by 200 pixels rightButton.down = function () { moveTetrominoRight(); this.alpha = 0.7; }; rightButton.up = function () { this.alpha = 1.0; }; game.addChild(rightButton); var rotateButton = new Button().init('↻'); rotateButton.x = 1300; rotateButton.y = 2432; // Move up by 200 pixels rotateButton.down = function () { rotateTetromino(); this.alpha = 0.7; }; rotateButton.up = function () { this.alpha = 1.0; }; game.addChild(rotateButton); var dropButton = new Button().init('↓'); dropButton.x = 1750; dropButton.y = 2432; // Move up by 200 pixels dropButton.down = function () { dropTetromino(); this.alpha = 0.7; }; dropButton.up = function () { this.alpha = 1.0; }; game.addChild(dropButton); // Tetromino types var tetrominoTypes = ['i', 'j', 'l', 'o', 's', 't', 'z']; // Helper functions function createRandomTetromino() { var type = tetrominoTypes[Math.floor(Math.random() * tetrominoTypes.length)]; return new Tetromino().init(type); } function spawnTetromino() { if (nextTetromino === null) { nextTetromino = createRandomTetromino(); } currentTetromino = nextTetromino; nextTetromino = createRandomTetromino(); // Position the tetromino at the top center of the grid currentTetromino.gridX = Math.floor(GRID_WIDTH / 2) - Math.floor(currentTetromino.shape.length / 2); currentTetromino.gridY = 0; // Update tetromino position currentTetromino.x = currentTetromino.gridX * BLOCK_SIZE; currentTetromino.y = currentTetromino.gridY * BLOCK_SIZE; // Add to game board gameBoard.addChild(currentTetromino); // Update next piece preview updateNextPiecePreview(); // Check if game is over if (!isValidPosition(currentTetromino)) { gameOver(); } } function updateNextPiecePreview() { // Clear previous preview while (nextPieceContainer.children.length > 0) { nextPieceContainer.removeChild(nextPieceContainer.children[0]); } // Create a copy of the next tetromino for preview var previewTetromino = new Tetromino().init(nextTetromino.type); // Center it in the preview area previewTetromino.x = 75 - previewTetromino.shape.length * BLOCK_SIZE / 4; previewTetromino.y = 75 - previewTetromino.shape.length * BLOCK_SIZE / 4; previewTetromino.scale.set(0.5, 0.5); nextPieceContainer.addChild(previewTetromino); } function isValidPosition(tetromino) { for (var y = 0; y < tetromino.shape.length; y++) { for (var x = 0; x < tetromino.shape[y].length; x++) { if (tetromino.shape[y][x]) { var gridX = tetromino.gridX + x; var gridY = tetromino.gridY + y; // Check if out of bounds if (gridX < 0 || gridX >= GRID_WIDTH || gridY >= GRID_HEIGHT) { return false; } // Check if overlapping with locked blocks (and not above the grid) if (gridY >= 0 && grid[gridY][gridX] !== null) { return false; } } } } return true; } function moveTetromino(dx, dy) { if (currentTetromino === null || isGameOver) { return false; } // Save original position var originalX = currentTetromino.gridX; var originalY = currentTetromino.gridY; // Try to move currentTetromino.gridX += dx; currentTetromino.gridY += dy; // Check if the new position is valid if (!isValidPosition(currentTetromino)) { // Revert position currentTetromino.gridX = originalX; currentTetromino.gridY = originalY; // If we tried to move down and failed, lock the tetromino if (dy > 0) { lockTetromino(); } return false; } // Update position currentTetromino.x = currentTetromino.gridX * BLOCK_SIZE; currentTetromino.y = currentTetromino.gridY * BLOCK_SIZE; return true; } function moveTetrominoLeft() { if (LK.ticks - inputDelay < 5) { return; } // Prevent too fast movement inputDelay = LK.ticks; moveTetromino(-1, 0); } function moveTetrominoRight() { if (LK.ticks - inputDelay < 5) { return; } // Prevent too fast movement inputDelay = LK.ticks; moveTetromino(1, 0); } function moveTetrominoDown() { return moveTetromino(0, 1); } function rotateTetromino() { if (currentTetromino === null || isGameOver) { return; } if (LK.ticks - inputDelay < 5) { return; } // Prevent too fast rotation inputDelay = LK.ticks; currentTetromino.rotate(); } function dropTetromino() { if (currentTetromino === null || isGameOver) { return; } if (LK.ticks - inputDelay < 5) { return; } // Prevent too fast dropping inputDelay = LK.ticks; // Move tetromino down until it can't move anymore while (moveTetrominoDown()) { // Continue moving down } } function lockTetromino() { if (currentTetromino === null) { return; } // Add blocks to grid for (var y = 0; y < currentTetromino.shape.length; y++) { for (var x = 0; x < currentTetromino.shape[y].length; x++) { if (currentTetromino.shape[y][x]) { var gridX = currentTetromino.gridX + x; var gridY = currentTetromino.gridY + y; // Skip if above the grid if (gridY < 0) { continue; } // Create a new block at this position var block = new Block().init(currentTetromino.type); block.x = gridX * BLOCK_SIZE; block.y = gridY * BLOCK_SIZE; // Add to grid and game board grid[gridY][gridX] = block; gameBoard.addChild(block); } } } // Remove the tetromino gameBoard.removeChild(currentTetromino); currentTetromino = null; // Play sound if (LK.getSound('tetromino_land')) { LK.getSound('tetromino_land').play(); } else { console.error("Sound 'tetromino_land' not found"); } // Check for completed lines checkLines(); // Spawn new tetromino spawnTetromino(); } function checkLines() { var completedLines = 0; var linesToClear = []; // Check for completed lines for (var y = 0; y < GRID_HEIGHT; y++) { var isLineComplete = true; for (var x = 0; x < GRID_WIDTH; x++) { if (grid[y][x] === null) { isLineComplete = false; break; } } if (isLineComplete) { completedLines++; linesToClear.push(y); } } // If no lines were completed, return if (completedLines === 0) { return; } // Add score based on number of lines cleared var points = 0; switch (completedLines) { case 1: points = 100 * level; break; case 2: points = 300 * level; break; case 3: points = 500 * level; break; case 4: points = 800 * level; break; } score = Math.min(score + points, 9999); lines += completedLines; // Update level var newLevel = Math.floor(lines / 10) + 1; if (newLevel > level) { level = newLevel; if (LK.getSound('level_up')) { LK.getSound('level_up').play(); } else { console.error("Sound 'level_up' not found"); } } // Play sound if (LK.getSound('line_clear')) { LK.getSound('line_clear').play(); } else { console.error("Sound 'line_clear' not found"); } if (LK.getSound('hacked_speech')) { LK.getSound('hacked_speech').play(); } else { console.error("Sound 'hacked_speech' not found"); } // Clear the lines clearLines(linesToClear); // Update UI updateUI(); // Save high score if (score > storage.highScore) { storage.highScore = score; } } function clearLines(linesToClear) { // Create and add 'HACKED' text var hackedText = new Text2('HACKED', { size: 200, fill: 0xffffff // White color }); hackedText.anchor.set(0.5, 0.5); hackedText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE / 2; hackedText.y = GRID_OFFSET_Y + GRID_HEIGHT * BLOCK_SIZE / 2; game.addChild(hackedText); // Play digitized speech saying 'hacked' if (LK.getSound('hacked_speech')) { LK.getSound('hacked_speech').play(); } else { console.error("Sound 'hacked_speech' not found"); } // Flash the 'HACKED' text tween(hackedText, { alpha: 0 }, { duration: 1600, repeat: 10, yoyo: true, onFinish: function onFinish() { game.removeChild(hackedText); } }); // Flash the lines to be cleared for (var i = 0; i < linesToClear.length; i++) { var y = linesToClear[i]; for (var x = 0; x < GRID_WIDTH; x++) { if (grid[y][x] !== null) { tween(grid[y][x], { alpha: 0 }, { duration: 200, onFinish: function onFinish() { tween(this, { alpha: 1 }, { duration: 200 }); } }); } } } // Schedule line clearing after the flash LK.setTimeout(function () { // Remove blocks from cleared lines with shattering effect for (var i = 0; i < linesToClear.length; i++) { var y = linesToClear[i]; for (var x = 0; x < GRID_WIDTH; x++) { if (grid[y][x] !== null) { // Create shattering effect createShatterEffect(grid[y][x]); gameBoard.removeChild(grid[y][x]); grid[y][x] = null; } } } // Function to create shattering effect function createShatterEffect(block) { var particles = []; var numParticles = 20; // Increased number of particles per block for (var i = 0; i < numParticles; i++) { var particle = LK.getAsset('block', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8, width: BLOCK_SIZE / 3, // Increased particle size height: BLOCK_SIZE / 3, // Increased particle size color: block.graphics.color }); particle.x = block.x + (Math.random() - 0.5) * BLOCK_SIZE; particle.y = block.y + (Math.random() - 0.5) * BLOCK_SIZE; particles.push(particle); gameBoard.addChild(particle); // Animate particle tween(particle, { x: particle.x + (Math.random() - 0.5) * 300, // Increased movement range y: particle.y + (Math.random() - 0.5) * 300, // Increased movement range alpha: 0 }, { duration: 1500, // Increased duration onFinish: function onFinish() { gameBoard.removeChild(this); } }); } } // Move down blocks above cleared lines for (var i = 0; i < linesToClear.length; i++) { var clearedY = linesToClear[i]; // Move all blocks above the cleared line down for (var y = clearedY; y > 0; y--) { for (var x = 0; x < GRID_WIDTH; x++) { grid[y][x] = grid[y - 1][x]; if (grid[y][x] !== null) { grid[y][x].y = y * BLOCK_SIZE; } } } // Clear the top line for (var x = 0; x < GRID_WIDTH; x++) { grid[0][x] = null; } } }, 400); } function updateUI() { scoreText.setText('Score: ' + score); levelText.setText('Level: ' + level); linesText.setText('Lines: ' + lines); // Set score to LK score system LK.setScore(score); } function gameOver() { isGameOver = true; // Flash the grid tween(gameBoard, { alpha: 0.5 }, { duration: 500, onFinish: function onFinish() { tween(gameBoard, { alpha: 1 }, { duration: 500 }); } }); // Show game over dialog LK.setTimeout(function () { LK.showGameOver(); }, 1000); } function resetGame() { // Clear the grid for (var y = 0; y < GRID_HEIGHT; y++) { for (var x = 0; x < GRID_WIDTH; x++) { if (grid[y][x] !== null) { gameBoard.removeChild(grid[y][x]); grid[y][x] = null; } } } // Reset game variables score = 0; lines = 0; level = storage.level || 1; isGameOver = false; // Clear any active tetromino if (currentTetromino !== null) { gameBoard.removeChild(currentTetromino); currentTetromino = null; } // Clear next tetromino nextTetromino = null; // Update UI updateUI(); // Spawn new tetromino spawnTetromino(); } // Game update logic game.update = function () { if (isGameOver) { return; } // Initialize game if needed if (currentTetromino === null && nextTetromino === null) { spawnTetromino(); } // Update drop timer var currentTime = LK.ticks; var dropInterval = DROP_INTERVAL * Math.pow(DROP_SPEED_INCREASE, level - 1); if (currentTime - lastDropTime >= dropInterval / 16.67) { // Convert ms to ticks (60 FPS) moveTetrominoDown(); lastDropTime = currentTime; } }; // Start background music LK.playMusic('bgmusic', { loop: true }); // Initialize game resetGame();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
level: 1
});
/****
* Classes
****/
var Block = Container.expand(function () {
var self = Container.call(this);
self.blockType = '';
self.init = function (type) {
self.blockType = type;
var color = 'block_' + type;
self.graphics = self.attachAsset(color, {
anchorX: 0,
anchorY: 0,
alpha: 0.9
});
return self;
};
return self;
});
var Button = Container.expand(function () {
var self = Container.call(this);
self.init = function (label, iconType) {
var buttonGraphics = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0,
shape: 'ellipse' // Change shape to ellipse to make it circular
});
var text = new Text2(label, {
size: 100,
fill: 0xFFFFFF // White color for icon
});
text.anchor.set(0.5, 0.5);
self.addChild(text);
self.down = function (x, y, obj) {
buttonGraphics.alpha = 0.7;
};
self.up = function (x, y, obj) {
buttonGraphics.alpha = 1.0;
};
return self;
};
return self;
});
var Tetromino = Container.expand(function () {
var self = Container.call(this);
self.blocks = [];
self.shape = [];
self.type = '';
self.rotation = 0;
self.gridX = 0;
self.gridY = 0;
self.init = function (type) {
self.type = type;
self.createShape(type);
return self;
};
self.createShape = function (type) {
// Clear existing blocks
for (var i = 0; i < self.blocks.length; i++) {
self.removeChild(self.blocks[i]);
}
self.blocks = [];
// Create new shape based on type
switch (type) {
case 'i':
self.shape = [[0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]];
break;
case 'j':
self.shape = [[1, 0, 0], [1, 1, 1], [0, 0, 0]];
break;
case 'l':
self.shape = [[0, 0, 1], [1, 1, 1], [0, 0, 0]];
break;
case 'o':
self.shape = [[1, 1], [1, 1]];
break;
case 's':
self.shape = [[0, 1, 1], [1, 1, 0], [0, 0, 0]];
break;
case 't':
self.shape = [[0, 1, 0], [1, 1, 1], [0, 0, 0]];
break;
case 'z':
self.shape = [[1, 1, 0], [0, 1, 1], [0, 0, 0]];
break;
}
// Create blocks based on shape
for (var y = 0; y < self.shape.length; y++) {
for (var x = 0; x < self.shape[y].length; x++) {
if (self.shape[y][x]) {
var block = new Block().init(type);
block.x = x * BLOCK_SIZE;
block.y = y * BLOCK_SIZE;
self.addChild(block);
self.blocks.push(block);
}
}
}
};
self.rotate = function () {
if (self.type === 'o') {
return;
} // O pieces don't rotate
// Create a new rotated matrix
var newShape = [];
var size = self.shape.length;
for (var y = 0; y < size; y++) {
newShape[y] = [];
for (var x = 0; x < size; x++) {
newShape[y][x] = self.shape[size - 1 - x][y];
}
}
// Save original shape in case rotation is invalid
var originalShape = self.shape;
self.shape = newShape;
// Update block positions
self.updateBlocks();
// Check if the rotation is valid
if (!isValidPosition(self)) {
// Revert to original shape
self.shape = originalShape;
self.updateBlocks();
return false;
}
if (LK.getSound('tetromino_rotate')) {
LK.getSound('tetromino_rotate').play();
} else {
console.error("Sound 'tetromino_rotate' not found");
}
return true;
};
self.updateBlocks = function () {
var blockIndex = 0;
for (var y = 0; y < self.shape.length; y++) {
for (var x = 0; x < self.shape[y].length; x++) {
if (self.shape[y][x]) {
self.blocks[blockIndex].x = x * BLOCK_SIZE;
self.blocks[blockIndex].y = y * BLOCK_SIZE;
blockIndex++;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x9bbc0f
});
/****
* Game Code
****/
// Constants
var BLOCK_SIZE = 100; // Block size in pixels
var GRID_WIDTH = 10; // Grid width in blocks
var GRID_HEIGHT = 20; // Grid height in blocks
var GRID_OFFSET_X = (2048 - GRID_WIDTH * BLOCK_SIZE) / 2; // Center grid x position
var GRID_OFFSET_Y = 100; // Grid y position
var DROP_INTERVAL = 1000; // Initial interval between block drops in ms
var DROP_SPEED_INCREASE = 0.8; // Speed multiplier per level
// Game variables
var grid = []; // Grid state
var currentTetromino = null; // Current active tetromino
var nextTetromino = null; // Next tetromino to drop
var score = 0;
var level = storage.level || 1;
var lines = 0;
var isGameOver = false;
var dropTimer = 0;
var lastDropTime = 0;
var inputDelay = 0; // Delay for input controls to prevent too fast movement
// Initialize grid
for (var y = 0; y < GRID_HEIGHT; y++) {
grid[y] = [];
for (var x = 0; x < GRID_WIDTH; x++) {
grid[y][x] = null;
}
}
// Create grid background with lines
var gridBackground = new Container();
for (var y = 0; y < GRID_HEIGHT; y++) {
for (var x = 0; x < GRID_WIDTH; x++) {
var cell = LK.getAsset('grid_cell', {
anchorX: 0,
anchorY: 0,
alpha: 0.5
});
cell.x = x * BLOCK_SIZE;
cell.y = y * BLOCK_SIZE;
gridBackground.addChild(cell);
// Add vertical line
if (x < GRID_WIDTH - 1) {
var verticalLine = LK.getAsset('block', {
anchorX: 0,
anchorY: 0,
alpha: 0.2,
width: 20,
// Increased thickness
height: BLOCK_SIZE,
color: 0x000000 // Black color
});
verticalLine.x = (x + 1) * BLOCK_SIZE - 1;
verticalLine.y = y * BLOCK_SIZE;
gridBackground.addChild(verticalLine);
}
// Add horizontal line
if (y < GRID_HEIGHT - 1) {
var horizontalLine = LK.getAsset('block', {
anchorX: 0,
anchorY: 0,
alpha: 0.2,
width: BLOCK_SIZE,
height: 20,
// Increased thickness
color: 0x000000 // Black color
});
horizontalLine.x = x * BLOCK_SIZE;
horizontalLine.y = (y + 1) * BLOCK_SIZE - 1;
gridBackground.addChild(horizontalLine);
}
}
}
gridBackground.x = GRID_OFFSET_X;
gridBackground.y = GRID_OFFSET_Y;
game.addChild(gridBackground);
// Create game board container
var gameBoard = new Container();
gameBoard.x = GRID_OFFSET_X;
gameBoard.y = GRID_OFFSET_Y;
game.addChild(gameBoard);
// Next piece preview
var nextPieceContainer = new Container();
nextPieceContainer.x = GRID_OFFSET_X - 200;
nextPieceContainer.y = GRID_OFFSET_Y + 100;
game.addChild(nextPieceContainer);
// Create UI text elements
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0x000000
});
scoreText.anchor.set(0, 0);
scoreText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 50;
scoreText.y = GRID_OFFSET_Y;
game.addChild(scoreText);
var levelText = new Text2('Level: 1', {
size: 80,
fill: 0x000000
});
levelText.anchor.set(0, 0);
levelText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 50;
levelText.y = GRID_OFFSET_Y + 120;
game.addChild(levelText);
var linesText = new Text2('Lines: 0', {
size: 80,
fill: 0x000000
});
linesText.anchor.set(0, 0);
linesText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 50;
linesText.y = GRID_OFFSET_Y + 250;
game.addChild(linesText);
// Create control buttons
var leftButton = new Button().init('←');
leftButton.x = 300;
leftButton.y = 2432; // Move up by 200 pixels
leftButton.down = function () {
moveTetrominoLeft();
this.alpha = 0.7;
};
leftButton.up = function () {
this.alpha = 1.0;
};
game.addChild(leftButton);
var rightButton = new Button().init('→');
rightButton.x = 750;
rightButton.y = 2432; // Move up by 200 pixels
rightButton.down = function () {
moveTetrominoRight();
this.alpha = 0.7;
};
rightButton.up = function () {
this.alpha = 1.0;
};
game.addChild(rightButton);
var rotateButton = new Button().init('↻');
rotateButton.x = 1300;
rotateButton.y = 2432; // Move up by 200 pixels
rotateButton.down = function () {
rotateTetromino();
this.alpha = 0.7;
};
rotateButton.up = function () {
this.alpha = 1.0;
};
game.addChild(rotateButton);
var dropButton = new Button().init('↓');
dropButton.x = 1750;
dropButton.y = 2432; // Move up by 200 pixels
dropButton.down = function () {
dropTetromino();
this.alpha = 0.7;
};
dropButton.up = function () {
this.alpha = 1.0;
};
game.addChild(dropButton);
// Tetromino types
var tetrominoTypes = ['i', 'j', 'l', 'o', 's', 't', 'z'];
// Helper functions
function createRandomTetromino() {
var type = tetrominoTypes[Math.floor(Math.random() * tetrominoTypes.length)];
return new Tetromino().init(type);
}
function spawnTetromino() {
if (nextTetromino === null) {
nextTetromino = createRandomTetromino();
}
currentTetromino = nextTetromino;
nextTetromino = createRandomTetromino();
// Position the tetromino at the top center of the grid
currentTetromino.gridX = Math.floor(GRID_WIDTH / 2) - Math.floor(currentTetromino.shape.length / 2);
currentTetromino.gridY = 0;
// Update tetromino position
currentTetromino.x = currentTetromino.gridX * BLOCK_SIZE;
currentTetromino.y = currentTetromino.gridY * BLOCK_SIZE;
// Add to game board
gameBoard.addChild(currentTetromino);
// Update next piece preview
updateNextPiecePreview();
// Check if game is over
if (!isValidPosition(currentTetromino)) {
gameOver();
}
}
function updateNextPiecePreview() {
// Clear previous preview
while (nextPieceContainer.children.length > 0) {
nextPieceContainer.removeChild(nextPieceContainer.children[0]);
}
// Create a copy of the next tetromino for preview
var previewTetromino = new Tetromino().init(nextTetromino.type);
// Center it in the preview area
previewTetromino.x = 75 - previewTetromino.shape.length * BLOCK_SIZE / 4;
previewTetromino.y = 75 - previewTetromino.shape.length * BLOCK_SIZE / 4;
previewTetromino.scale.set(0.5, 0.5);
nextPieceContainer.addChild(previewTetromino);
}
function isValidPosition(tetromino) {
for (var y = 0; y < tetromino.shape.length; y++) {
for (var x = 0; x < tetromino.shape[y].length; x++) {
if (tetromino.shape[y][x]) {
var gridX = tetromino.gridX + x;
var gridY = tetromino.gridY + y;
// Check if out of bounds
if (gridX < 0 || gridX >= GRID_WIDTH || gridY >= GRID_HEIGHT) {
return false;
}
// Check if overlapping with locked blocks (and not above the grid)
if (gridY >= 0 && grid[gridY][gridX] !== null) {
return false;
}
}
}
}
return true;
}
function moveTetromino(dx, dy) {
if (currentTetromino === null || isGameOver) {
return false;
}
// Save original position
var originalX = currentTetromino.gridX;
var originalY = currentTetromino.gridY;
// Try to move
currentTetromino.gridX += dx;
currentTetromino.gridY += dy;
// Check if the new position is valid
if (!isValidPosition(currentTetromino)) {
// Revert position
currentTetromino.gridX = originalX;
currentTetromino.gridY = originalY;
// If we tried to move down and failed, lock the tetromino
if (dy > 0) {
lockTetromino();
}
return false;
}
// Update position
currentTetromino.x = currentTetromino.gridX * BLOCK_SIZE;
currentTetromino.y = currentTetromino.gridY * BLOCK_SIZE;
return true;
}
function moveTetrominoLeft() {
if (LK.ticks - inputDelay < 5) {
return;
} // Prevent too fast movement
inputDelay = LK.ticks;
moveTetromino(-1, 0);
}
function moveTetrominoRight() {
if (LK.ticks - inputDelay < 5) {
return;
} // Prevent too fast movement
inputDelay = LK.ticks;
moveTetromino(1, 0);
}
function moveTetrominoDown() {
return moveTetromino(0, 1);
}
function rotateTetromino() {
if (currentTetromino === null || isGameOver) {
return;
}
if (LK.ticks - inputDelay < 5) {
return;
} // Prevent too fast rotation
inputDelay = LK.ticks;
currentTetromino.rotate();
}
function dropTetromino() {
if (currentTetromino === null || isGameOver) {
return;
}
if (LK.ticks - inputDelay < 5) {
return;
} // Prevent too fast dropping
inputDelay = LK.ticks;
// Move tetromino down until it can't move anymore
while (moveTetrominoDown()) {
// Continue moving down
}
}
function lockTetromino() {
if (currentTetromino === null) {
return;
}
// Add blocks to grid
for (var y = 0; y < currentTetromino.shape.length; y++) {
for (var x = 0; x < currentTetromino.shape[y].length; x++) {
if (currentTetromino.shape[y][x]) {
var gridX = currentTetromino.gridX + x;
var gridY = currentTetromino.gridY + y;
// Skip if above the grid
if (gridY < 0) {
continue;
}
// Create a new block at this position
var block = new Block().init(currentTetromino.type);
block.x = gridX * BLOCK_SIZE;
block.y = gridY * BLOCK_SIZE;
// Add to grid and game board
grid[gridY][gridX] = block;
gameBoard.addChild(block);
}
}
}
// Remove the tetromino
gameBoard.removeChild(currentTetromino);
currentTetromino = null;
// Play sound
if (LK.getSound('tetromino_land')) {
LK.getSound('tetromino_land').play();
} else {
console.error("Sound 'tetromino_land' not found");
}
// Check for completed lines
checkLines();
// Spawn new tetromino
spawnTetromino();
}
function checkLines() {
var completedLines = 0;
var linesToClear = [];
// Check for completed lines
for (var y = 0; y < GRID_HEIGHT; y++) {
var isLineComplete = true;
for (var x = 0; x < GRID_WIDTH; x++) {
if (grid[y][x] === null) {
isLineComplete = false;
break;
}
}
if (isLineComplete) {
completedLines++;
linesToClear.push(y);
}
}
// If no lines were completed, return
if (completedLines === 0) {
return;
}
// Add score based on number of lines cleared
var points = 0;
switch (completedLines) {
case 1:
points = 100 * level;
break;
case 2:
points = 300 * level;
break;
case 3:
points = 500 * level;
break;
case 4:
points = 800 * level;
break;
}
score = Math.min(score + points, 9999);
lines += completedLines;
// Update level
var newLevel = Math.floor(lines / 10) + 1;
if (newLevel > level) {
level = newLevel;
if (LK.getSound('level_up')) {
LK.getSound('level_up').play();
} else {
console.error("Sound 'level_up' not found");
}
}
// Play sound
if (LK.getSound('line_clear')) {
LK.getSound('line_clear').play();
} else {
console.error("Sound 'line_clear' not found");
}
if (LK.getSound('hacked_speech')) {
LK.getSound('hacked_speech').play();
} else {
console.error("Sound 'hacked_speech' not found");
}
// Clear the lines
clearLines(linesToClear);
// Update UI
updateUI();
// Save high score
if (score > storage.highScore) {
storage.highScore = score;
}
}
function clearLines(linesToClear) {
// Create and add 'HACKED' text
var hackedText = new Text2('HACKED', {
size: 200,
fill: 0xffffff // White color
});
hackedText.anchor.set(0.5, 0.5);
hackedText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE / 2;
hackedText.y = GRID_OFFSET_Y + GRID_HEIGHT * BLOCK_SIZE / 2;
game.addChild(hackedText);
// Play digitized speech saying 'hacked'
if (LK.getSound('hacked_speech')) {
LK.getSound('hacked_speech').play();
} else {
console.error("Sound 'hacked_speech' not found");
}
// Flash the 'HACKED' text
tween(hackedText, {
alpha: 0
}, {
duration: 1600,
repeat: 10,
yoyo: true,
onFinish: function onFinish() {
game.removeChild(hackedText);
}
});
// Flash the lines to be cleared
for (var i = 0; i < linesToClear.length; i++) {
var y = linesToClear[i];
for (var x = 0; x < GRID_WIDTH; x++) {
if (grid[y][x] !== null) {
tween(grid[y][x], {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
tween(this, {
alpha: 1
}, {
duration: 200
});
}
});
}
}
}
// Schedule line clearing after the flash
LK.setTimeout(function () {
// Remove blocks from cleared lines with shattering effect
for (var i = 0; i < linesToClear.length; i++) {
var y = linesToClear[i];
for (var x = 0; x < GRID_WIDTH; x++) {
if (grid[y][x] !== null) {
// Create shattering effect
createShatterEffect(grid[y][x]);
gameBoard.removeChild(grid[y][x]);
grid[y][x] = null;
}
}
}
// Function to create shattering effect
function createShatterEffect(block) {
var particles = [];
var numParticles = 20; // Increased number of particles per block
for (var i = 0; i < numParticles; i++) {
var particle = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8,
width: BLOCK_SIZE / 3,
// Increased particle size
height: BLOCK_SIZE / 3,
// Increased particle size
color: block.graphics.color
});
particle.x = block.x + (Math.random() - 0.5) * BLOCK_SIZE;
particle.y = block.y + (Math.random() - 0.5) * BLOCK_SIZE;
particles.push(particle);
gameBoard.addChild(particle);
// Animate particle
tween(particle, {
x: particle.x + (Math.random() - 0.5) * 300,
// Increased movement range
y: particle.y + (Math.random() - 0.5) * 300,
// Increased movement range
alpha: 0
}, {
duration: 1500,
// Increased duration
onFinish: function onFinish() {
gameBoard.removeChild(this);
}
});
}
}
// Move down blocks above cleared lines
for (var i = 0; i < linesToClear.length; i++) {
var clearedY = linesToClear[i];
// Move all blocks above the cleared line down
for (var y = clearedY; y > 0; y--) {
for (var x = 0; x < GRID_WIDTH; x++) {
grid[y][x] = grid[y - 1][x];
if (grid[y][x] !== null) {
grid[y][x].y = y * BLOCK_SIZE;
}
}
}
// Clear the top line
for (var x = 0; x < GRID_WIDTH; x++) {
grid[0][x] = null;
}
}
}, 400);
}
function updateUI() {
scoreText.setText('Score: ' + score);
levelText.setText('Level: ' + level);
linesText.setText('Lines: ' + lines);
// Set score to LK score system
LK.setScore(score);
}
function gameOver() {
isGameOver = true;
// Flash the grid
tween(gameBoard, {
alpha: 0.5
}, {
duration: 500,
onFinish: function onFinish() {
tween(gameBoard, {
alpha: 1
}, {
duration: 500
});
}
});
// Show game over dialog
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
function resetGame() {
// Clear the grid
for (var y = 0; y < GRID_HEIGHT; y++) {
for (var x = 0; x < GRID_WIDTH; x++) {
if (grid[y][x] !== null) {
gameBoard.removeChild(grid[y][x]);
grid[y][x] = null;
}
}
}
// Reset game variables
score = 0;
lines = 0;
level = storage.level || 1;
isGameOver = false;
// Clear any active tetromino
if (currentTetromino !== null) {
gameBoard.removeChild(currentTetromino);
currentTetromino = null;
}
// Clear next tetromino
nextTetromino = null;
// Update UI
updateUI();
// Spawn new tetromino
spawnTetromino();
}
// Game update logic
game.update = function () {
if (isGameOver) {
return;
}
// Initialize game if needed
if (currentTetromino === null && nextTetromino === null) {
spawnTetromino();
}
// Update drop timer
var currentTime = LK.ticks;
var dropInterval = DROP_INTERVAL * Math.pow(DROP_SPEED_INCREASE, level - 1);
if (currentTime - lastDropTime >= dropInterval / 16.67) {
// Convert ms to ticks (60 FPS)
moveTetrominoDown();
lastDropTime = currentTime;
}
};
// Start background music
LK.playMusic('bgmusic', {
loop: true
});
// Initialize game
resetGame();