/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Column = Container.expand(function (gemTypes) { var self = Container.call(this); self.gems = []; self.isActive = false; // Create the three gems that form the column for (var i = 0; i < 3; i++) { var type = gemTypes && gemTypes[i] ? gemTypes[i] : null; var gem = new Gem(type); gem.y = -i * CELL_SIZE; self.gems.push(gem); self.addChild(gem); } self.rotate = function () { if (!self.isActive) { return; } // Move the last gem to a temporary position for animation var lastGem = self.gems.pop(); var firstGem = self.gems[0]; var middleGem = self.gems[1]; // Animate the rotation LK.getSound('rotate').play(); // Update the gems array with the new order self.gems = [lastGem, firstGem, middleGem]; // Update positions to match new order tween(lastGem, { y: firstGem.y }, { duration: 150 }); tween(firstGem, { y: middleGem.y }, { duration: 150 }); tween(middleGem, { y: lastGem.y + 2 * CELL_SIZE }, { duration: 150, onFinish: function onFinish() { // Reset positions after animation for (var i = 0; i < 3; i++) { self.gems[i].y = -i * CELL_SIZE; } } }); }; self.getTypes = function () { return self.gems.map(function (gem) { return gem.type; }); }; return self; }); var GameBoard = Container.expand(function () { var self = Container.call(this); self.grid = []; self.cellContainer = self.addChild(new Container()); self.gemContainer = self.addChild(new Container()); // Create background var boardBg = self.cellContainer.attachAsset('board_bg', { anchorX: 0.5, // Center horizontally anchorY: 0.5 // Center vertically }); boardBg.x = COLS * CELL_SIZE / 2; // Center the background on the board boardBg.y = ROWS * CELL_SIZE / 2; // Center the background on the board // Create grid cells for (var row = 0; row < ROWS; row++) { self.grid[row] = []; for (var col = 0; col < COLS; col++) { // Create cell background var cell = LK.getAsset('board_cell', { anchorX: 0.5, anchorY: 0.5, x: col * CELL_SIZE + CELL_SIZE / 2, y: row * CELL_SIZE + CELL_SIZE / 2, alpha: 0 }); self.cellContainer.addChild(cell); self.grid[row][col] = null; } } self.addGemToGrid = function (gem, row, col) { if (row < 0 || row >= ROWS || col < 0 || col >= COLS) { return false; } if (self.grid[row][col] !== null) { return false; } gem.x = col * CELL_SIZE + CELL_SIZE / 2; gem.y = row * CELL_SIZE + CELL_SIZE / 2; self.gemContainer.addChild(gem); self.grid[row][col] = gem; return true; }; self.removeGemFromGrid = function (row, col) { if (row < 0 || row >= ROWS || col < 0 || col >= COLS) { return null; } var gem = self.grid[row][col]; if (gem) { self.grid[row][col] = null; delete frozenGems[row + ',' + col]; // Remove gem from the dictionary return gem; } return null; }; self.isColumnFull = function (col) { return self.grid[0][col] !== null; }; self.canPlaceColumn = function (col) { // Check if any of the top 3 rows in the column are occupied for (var row = 0; row < 3; row++) { if (col < 0 || col >= COLS) { return false; } if (row < ROWS && self.grid[row][col] !== null) { return false; } } return true; }; self.findFirstEmptyRow = function (col) { for (var row = ROWS - 1; row >= 0; row--) { if (self.grid[row][col] === null) { return row; } } return -1; // Column is full }; self.placeColumn = function (column, col, callback) { var gems = column.gems; var placedGems = []; var firstEmptyRow = self.findFirstEmptyRow(col); if (firstEmptyRow < 0 || firstEmptyRow < gems.length - 1) { if (callback) { callback(false); } return; } // Place gems from bottom to top for (var i = 0; i < gems.length; i++) { var row = firstEmptyRow - i; if (row >= 0) { // Use the existing gem instead of creating a new one var gem = gems[i]; placedGems.push({ gem: gem, row: row, col: col }); // Update the gem's position gem.x = col * CELL_SIZE + CELL_SIZE / 2; gem.y = row * CELL_SIZE + CELL_SIZE / 2; // Add the gem to the grid self.gemContainer.addChild(gem); self.grid[row][col] = gem; frozenGems[row + ',' + col] = gem; // Store gem in the dictionary // If it's the last gem, notify callback if (i === gems.length - 1) { LK.getSound('drop').play(); if (callback) { callback(true, placedGems); } } } } }; self.checkMatches = function () { var matchedCells = []; // Check horizontal matches for (var row = 0; row < ROWS; row++) { var count = 1; var type = null; for (var col = 0; col < COLS; col++) { var gem = self.grid[row][col]; if (gem && type === gem.type) { count++; } else { if (count >= 3) { for (var i = 0; i < count; i++) { matchedCells.push({ row: row, col: col - 1 - i }); } } count = 1; type = gem ? gem.type : null; } } if (count >= 3) { for (var i = 0; i < count; i++) { matchedCells.push({ row: row, col: COLS - 1 - i }); } } } // Check vertical matches for (var col = 0; col < COLS; col++) { var count = 1; var type = null; for (var row = 0; row < ROWS; row++) { var gem = self.grid[row][col]; if (gem && type === gem.type) { count++; } else { if (count >= 3) { for (var i = 0; i < count; i++) { matchedCells.push({ row: row - 1 - i, col: col }); } } count = 1; type = gem ? gem.type : null; } } if (count >= 3) { for (var i = 0; i < count; i++) { matchedCells.push({ row: ROWS - 1 - i, col: col }); } } } // Remove duplicates from matchedCells var uniqueMatches = []; var matchMap = {}; for (var i = 0; i < matchedCells.length; i++) { var cell = matchedCells[i]; var key = cell.row + "," + cell.col; if (!matchMap[key]) { matchMap[key] = true; uniqueMatches.push(cell); } } return uniqueMatches; }; self.removeMatches = function (matches, callback) { if (matches.length === 0) { if (callback) { callback(0); } return; } var remainingAnimations = matches.length; var points = matches.length * 10; // Always play match sound and flash effect when matches are found // Force sound to play even if it's already playing LK.getSound('match').stop(); LK.getSound('match').play(); // Flash for any match (3 or more gems) LK.effects.flashScreen(0xFFFFFF, 300); matches.forEach(function (match) { var gem = self.grid[match.row][match.col]; if (gem) { self.grid[match.row][match.col] = null; delete frozenGems[match.row + ',' + match.col]; // Remove gem from the dictionary gem.highlight(); LK.setTimeout(function () { gem.animateMatch(function () { self.gemContainer.removeChild(gem); remainingAnimations--; if (remainingAnimations === 0) { if (callback) { callback(points); } } }); }, 100); } else { remainingAnimations--; if (remainingAnimations === 0 && callback) { callback(points); } } }); }; self.applyGravity = function (callback) { var movedGems = false; var animationsRunning = 0; // Iterate bottom to top, right to left for (var col = 0; col < COLS; col++) { for (var row = ROWS - 1; row > 0; row--) { if (self.grid[row][col] === null) { // Find the first non-null gem above this position for (var checkRow = row - 1; checkRow >= 0; checkRow--) { if (self.grid[checkRow][col] !== null) { var gem = self.grid[checkRow][col]; self.grid[checkRow][col] = null; self.grid[row][col] = gem; // Update the frozenGems dictionary delete frozenGems[checkRow + ',' + col]; frozenGems[row + ',' + col] = gem; movedGems = true; animationsRunning++; gem.animateDrop(row * CELL_SIZE + CELL_SIZE / 2, 0, function () { animationsRunning--; if (animationsRunning === 0 && callback) { callback(movedGems); } }); break; } } } } } if (animationsRunning === 0 && callback) { callback(movedGems); } }; return self; }); var Gem = Container.expand(function (type) { var self = Container.call(this); self.type = type || getRandomGemType(); var gemSprite = self.attachAsset('gem_' + self.type, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.95, scaleY: 0.95 }); self.setType = function (newType) { self.type = newType; self.removeChildren(); gemSprite = self.attachAsset('gem_' + newType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.95, scaleY: 0.95 }); }; self.highlight = function () { tween(gemSprite, { scaleX: 1.1, scaleY: 1.1 }, { duration: 200, easing: tween.easeOut }); }; self.unhighlight = function () { tween(gemSprite, { scaleX: 0.95, scaleY: 0.95 }, { duration: 200, easing: tween.easeOut }); }; self.animateMatch = function (callback) { tween(gemSprite, { alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { if (callback) { callback(); } } }); }; self.animateDrop = function (targetY, delay, callback) { tween(self, { y: targetY }, { duration: 300 + delay, easing: tween.bounceOut, onFinish: function onFinish() { if (callback) { callback(); } } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111122 }); /**** * Game Code ****/ // Constants var CELL_SIZE = 120; var COLS = 25; var ROWS = Math.floor(2732 / CELL_SIZE); //var ROWS = 25; var GEM_TYPES = ['red', 'blue', 'green', 'yellow', 'purple']; var DROP_INTERVAL_START = 60; // ms var DROP_INTERVAL_MIN = 60; // ms var DROP_DELTA = 20; var LEVEL_THRESHOLD = 50; // Points needed to level up // Game state var board; var activeColumn; var nextColumn; var dropInterval; var dropTimer = 0; var fallSpeed = DROP_INTERVAL_START; var gameActive = false; var score = 0; var level = 1; var nextColumnPreview; var columnPositionX = 0; // Dictionary to track frozen gems var frozenGems = {}; // UI elements var scoreText; var levelText; var highScoreText; function getRandomGemType() { var index = Math.floor(Math.random() * GEM_TYPES.length); return GEM_TYPES[index]; } function setupUI() { // Score text scoreText = new Text2('SCORE: 0', { size: 50, fill: 0x000000 // Changed to black }); scoreText.anchor.set(0.5, 0); scoreText.y = 50; LK.gui.top.addChild(scoreText); // Level text levelText = new Text2('LEVEL: 1', { size: 50, fill: 0x000000 // Changed to black }); levelText.anchor.set(0.5, 0); levelText.y = 100; LK.gui.top.addChild(levelText); // High score text var highScore = storage.highScore || 0; highScoreText = new Text2('BEST: ' + highScore, { size: 40, fill: 0x800080 // Changed to purple }); highScoreText.anchor.set(0.5, 0); highScoreText.y = 150; LK.gui.top.addChild(highScoreText); } function updateScore(points) { score += points; scoreText.setText('SCORE: ' + score); // Check if player leveled up var newLevel = Math.floor(score / LEVEL_THRESHOLD) + 1; if (newLevel > level) { level = newLevel; levelText.setText('LEVEL: ' + level); // Increase game speed fallSpeed = Math.max(DROP_INTERVAL_MIN, DROP_INTERVAL_START - (level - 1) * DROP_DELTA); // Adjust fallSpeed based on the current level // Play level up sound LK.getSound('levelup').play(); // Flash effect for level up LK.effects.flashScreen(0x00FFFF, 500); } // Update high score if needed var highScore = storage.highScore || 0; if (score > highScore) { storage.highScore = score; highScoreText.setText('BEST: ' + score); } } function createBoard() { board = new GameBoard(); board.x = (2048 - COLS * CELL_SIZE) / 2; board.y = (2732 - ROWS * CELL_SIZE) / 2; game.addChild(board); } function createNextColumnPreview() { nextColumnPreview = new Container(); nextColumnPreview.x = 2048 - 200; nextColumnPreview.y = 400; var previewLabel = new Text2('NEXT', { size: 40, fill: 0xFFFFFF }); previewLabel.anchor.set(0.5, 0); nextColumnPreview.addChild(previewLabel); game.addChild(nextColumnPreview); } function updateNextColumnPreview() { // Clear previous preview for (var i = nextColumnPreview.children.length - 1; i > 0; i--) { nextColumnPreview.removeChild(nextColumnPreview.children[i]); } // Add new gems to preview in the correct order if (nextColumn) { // Loop through gems in reverse order to match the visual appearance in the game for (var i = nextColumn.gems.length - 1; i >= 0; i--) { var previewGem = new Gem(nextColumn.gems[i].type); previewGem.x = 0; // Position from top to bottom in the preview previewGem.y = 80 + (nextColumn.gems.length - 1 - i) * CELL_SIZE; previewGem.scale.set(0.8); nextColumnPreview.addChild(previewGem); } } } function createNewColumn() { var column = new Column(); column.isActive = true; return column; } function startGame() { // Reset game state score = 0; level = 1; fallSpeed = DROP_INTERVAL_START; // Initialize fallSpeed with the starting drop interval gameActive = true; dropTimer = 0; // Clear frozen gems dictionary frozenGems = {}; // Create the preview for the next column if (nextColumnPreview) { game.removeChild(nextColumnPreview); } createNextColumnPreview(); // Set up UI setupUI(); // Create initial columns activeColumn = createNewColumn(); nextColumn = createNewColumn(); updateNextColumnPreview(); // Position active column columnPositionX = Math.floor(COLS / 2); activeColumn.x = board.x + columnPositionX * CELL_SIZE + CELL_SIZE / 2; activeColumn.y = 0; game.addChild(activeColumn); // Start music LK.playMusic('bgmusic', { fade: { start: 0, end: 0.4, duration: 1000 } }); } function processMatches(callback) { var matches = board.checkMatches(); if (matches.length > 0) { board.removeMatches(matches, function (points) { updateScore(points); board.applyGravity(function (movedGems) { // Check for chain reactions if (movedGems) { processMatches(callback); } else { if (callback) { callback(); } } }); }); } else { if (callback) { callback(); } } } function spawnNewColumn() { activeColumn = nextColumn; nextColumn = createNewColumn(); updateNextColumnPreview(); columnPositionX = Math.floor(COLS / 2); activeColumn.x = board.x + columnPositionX * CELL_SIZE + CELL_SIZE / 2; activeColumn.y = 0; game.addChild(activeColumn); // Check if game over if (!board.canPlaceColumn(columnPositionX)) { gameActive = false; LK.showGameOver(); } else { activeColumn.isActive = true; // Reactivate the new column } } function moveColumnLeft() { if (!gameActive || !activeColumn || !activeColumn.isActive) { return; } if (columnPositionX > 0) { // Check if there's a collision in the new position var canMove = true; var currentRow = Math.floor(activeColumn.y / CELL_SIZE); for (var i = 0; i < activeColumn.gems.length; i++) { var gemRow = currentRow - i; if (gemRow >= 0 && gemRow < ROWS) { var gemKey = gemRow + ',' + (columnPositionX - 1); if (frozenGems[gemKey]) { canMove = false; break; } } } if (canMove) { columnPositionX--; activeColumn.x = board.x + columnPositionX * CELL_SIZE + CELL_SIZE / 2; } } } function moveColumnRight() { if (!gameActive || !activeColumn || !activeColumn.isActive) { return; } if (columnPositionX < COLS - 1) { // Check if there's a collision in the new position var canMove = true; var currentRow = Math.floor(activeColumn.y / CELL_SIZE); for (var i = 0; i < activeColumn.gems.length; i++) { var gemRow = currentRow - i; if (gemRow >= 0 && gemRow < ROWS) { var gemKey = gemRow + ',' + (columnPositionX + 1); if (frozenGems[gemKey]) { canMove = false; break; } } } if (canMove) { columnPositionX++; activeColumn.x = board.x + columnPositionX * CELL_SIZE + CELL_SIZE / 2; } } } function rotateColumn() { if (!gameActive || !activeColumn) { return; } activeColumn.rotate(); } function dropColumn() { if (!gameActive || !activeColumn || !activeColumn.isActive) { return; } activeColumn.x = board.x + columnPositionX * CELL_SIZE + CELL_SIZE / 2; activeColumn.isActive = false; board.placeColumn(activeColumn, columnPositionX, function (success, placedGems) { if (success) { // Store the positions of the frozen gems placedGems.forEach(function (placedGem) { frozenGems[placedGem.row + ',' + placedGem.col] = placedGem.gem; }); game.removeChild(activeColumn); processMatches(function () { spawnNewColumn(); // Spawn a new column after processing matches }); } else { // Failed to place column, game over gameActive = false; LK.showGameOver(); } }); } // Input handlers game.down = function (x, y, obj) { if (!gameActive) { return; } // Determine which action to take based on touch position var boardX = x - board.x; // If touch is on the board if (boardX >= 0 && boardX < COLS * CELL_SIZE) { var touchCol = Math.floor(boardX / CELL_SIZE); // If touch is on the left side of the active column, move left if (touchCol < columnPositionX) { moveColumnLeft(); } // If touch is on the right side of the active column, move right else if (touchCol > columnPositionX) { moveColumnRight(); } // If touch is on the active column, rotate it else { rotateColumn(); } } }; game.move = function (x, y, obj) { // Optional: handle drag for more fluid control }; game.up = function (x, y, obj) { // Optional: implement swipe detection for quick drop if (gameActive && y > game.down.y + 100) { dropColumn(); } }; game.update = function () { if (!gameActive || !activeColumn || !activeColumn.isActive) { return; } // Update column position using rows var currentRow = Math.floor(activeColumn.y / CELL_SIZE); // Calculate the next row position based on the current fall speed dropTimer += fallSpeed; // Increment dropTimer by fallSpeed if (dropTimer >= 1000) { // Check if dropTimer has reached the threshold for a drop // 1000ms is the base interval for a row drop dropTimer = 0; // Check if the column can move down var canMoveDown = true; var nextRow = currentRow + 1; // Check if any gem in the column would collide with a frozen gem for (var i = 0; i < activeColumn.gems.length; i++) { var gemRow = nextRow - i; if (gemRow >= 0 && gemRow < ROWS) { var gemKey = gemRow + ',' + columnPositionX; if (frozenGems[gemKey]) { canMoveDown = false; break; } } } // Check if column reached the bottom // The bottom-most gem in the column is at position nextRow // So we need to check if this position is beyond the board if (nextRow >= ROWS) { canMoveDown = false; } if (canMoveDown) { activeColumn.y = nextRow * CELL_SIZE; // Move down } else { // Column has hit something or reached the bottom dropColumn(); return; } } // Check for immediate collisions (for fast drops or manual movements) var checkRow = Math.floor(activeColumn.y / CELL_SIZE); for (var i = 0; i < activeColumn.gems.length; i++) { var gemRow = checkRow - i; if (gemRow >= 0 && gemRow < ROWS) { var gemKey = gemRow + ',' + columnPositionX; if (frozenGems[gemKey]) { // Move column back up to avoid overlap activeColumn.y = (gemRow + i) * CELL_SIZE; dropColumn(); return; } } } // Check if column reached the bottom // Only freeze the column if the bottom-most gem has reached the bottom row if (checkRow >= ROWS) { activeColumn.y = (ROWS - 1) * CELL_SIZE; dropColumn(); return; } }; // Initialize the board if (board) { game.removeChild(board); } createBoard(); // Create and display the title image in the center of the screen var titleImage = LK.getAsset('title', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); var blackBg = LK.getAsset('blackBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0.75 }); game.addChild(blackBg); game.addChild(titleImage); startGameText = new Text2('COLUMNI', { size: 150, fill: 0xFFFFFF, align: "center", font: "Comic Sans MS", fontWeight: "bold" }); // Rainbow color array var rainbowColors = [0xFF0000, 0xFF7F00, 0xFFFF00, 0x00FF00, 0x0000FF, 0x4B0082, 0x8B00FF]; var colorIndex = 0; // Function to cycle through rainbow colors function cycleRainbowColors() { startGameText.tint = rainbowColors[colorIndex]; colorIndex = (colorIndex + 1) % rainbowColors.length; } // Set interval to change color every 500ms LK.setInterval(cycleRainbowColors, 500); startGameText2 = new Text2('Press on the logo to start...', { size: 150, fill: 0xffffff // Changed to black, }); startGameText.y = 2732 / 2 + 400; startGameText.x = 2048 / 2 - 400; startGameText.addChild(startGameText2); startGameText2.y = 200; startGameText2.x = -400; game.addChild(startGameText); // Function to start the game // Start the game when the title image is clicked titleImage.down = function (x, y, obj) { titleImage.destroy(); startGameText.destroy(); blackBg.destroy(); startGame(); };
===================================================================
--- original.js
+++ change.js
@@ -410,10 +410,11 @@
var COLS = 25;
var ROWS = Math.floor(2732 / CELL_SIZE);
//var ROWS = 25;
var GEM_TYPES = ['red', 'blue', 'green', 'yellow', 'purple'];
-var DROP_INTERVAL_START = 40; // ms
-var DROP_INTERVAL_MIN = 40; // ms
+var DROP_INTERVAL_START = 60; // ms
+var DROP_INTERVAL_MIN = 60; // ms
+var DROP_DELTA = 20;
var LEVEL_THRESHOLD = 50; // Points needed to level up
// Game state
var board;
var activeColumn;
@@ -471,9 +472,9 @@
if (newLevel > level) {
level = newLevel;
levelText.setText('LEVEL: ' + level);
// Increase game speed
- fallSpeed = Math.max(DROP_INTERVAL_MIN, DROP_INTERVAL_START - (level - 1) * 10); // Adjust fallSpeed based on the current level
+ fallSpeed = Math.max(DROP_INTERVAL_MIN, DROP_INTERVAL_START - (level - 1) * DROP_DELTA); // Adjust fallSpeed based on the current level
// Play level up sound
LK.getSound('levelup').play();
// Flash effect for level up
LK.effects.flashScreen(0x00FFFF, 500);
@@ -792,27 +793,32 @@
game.addChild(blackBg);
game.addChild(titleImage);
startGameText = new Text2('COLUMNI', {
size: 150,
- fill: 0xFFFFFF // Changed to black,
+ fill: 0xFFFFFF,
+ align: "center",
+ font: "Comic Sans MS",
+ fontWeight: "bold"
});
-// Tween to change the color of 'COLUMNI' text over time
-tween(startGameText, {
- fill: 0xFF0000 // Change to red
-}, {
- duration: 1000,
- // Loop indefinitely
- easing: tween.easeInOut // Smooth transition
-});
-startGameText2 = new Text2('Press any button to start...', {
+// Rainbow color array
+var rainbowColors = [0xFF0000, 0xFF7F00, 0xFFFF00, 0x00FF00, 0x0000FF, 0x4B0082, 0x8B00FF];
+var colorIndex = 0;
+// Function to cycle through rainbow colors
+function cycleRainbowColors() {
+ startGameText.tint = rainbowColors[colorIndex];
+ colorIndex = (colorIndex + 1) % rainbowColors.length;
+}
+// Set interval to change color every 500ms
+LK.setInterval(cycleRainbowColors, 500);
+startGameText2 = new Text2('Press on the logo to start...', {
size: 150,
fill: 0xffffff // Changed to black,
});
startGameText.y = 2732 / 2 + 400;
-startGameText.x = 2048 / 2 - 300;
+startGameText.x = 2048 / 2 - 400;
startGameText.addChild(startGameText2);
-startGameText2.y = 100;
-startGameText2.x = -450;
+startGameText2.y = 200;
+startGameText2.x = -400;
game.addChild(startGameText);
// Function to start the game
// Start the game when the title image is clicked
titleImage.down = function (x, y, obj) {
Ancient greece background pixel style ruins. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A blue gem. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A green gem. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A purple gem. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A red gem. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A yellow gem. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows