User prompt
Make touch screen buttons bigger
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'dropButton.x = 1900')' in or related to this line: 'dropButton.x = 1900;' Line Number: 293
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'rotateButton.x = 1650')' in or related to this line: 'rotateButton.x = 1650;' Line Number: 281
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'rightButton.x = 350')' in or related to this line: 'rightButton.x = 350;' Line Number: 269
User prompt
Do the same thing again
User prompt
Make play screen much bigger but keep control buttons the same size
User prompt
Make Tetris piece smaller
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'dropButton.x = 1948')' in or related to this line: 'dropButton.x = 1948;' Line Number: 292
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'rotateButton.x = 1698')' in or related to this line: 'rotateButton.x = 1698;' Line Number: 289
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'rightButton.x = 350')' in or related to this line: 'rightButton.x = 350;' Line Number: 286
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'nextPieceText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 100')' in or related to this line: 'nextPieceText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 100;' Line Number: 267
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'linesText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 100')' in or related to this line: 'linesText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 100;' Line Number: 253
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'levelText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 100')' in or related to this line: 'levelText.x = GRID_OFFSET_X + GRID_WIDTH * BLOCK_SIZE + 100;' Line Number: 240
User prompt
Make bigger
Code edit (1 edits merged)
Please save this source code
User prompt
Tetris Horizon
Initial prompt
Simpler big game of Tetris but make it landscape. Easy viable touch pad area on screen with icons
/**** * 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();