Code edit (1 edits merged)
Please save this source code
User prompt
Touch Tetrimino Blitz
User prompt
Touch controls: On-screen buttons (← → ↓ ↑ Rotate + Hard Drop). Responsive design: Fits all screen sizes (portrait/landscape). Performance: 60 FPS even on low-end devices."
Initial prompt
Create a 1:1 exact clone of the original Tetris with these SPECIFIC details: Core Mechanics: 7 Tetrominoes (I, J, L, O, S, T, Z) with identical shapes/colors. Hard drop (instant fall), soft drop (accelerated fall), hold piece. Rotation system: Classic Nintendo "wall kick" behavior (test with tight spaces). Game Rules: Blocks lock instantly on landing (no delay). Line clear: 1 line = 100 pts, 2 lines = 300 pts, 3 lines = 500 pts, Tetris (4 lines) = 800 pts. Level progression: Speed increases every 10 lines (Lv1 = 0.8s/fall, Lv10 = 0.1s/fall). Visuals/Sound: Exact 8-bit aesthetic: Grey background, black playfield grid. I-piece = cyan, O-piece = yellow, etc. (original NES colors). Sound effects: Move: blip Rotate: click Line clear: fanfare Controls: Keyboard: ← → = Move ↑ = Rotate ↓ = Soft drop Space = Hard drop Touch: On-screen buttons (if mobile). Mandatory Tests: T-spin must work (T-piece into 3-corner slot). "Death" when blocks stack to top. NO deviations from classic Tetris. Use the 1989 NES version as reference."
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Board class var Board = Container.expand(function () { var self = Container.call(this); self.grid = []; self.cells = []; self.bg = null; self.init = function () { self.grid = []; for (var y = 0; y < BOARD_ROWS; ++y) { var row = []; for (var x = 0; x < BOARD_COLS; ++x) { row.push(null); } self.grid.push(row); } self.bg = self.attachAsset('boardbg', { anchorX: 0, anchorY: 0, width: BOARD_COLS * CELL_SIZE, height: BOARD_ROWS * CELL_SIZE, x: 0, y: 0 }); }; self.isInside = function (x, y) { return x >= 0 && x < BOARD_COLS && y >= 0 && y < BOARD_ROWS; }; self.isCellEmpty = function (x, y) { if (!self.isInside(x, y)) return false; return self.grid[y][x] === null; }; self.canPlace = function (type, xCell, yCell, rotation) { var shape = TETROMINOS[type][rotation]; for (var i = 0; i < shape.length; ++i) { var x = xCell + shape[i][0]; var y = yCell + shape[i][1]; if (!self.isInside(x, y) || self.grid[y][x] !== null) { return false; } } return true; }; self.placeTetrimino = function (type, xCell, yCell, rotation) { var shape = TETROMINOS[type][rotation]; for (var i = 0; i < shape.length; ++i) { var x = xCell + shape[i][0]; var y = yCell + shape[i][1]; self.grid[y][x] = type; } }; self.clearLines = function () { var lines = []; for (var y = 0; y < BOARD_ROWS; ++y) { var full = true; for (var x = 0; x < BOARD_COLS; ++x) { if (self.grid[y][x] === null) { full = false; break; } } if (full) lines.push(y); } for (var i = 0; i < lines.length; ++i) { var y = lines[i]; for (var yy = y; yy > 0; --yy) { for (var x = 0; x < BOARD_COLS; ++x) { self.grid[yy][x] = self.grid[yy - 1][x]; } } for (var x = 0; x < BOARD_COLS; ++x) { self.grid[0][x] = null; } } return lines.length; }; self.renderBlocks = function () { // Remove old for (var i = 0; i < self.cells.length; ++i) { self.cells[i].destroy(); } self.cells = []; // Draw new for (var y = 0; y < BOARD_ROWS; ++y) { for (var x = 0; x < BOARD_COLS; ++x) { var type = self.grid[y][x]; if (type) { var block = self.attachAsset(getTetrominoAsset(type), { anchorX: 0.5, anchorY: 0.5, x: x * CELL_SIZE + CELL_SIZE / 2, y: y * CELL_SIZE + CELL_SIZE / 2 }); self.cells.push(block); } } } }; return self; }); // Button class var GameButton = Container.expand(function () { var self = Container.call(this); self.bg = null; self.icon = null; self.label = null; self.action = null; self.init = function (iconType, label, action) { self.bg = self.attachAsset('btn', { anchorX: 0.5, anchorY: 0.5 }); if (iconType === 'left') { self.icon = self.attachAsset('btnArrow', { anchorX: 0.5, anchorY: 0.5, rotation: Math.PI }); } else if (iconType === 'right') { self.icon = self.attachAsset('btnArrow', { anchorX: 0.5, anchorY: 0.5, rotation: 0 }); } else if (iconType === 'down') { self.icon = self.attachAsset('btnArrow', { anchorX: 0.5, anchorY: 0.5, rotation: Math.PI / 2 }); } else if (iconType === 'up') { self.icon = self.attachAsset('btnArrow', { anchorX: 0.5, anchorY: 0.5, rotation: -Math.PI / 2 }); } else if (iconType === 'rotate') { self.label = new Text2('⟳', { size: 90, fill: "#fff" }); self.label.anchor.set(0.5, 0.5); self.addChild(self.label); } else if (iconType === 'drop') { self.label = new Text2('⇩', { size: 90, fill: "#fff" }); self.label.anchor.set(0.5, 0.5); self.addChild(self.label); } self.action = action; }; self.down = function (x, y, obj) { if (self.action) self.action(); }; return self; }); // Tetrimino class var Tetrimino = Container.expand(function () { var self = Container.call(this); self.type = null; self.rotation = 0; self.xCell = 0; self.yCell = 0; self.blocks = []; self.ghostBlocks = []; self.isGhost = false; self.init = function (type, rotation, xCell, yCell, isGhost) { self.type = type; self.rotation = rotation; self.xCell = xCell; self.yCell = yCell; self.isGhost = !!isGhost; self.clearBlocks(); self.createBlocks(); }; self.clearBlocks = function () { for (var i = 0; i < self.blocks.length; ++i) { self.blocks[i].destroy(); } self.blocks = []; for (var i = 0; i < self.ghostBlocks.length; ++i) { self.ghostBlocks[i].destroy(); } self.ghostBlocks = []; }; self.createBlocks = function () { var shape = self.isGhost ? getGhostAsset() : getTetrominoAsset(self.type); var positions = TETROMINOS[self.type][self.rotation]; for (var i = 0; i < positions.length; ++i) { var block = self.attachAsset(shape, { anchorX: 0.5, anchorY: 0.5, x: positions[i][0] * CELL_SIZE + CELL_SIZE / 2, y: positions[i][1] * CELL_SIZE + CELL_SIZE / 2, alpha: self.isGhost ? 0.3 : 1 }); self.blocks.push(block); } }; self.setPosition = function (xCell, yCell) { self.xCell = xCell; self.yCell = yCell; self.x = BOARD_X + xCell * CELL_SIZE; self.y = BOARD_Y + yCell * CELL_SIZE; }; self.setRotation = function (rotation) { self.rotation = rotation; self.clearBlocks(); self.createBlocks(); self.setPosition(self.xCell, self.yCell); }; self.getBlockPositions = function (xCell, yCell, rotation) { var pos = []; var shape = TETROMINOS[self.type][rotation]; for (var i = 0; i < shape.length; ++i) { pos.push([xCell + shape[i][0], yCell + shape[i][1]]); } return pos; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181828 }); /**** * Game Code ****/ // Tetrimino definitions (relative block positions for each rotation) // Button shapes // Board background // Ghost piece (semi-transparent) // Tetrimino colors (7 types) // Responsive scaling var TETROMINOS = { I: [[[0, 1], [1, 1], [2, 1], [3, 1]], // 0 [[2, 0], [2, 1], [2, 2], [2, 3]], // 1 [[0, 2], [1, 2], [2, 2], [3, 2]], // 2 [[1, 0], [1, 1], [1, 2], [1, 3]] // 3 ], O: [[[1, 0], [2, 0], [1, 1], [2, 1]], // 0 [[1, 0], [2, 0], [1, 1], [2, 1]], // 1 [[1, 0], [2, 0], [1, 1], [2, 1]], // 2 [[1, 0], [2, 0], [1, 1], [2, 1]] // 3 ], T: [[[1, 0], [0, 1], [1, 1], [2, 1]], // 0 [[1, 0], [1, 1], [2, 1], [1, 2]], // 1 [[0, 1], [1, 1], [2, 1], [1, 2]], // 2 [[1, 0], [0, 1], [1, 1], [1, 2]] // 3 ], S: [[[1, 0], [2, 0], [0, 1], [1, 1]], // 0 [[1, 0], [1, 1], [2, 1], [2, 2]], // 1 [[1, 1], [2, 1], [0, 2], [1, 2]], // 2 [[0, 0], [0, 1], [1, 1], [1, 2]] // 3 ], Z: [[[0, 0], [1, 0], [1, 1], [2, 1]], // 0 [[2, 0], [1, 1], [2, 1], [1, 2]], // 1 [[0, 1], [1, 1], [1, 2], [2, 2]], // 2 [[1, 0], [0, 1], [1, 1], [0, 2]] // 3 ], J: [[[0, 0], [0, 1], [1, 1], [2, 1]], // 0 [[1, 0], [2, 0], [1, 1], [1, 2]], // 1 [[0, 1], [1, 1], [2, 1], [2, 2]], // 2 [[1, 0], [1, 1], [0, 2], [1, 2]] // 3 ], L: [[[2, 0], [0, 1], [1, 1], [2, 1]], // 0 [[1, 0], [1, 1], [1, 2], [2, 2]], // 1 [[0, 1], [1, 1], [2, 1], [0, 2]], // 2 [[0, 0], [1, 0], [1, 1], [1, 2]] // 3 ] }; var TETROMINO_TYPES = ['I', 'O', 'T', 'S', 'Z', 'J', 'L']; // Board constants var BOARD_COLS = 10; var BOARD_ROWS = 20; var CELL_SIZE = 80; // px, will be scaled to fit var BOARD_X = 0; var BOARD_Y = 0; // Helper: get color asset id for a tetromino type function getTetrominoAsset(type) { return type; } // Helper: get ghost asset function getGhostAsset() { return 'ghost'; } function updateBoardLayout() { // Center board BOARD_X = Math.floor((2048 - BOARD_COLS * CELL_SIZE) / 2); BOARD_Y = Math.floor((2732 - BOARD_ROWS * CELL_SIZE) / 2); if (BOARD_X < 120) BOARD_X = 120; // Avoid top left menu if (BOARD_Y < 0) BOARD_Y = 0; } // Game state var board = null; var current = null; var nextType = null; var ghost = null; var dropTimer = 0; var dropInterval = 36; // 36 ticks = 0.6s at 60fps var moveDelay = 0; var moveRepeat = 0; var gameOver = false; var score = 0; var linesCleared = 0; var level = 1; var softDrop = false; var hardDrop = false; var controlsLocked = false; // UI var scoreTxt = null; var linesTxt = null; var levelTxt = null; var btnLeft = null; var btnRight = null; var btnDown = null; var btnRotate = null; var btnDrop = null; var buttons = []; // Helper: random tetromino function randomTetromino() { return TETROMINO_TYPES[Math.floor(Math.random() * TETROMINO_TYPES.length)]; } // Helper: spawn new tetrimino function spawnTetrimino() { var type = nextType || randomTetromino(); nextType = randomTetromino(); var t = new Tetrimino(); t.init(type, 0, Math.floor(BOARD_COLS / 2) - 2, 0, false); return t; } // Helper: check game over function checkGameOver() { if (!board.canPlace(current.type, current.xCell, current.yCell, current.rotation)) { gameOver = true; LK.showGameOver(); } } // Helper: lock current piece function lockCurrent() { board.placeTetrimino(current.type, current.xCell, current.yCell, current.rotation); var cleared = board.clearLines(); if (cleared > 0) { score += [0, 40, 100, 300, 1200][cleared] * level; linesCleared += cleared; if (linesCleared >= level * 10) { level += 1; if (dropInterval > 6) dropInterval -= 3; } } board.renderBlocks(); updateScore(); current.destroy(); current = spawnTetrimino(); game.addChild(current); updateGhost(); checkGameOver(); } // Helper: update ghost piece function updateGhost() { if (ghost) ghost.destroy(); ghost = new Tetrimino(); ghost.init(current.type, current.rotation, current.xCell, current.yCell, true); var y = current.yCell; while (board.canPlace(current.type, current.xCell, y + 1, current.rotation)) { y += 1; } ghost.setPosition(current.xCell, y); game.addChild(ghost); ghost.zIndex = -1; } // Helper: update score UI function updateScore() { scoreTxt.setText(score + ""); linesTxt.setText("Lines: " + linesCleared); levelTxt.setText("Level: " + level); } // Helper: move current piece function tryMove(dx, dy, rot) { if (controlsLocked) return; var nx = current.xCell + (dx || 0); var ny = current.yCell + (dy || 0); var nr = (current.rotation + (rot || 0) + 4) % 4; if (board.canPlace(current.type, nx, ny, nr)) { current.setRotation(nr); current.setPosition(nx, ny); updateGhost(); return true; } return false; } // Helper: hard drop function doHardDrop() { if (controlsLocked) return; var y = current.yCell; while (board.canPlace(current.type, current.xCell, y + 1, current.rotation)) { y += 1; } current.setPosition(current.xCell, y); lockCurrent(); } // Helper: soft drop function doSoftDrop() { if (controlsLocked) return; if (!tryMove(0, 1, 0)) { lockCurrent(); } } // Helper: tick drop function tickDrop() { if (controlsLocked) return; if (!tryMove(0, 1, 0)) { lockCurrent(); } } // UI: create buttons function createButtons() { var yBase = 2732 - 220; var xBase = 2048 / 2; btnLeft = new GameButton(); btnLeft.init('left', '', function () { tryMove(-1, 0, 0); }); btnLeft.x = xBase - 320; btnLeft.y = yBase; btnRight = new GameButton(); btnRight.init('right', '', function () { tryMove(1, 0, 0); }); btnRight.x = xBase - 80; btnRight.y = yBase; btnDown = new GameButton(); btnDown.init('down', '', function () { doSoftDrop(); }); btnDown.x = xBase + 160; btnDown.y = yBase; btnRotate = new GameButton(); btnRotate.init('rotate', '', function () { tryMove(0, 0, 1); }); btnRotate.x = xBase + 400; btnRotate.y = yBase; btnDrop = new GameButton(); btnDrop.init('drop', '', function () { doHardDrop(); }); btnDrop.x = xBase + 640; btnDrop.y = yBase; buttons = [btnLeft, btnRight, btnDown, btnRotate, btnDrop]; for (var i = 0; i < buttons.length; ++i) { LK.gui.bottom.addChild(buttons[i]); } } // UI: create score function createScoreUI() { scoreTxt = new Text2("0", { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.x = 2048 / 2; scoreTxt.y = 40; linesTxt = new Text2("Lines: 0", { size: 60, fill: "#fff" }); linesTxt.anchor.set(0.5, 0); LK.gui.top.addChild(linesTxt); linesTxt.x = 2048 / 2 - 300; linesTxt.y = 180; levelTxt = new Text2("Level: 1", { size: 60, fill: "#fff" }); levelTxt.anchor.set(0.5, 0); LK.gui.top.addChild(levelTxt); levelTxt.x = 2048 / 2 + 300; levelTxt.y = 180; } // Touch drag controls (swipe left/right/down/up) var dragStartX = null, dragStartY = null, dragActive = false; game.down = function (x, y, obj) { dragStartX = x; dragStartY = y; dragActive = true; }; game.up = function (x, y, obj) { dragActive = false; }; game.move = function (x, y, obj) { if (!dragActive) return; var dx = x - dragStartX; var dy = y - dragStartY; if (Math.abs(dx) > 80) { if (dx > 0) { tryMove(1, 0, 0); } else { tryMove(-1, 0, 0); } dragStartX = x; dragStartY = y; } if (Math.abs(dy) > 80) { if (dy > 0) { doSoftDrop(); } else { tryMove(0, 0, 1); } dragStartX = x; dragStartY = y; } }; // Main game setup function startGame() { updateBoardLayout(); board = new Board(); board.init(); board.x = BOARD_X; board.y = BOARD_Y; game.addChild(board); board.renderBlocks(); current = spawnTetrimino(); game.addChild(current); updateGhost(); score = 0; linesCleared = 0; level = 1; dropInterval = 36; gameOver = false; updateScore(); } // UI setup createScoreUI(); createButtons(); startGame(); // Main game loop game.update = function () { if (gameOver) return; dropTimer++; if (dropTimer >= (softDrop ? 3 : dropInterval)) { tickDrop(); dropTimer = 0; } }; // Reset on game over LK.on('gameover', function () { controlsLocked = true; LK.setTimeout(function () { controlsLocked = false; startGame(); }, 1200); });
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,590 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+// Board class
+var Board = Container.expand(function () {
+ var self = Container.call(this);
+ self.grid = [];
+ self.cells = [];
+ self.bg = null;
+ self.init = function () {
+ self.grid = [];
+ for (var y = 0; y < BOARD_ROWS; ++y) {
+ var row = [];
+ for (var x = 0; x < BOARD_COLS; ++x) {
+ row.push(null);
+ }
+ self.grid.push(row);
+ }
+ self.bg = self.attachAsset('boardbg', {
+ anchorX: 0,
+ anchorY: 0,
+ width: BOARD_COLS * CELL_SIZE,
+ height: BOARD_ROWS * CELL_SIZE,
+ x: 0,
+ y: 0
+ });
+ };
+ self.isInside = function (x, y) {
+ return x >= 0 && x < BOARD_COLS && y >= 0 && y < BOARD_ROWS;
+ };
+ self.isCellEmpty = function (x, y) {
+ if (!self.isInside(x, y)) return false;
+ return self.grid[y][x] === null;
+ };
+ self.canPlace = function (type, xCell, yCell, rotation) {
+ var shape = TETROMINOS[type][rotation];
+ for (var i = 0; i < shape.length; ++i) {
+ var x = xCell + shape[i][0];
+ var y = yCell + shape[i][1];
+ if (!self.isInside(x, y) || self.grid[y][x] !== null) {
+ return false;
+ }
+ }
+ return true;
+ };
+ self.placeTetrimino = function (type, xCell, yCell, rotation) {
+ var shape = TETROMINOS[type][rotation];
+ for (var i = 0; i < shape.length; ++i) {
+ var x = xCell + shape[i][0];
+ var y = yCell + shape[i][1];
+ self.grid[y][x] = type;
+ }
+ };
+ self.clearLines = function () {
+ var lines = [];
+ for (var y = 0; y < BOARD_ROWS; ++y) {
+ var full = true;
+ for (var x = 0; x < BOARD_COLS; ++x) {
+ if (self.grid[y][x] === null) {
+ full = false;
+ break;
+ }
+ }
+ if (full) lines.push(y);
+ }
+ for (var i = 0; i < lines.length; ++i) {
+ var y = lines[i];
+ for (var yy = y; yy > 0; --yy) {
+ for (var x = 0; x < BOARD_COLS; ++x) {
+ self.grid[yy][x] = self.grid[yy - 1][x];
+ }
+ }
+ for (var x = 0; x < BOARD_COLS; ++x) {
+ self.grid[0][x] = null;
+ }
+ }
+ return lines.length;
+ };
+ self.renderBlocks = function () {
+ // Remove old
+ for (var i = 0; i < self.cells.length; ++i) {
+ self.cells[i].destroy();
+ }
+ self.cells = [];
+ // Draw new
+ for (var y = 0; y < BOARD_ROWS; ++y) {
+ for (var x = 0; x < BOARD_COLS; ++x) {
+ var type = self.grid[y][x];
+ if (type) {
+ var block = self.attachAsset(getTetrominoAsset(type), {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: x * CELL_SIZE + CELL_SIZE / 2,
+ y: y * CELL_SIZE + CELL_SIZE / 2
+ });
+ self.cells.push(block);
+ }
+ }
+ }
+ };
+ return self;
+});
+// Button class
+var GameButton = Container.expand(function () {
+ var self = Container.call(this);
+ self.bg = null;
+ self.icon = null;
+ self.label = null;
+ self.action = null;
+ self.init = function (iconType, label, action) {
+ self.bg = self.attachAsset('btn', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ if (iconType === 'left') {
+ self.icon = self.attachAsset('btnArrow', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ rotation: Math.PI
+ });
+ } else if (iconType === 'right') {
+ self.icon = self.attachAsset('btnArrow', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ rotation: 0
+ });
+ } else if (iconType === 'down') {
+ self.icon = self.attachAsset('btnArrow', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ rotation: Math.PI / 2
+ });
+ } else if (iconType === 'up') {
+ self.icon = self.attachAsset('btnArrow', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ rotation: -Math.PI / 2
+ });
+ } else if (iconType === 'rotate') {
+ self.label = new Text2('⟳', {
+ size: 90,
+ fill: "#fff"
+ });
+ self.label.anchor.set(0.5, 0.5);
+ self.addChild(self.label);
+ } else if (iconType === 'drop') {
+ self.label = new Text2('⇩', {
+ size: 90,
+ fill: "#fff"
+ });
+ self.label.anchor.set(0.5, 0.5);
+ self.addChild(self.label);
+ }
+ self.action = action;
+ };
+ self.down = function (x, y, obj) {
+ if (self.action) self.action();
+ };
+ return self;
+});
+// Tetrimino class
+var Tetrimino = Container.expand(function () {
+ var self = Container.call(this);
+ self.type = null;
+ self.rotation = 0;
+ self.xCell = 0;
+ self.yCell = 0;
+ self.blocks = [];
+ self.ghostBlocks = [];
+ self.isGhost = false;
+ self.init = function (type, rotation, xCell, yCell, isGhost) {
+ self.type = type;
+ self.rotation = rotation;
+ self.xCell = xCell;
+ self.yCell = yCell;
+ self.isGhost = !!isGhost;
+ self.clearBlocks();
+ self.createBlocks();
+ };
+ self.clearBlocks = function () {
+ for (var i = 0; i < self.blocks.length; ++i) {
+ self.blocks[i].destroy();
+ }
+ self.blocks = [];
+ for (var i = 0; i < self.ghostBlocks.length; ++i) {
+ self.ghostBlocks[i].destroy();
+ }
+ self.ghostBlocks = [];
+ };
+ self.createBlocks = function () {
+ var shape = self.isGhost ? getGhostAsset() : getTetrominoAsset(self.type);
+ var positions = TETROMINOS[self.type][self.rotation];
+ for (var i = 0; i < positions.length; ++i) {
+ var block = self.attachAsset(shape, {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: positions[i][0] * CELL_SIZE + CELL_SIZE / 2,
+ y: positions[i][1] * CELL_SIZE + CELL_SIZE / 2,
+ alpha: self.isGhost ? 0.3 : 1
+ });
+ self.blocks.push(block);
+ }
+ };
+ self.setPosition = function (xCell, yCell) {
+ self.xCell = xCell;
+ self.yCell = yCell;
+ self.x = BOARD_X + xCell * CELL_SIZE;
+ self.y = BOARD_Y + yCell * CELL_SIZE;
+ };
+ self.setRotation = function (rotation) {
+ self.rotation = rotation;
+ self.clearBlocks();
+ self.createBlocks();
+ self.setPosition(self.xCell, self.yCell);
+ };
+ self.getBlockPositions = function (xCell, yCell, rotation) {
+ var pos = [];
+ var shape = TETROMINOS[self.type][rotation];
+ for (var i = 0; i < shape.length; ++i) {
+ pos.push([xCell + shape[i][0], yCell + shape[i][1]]);
+ }
+ return pos;
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
+ backgroundColor: 0x181828
+});
+
+/****
+* Game Code
+****/
+// Tetrimino definitions (relative block positions for each rotation)
+// Button shapes
+// Board background
+// Ghost piece (semi-transparent)
+// Tetrimino colors (7 types)
+// Responsive scaling
+var TETROMINOS = {
+ I: [[[0, 1], [1, 1], [2, 1], [3, 1]],
+ // 0
+ [[2, 0], [2, 1], [2, 2], [2, 3]],
+ // 1
+ [[0, 2], [1, 2], [2, 2], [3, 2]],
+ // 2
+ [[1, 0], [1, 1], [1, 2], [1, 3]] // 3
+ ],
+ O: [[[1, 0], [2, 0], [1, 1], [2, 1]],
+ // 0
+ [[1, 0], [2, 0], [1, 1], [2, 1]],
+ // 1
+ [[1, 0], [2, 0], [1, 1], [2, 1]],
+ // 2
+ [[1, 0], [2, 0], [1, 1], [2, 1]] // 3
+ ],
+ T: [[[1, 0], [0, 1], [1, 1], [2, 1]],
+ // 0
+ [[1, 0], [1, 1], [2, 1], [1, 2]],
+ // 1
+ [[0, 1], [1, 1], [2, 1], [1, 2]],
+ // 2
+ [[1, 0], [0, 1], [1, 1], [1, 2]] // 3
+ ],
+ S: [[[1, 0], [2, 0], [0, 1], [1, 1]],
+ // 0
+ [[1, 0], [1, 1], [2, 1], [2, 2]],
+ // 1
+ [[1, 1], [2, 1], [0, 2], [1, 2]],
+ // 2
+ [[0, 0], [0, 1], [1, 1], [1, 2]] // 3
+ ],
+ Z: [[[0, 0], [1, 0], [1, 1], [2, 1]],
+ // 0
+ [[2, 0], [1, 1], [2, 1], [1, 2]],
+ // 1
+ [[0, 1], [1, 1], [1, 2], [2, 2]],
+ // 2
+ [[1, 0], [0, 1], [1, 1], [0, 2]] // 3
+ ],
+ J: [[[0, 0], [0, 1], [1, 1], [2, 1]],
+ // 0
+ [[1, 0], [2, 0], [1, 1], [1, 2]],
+ // 1
+ [[0, 1], [1, 1], [2, 1], [2, 2]],
+ // 2
+ [[1, 0], [1, 1], [0, 2], [1, 2]] // 3
+ ],
+ L: [[[2, 0], [0, 1], [1, 1], [2, 1]],
+ // 0
+ [[1, 0], [1, 1], [1, 2], [2, 2]],
+ // 1
+ [[0, 1], [1, 1], [2, 1], [0, 2]],
+ // 2
+ [[0, 0], [1, 0], [1, 1], [1, 2]] // 3
+ ]
+};
+var TETROMINO_TYPES = ['I', 'O', 'T', 'S', 'Z', 'J', 'L'];
+// Board constants
+var BOARD_COLS = 10;
+var BOARD_ROWS = 20;
+var CELL_SIZE = 80; // px, will be scaled to fit
+var BOARD_X = 0;
+var BOARD_Y = 0;
+// Helper: get color asset id for a tetromino type
+function getTetrominoAsset(type) {
+ return type;
+}
+// Helper: get ghost asset
+function getGhostAsset() {
+ return 'ghost';
+}
+function updateBoardLayout() {
+ // Center board
+ BOARD_X = Math.floor((2048 - BOARD_COLS * CELL_SIZE) / 2);
+ BOARD_Y = Math.floor((2732 - BOARD_ROWS * CELL_SIZE) / 2);
+ if (BOARD_X < 120) BOARD_X = 120; // Avoid top left menu
+ if (BOARD_Y < 0) BOARD_Y = 0;
+}
+// Game state
+var board = null;
+var current = null;
+var nextType = null;
+var ghost = null;
+var dropTimer = 0;
+var dropInterval = 36; // 36 ticks = 0.6s at 60fps
+var moveDelay = 0;
+var moveRepeat = 0;
+var gameOver = false;
+var score = 0;
+var linesCleared = 0;
+var level = 1;
+var softDrop = false;
+var hardDrop = false;
+var controlsLocked = false;
+// UI
+var scoreTxt = null;
+var linesTxt = null;
+var levelTxt = null;
+var btnLeft = null;
+var btnRight = null;
+var btnDown = null;
+var btnRotate = null;
+var btnDrop = null;
+var buttons = [];
+// Helper: random tetromino
+function randomTetromino() {
+ return TETROMINO_TYPES[Math.floor(Math.random() * TETROMINO_TYPES.length)];
+}
+// Helper: spawn new tetrimino
+function spawnTetrimino() {
+ var type = nextType || randomTetromino();
+ nextType = randomTetromino();
+ var t = new Tetrimino();
+ t.init(type, 0, Math.floor(BOARD_COLS / 2) - 2, 0, false);
+ return t;
+}
+// Helper: check game over
+function checkGameOver() {
+ if (!board.canPlace(current.type, current.xCell, current.yCell, current.rotation)) {
+ gameOver = true;
+ LK.showGameOver();
+ }
+}
+// Helper: lock current piece
+function lockCurrent() {
+ board.placeTetrimino(current.type, current.xCell, current.yCell, current.rotation);
+ var cleared = board.clearLines();
+ if (cleared > 0) {
+ score += [0, 40, 100, 300, 1200][cleared] * level;
+ linesCleared += cleared;
+ if (linesCleared >= level * 10) {
+ level += 1;
+ if (dropInterval > 6) dropInterval -= 3;
+ }
+ }
+ board.renderBlocks();
+ updateScore();
+ current.destroy();
+ current = spawnTetrimino();
+ game.addChild(current);
+ updateGhost();
+ checkGameOver();
+}
+// Helper: update ghost piece
+function updateGhost() {
+ if (ghost) ghost.destroy();
+ ghost = new Tetrimino();
+ ghost.init(current.type, current.rotation, current.xCell, current.yCell, true);
+ var y = current.yCell;
+ while (board.canPlace(current.type, current.xCell, y + 1, current.rotation)) {
+ y += 1;
+ }
+ ghost.setPosition(current.xCell, y);
+ game.addChild(ghost);
+ ghost.zIndex = -1;
+}
+// Helper: update score UI
+function updateScore() {
+ scoreTxt.setText(score + "");
+ linesTxt.setText("Lines: " + linesCleared);
+ levelTxt.setText("Level: " + level);
+}
+// Helper: move current piece
+function tryMove(dx, dy, rot) {
+ if (controlsLocked) return;
+ var nx = current.xCell + (dx || 0);
+ var ny = current.yCell + (dy || 0);
+ var nr = (current.rotation + (rot || 0) + 4) % 4;
+ if (board.canPlace(current.type, nx, ny, nr)) {
+ current.setRotation(nr);
+ current.setPosition(nx, ny);
+ updateGhost();
+ return true;
+ }
+ return false;
+}
+// Helper: hard drop
+function doHardDrop() {
+ if (controlsLocked) return;
+ var y = current.yCell;
+ while (board.canPlace(current.type, current.xCell, y + 1, current.rotation)) {
+ y += 1;
+ }
+ current.setPosition(current.xCell, y);
+ lockCurrent();
+}
+// Helper: soft drop
+function doSoftDrop() {
+ if (controlsLocked) return;
+ if (!tryMove(0, 1, 0)) {
+ lockCurrent();
+ }
+}
+// Helper: tick drop
+function tickDrop() {
+ if (controlsLocked) return;
+ if (!tryMove(0, 1, 0)) {
+ lockCurrent();
+ }
+}
+// UI: create buttons
+function createButtons() {
+ var yBase = 2732 - 220;
+ var xBase = 2048 / 2;
+ btnLeft = new GameButton();
+ btnLeft.init('left', '', function () {
+ tryMove(-1, 0, 0);
+ });
+ btnLeft.x = xBase - 320;
+ btnLeft.y = yBase;
+ btnRight = new GameButton();
+ btnRight.init('right', '', function () {
+ tryMove(1, 0, 0);
+ });
+ btnRight.x = xBase - 80;
+ btnRight.y = yBase;
+ btnDown = new GameButton();
+ btnDown.init('down', '', function () {
+ doSoftDrop();
+ });
+ btnDown.x = xBase + 160;
+ btnDown.y = yBase;
+ btnRotate = new GameButton();
+ btnRotate.init('rotate', '', function () {
+ tryMove(0, 0, 1);
+ });
+ btnRotate.x = xBase + 400;
+ btnRotate.y = yBase;
+ btnDrop = new GameButton();
+ btnDrop.init('drop', '', function () {
+ doHardDrop();
+ });
+ btnDrop.x = xBase + 640;
+ btnDrop.y = yBase;
+ buttons = [btnLeft, btnRight, btnDown, btnRotate, btnDrop];
+ for (var i = 0; i < buttons.length; ++i) {
+ LK.gui.bottom.addChild(buttons[i]);
+ }
+}
+// UI: create score
+function createScoreUI() {
+ scoreTxt = new Text2("0", {
+ size: 120,
+ fill: "#fff"
+ });
+ scoreTxt.anchor.set(0.5, 0);
+ LK.gui.top.addChild(scoreTxt);
+ scoreTxt.x = 2048 / 2;
+ scoreTxt.y = 40;
+ linesTxt = new Text2("Lines: 0", {
+ size: 60,
+ fill: "#fff"
+ });
+ linesTxt.anchor.set(0.5, 0);
+ LK.gui.top.addChild(linesTxt);
+ linesTxt.x = 2048 / 2 - 300;
+ linesTxt.y = 180;
+ levelTxt = new Text2("Level: 1", {
+ size: 60,
+ fill: "#fff"
+ });
+ levelTxt.anchor.set(0.5, 0);
+ LK.gui.top.addChild(levelTxt);
+ levelTxt.x = 2048 / 2 + 300;
+ levelTxt.y = 180;
+}
+// Touch drag controls (swipe left/right/down/up)
+var dragStartX = null,
+ dragStartY = null,
+ dragActive = false;
+game.down = function (x, y, obj) {
+ dragStartX = x;
+ dragStartY = y;
+ dragActive = true;
+};
+game.up = function (x, y, obj) {
+ dragActive = false;
+};
+game.move = function (x, y, obj) {
+ if (!dragActive) return;
+ var dx = x - dragStartX;
+ var dy = y - dragStartY;
+ if (Math.abs(dx) > 80) {
+ if (dx > 0) {
+ tryMove(1, 0, 0);
+ } else {
+ tryMove(-1, 0, 0);
+ }
+ dragStartX = x;
+ dragStartY = y;
+ }
+ if (Math.abs(dy) > 80) {
+ if (dy > 0) {
+ doSoftDrop();
+ } else {
+ tryMove(0, 0, 1);
+ }
+ dragStartX = x;
+ dragStartY = y;
+ }
+};
+// Main game setup
+function startGame() {
+ updateBoardLayout();
+ board = new Board();
+ board.init();
+ board.x = BOARD_X;
+ board.y = BOARD_Y;
+ game.addChild(board);
+ board.renderBlocks();
+ current = spawnTetrimino();
+ game.addChild(current);
+ updateGhost();
+ score = 0;
+ linesCleared = 0;
+ level = 1;
+ dropInterval = 36;
+ gameOver = false;
+ updateScore();
+}
+// UI setup
+createScoreUI();
+createButtons();
+startGame();
+// Main game loop
+game.update = function () {
+ if (gameOver) return;
+ dropTimer++;
+ if (dropTimer >= (softDrop ? 3 : dropInterval)) {
+ tickDrop();
+ dropTimer = 0;
+ }
+};
+// Reset on game over
+LK.on('gameover', function () {
+ controlsLocked = true;
+ LK.setTimeout(function () {
+ controlsLocked = false;
+ startGame();
+ }, 1200);
});
\ No newline at end of file