Code edit (1 edits merged)
Please save this source code
User prompt
Sometimes the columns get frozen at a position and the logic works (following pieces detec they are frozen at the right place) but they render on top of the screen. Fix it.
User prompt
Fix an issue when the columns, when frozen, they move to the top of the screen instead of stucking in place. This seems to happen when they stuck / get frozen and you still rotate them.
User prompt
Sometimes if i click on the gems as they change from falling to getting stuck, there is a weird behaviour where then are rendered on the top of the screen / cells instead of on the place where they were stuck. Please fix.
User prompt
Sometimes the gems match and they disappear and everything is ok except that the screen does not flash or the sound is not played. Can you fix?
User prompt
If my click / tap on the screen is not on the falling gems, the falling gems should fall at 10x speed during the duration of the tap.
User prompt
By tapping anywhere else when there is no falling gems (it may be existing stuck / frozen gems, that's fine!) make the falling gems fall at a 10x speed
User prompt
If you click on a falling gems now it rotates the gems. Thats good. But I want another behaviour as well: if you don't release the mouse / untap the screen, it will fall down at a 10x speed instead of rotating gems. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
If you click / tap a falling gem but dont' release the button, it will fall at a 10x speed instead of rotating gems.
Code edit (5 edits merged)
Please save this source code
User prompt
Add a text2 "Next" above the next gems
User prompt
If I click on the bottom part of the screen, like the 10% percent of the bottom, the currently falling gems should fall at a much higher speed, like 10x
User prompt
Right now only 3 equal gems in vertical or horizontal "match". Please make it so that three equal color gems in diagonal also match.
Code edit (1 edits merged)
Please save this source code
Code edit (13 edits merged)
Please save this source code
User prompt
Do it
Code edit (1 edits merged)
Please save this source code
User prompt
No! The other way around, don't destory boardBg! It should be shown still after starting the game
User prompt
Fix: by clickin on titleImage, the boardBg is destroyed?
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught TypeError: titleImage.Destroy is not a function' in or related to this line: 'titleImage.Destroy();' Line Number: 833
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (2 edits merged)
Please save this source code
/**** * 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 = 40; // ms var DROP_INTERVAL_MIN = 40; // ms 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) * 10); // 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 = {}; // Initialize the board if (board) { game.removeChild(board); } // 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) { startGame(); 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('Press any button to start...', { size: 150, fill: 0xFFFFFF // Changed to black, }); startGameText2 = new Text2('Press any button to start...', { size: 150, fill: 0xffffff // Changed to black, }); startGameText.y = 2732 / 2 + 500; startGameText.x = 2048 / 2 - 800; startGameText.addChild(startGameText2); game.addChild(startGameText); // Function to start the game // Start the game when the title image is clicked titleImage.down = function (x, y, obj) { startGame(); titleImage.destroy(); startGameText.destroy(); blackBg.destroy(); };
===================================================================
--- original.js
+++ change.js
@@ -814,6 +814,5 @@
startGame();
titleImage.destroy();
startGameText.destroy();
blackBg.destroy();
- boardBg.destroy();
};
\ No newline at end of file