User prompt
Go optimum for game
User prompt
Add levels if player beat first level target must be rise by 600 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
You must fix the bugs
User prompt
Crystal must be slideable ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Crystal Crush Blast
Initial prompt
Create a candy crush game with powerfull add ons
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { level: 1, targetScore: 5000 }); /**** * Classes ****/ var Crystal = Container.expand(function (crystalType) { var self = Container.call(this); self.crystalType = crystalType; self.gridX = 0; self.gridY = 0; self.isSelected = false; self.isAnimating = false; var assetName = 'crystal_' + crystalType; if (crystalType === 'bomb') assetName = 'bomb_crystal';else if (crystalType === 'lightning') assetName = 'lightning_crystal';else if (crystalType === 'rainbow') assetName = 'rainbow_crystal'; var crystalGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.setSelected = function (selected) { self.isSelected = selected; if (selected) { crystalGraphics.scaleX = 1.2; crystalGraphics.scaleY = 1.2; } else { crystalGraphics.scaleX = 1.0; crystalGraphics.scaleY = 1.0; } }; self.setGridPosition = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = GRID_START_X + gridX * CELL_SIZE + CELL_SIZE / 2; self.y = GRID_START_Y + gridY * CELL_SIZE + CELL_SIZE / 2; }; self.animateToPosition = function (newX, newY, callback) { self.isAnimating = true; tween(self, { x: newX, y: newY }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { self.isAnimating = false; if (callback) callback(); } }); }; self.down = function (x, y, obj) { if (self.isAnimating || gameState !== 'playing') return; handleCrystalClick(self); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ var GRID_SIZE = 8; var CELL_SIZE = 240; var GRID_START_X = (2048 - GRID_SIZE * CELL_SIZE) / 2; var GRID_START_Y = (2732 - GRID_SIZE * CELL_SIZE) / 2; var crystalTypes = ['red', 'blue', 'green', 'yellow', 'purple', 'orange']; var grid = []; var selectedCrystal = null; var gameState = 'playing'; var score = 0; var movesLeft = 30; var currentLevel = storage.level || 1; var targetScore = storage.targetScore || 5000; var frameCount = 0; var performanceMode = false; // Performance optimization flags var ENABLE_PARTICLE_EFFECTS = true; var ENABLE_SMOOTH_ANIMATIONS = true; // UI Elements var scoreText = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); scoreText.x = 50; scoreText.y = 100; game.addChild(scoreText); var movesText = new Text2('Moves: 30', { size: 80, fill: 0xFFFFFF }); movesText.anchor.set(1, 0); movesText.x = 1998; movesText.y = 100; game.addChild(movesText); var targetText = new Text2('Target: ' + targetScore, { size: 60, fill: 0xFFFF00 }); targetText.anchor.set(0.5, 0); targetText.x = 1024; targetText.y = 50; game.addChild(targetText); var levelText = new Text2('Level: ' + currentLevel, { size: 60, fill: 0x00FF00 }); levelText.anchor.set(0.5, 0); levelText.x = 1024; levelText.y = 120; game.addChild(levelText); // Initialize grid background function createGridBackground() { for (var x = 0; x < GRID_SIZE; x++) { for (var y = 0; y < GRID_SIZE; y++) { var cell = LK.getAsset('grid_cell', { anchorX: 0.5, anchorY: 0.5 }); cell.x = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2; cell.y = GRID_START_Y + y * CELL_SIZE + CELL_SIZE / 2; cell.alpha = 0.3; game.addChild(cell); } } } // Initialize game grid function initializeGrid() { grid = []; for (var x = 0; x < GRID_SIZE; x++) { grid[x] = []; for (var y = 0; y < GRID_SIZE; y++) { var crystalType = crystalTypes[Math.floor(Math.random() * crystalTypes.length)]; var crystal = new Crystal(crystalType); crystal.setGridPosition(x, y); grid[x][y] = crystal; game.addChild(crystal); } } // Remove initial matches removeInitialMatches(); } function removeInitialMatches() { var hasMatches = true; while (hasMatches) { hasMatches = false; for (var x = 0; x < GRID_SIZE; x++) { for (var y = 0; y < GRID_SIZE; y++) { if (checkMatchAt(x, y).length >= 3) { hasMatches = true; var newType = crystalTypes[Math.floor(Math.random() * crystalTypes.length)]; // Remove old crystal and create new one with different type var oldCrystal = grid[x][y]; game.removeChild(oldCrystal); var newCrystal = new Crystal(newType); newCrystal.setGridPosition(x, y); grid[x][y] = newCrystal; game.addChild(newCrystal); } } } } } function handleCrystalClick(crystal) { if (!selectedCrystal) { selectedCrystal = crystal; crystal.setSelected(true); } else if (selectedCrystal === crystal) { selectedCrystal.setSelected(false); selectedCrystal = null; } else { // Check if crystals are adjacent var dx = Math.abs(selectedCrystal.gridX - crystal.gridX); var dy = Math.abs(selectedCrystal.gridY - crystal.gridY); if (dx === 1 && dy === 0 || dx === 0 && dy === 1) { swapCrystals(selectedCrystal, crystal); } selectedCrystal.setSelected(false); selectedCrystal = null; } } function swapCrystals(crystal1, crystal2) { if (movesLeft <= 0) return; // Store original positions var crystal1OriginalX = crystal1.x; var crystal1OriginalY = crystal1.y; var crystal2OriginalX = crystal2.x; var crystal2OriginalY = crystal2.y; // Store original grid positions var crystal1GridX = crystal1.gridX; var crystal1GridY = crystal1.gridY; var crystal2GridX = crystal2.gridX; var crystal2GridY = crystal2.gridY; // Temporarily swap grid positions crystal1.gridX = crystal2GridX; crystal1.gridY = crystal2GridY; crystal2.gridX = crystal1GridX; crystal2.gridY = crystal1GridY; grid[crystal1GridX][crystal1GridY] = crystal2; grid[crystal2GridX][crystal2GridY] = crystal1; // Animate crystals sliding to each other's positions var animationsCompleted = 0; crystal1.animateToPosition(crystal2OriginalX, crystal2OriginalY, function () { animationsCompleted++; if (animationsCompleted === 2) { checkSwapResult(); } }); crystal2.animateToPosition(crystal1OriginalX, crystal1OriginalY, function () { animationsCompleted++; if (animationsCompleted === 2) { checkSwapResult(); } }); function checkSwapResult() { // Check for matches after swap var matches1 = checkMatchAt(crystal1.gridX, crystal1.gridY); var matches2 = checkMatchAt(crystal2.gridX, crystal2.gridY); if (matches1.length >= 3 || matches2.length >= 3) { movesLeft--; updateUI(); processMatches(); LK.getSound('match').play(); } else { // Swap back if no matches - animate back to original positions crystal1.gridX = crystal1GridX; crystal1.gridY = crystal1GridY; crystal2.gridX = crystal2GridX; crystal2.gridY = crystal2GridY; grid[crystal1GridX][crystal1GridY] = crystal1; grid[crystal2GridX][crystal2GridY] = crystal2; crystal1.animateToPosition(crystal1OriginalX, crystal1OriginalY, null); crystal2.animateToPosition(crystal2OriginalX, crystal2OriginalY, null); } } } function checkMatchAt(x, y) { if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE || !grid[x] || !grid[x][y]) return []; var crystal = grid[x][y]; if (!crystal || crystal.isAnimating) return []; // Skip animating crystals var matches = [crystal]; var type = crystal.crystalType; // Check horizontal matches var left = x - 1; while (left >= 0 && grid[left][y] && grid[left][y].crystalType === type) { matches.push(grid[left][y]); left--; } var right = x + 1; while (right < GRID_SIZE && grid[right][y] && grid[right][y].crystalType === type) { matches.push(grid[right][y]); right++; } if (matches.length >= 3) return matches; // Check vertical matches matches = [crystal]; var up = y - 1; while (up >= 0 && grid[x][up] && grid[x][up].crystalType === type) { matches.push(grid[x][up]); up--; } var down = y + 1; while (down < GRID_SIZE && grid[x][down] && grid[x][down].crystalType === type) { matches.push(grid[x][down]); down++; } return matches.length >= 3 ? matches : []; } function processMatches() { var allMatches = []; var matchedCrystals = {}; // Find all matches - optimized with early detection for (var x = 0; x < GRID_SIZE; x++) { for (var y = 0; y < GRID_SIZE; y++) { if (!grid[x] || !grid[x][y]) continue; var crystalKey = x + '_' + y; if (matchedCrystals[crystalKey]) continue; // Skip already processed crystals var matches = checkMatchAt(x, y); if (matches.length >= 3) { for (var i = 0; i < matches.length; i++) { var crystal = matches[i]; var key = crystal.gridX + '_' + crystal.gridY; if (!matchedCrystals[key]) { allMatches.push(crystal); matchedCrystals[key] = true; } } } } } if (allMatches.length > 0) { // Calculate score with combo bonus var baseScore = allMatches.length * 100; var comboBonus = allMatches.length >= 5 ? 500 : allMatches.length >= 4 ? 200 : 0; score += baseScore + comboBonus; // Visual feedback for large matches if (allMatches.length >= 4) { LK.effects.flashScreen(0xFFD700, 300); } // Remove matched crystals with fade effect for (var i = 0; i < allMatches.length; i++) { var crystal = allMatches[i]; tween(crystal, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 200, onFinish: function onFinish() { game.removeChild(crystal); } }); grid[crystal.gridX][crystal.gridY] = null; } // Drop crystals down dropCrystals(); updateUI(); checkWinCondition(); } } function dropCrystals() { var animationsInProgress = 0; var animationsCompleted = 0; var animationCallback = function animationCallback() { animationsCompleted++; if (animationsCompleted === animationsInProgress) { checkForMatches(); } }; for (var x = 0; x < GRID_SIZE; x++) { var writeIndex = GRID_SIZE - 1; // Move existing crystals down for (var y = GRID_SIZE - 1; y >= 0; y--) { if (grid[x][y] !== null) { if (y !== writeIndex) { grid[x][writeIndex] = grid[x][y]; grid[x][y] = null; grid[x][writeIndex].gridY = writeIndex; // Animate crystal falling to new position var targetX = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2; var targetY = GRID_START_Y + writeIndex * CELL_SIZE + CELL_SIZE / 2; animationsInProgress++; grid[x][writeIndex].animateToPosition(targetX, targetY, animationCallback); } writeIndex--; } } // Fill empty spaces with new crystals for (var emptyY = writeIndex; emptyY >= 0; emptyY--) { var crystalType = crystalTypes[Math.floor(Math.random() * crystalTypes.length)]; var newCrystal = new Crystal(crystalType); // Start new crystals above the grid and animate them falling down newCrystal.x = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2; newCrystal.y = GRID_START_Y - (writeIndex - emptyY + 1) * CELL_SIZE + CELL_SIZE / 2; newCrystal.gridX = x; newCrystal.gridY = emptyY; grid[x][emptyY] = newCrystal; game.addChild(newCrystal); var targetX = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2; var targetY = GRID_START_Y + emptyY * CELL_SIZE + CELL_SIZE / 2; animationsInProgress++; newCrystal.animateToPosition(targetX, targetY, animationCallback); } } // If no animations are in progress, check immediately if (animationsInProgress === 0) { LK.setTimeout(function () { processMatches(); }, 100); } } function checkForMatches() { // Check for new matches after dropping LK.setTimeout(function () { processMatches(); }, 100); } var lastScore = -1; var lastMoves = -1; var lastTarget = -1; var lastLevel = -1; function updateUI() { if (score !== lastScore) { scoreText.setText('Score: ' + score); lastScore = score; } if (movesLeft !== lastMoves) { movesText.setText('Moves: ' + movesLeft); lastMoves = movesLeft; } if (targetScore !== lastTarget) { targetText.setText('Target: ' + targetScore); lastTarget = targetScore; } if (currentLevel !== lastLevel) { levelText.setText('Level: ' + currentLevel); lastLevel = currentLevel; } } function checkWinCondition() { if (score >= targetScore) { // Level completed - advance to next level currentLevel++; targetScore += 600; // Save progress to storage storage.level = currentLevel; storage.targetScore = targetScore; // Reset for next level score = 0; movesLeft = 30; gameState = 'playing'; // Clear and reinitialize the grid clearGrid(); initializeGrid(); updateUI(); // Show level completion message LK.effects.flashScreen(0x00FF00, 500); } else if (movesLeft <= 0) { gameState = 'lost'; LK.showGameOver(); } } function clearGrid() { for (var x = 0; x < GRID_SIZE; x++) { for (var y = 0; y < GRID_SIZE; y++) { if (grid[x] && grid[x][y]) { game.removeChild(grid[x][y]); grid[x][y] = null; } } } } // Initialize game createGridBackground(); initializeGrid(); updateUI(); game.update = function () { // Game loop - matches are processed through event system };
===================================================================
--- original.js
+++ change.js
@@ -81,8 +81,13 @@
var score = 0;
var movesLeft = 30;
var currentLevel = storage.level || 1;
var targetScore = storage.targetScore || 5000;
+var frameCount = 0;
+var performanceMode = false;
+// Performance optimization flags
+var ENABLE_PARTICLE_EFFECTS = true;
+var ENABLE_SMOOTH_ANIMATIONS = true;
// UI Elements
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
@@ -240,10 +245,11 @@
}
}
}
function checkMatchAt(x, y) {
- if (!grid[x] || !grid[x][y]) return [];
+ if (x < 0 || x >= GRID_SIZE || y < 0 || y >= GRID_SIZE || !grid[x] || !grid[x][y]) return [];
var crystal = grid[x][y];
+ if (!crystal || crystal.isAnimating) return []; // Skip animating crystals
var matches = [crystal];
var type = crystal.crystalType;
// Check horizontal matches
var left = x - 1;
@@ -272,28 +278,50 @@
return matches.length >= 3 ? matches : [];
}
function processMatches() {
var allMatches = [];
- // Find all matches
+ var matchedCrystals = {};
+ // Find all matches - optimized with early detection
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
+ if (!grid[x] || !grid[x][y]) continue;
+ var crystalKey = x + '_' + y;
+ if (matchedCrystals[crystalKey]) continue; // Skip already processed crystals
var matches = checkMatchAt(x, y);
if (matches.length >= 3) {
for (var i = 0; i < matches.length; i++) {
- if (allMatches.indexOf(matches[i]) === -1) {
- allMatches.push(matches[i]);
+ var crystal = matches[i];
+ var key = crystal.gridX + '_' + crystal.gridY;
+ if (!matchedCrystals[key]) {
+ allMatches.push(crystal);
+ matchedCrystals[key] = true;
}
}
}
}
}
if (allMatches.length > 0) {
- // Calculate score
- score += allMatches.length * 100;
- // Remove matched crystals
+ // Calculate score with combo bonus
+ var baseScore = allMatches.length * 100;
+ var comboBonus = allMatches.length >= 5 ? 500 : allMatches.length >= 4 ? 200 : 0;
+ score += baseScore + comboBonus;
+ // Visual feedback for large matches
+ if (allMatches.length >= 4) {
+ LK.effects.flashScreen(0xFFD700, 300);
+ }
+ // Remove matched crystals with fade effect
for (var i = 0; i < allMatches.length; i++) {
var crystal = allMatches[i];
- game.removeChild(crystal);
+ tween(crystal, {
+ alpha: 0,
+ scaleX: 0.1,
+ scaleY: 0.1
+ }, {
+ duration: 200,
+ onFinish: function onFinish() {
+ game.removeChild(crystal);
+ }
+ });
grid[crystal.gridX][crystal.gridY] = null;
}
// Drop crystals down
dropCrystals();
@@ -303,8 +331,14 @@
}
function dropCrystals() {
var animationsInProgress = 0;
var animationsCompleted = 0;
+ var animationCallback = function animationCallback() {
+ animationsCompleted++;
+ if (animationsCompleted === animationsInProgress) {
+ checkForMatches();
+ }
+ };
for (var x = 0; x < GRID_SIZE; x++) {
var writeIndex = GRID_SIZE - 1;
// Move existing crystals down
for (var y = GRID_SIZE - 1; y >= 0; y--) {
@@ -316,14 +350,9 @@
// Animate crystal falling to new position
var targetX = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2;
var targetY = GRID_START_Y + writeIndex * CELL_SIZE + CELL_SIZE / 2;
animationsInProgress++;
- grid[x][writeIndex].animateToPosition(targetX, targetY, function () {
- animationsCompleted++;
- if (animationsCompleted === animationsInProgress) {
- checkForMatches();
- }
- });
+ grid[x][writeIndex].animateToPosition(targetX, targetY, animationCallback);
}
writeIndex--;
}
}
@@ -340,14 +369,9 @@
game.addChild(newCrystal);
var targetX = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2;
var targetY = GRID_START_Y + emptyY * CELL_SIZE + CELL_SIZE / 2;
animationsInProgress++;
- newCrystal.animateToPosition(targetX, targetY, function () {
- animationsCompleted++;
- if (animationsCompleted === animationsInProgress) {
- checkForMatches();
- }
- });
+ newCrystal.animateToPosition(targetX, targetY, animationCallback);
}
}
// If no animations are in progress, check immediately
if (animationsInProgress === 0) {
@@ -361,13 +385,29 @@
LK.setTimeout(function () {
processMatches();
}, 100);
}
+var lastScore = -1;
+var lastMoves = -1;
+var lastTarget = -1;
+var lastLevel = -1;
function updateUI() {
- scoreText.setText('Score: ' + score);
- movesText.setText('Moves: ' + movesLeft);
- targetText.setText('Target: ' + targetScore);
- levelText.setText('Level: ' + currentLevel);
+ if (score !== lastScore) {
+ scoreText.setText('Score: ' + score);
+ lastScore = score;
+ }
+ if (movesLeft !== lastMoves) {
+ movesText.setText('Moves: ' + movesLeft);
+ lastMoves = movesLeft;
+ }
+ if (targetScore !== lastTarget) {
+ targetText.setText('Target: ' + targetScore);
+ lastTarget = targetScore;
+ }
+ if (currentLevel !== lastLevel) {
+ levelText.setText('Level: ' + currentLevel);
+ lastLevel = currentLevel;
+ }
}
function checkWinCondition() {
if (score >= targetScore) {
// Level completed - advance to next level