Code edit (1 edits merged)
Please save this source code
User prompt
in the bottom center of the playing field it should say “Glaud” in small orange. you will add this text.
User prompt
small orange “Glaud” in the top center of the playing field.
User prompt
small orange “Glaud” in the top center of the playing field.
User prompt
In the top right corner of the screen it should say little orange “Glaud”.
User prompt
make a background that completely covers the back.
User prompt
make a background that completely covers the back.
User prompt
Please fix the bug: 'Timeout.tick error: popPromises is not defined' in or related to this line: 'popPromises.push({' Line Number: 772
User prompt
Please fix the bug: 'Timeout.tick error: popPromises is not defined' in or related to this line: 'popPromises.push({' Line Number: 771
User prompt
Please fix the bug: 'Timeout.tick error: popPromises is not defined' in or related to this line: 'popPromises.push({' Line Number: 772
User prompt
Please fix the bug: 'Timeout.tick error: popPromises is not defined' in or related to this line: 'popPromises.push({' Line Number: 773
User prompt
Please fix the bug: 'Timeout.tick error: Cannot read properties of undefined (reading 'push')' in or related to this line: 'popPromises.push({' Line Number: 742
User prompt
Please fix the bug: 'Timeout.tick error: handleCompletion is not defined' in or related to this line: 'handleCompletion(actions, function () {' Line Number: 266
User prompt
slide the game background 200 to the right
User prompt
slide the playground 200 to the right
User prompt
Make sure that the Level text and the playing field are in the same place horizontally.
User prompt
Problem: The "Level: X" text is currently positioned automatically by the GUI system (LK.gui.top), likely near the very top of the screen, not aligned with the game board which is shifted down. Goal: Align the top of the "Level: X" text horizontally with the top edge of the gameBoardContainer. Solution Explanation: Remove from GUI Control: Stop adding the levelTxt to the automatic GUI container (LK.gui.top.addChild(levelTxt)). The GUI system's automatic placement prevents precise alignment with the board. Add to Main Stage: Instead, add the levelTxt directly to the main game stage using game.addChild(levelTxt). This allows you to manually control its exact x and y coordinates. Set Horizontal Position (X): Calculate the desired horizontal position. To center it on the screen, set levelTxt.x = screenWidth / 2; (e.g., levelTxt.x = 2048 / 2;). Ensure the text's horizontal anchor is set to 0.5 (levelTxt.anchor.set(0.5, 0);) so it centers correctly around this point. Set Vertical Position (Y): This is the key step. Set the levelTxt.y coordinate to be exactly the same as the calculated top y coordinate of the gameBoardContainer. In your setupGame function, this value is stored in the containerY variable (const containerY = (2732 - boardPixelHeight) / 2 + 200;). So, set levelTxt.y = containerY;. Anchor: Make sure the text's vertical anchor is 0 (levelTxt.anchor.set(0.5, 0);). This ensures that the top of the text aligns with the containerY value. (Optional) Add Padding: If you don't want the text touching the board, subtract a small amount from the y coordinate after setting it: levelTxt.y = containerY - 80; (adjust the 80 value for desired spacing). By adding the text directly to the game stage and manually setting its y coordinate to match the gameBoardContainer's calculated y coordinate, you achieve the desired horizontal alignment.
User prompt
align horizontally with the level.
User prompt
Method 1: Increase the Size of Each Cell and Candy Concept: Instead of an 8x8 grid using, say, 100x100 pixel cells, you change the fundamental unit to be 200x200 pixels. Everything is drawn larger from the start. Graphics: Your images for candies and cell backgrounds should ideally be designed for this larger size to avoid looking pixelated when drawn bigger. If using shapes drawn by code, their dimensions should be doubled. Positioning: Calculations that place candies based on their row, column, and the cell size will automatically adjust because the cell size value itself has increased. Centering the whole board on the screen should also still work correctly using the board's new, larger total dimensions. Pros: Results in the sharpest visuals with no blurriness. Interaction (clicking) usually works without extra adjustments. Cons: May require you to have or create larger, higher-resolution graphics for your candies and cells. Method 2: Scale the Entire Game Board Container Concept: Keep the original size for each cell and candy (e.g., 100x100 pixels). After the entire 8x8 grid is put together in its container, you tell the game engine to simply stretch that whole container to be twice as wide and twice as tall. Graphics: You don't need to change your original candy or cell graphics. However, because they are being stretched like a photograph being enlarged, they will likely look blurry or pixelated. Positioning: This is trickier. Just scaling the container usually makes it expand from its top-left corner. To keep the enlarged board centered (or correctly shifted), you must calculate its starting position based on its final, scaled-up size, not its original size. This requires more careful calculation. Pros: Quicker to implement as you don't need to change assets or fundamental size variables. Cons: Visual quality suffers (blurriness/pixelation). Positioning math is more complex to get right. Click detection might need careful testing to ensure it still aligns correctly with the scaled graphics. Recommendation: For the best visual result, Method 1 (increasing cell size) is generally preferred, assuming you have or can create suitable graphics. If visual quality is less critical or you need a faster change, Method 2 (scaling the container) works, but pay close attention to correctly calculating the final position to keep it centered or shifted as intended. 14.5s
User prompt
double your playground
User prompt
Please fix the bug: 'ReferenceError: resolveDropsAndFills is not defined' in or related to this line: 'resolveDropsAndFills();' Line Number: 323
User prompt
Implementing Special Candy Swap Combinations Here's a breakdown of how to implement two common special candy swap effects, focusing on the logic, code steps, and crucial points to avoid errors: Mechanic 1: Color Bomb + Regular Candy Swap Goal: Swapping a Color Bomb (from a 5-match) with an adjacent normal candy destroys the bomb itself and all other candies on the board matching the normal candy's color. Implementation Steps: Modify swapCandies: Inside the callback after the swap animation finishes (e.g., within handleCompletion). Check if one candy is type === 'colorBomb' and the other !isSpecial (and a normal color type). If true: Store the normal candy's type on the bomb object (e.g., bombCandy.swappedWithType = normalCandy.type;). Set isSwapping = false;. Skip the regular findCompleteMatches() check. Call a new function: executeColorBombEffect(bombCandy, bombCandy.swappedWithType);. Create executeColorBombEffect(bombCandy, targetType): Set isProcessing = true;. Create an empty list: let candiesToDestroy = [];. Add bombCandy to candiesToDestroy. Set board[bombCandy.row][bombCandy.col] = null;. If targetType is valid (not special, not null): Iterate through all candies on the board. If a candy's type === targetType and !c.isSpecial, add it to candiesToDestroy (if not already added) and set its board position to null. If candiesToDestroy contains more than just the bomb: Play sound (LK.getSound('special').play()). Add score. updateUI(). Create an actions array for handleCompletion. For each candy in candiesToDestroy: Add a pop action: actions.push({ then: (resolve) => candy.pop(delay, resolve) });. Use a small, potentially staggered delay. Remove the candy from the global candies array. Use handleCompletion(actions, () => { ... }); to wait for all pops. Inside the handleCompletion callback: candiesToDestroy.forEach(c => c.destroy()); (Cleanup). Call resolveDropsAndFills(); to refill the board. If candiesToDestroy only contains the bomb (no targets found): Handle just popping the bomb itself using the same actions/handleCompletion structure, then call resolveDropsAndFills(). Specific Considerations (Color Bomb): Targeting: Ensure targetType is correctly identified and passed. Avoid Targeting Specials: The effect typically doesn't destroy other special candies of the target color. Add !c.isSpecial check. Self-Destruct: The Color Bomb must destroy itself. Chain Reactions: Activating other specials destroyed by the bomb is complex; avoid implementing this initially for simplicity. Bomb + Bomb: Requires a separate if check in swapCandies and a different effect function (e.g., executeBoardClearEffect). Mechanic 2: Striped Candy + Striped Candy Swap Goal: Swapping any two Striped Candies (type 'stripedH' or 'stripedV', from 4-matches) destroys all candies in both the entire row AND the entire column where the swap occurred. Implementation Steps: Modify swapCandies: Inside the animation completion callback, after the Color Bomb check. Add an else if block: else if ((candy1.type === 'stripedH' || candy1.type === 'stripedV') && (candy2.type === 'stripedH' || candy2.type === 'stripedV')). If true: Set isSwapping = false;. Skip the regular findCompleteMatches() check. Call a new function: executeDoubleStripedEffect(candy1); (Pass one candy to get the final position). Create executeDoubleStripedEffect(centerCandy): Set isProcessing = true;. Get the activation position: const targetRow = centerCandy.row;, const targetCol = centerCandy.col;. Create an empty list: let candiesToDestroy = [];. Scan Row: Iterate c from 0 to BOARD_SIZE-1. Add board[targetRow]?.[c] to candiesToDestroy if it exists and isn't already added. Scan Column: Iterate r from 0 to BOARD_SIZE-1. Add board[r]?.[targetCol] to candiesToDestroy if it exists and isn't already added. Set the board position to null for all candies in candiesToDestroy. Play a powerful sound. Add significant score. updateUI(). Create an actions array for handleCompletion. For each candy in candiesToDestroy: Calculate delay based on Manhattan distance from the center: const delay = (Math.abs(candy.row - targetRow) + Math.abs(candy.col - targetCol)) * 35;. Add a pop action: actions.push({ then: (resolve) => candy.pop(delay, resolve) });. Remove the candy from the global candies array. Use handleCompletion(actions, () => { ... }); to wait for all pops. Inside the handleCompletion callback: candiesToDestroy.forEach(c => c.destroy()); (Cleanup). Call resolveDropsAndFills(); to refill the board. Specific Considerations (Striped + Striped): Trigger: Ensure the if condition correctly identifies two striped types. No Duplicates: Use indexOf check when adding to candiesToDestroy to avoid adding the center candy twice. Self-Destruct: Both initial striped candies must be included in the destruction. Chain Reactions: Typically, other special candies caught in the crossfire are just destroyed, not activated. Keep it simple initially. Visuals: The distance-based delay creates a nice radiating explosion effect. General Critical Considerations (Apply to BOTH Mechanics): TIMING / SYNCHRONIZATION IS PARAMOUNT: This is the most common failure point. The entire sequence for the special effect (finding targets, updating the model, playing animations, waiting for animations, cleaning up) must complete before resolveDropsAndFills is called. The isProcessing flag must lock the game during this entire time. Using a reliable waiting mechanism (handleCompletion or Promises if available) is essential. Simple setTimeout delays for activation will not work correctly and will break the game state. Update Model First: Always set board[r][c] = null for candies being destroyed before their pop animation starts. Cleanup: Remember to destroy() the candy objects and remove them from the candies array after their animations finish. Other Combinations: Plan for other swaps (Striped+Bomb, Wrapped+Striped, etc.). Each needs its own else if in swapCandies and a dedicated execute...Effect function. Clear Code: Keep the effect logic separate and well-commented. Remove unused variables (isDropping). Implementing these special swaps requires careful attention to the sequence of events and ensuring the game waits appropriately at each stage. Get the timing right, and these effects will greatly enhance the game!
User prompt
Understanding the Logic: Trigger: Swapping a colorBomb type candy with an adjacent normal (non-special) candy. Target: The type (e.g., 'redCandy', 'blueCandy') of the normal candy it was swapped with. Action: Destroy the Color Bomb and all candies on the board that match the targetType. Result: Initiate the standard board resolution process (drops and fills) for the cleared spaces. 2. How to Implement in Code (Steps): Update swapCandies Function: When two candies (candy1, candy2) are swapped, check their types. If one is colorBomb and the other is a normal candy (check !candyX.isSpecial and ensure its type is a standard color), do not proceed to the regular findMatches check after the swap animation. Instead, identify which candy is the bomb and which is the target. Store the targetType (e.g., bombCandy.swappedWithType = targetCandy.type;). After the swap animation completes (using handleCompletion or similar), call a new dedicated function, like executeColorBombEffect(bombCandy, targetType). Crucially: Ensure the swap animation fully completes before calling the effect function. Create executeColorBombEffect(bombCandy, targetType) Function: This function takes the bombCandy object and the targetType string as arguments. Lock: Set isProcessing = true; immediately to prevent other actions. Find Targets: Iterate through the candies array or the board grid. Identify the bombCandy itself. Identify all other candies where candy.type === targetType. Important: Usually, you don't destroy other special candies of the target color, so add a check like && !c.isSpecial. Collect all these identified candies (the bomb + targets) into a temporary list (e.g., candiesToDestroyByBomb). Update Model: Immediately set the board grid positions for all candies in candiesToDestroyByBomb to null. This is vital for the subsequent drop/fill logic. Prepare Animations: Create a list of "action" objects for your handleCompletion function. For each candy in candiesToDestroyByBomb, add an action that calls its pop() method. Include the bombCandy itself in this list. Stagger the delay parameter in pop() animations for a better visual effect (e.g., based on distance from the bomb, or just index). Sound & Score: Play a specific sound effect (LK.getSound('special').play() or a unique "zap" sound). Add score based on the number of candies destroyed. Wait: Use your handleCompletion mechanism to wait for all the pop() animations in your actions list to finish. Cleanup: Inside the handleCompletion callback (once all pops are done), physically destroy() all the popped candy objects and remove them from the main candies array. Next Step: After the cleanup is complete, call resolveDropsAndFills() to trigger the normal falling and refilling process for the spaces cleared by the bomb. 3. Important Considerations & Avoiding Pitfalls: Timing & Synchronization (Most Critical!): The entire Color Bomb effect (finding targets, popping animations, cleanup) MUST complete before the game attempts to handle drops (resolveDropsAndFills). Executing the effect asynchronously (like with a simple setTimeout) without proper waiting will break the game logic, leading to freezes, crashes, or unpredictable behavior. The isProcessing flag must protect this whole sequence, and a robust waiting mechanism (handleCompletion or Promises if supported) is essential. Target Color Identification: Reliably passing the correct targetType from swapCandies to executeColorBombEffect is key. Using a temporary property on the bomb object (like swappedWithType) set during the swap is a reasonable approach. Update Model First: Remove the targeted candies from the board array before starting their pop animations. This ensures handleDrops sees the correct empty spaces later. Self-Destruction: Ensure the Color Bomb itself is included in the list of candies to be destroyed. Targeting Other Specials: Decide if a Color Bomb should destroy other special candies that happen to be the target color. Typically, they don't. Add an !c.isSpecial check when identifying targets. Chain Reactions (Advanced): What happens if a Color Bomb destroys a striped candy? Should the striped candy also activate? This requires careful design. Implementing chains significantly increases complexity, as executeColorBombEffect would need to detect destroyed specials, trigger their effects recursively, and wait for everything to finish. It's easier to not implement chains initially. Color Bomb + Color Bomb Swap: Swapping two Color Bombs usually clears the entire screen. This requires a specific check in swapCandies and likely a separate function (e.g., executeBoardClearEffect). Visual Effect: Instead of all target candies popping simultaneously, consider staggering the pop() delays to create an effect that radiates outwards from the bomb or appears more random and impactful. Code Clarity: Keep the code organized. Separate the effect logic clearly. Remove unused variables like isDropping. By carefully managing the sequence of operations and ensuring the game waits correctly for animations and effects to complete, you can implement this powerful and fun Color Bomb mechanic successfully.
User prompt
Please fix the bug: 'Uncaught ReferenceError: isDropping is not defined' in or related to this line: 'if (game && typeof game.handleCandyClick === 'function' && !isSwapping && !isDropping) {' Line Number: 181
User prompt
Please fix the bug: 'Uncaught ReferenceError: isDropping is not defined' in or related to this line: 'if (game && typeof game.handleCandyClick === 'function' && !isSwapping && !isDropping) {' Line Number: 181
/**** 
* Plugins
****/ 
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
	level: 1,
	score: 0,
	highScore: 0
});
/**** 
* Classes
****/ 
var Candy = Container.expand(function (type, row, col) {
	var self = Container.call(this);
	self.type = type || 'redCandy';
	self.row = row || 0;
	self.col = col || 0;
	self.isSpecial = false; // Flag indicating if this candy is special (e.g., striped, wrapped)
	// Visual representation
	var visual = self.attachAsset(self.type, {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 1
	});
	// Selection overlay (hidden by default)
	var overlay = self.attachAsset('selectedOverlay', {
		anchorX: 0.5,
		anchorY: 0.5,
		alpha: 0,
		visible: false // Start invisible
	});
	// Marker for special candies (optional, initially hidden)
	var specialMarker = null;
	self.setSpecial = function (special) {
		self.isSpecial = special;
		// Add visual distinction for special candies if needed
		if (special) {
			if (!specialMarker) {
				// Example: Add a marker instead of scaling/pulsing the base visual
				specialMarker = self.attachAsset('specialMarkerCandy', {
					anchorX: 0.5,
					anchorY: 0.5,
					alpha: 1 // Make it visible
				});
			}
			// Optional: Pulse animation for special marker
			tween.stop(specialMarker); // Stop previous tweens on marker
			specialMarker.alpha = 0.6;
			tween(specialMarker, {
				alpha: 1.0
			}, {
				duration: 800,
				easing: tween.easeInOut,
				yoyo: true,
				// Go back and forth
				loop: true // Repeat indefinitely
			});
		} else {
			if (specialMarker) {
				tween.stop(specialMarker); // Stop animation
				specialMarker.destroy();
				specialMarker = null;
			}
		}
	};
	self.select = function () {
		overlay.visible = true;
		tween.stop(overlay, {
			alpha: true
		}); // Stop previous animation
		tween(overlay, {
			alpha: 0.4
		}, {
			duration: 150
		});
	};
	self.deselect = function () {
		tween.stop(overlay, {
			alpha: true
		}); // Stop previous animation
		tween(overlay, {
			alpha: 0
		}, {
			duration: 150,
			onFinish: function onFinish() {
				overlay.visible = false; // Hide after fade out
			}
		});
	};
	self.moveTo = function (newRow, newCol, animate, delay, onComplete) {
		self.row = newRow;
		self.col = newCol;
		var newX = CELL_SIZE * self.col + CELL_SIZE / 2;
		var newY = CELL_SIZE * self.row + CELL_SIZE / 2;
		if (animate) {
			tween.stop(self, {
				x: true,
				y: true
			}); // Stop previous movement
			tween(self, {
				x: newX,
				y: newY
			}, {
				duration: 300,
				// Consistent duration
				delay: delay || 0,
				easing: tween.easeOutQuad,
				// Smooth easing
				onFinish: onComplete
			});
		} else {
			tween.stop(self, {
				x: true,
				y: true
			}); // Ensure any running tween is stopped
			self.x = newX;
			self.y = newY;
			if (onComplete) {
				onComplete();
			}
		}
	};
	// Pop animation - Returns the tween instance
	self.pop = function (delay, onComplete) {
		tween.stop(self, {
			alpha: true,
			scaleX: true,
			scaleY: true
		}); // Stop other tweens
		return tween(self, {
			alpha: 0,
			scaleX: 0.1,
			// Shrink down 
			scaleY: 0.1
		}, {
			duration: 250,
			delay: delay || 0,
			easing: tween.easeInQuad,
			// Ease in for disappearance 
			onFinish: function onFinish() {
				// Note: Actual destruction and removal from arrays happen elsewhere 
				if (onComplete) {
					onComplete();
				}
			}
		});
	};
	// Override destroy to clean up marker tween
	var baseDestroy = self.destroy;
	self.destroy = function () {
		if (specialMarker) {
			tween.stop(specialMarker);
		}
		baseDestroy.call(self); // Call original destroy
	};
	self.down = function (x, y, obj) {
		// Delegate to the main game click handler
		if (game && typeof game.handleCandyClick === 'function' && !isSwapping && !isDropping) {
			game.handleCandyClick(self);
		}
	};
	return self;
});
var GameBoard = Container.expand(function (rows, cols) {
	var self = Container.call(this);
	self.rows = rows || 8;
	self.cols = cols || 8;
	// Background board
	var boardVisual = self.attachAsset('gameBoard', {
		anchorX: 0.5,
		anchorY: 0.5
	});
	boardVisual.width = CELL_SIZE * self.cols; // Match width to grid
	boardVisual.height = CELL_SIZE * self.rows; // Match height to grid
	// Cell backgrounds
	self.cells = [];
	for (var row = 0; row < self.rows; row++) {
		self.cells[row] = []; // Initialize row array
		for (var col = 0; col < self.cols; col++) {
			var cell = self.attachAsset('cellBackground', {
				anchorX: 0.5,
				anchorY: 0.5,
				x: col * CELL_SIZE + CELL_SIZE / 2,
				y: row * CELL_SIZE + CELL_SIZE / 2
			});
			self.cells[row][col] = cell; // Store cell reference if needed later
		}
	}
	return self;
});
/**** 
* Initialize Game
****/ 
var game = new LK.Game({
	backgroundColor: 0x000000 // Set background color to black 
});
/**** 
* Game Code
****/ 
var popPromises = []; // Initialize popPromises array to store promises for animations
function handleCompletion(actions, callback) {
	var remaining = actions.length;
	if (remaining === 0) {
		callback();
	}
	actions.forEach(function (action) {
		action.then(function () {
			remaining--;
			if (remaining === 0) {
				callback();
			}
		});
	});
}
function resolveDropsAndFills() {
	handleDrops();
}
function executeDoubleStripedEffect(centerCandy) {
	isProcessing = true;
	var targetRow = centerCandy.row;
	var targetCol = centerCandy.col;
	var candiesToDestroy = [];
	for (var c = 0; c < BOARD_SIZE; c++) {
		var _board$targetRow;
		var candy = (_board$targetRow = board[targetRow]) === null || _board$targetRow === void 0 ? void 0 : _board$targetRow[c];
		if (candy && candiesToDestroy.indexOf(candy) === -1) {
			candiesToDestroy.push(candy);
			board[targetRow][c] = null;
		}
	}
	// Game state variables
	var popPromises = []; // Initialize popPromises array to store promises for animations
	for (var r = 0; r < BOARD_SIZE; r++) {
		var _board$r;
		var candy = (_board$r = board[r]) === null || _board$r === void 0 ? void 0 : _board$r[targetCol];
		if (candy && candiesToDestroy.indexOf(candy) === -1) {
			candiesToDestroy.push(candy);
			board[r][targetCol] = null;
		}
	}
	// Play sound and update score
	LK.getSound('special').play();
	score += candiesToDestroy.length * 20;
	updateUI();
	// Prepare animations
	var actions = [];
	candiesToDestroy.forEach(function (candy) {
		var delay = (Math.abs(candy.row - targetRow) + Math.abs(candy.col - targetCol)) * 35;
		actions.push({
			then: function then(resolve) {
				return candy.pop(delay, resolve);
			}
		});
	});
	// Wait for animations to complete
	handleCompletion(actions, function () {
		candiesToDestroy.forEach(function (c) {
			return c.destroy();
		});
		resolveDropsAndFills();
	});
}
function executeColorBombEffect(bombCandy, targetType) {
	isProcessing = true; // Lock the board
	// Find all candies of the targetType
	var candiesToDestroyByBomb = [];
	candies.forEach(function (c) {
		if (c.type === targetType && !c.isSpecial) {
			candiesToDestroyByBomb.push(c);
		}
	});
	// Include the bombCandy itself
	candiesToDestroyByBomb.push(bombCandy);
	// Update board model
	candiesToDestroyByBomb.forEach(function (candy) {
		board[candy.row][candy.col] = null;
	});
	// Prepare animations
	candiesToDestroyByBomb.forEach(function (candy, index) {
		popPromises.push({
			then: function then(resolve) {
				candy.pop(index * 30, function () {
					candy.destroy();
					resolve();
				});
			}
		});
	});
	// Play sound and update score
	LK.getSound('special').play();
	score += candiesToDestroyByBomb.length * 10;
	updateUI();
	// Wait for animations to complete
	(function (promises, callback) {
		var remaining = promises.length;
		if (remaining === 0) {
			callback();
		}
		promises.forEach(function (promise) {
			promise.then(function () {
				remaining--;
				if (remaining === 0) {
					callback();
				}
			});
		});
	})(popPromises, function () {
		// Cleanup and proceed to drop/fill
		candiesToDestroyByBomb.forEach(function (candy) {
			var index = candies.indexOf(candy);
			if (index > -1) {
				candies.splice(index, 1);
			}
		});
		resolveDropsAndFills();
	});
}
function activateSpecialCandy(candy) {
	switch (candy.type) {
		case 'colorBomb':
			// Clear all candies of the color it was swapped with
			var targetType = candy.swappedWithType; // Assume swappedWithType is set during swap
			candies.forEach(function (c) {
				if (c.type === targetType) {
					popPromises.push({
						then: function then(resolve) {
							c.pop(0, function () {
								c.destroy();
								resolve();
							});
						}
					});
				}
			});
			break;
		case 'stripedH':
			// Clear entire row
			for (var col = 0; col < BOARD_SIZE; col++) {
				var rowCandy = board[candy.row][col];
				if (rowCandy) {
					popPromises.push({
						then: function then(resolve) {
							rowCandy.pop(col * 50, function () {
								// Add delay based on column index
								rowCandy.destroy();
								resolve();
							});
						}
					});
				}
			}
			break;
		case 'stripedV':
			// Clear entire column
			for (var row = 0; row < BOARD_SIZE; row++) {
				var colCandy = board[row][candy.col];
				if (colCandy) {
					colCandy.pop(row * 50, function () {
						// Add delay based on row index
						colCandy.destroy();
					});
				}
			}
			break;
		default:
			break;
	}
}
// Sound for special candy creation/activation
// Keep as is for selection
// Example: Could be a different shape/texture
// Renamed specialCandy shape slightly for clarity
// Assets definition looks good, assuming LK.init.shape/sound works as expected.
// Constants
var BOARD_SIZE = 8;
var CELL_SIZE = 200; // Increase size of each cell for better visibility
var CANDY_TYPES = ['redCandy', 'yellowCandy', 'greenCandy', 'blueCandy', 'purpleCandy', 'orangeCandy'];
var NUM_CANDY_TYPES = CANDY_TYPES.length;
// Game state variables
var board; // 2D array holding Candy objects or null
var candies = []; // Flat list of all active Candy objects on the board
var selectedCandy = null;
var isSwapping = false; // True during the swap animation
var isProcessing = false; // General flag for when board is resolving (matches, drops, fills)
var isDropping = false; // Flag to indicate if candies are currently dropping
var moves = 0;
var score = 0;
var targetScore = 0;
var level = storage.level || 1;
var highScore = storage.highScore || 0;
// Game elements
var gameBoardContainer; // Holds the visual board and candies
var scoreTxt, movesTxt, levelTxt;
// --- Core Game Logic Functions ---
function setupGame() {
	// Create and center the game board container
	var background = LK.getAsset('gameBoard', {
		anchorX: 0.5,
		anchorY: 0.5,
		x: 2048 / 2,
		y: 2732 / 2,
		scaleX: 2048 / 100,
		scaleY: 2732 / 100
	});
	game.addChild(background);
	gameBoardContainer = new GameBoard(BOARD_SIZE, BOARD_SIZE);
	// Center the board (assuming 2048x2732 is the stage size)
	gameBoardContainer.x = (2048 - gameBoardContainer.width) / 2 + 400; // Shift right by 400 pixels
	gameBoardContainer.y = (2732 - gameBoardContainer.height) / 2 + 200; // Shift down by 200 pixels
	game.addChild(gameBoardContainer);
	// Setup UI elements
	scoreTxt = new Text2('Score: 0', {
		size: 70,
		fill: 0xFFFFFF,
		align: 'right'
	});
	scoreTxt.anchor.set(1, 0); // Anchor top-right
	LK.gui.topRight.addChild(scoreTxt);
	scoreTxt.x = -20; // Add some padding from the edge
	movesTxt = new Text2('Moves: 0', {
		size: 70,
		fill: 0xFFFFFF,
		align: 'left'
	});
	movesTxt.anchor.set(0, 0); // Anchor top-left
	LK.gui.topLeft.addChild(movesTxt);
	movesTxt.x = 20; // Add some padding from the edge
	levelTxt = new Text2('Level: 1', {
		size: 70,
		fill: 0xFFFFFF
	});
	levelTxt.anchor.set(0.5, 0); // Anchor top-center
	LK.gui.top.addChild(levelTxt);
	// Add "Glaud" text in small orange at the bottom center
	var glaudTxt = new Text2('Glaud', {
		size: 40,
		fill: 0xFF8C00 // Orange color
	});
	glaudTxt.anchor.set(0.5, 1); // Anchor bottom-center
	LK.gui.bottom.addChild(glaudTxt);
	// Initialize the first level
	initializeLevel(level);
	// Start background music
	LK.playMusic('bgmusic', {
		loop: true,
		fade: {
			start: 0,
			end: 0.5,
			duration: 1000
		}
	});
	// Attach global click listener (if Candy.down isn't sufficient)
	// game.down = handleGameClick; // Use this if clicking empty cells should deselect
}
function initializeLevel(levelNum) {
	level = levelNum;
	score = 0;
	moves = 20 + (level - 1) * 2; // Example: Increase moves per level
	targetScore = 1000 * level; // Example: Increase target score per level
	// Add additional objectives like clearing jellies or bringing ingredients down
	var objectives = {
		clearJellies: Math.floor(level / 2),
		bringIngredients: Math.floor(level / 3)
	};
	selectedCandy = null;
	isSwapping = false;
	isProcessing = false;
	updateUI();
	initializeBoard();
}
function initializeBoard() {
	// Clear existing candies visually and from arrays
	for (var i = 0; i < candies.length; i++) {
		candies[i].destroy();
	}
	candies = [];
	board = [];
	// Create the board array and fill with candies
	for (var row = 0; row < BOARD_SIZE; row++) {
		board[row] = [];
		for (var col = 0; col < BOARD_SIZE; col++) {
			board[row][col] = null; // Ensure initialized to null
			// Generate candy ensuring no initial matches
			var attempts = 0;
			do {
				var type = getRandomCandyType();
				// Temporarily place to check for matches
				var tempCandy = {
					type: type
				}; // Lightweight check
				board[row][col] = tempCandy;
				attempts++;
				if (attempts > 50) {
					// Safety break for rare impossible scenarios
					console.warn("Struggling to place initial candy without match at", row, col);
					break;
				}
			} while (checkMatchAt(row, col));
			// If check passed or safety break, create the real candy
			var candy = new Candy(type, row, col);
			candy.moveTo(row, col, false); // Place instantly
			gameBoardContainer.addChild(candy);
			board[row][col] = candy; // Place real candy in board
			candies.push(candy);
		}
	}
	// Final check: Ensure there are possible moves
	if (!hasPossibleMoves()) {
		console.log("No initial moves, reshuffling...");
		// Use LK.setTimeout to allow the initial draw frame to happen first
		LK.setTimeout(reshuffleBoard, 100);
	}
}
// Helper to check for matches *only* at a specific new candy's location
function checkMatchAt(row, col) {
	var candy = board[row][col];
	if (!candy) {
		return false;
	}
	var type = candy.type;
	// Check Horizontal
	var count = 1;
	if (col > 0 && board[row][col - 1] && board[row][col - 1].type === type) {
		count++;
	}
	if (col > 1 && board[row][col - 2] && board[row][col - 2].type === type) {
		count++;
	}
	if (count >= 3) {
		return true;
	}
	// Check Vertical
	count = 1;
	if (row > 0 && board[row - 1][col] && board[row - 1][col].type === type) {
		count++;
	}
	if (row > 1 && board[row - 2][col] && board[row - 2][col].type === type) {
		count++;
	}
	if (count >= 3) {
		return true;
	}
	return false;
}
function getRandomCandyType() {
	var typesAvailable = Math.min(NUM_CANDY_TYPES, 3 + Math.floor(level / 3)); // Slower difficulty ramp
	return CANDY_TYPES[Math.floor(Math.random() * typesAvailable)];
}
function updateUI() {
	scoreTxt.setText('Score: ' + score);
	movesTxt.setText('Moves: ' + moves);
	levelTxt.setText('Level: ' + level);
}
// Renamed from handleCandyClick to be clearer
game.handleCandyClick = function (candy) {
	if (isProcessing || isSwapping || isDropping) {
		return;
	} // Ignore clicks during processing or animations
	if (selectedCandy === null) {
		selectedCandy = candy;
		// Highlight the cell background
		gameBoardContainer.cells[candy.row][candy.col].tint = 0xffff00; // Highlight with yellow
	} else {
		// Second selection
		if (selectedCandy === candy) {
			// Deselect if same candy clicked
			// Revert cell background tint
			gameBoardContainer.cells[selectedCandy.row][selectedCandy.col].tint = 0x704f3d; // Original color
			gameBoardContainer.cells[selectedCandy.row][selectedCandy.col].tint = 0x704f3d; // Revert tint
			selectedCandy = null;
		} else {
			// Check if candies are adjacent
			var rowDiff = Math.abs(selectedCandy.row - candy.row);
			var colDiff = Math.abs(selectedCandy.col - candy.col);
			if (rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1) {
				// Adjacent: Initiate swap
				selectedCandy.deselect(); // Deselect the first one visually
				swapCandies(selectedCandy, candy);
				selectedCandy = null; // Clear selection after initiating swap
			} else {
				// Not adjacent: Change selection to the new candy
				// Revert previous selection's cell background tint
				gameBoardContainer.cells[selectedCandy.row][selectedCandy.col].tint = 0x704f3d; // Original color
				selectedCandy = candy;
				// Highlight new selection's cell background
				gameBoardContainer.cells[candy.row][candy.col].tint = 0xffff00; // Highlight with yellow
				LK.getSound('invalid').play(); // Sound for invalid selection change
			}
		}
	}
};
function swapCandies(candy1, candy2) {
	if (isSwapping || isProcessing) {
		return;
	} // Prevent overlapping swaps
	isSwapping = true;
	// Store original positions for potential revert
	var candy1Row = candy1.row;
	var candy1Col = candy1.col;
	var candy2Row = candy2.row;
	var candy2Col = candy2.col;
	// Track the type of candy a color bomb is swapped with
	if (candy1.type === 'colorBomb') {
		candy1.swappedWithType = candy2.type;
	} else if (candy2.type === 'colorBomb') {
		candy2.swappedWithType = candy1.type;
	}
	// Update board model immediately
	board[candy1Row][candy1Col] = candy2;
	board[candy2Row][candy2Col] = candy1;
	// Update candy objects' internal row/col
	candy1.row = candy2Row;
	candy1.col = candy2Col;
	candy2.row = candy1Row;
	candy2.col = candy1Col;
	// Animate the swap visually
	LK.getSound('swap').play();
	var tween1 = candy1.moveTo(candy2Row, candy2Col, true);
	var tween2 = candy2.moveTo(candy1Row, candy1Col, true);
	// After animation finishes (wait for the longer of the two, though they should be same duration)
	// Using a small timeout is simpler than tracking both tweens
	LK.setTimeout(function () {
		// Check if the swap resulted in a match
		var matchFound = checkMatchAt(candy1.row, candy1.col) || checkMatchAt(candy2.row, candy2.col) || findMatches().length > 0; // More robust check
		// Check for color bomb swap
		if (candy1.type === 'colorBomb' && !candy2.isSpecial) {
			executeColorBombEffect(candy1, candy2.type);
			isSwapping = false;
			return;
		} else if (candy2.type === 'colorBomb' && !candy1.isSpecial) {
			executeColorBombEffect(candy2, candy1.type);
			isSwapping = false;
			return;
		}
		if (matchFound) {
			// Valid swap, deduct move and process matches
			moves--;
			updateUI();
			isSwapping = false; // Allow processing now
			processBoard(); // Start the match -> drop -> fill cycle
			// Check game state later, after processing finishes
		} else if ((candy1.type === 'stripedH' || candy1.type === 'stripedV') && (candy2.type === 'stripedH' || candy2.type === 'stripedV')) {
			executeDoubleStripedEffect(candy1);
			isSwapping = false;
			return;
		} else {
			// Invalid swap, animate back
			LK.getSound('invalid').play();
			// Update board model back
			board[candy1Row][candy1Col] = candy1;
			board[candy2Row][candy2Col] = candy2;
			// Update candy objects' internal row/col back
			candy1.row = candy1Row;
			candy1.col = candy1Col;
			candy2.row = candy2Row;
			candy2.col = candy2Col;
			// Animate back
			candy1.moveTo(candy1Row, candy1Col, true);
			candy2.moveTo(candy2Row, candy2Col, true);
			// After swap back animation finishes
			LK.setTimeout(function () {
				isSwapping = false; // Ready for next input
				gameBoardContainer.cells[candy1.row][candy1.col].tint = 0x704f3d; // Revert tint
				gameBoardContainer.cells[candy2.row][candy2.col].tint = 0x704f3d; // Revert tint
				selectedCandy = null;
			}, 310); // Slightly longer than moveTo duration
		}
	}, 310); // Wait for swap animation to mostly complete
}
// Main processing loop trigger
function processBoard() {
	if (isProcessing) {
		return;
	} // Avoid re-entry
	isProcessing = true;
	handleMatches();
}
// Step 1: Find and handle matches
function handleMatches() {
	var matches = findMatches();
	if (matches.length > 0) {
		LK.getSound('match').play();
		var pointsEarned = 0;
		var specialCandiesToCreate = []; // Store {row, col, type} for special creation
		// Mark candies for removal and calculate score/special
		var candiesToRemove = [];
		matches.forEach(function (match) {
			var matchSize = match.length;
			var pointsPerCandy = 10 * matchSize; // Bonus for longer matches
			// Decide if a special candy should be created from this match
			var createSpecial = null; // { type: 'stripedH', row: r, col: c } etc.
			if (matchSize >= 5) {
				// 5-in-a-row -> Color Bomb (example)
				createSpecial = {
					type: 'colorBomb',
					row: match.row,
					col: match.col + (match.horizontal ? Math.floor(matchSize / 2) : 0)
				};
				if (!match.horizontal) {
					createSpecial.col = match.col;
				}
				if (!match.horizontal) {
					createSpecial.row = match.row + Math.floor(matchSize / 2);
				}
			} else if (matchSize === 4) {
				// 4-in-a-row -> Striped (example)
				createSpecial = {
					type: match.horizontal ? 'stripedV' : 'stripedH',
					row: match.row,
					col: match.col + (match.horizontal ? Math.floor(matchSize / 2) : 0)
				};
				if (!match.horizontal) {
					createSpecial.col = match.col;
				}
				if (!match.horizontal) {
					createSpecial.row = match.row + Math.floor(matchSize / 2);
				}
			} // Add logic for L/T shapes for Wrapped candies if needed
			var specialCreated = false;
			for (var j = 0; j < matchSize; j++) {
				var row = match.horizontal ? match.row : match.row + j;
				var col = match.horizontal ? match.col + j : match.col;
				var candy = board[row][col];
				if (candy && candiesToRemove.indexOf(candy) === -1) {
					if (candy.isSpecial) {
						popPromises.push({
							then: function then(resolve) {
								setTimeout(function () {
									activateSpecialCandy(candy);
									resolve();
								}, 300); // Delay special candy activation to allow normal match processing
							}
						});
					}
					// Ensure not already marked
					candiesToRemove.push(candy);
					pointsEarned += pointsPerCandy * (candy.isSpecial ? 2 : 1); // More points for popping special candies
					// If this is the spot for special creation, mark it
					if (createSpecial && row === createSpecial.row && col === createSpecial.col) {
						specialCandiesToCreate.push(createSpecial);
						specialCreated = true;
					}
				}
			}
			// If special should be created but wasn't assigned to a specific candy (e.g., user swipe location matters), handle here
			// if (createSpecial && !specialCreated) { ... }
		});
		score += pointsEarned;
		updateUI();
		// Animate removal
		candiesToRemove.forEach(function (candy, index) {
			// Remove from board model immediately
			if (board[candy.row][candy.col] === candy) {
				board[candy.row][candy.col] = null;
			}
			// Animate pop
			popPromises.push({
				then: function then(resolve) {
					candy.pop(index * 30, resolve); // Pass resolve directly to pop as onComplete
				}
			});
			// Remove from the master list
			var listIndex = candies.indexOf(candy);
			if (listIndex > -1) {
				candies.splice(listIndex, 1);
			}
		});
		// After all pops are visually done, proceed to drop/fill
		(function (promises, callback) {
			var remaining = promises.length;
			if (remaining === 0) {
				callback();
			}
			promises.forEach(function (promise) {
				promise.then(function () {
					remaining--;
					if (remaining === 0) {
						callback();
					}
				});
			});
		})(popPromises, function () {
			candiesToRemove.forEach(function (candy) {
				return candy.destroy();
			}); // Final cleanup
			// Now create the special candies *before* dropping
			specialCandiesToCreate.forEach(function (spec) {
				var newType = spec.type; // Use the specified special type
				// In a real game, 'spec.type' would determine the *kind* of special candy
				// For now, we just make it visually 'special'
				var specialCandy = new Candy(spec.type, spec.row, spec.col);
				specialCandy.setSpecial(true); // Make it special
				specialCandy.moveTo(spec.row, spec.col, false); // Place instantly
				gameBoardContainer.addChild(specialCandy);
				board[spec.row][spec.col] = specialCandy;
				candies.push(specialCandy);
				LK.getSound('special').play();
			});
			handleDrops(); // Proceed to next step
		});
	} else {
		// No matches found, board is stable
		isProcessing = false;
		selectedCandy = null; // Clear selection
		// Check for possible moves ONLY if the board is stable
		if (!hasPossibleMoves()) {
			reshuffleBoard(); // This will set isProcessing again briefly
		} else {
			checkGameState(); // Check win/loss only when stable and moves exist
		}
	}
}
// Step 2: Handle candies dropping down
function handleDrops() {
	var dropsOccurred = false;
	var dropPromises = [];
	for (var col = 0; col < BOARD_SIZE; col++) {
		var emptyRow = BOARD_SIZE - 1; // Start checking from bottom
		// Find the lowest empty spot
		for (var row = BOARD_SIZE - 1; row >= 0; row--) {
			if (board[row][col] === null) {
				emptyRow = row; // Found the lowest empty row in this column
				break; // No need to check further up for the initial empty spot
			}
		}
		// Now check candies above this empty spot
		for (var row = BOARD_SIZE - 1; row >= 0; row--) {
			if (board[row][col] !== null) {
				// Check if there's a gap below this candy
				var targetRow = row;
				while (targetRow + 1 < BOARD_SIZE && board[targetRow + 1][col] === null) {
					targetRow++;
				}
				if (targetRow !== row) {
					// Move in model
					var candy = board[row][col];
					board[targetRow][col] = candy;
					board[row][col] = null;
					// Animate move
					var dropTween = candy.moveTo(targetRow, col, true, 0); // Drop animation
					dropPromises.push({
						then: function then(resolve) {
							if (dropTween && dropTween.vars) {
								dropTween.vars.onFinish = resolve;
							} else {
								resolve(); // Resolve immediately if dropTween or vars is undefined
							}
						}
					});
					dropsOccurred = true;
				}
			}
		}
	}
	if (dropsOccurred) {
		(function (promises, callback) {
			var remaining = promises.length;
			if (remaining === 0) {
				callback();
			}
			promises.forEach(function (promise) {
				promise.then(function () {
					remaining--;
					if (remaining === 0) {
						callback();
					}
				});
			});
		})(dropPromises, handleFills); // Wait for drops, then fill 
	} else {
		handleFills(); // No drops, proceed directly to filling
	}
}
// Step 3: Fill empty spaces from the top
function handleFills() {
	var fillsOccurred = false;
	var fillPromises = [];
	for (var col = 0; col < BOARD_SIZE; col++) {
		var fillRow = -1; // Start generating candies above the board
		for (var row = BOARD_SIZE - 1; row >= 0; row--) {
			if (board[row][col] === null) {
				// Found an empty spot
				var type = getRandomCandyType();
				var candy = new Candy(type, fillRow, col); // Create above the board
				candy.moveTo(fillRow, col, false); // Place instantly above
				gameBoardContainer.addChild(candy);
				board[row][col] = candy; // Assign to target cell in model
				candies.push(candy);
				// Animate move into place
				var delay = (-fillRow - 1) * 50; // Stagger based on how high it starts
				var fillTween = candy.moveTo(row, col, true, delay);
				fillPromises.push({
					then: function then(resolve) {
						if (fillTween && fillTween.vars) {
							fillTween.vars.onFinish = resolve;
						} else {
							resolve(); // Resolve immediately if fillTween or vars is undefined
						}
					}
				});
				fillsOccurred = true;
				fillRow--; // Next candy starts even higher
			}
		}
	}
	if (fillsOccurred) {
		if (typeof Promise !== 'undefined' && Promise.all) {
			Promise.all(fillPromises).then(handleMatches); // Wait for fills, then check for new matches (cascade)
		} else {
			console.error("Promise is not supported in this environment.");
			handleMatches(); // Fallback to directly calling handleMatches
		}
	} else {
		// No fills needed, implies no matches occurred in the previous step either. Board is stable.
		handleMatches(); // Final check (will find no matches and trigger end processing)
	}
}
function findMatches() {
	var matches = [];
	var checked = []; // Keep track of candies already part of a found match
	function markChecked(candy) {
		if (candy && checked.indexOf(candy) === -1) {
			checked.push(candy);
		}
	}
	// Check horizontal matches
	for (var row = 0; row < BOARD_SIZE; row++) {
		for (var col = 0; col < BOARD_SIZE - 2; col++) {
			var candy1 = board[row][col];
			if (!candy1 || checked.indexOf(candy1) > -1) {
				continue;
			} // Skip nulls or already checked
			var candy2 = board[row][col + 1];
			var candy3 = board[row][col + 2];
			if (candy2 && candy3 && candy1.type === candy2.type && candy1.type === candy3.type) {
				// Found a horizontal match of at least 3
				var currentMatch = [candy1, candy2, candy3];
				var matchLength = 3;
				// Check for longer match
				for (var k = col + 3; k < BOARD_SIZE; k++) {
					var nextCandy = board[row][k];
					if (nextCandy && nextCandy.type === candy1.type) {
						currentMatch.push(nextCandy);
						matchLength++;
					} else {
						break; // End of match
					}
				}
				// Store match details
				matches.push({
					candies: currentMatch,
					// Store the actual candy objects
					row: row,
					col: col,
					length: matchLength,
					horizontal: true
				});
				// Mark all candies in this match as checked
				currentMatch.forEach(markChecked);
				col += matchLength - 1; // Skip checked candies in the outer loop
			}
		}
	}
	// Check vertical matches
	for (var col = 0; col < BOARD_SIZE; col++) {
		for (var row = 0; row < BOARD_SIZE - 2; row++) {
			var candy1 = board[row][col];
			// Skip nulls or candies already part of a horizontal match
			if (!candy1 || checked.indexOf(candy1) > -1) {
				continue;
			}
			var candy2 = board[row + 1][col];
			var candy3 = board[row + 2][col];
			if (candy2 && candy3 && candy1.type === candy2.type && candy1.type === candy3.type) {
				// Found a vertical match of at least 3
				var currentMatch = [candy1, candy2, candy3];
				var matchLength = 3;
				// Check for longer match
				for (var k = row + 3; k < BOARD_SIZE; k++) {
					var nextCandy = board[k][col];
					if (nextCandy && nextCandy.type === candy1.type) {
						// Avoid adding if already part of a horizontal match found earlier
						if (checked.indexOf(nextCandy) === -1) {
							currentMatch.push(nextCandy);
						} else {
							// If it was part of a horizontal match, just increase length but don't re-add
						}
						matchLength++;
					} else {
						break; // End of match
					}
				}
				// Store match details
				matches.push({
					candies: currentMatch,
					// Store the actual candy objects
					row: row,
					col: col,
					length: matchLength,
					horizontal: false
				});
				// Mark all candies in this match as checked
				currentMatch.forEach(markChecked);
				row += matchLength - 1; // Skip checked candies in the outer loop
			}
		}
	}
	return matches;
}
function hasPossibleMoves() {
	// Check horizontal swaps that could create a match
	for (var row = 0; row < BOARD_SIZE; row++) {
		for (var col = 0; col < BOARD_SIZE - 1; col++) {
			if (canSwapCreateMatch(row, col, row, col + 1)) {
				return true;
			}
		}
	}
	// Check vertical swaps that could create a match
	for (var row = 0; row < BOARD_SIZE - 1; row++) {
		for (var col = 0; col < BOARD_SIZE; col++) {
			if (canSwapCreateMatch(row, col, row + 1, col)) {
				return true;
			}
		}
	}
	return false; // No possible moves found
}
// Helper function for hasPossibleMoves
function canSwapCreateMatch(r1, c1, r2, c2) {
	var candy1 = board[r1][c1];
	var candy2 = board[r2][c2];
	if (!candy1 || !candy2) {
		return false;
	} // Cannot swap with empty space
	// Temporarily swap types in the model (don't need full object swap)
	board[r1][c1] = candy2;
	board[r2][c2] = candy1;
	// Check if this swap creates a match around either position
	var createsMatch = checkMatchAt(r1, c1) || checkMatchAt(r2, c2) || findMatches().length > 0; // Simplified check
	// Swap back
	board[r1][c1] = candy1;
	board[r2][c2] = candy2;
	return createsMatch;
}
function reshuffleBoard() {
	if (isProcessing) {
		// Don't reshuffle if already processing
		LK.setTimeout(reshuffleBoard, 500); // Try again later
		return;
	}
	isProcessing = true; // Block input during reshuffle
	// Display reshuffling message
	var reshuffleTxt = new Text2('Reshuffling...', {
		size: 100,
		fill: 0xFFFFFF
	});
	reshuffleTxt.anchor.set(0.5, 0.5);
	LK.gui.center.addChild(reshuffleTxt);
	// 1. Collect all current candy types and special status
	var currentCandiesInfo = [];
	candies.forEach(function (candy) {
		currentCandiesInfo.push({
			type: candy.type,
			isSpecial: candy.isSpecial // Retain special status during reshuffle
		});
		candy.destroy(); // Remove old visuals immediately
	});
	candies = []; // Clear the list
	board = []; // Clear the board model
	// 2. Shuffle the collected info
	for (var i = currentCandiesInfo.length - 1; i > 0; i--) {
		var j = Math.floor(Math.random() * (i + 1));
		var _ref = [currentCandiesInfo[j], currentCandiesInfo[i]];
		currentCandiesInfo[i] = _ref[0];
		currentCandiesInfo[j] = _ref[1];
	}
	// 3. Rebuild the board ensuring no initial matches AND possible moves
	var attempts = 0;
	do {
		attempts++;
		if (attempts > 10) {
			// Safety break if it struggles too much
			console.error("Failed to reshuffle into a valid state after 10 attempts.");
			// Could force a simple known-good layout here or reset level
			isProcessing = false; // Unlock
			reshuffleTxt.destroy();
			// Maybe show a "No more moves" game over?
			checkGameState(true); // Force check, likely leading to game over if moves are 0
			return;
		}
		// Clear board for re-attempt
		candies.forEach(function (c) {
			return c.destroy();
		});
		candies = [];
		board = [];
		var infoIndex = 0;
		for (var row = 0; row < BOARD_SIZE; row++) {
			board[row] = [];
			for (var col = 0; col < BOARD_SIZE; col++) {
				var info = currentCandiesInfo[infoIndex++];
				board[row][col] = null; // Temp null for checkMatchAt
				// Ensure no immediate matches with shuffled type
				var placeAttempts = 0;
				do {
					var currentInfo = info; // Use the shuffled info
					board[row][col] = {
						type: currentInfo.type,
						isSpecial: currentInfo.isSpecial
					}; // Temp place
					placeAttempts++;
					if (placeAttempts > currentCandiesInfo.length) {
						// Avoid infinite loop if types are very limited
						// Swap with a later random type if stuck
						var swapIdx = infoIndex + Math.floor(Math.random() * (currentCandiesInfo.length - infoIndex));
						if (swapIdx < currentCandiesInfo.length) {
							var _ref2 = [currentCandiesInfo[swapIdx], currentCandiesInfo[infoIndex - 1]];
							currentCandiesInfo[infoIndex - 1] = _ref2[0];
							currentCandiesInfo[swapIdx] = _ref2[1];
							info = currentCandiesInfo[infoIndex - 1]; // Get the newly swapped info
						}
						board[row][col] = {
							type: info.type,
							isSpecial: info.isSpecial
						}; // Try again with swapped
						break; // Exit do-while for this cell after swap
					}
				} while (checkMatchAt(row, col)); // Check if placing this type creates a match
				// Create the actual candy
				var candy = new Candy(board[row][col].type, row, col);
				candy.setSpecial(board[row][col].isSpecial);
				candy.alpha = 0; // Start invisible
				candy.moveTo(row, col, false);
				gameBoardContainer.addChild(candy);
				board[row][col] = candy; // Replace temp object with real candy
				candies.push(candy);
				// Animate fade-in
				tween(candy, {
					alpha: 1
				}, {
					duration: 300,
					delay: (row + col) * 20
				});
			}
		}
		// Loop condition: Keep reshuffling if the new board has no possible moves
	} while (!hasPossibleMoves() && attempts < 10);
	// Reshuffle successful
	LK.setTimeout(function () {
		reshuffleTxt.destroy();
		isProcessing = false; // Unlock the board
	}, 500); // Keep message visible briefly
}
function checkGameState() {
	var forceCheck = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
	// Don't check state while the board is actively processing, unless forced (e.g., reshuffle failed)
	if (isProcessing && !forceCheck) {
		return;
	}
	// Check Win Condition
	if (score >= targetScore) {
		console.log("Level " + level + " Complete!");
		isProcessing = true; // Prevent further input
		// Update storage
		level++;
		storage.level = level;
		if (score > highScore) {
			highScore = score;
			storage.highScore = highScore;
		}
		storage.score = score; // Save current score too, maybe for win screen display
		LK.setTimeout(function () {
			LK.showYouWin({
				level: level - 1,
				score: score,
				highScore: highScore
			});
		}, 1000); // Delay for effect
	}
	// Check Lose Condition
	else if (moves <= 0) {
		console.log("Game Over - Out of Moves!");
		isProcessing = true; // Prevent further input
		// Update high score if needed
		if (score > highScore) {
			highScore = score;
			storage.highScore = highScore;
		}
		storage.score = score; // Save final score
		LK.setTimeout(function () {
			LK.showGameOver({
				level: level,
				score: score,
				highScore: highScore
			});
		}, 1000); // Delay for effect
	}
	// Note: The "no possible moves" scenario is now handled by reshuffleBoard,
	// but if reshuffling fails repeatedly, it might also lead to a game over state here.
}
// --- Game Start ---
setupGame(); ===================================================================
--- original.js
+++ change.js
:quality(85)/https://cdn.frvr.ai/67f7fc73de9c6f2eb0513ba7.png%3F3) 
 Generate a high-quality icon asset of the specific **blue candy** shown in the provided Candy Crush screenshot. * **Shape:** A smooth, **round, slightly flattened sphere or thick disc** shape with perfectly curved edges. It should look plump and solid. * **Color:** A bright, **vibrant, medium blue**. Clear and saturated, avoiding overly dark (navy) or light (sky blue) tones. * **Surface & Finish:** **Highly glossy** and reflective, like polished hard candy or a glass marble. The surface should look perfectly smooth. * **Lighting & Highlights:** Features a **prominent, distinct, curved white specular highlight** positioned near the **top-left edge**, following the candy's spherical contour. Additional subtle, broader highlights should be visible across the top surface, giving it dimension. Clear shading should be present on the bottom and right sides to emphasize its **3D, spherical volume**. * **Style:** Clean, **stylized 3D render**, matching the cheerful, polished, and sli. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67f7fe6dde9c6f2eb0513c6c.png%3F3) 
 Basic Prompt: Bright green candy, emerald green tones, crystallized texture, covered in small sugar granules, looking sweet and delicious. More Detailed Prompt (Optional): Bright green candy, with hints of emerald green and light lime green sparkles, crystallized texture, covered in small sugar granules, looking fresh and delicious as if it was just made. There is a faint halo of light around the candy, and the background is blurred. Additional Details to Add to the Prompt: Shape: Round, square, heart-shaped, etc. Material: Glass, frosted glass, sugar crystal, etc. Lighting: Soft, hard, dramatic, etc. Background: Solid color, patterned, blurred, themed (e.g., candy store), etc. Additional Objects: Other candies, paper packaging, ribbon, etc. Example Combined Prompt: Round, bright green candy with emerald green and lime green tones, crystallized texture, covered in small sugar granules, looking fresh and delicious. Soft lighting, blurred background.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67f7ff20de9c6f2eb0513c8e.png%3F3) 
 Basic Prompt: Bright orange candy, in shades of orange, with a smooth and glossy surface, sweet and appealing looking confection. More Detailed Prompt (Optional): Vibrant orange candy, luminous as if lit by sunlight, with hints of orange and light tangerine tones, possessing a smooth and flawless surface, high-quality candy. The candy has light reflections and the background is softly blurred. Additional Details to Add to the Prompt: Shape: Sphere, cube, star, etc. Texture: Smooth, matte, slightly grainy, etc. Lighting: Natural light, studio light, warm light, cool light, etc. Background: White, colored, patterned, candy store, kitchen counter, etc. Additional Objects: Candy wrapper, glass jar, candy stand, etc. Example Combined Prompt: Sphere-shaped, bright orange candy, luminous as if lit by sunlight, with hints of orange and light tangerine tones, possessing a smooth and flawless surface, high-quality candy. There are distinct light reflections on the candy. The background is white. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67f80063347c096bf4d62455.png%3F3) 
 Basic Prompt: Bright purple candy, in shades of lavender and violet, with a smooth surface, sweet and enticing looking confection. More Detailed Prompt (Optional): Deep purple in color, with hints of lavender and light lilac where the light hits it, possessing a smooth and glossy surface, crystal clear like glass, high-quality purple candy. There's a subtle halo of light around the candy, and the background is softly blurred. Additional Details to Add to the Prompt: Shape: Crystal, drop, heart, round, etc. Texture: Glossy, matte, frosted, slightly rough, etc. Lighting: Soft, dramatic, natural light, artificial light, etc. Background: Solid color, patterned, candy store, countertop, etc. Additional Objects: Other candies, transparent packaging, ribbon, etc. Basic Prompt: Bright purple candy, in shades of lavender and violet, with a smooth surface, sweet and enticing looking confection. More Detailed Prompt (Optional): Deep purple in color, with hints of lavender and light lilac where th. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67f800af347c096bf4d62460.png%3F3) 
 Basic Prompt: Bright yellow candy, lemon yellow color, smooth surface, sweet and tempting looking treat. More Detailed Prompt (Optional): Vibrant, sunny yellow candy, with subtle hints of lemon and gold. It has a smooth, glossy surface, almost like glass. The light catches it just right, creating a small sparkle. The background is slightly blurred. More Variations to add (Optional): Shape: Sphere, star, gumball, square, etc. Texture: Gummy, hard candy, crystalline, etc. Lighting: Soft, harsh, natural, artificial, etc. Background: Plain, gradient, candy store, etc. Additions: Candy wrapper, other candies, etc. Example Combination Prompt: Round, bright yellow candy like a gumball. Has a smooth, glossy surface with soft lighting. Hints of lemon and gold colors, p. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67f80158347c096bf4d6247b.png%3F3) 
 Basic Prompt: Bright red candy, vibrant crimson color, smooth surface, sweet and appealing treat. More Detailed Prompt (Optional): A glistening, ruby-red candy, with a smooth, reflective surface. The color is a rich, deep crimson, almost like a precious gem. The light catches it beautifully, creating highlights and shadows. The background is soft and blurred. Possible Variations to Add (Optional): Shape: Heart, sphere, cube, star, twisted, etc. Texture: Glossy, matte, gummy, hard, crystalline, etc. Lighting: Soft, harsh, natural, artificial, dramatic, etc. Background: Solid color, patterned, candy store, blurred, bokeh, etc. Additions: Candy wrapper, other candies, sprinkles, sugar coating, etc. Example Combination Prompt: A heart-shaped, glistening, ruby-red candy with a smooth, reflective surface. Rich, deep crimson color. Soft lighting, blurred background.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67f80a49373bb4eb006be2cf.png%3F3) 
 Generate a high-quality asset representing a **single square background tile** for a match-3 game cell, inspired by the cell interiors in the provided Candy Crush screenshot, but made **significantly more transparent**. * **Shape:** A perfect **square**. * **Base Appearance:** * **Color:** A **muted, desaturated, cool grey-blue** or **dark teal-grey**. * **Texture:** Contains an **extremely subtle, fine-grained texture** (like faint diagonal lines or uniform digital noise) integrated into the color. * **Transparency:** The key feature is **increased translucency**. The tile should be **moderately to significantly see-through**, allowing layers placed underneath it to be clearly visible. It should *not* be fully opaque like the original screenshot implies, nor fully transparent (invisible). Aim for roughly **40-60% opacity**. * **Lighting:** Maintain **soft, even, ambient lighting** across the surface of the square. No internal highlights or shadows within the tile. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
:quality(85)/https://cdn.frvr.ai/67f81a0358b4222a3fa333e7.png%3F3)