/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
currentLevel: 1
});
/****
* Classes
****/
var Flower = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.isMatched = false;
self.isMoving = false;
// Create flower asset based on type
var flowerGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.9,
scaleY: 0.9
});
// Method to handle selection/deselection visual effect
self.setSelected = function (selected) {
if (selected) {
tween(flowerGraphics, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut
});
} else {
tween(flowerGraphics, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 200,
easing: tween.easeOut
});
}
};
// Method to handle match animation
self.animateMatch = function (callback) {
self.isMatched = true;
tween(flowerGraphics, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (callback) {
callback();
}
}
});
};
// Method to move flower to a new position
self.moveTo = function (newX, newY, duration, callback) {
self.isMoving = true;
tween(self, {
x: newX,
y: newY
}, {
duration: duration || 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isMoving = false;
if (callback) {
callback();
}
}
});
};
// Event handlers
self.down = function (x, y, obj) {
// Handle touch/click in game logic
if (!self.isMatched && !self.isMoving && !gameOver) {
selectFlower(self);
}
};
return self;
});
var GameBoard = Container.expand(function (rows, cols, cellSize) {
var self = Container.call(this);
self.rows = rows;
self.cols = cols;
self.cellSize = cellSize;
self.boardWidth = cols * cellSize;
self.boardHeight = rows * cellSize;
self.grid = [];
// Create board background
var boardBackground = self.attachAsset('board', {
anchorX: 0.5,
anchorY: 0.5,
width: self.boardWidth,
height: self.boardHeight
});
// Initialize grid with cells
self.initializeGrid = function () {
// 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('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: cellSize - 10,
height: cellSize - 10
});
// Position cell
cell.x = col * cellSize - self.boardWidth / 2 + cellSize / 2;
cell.y = row * cellSize - self.boardHeight / 2 + cellSize / 2;
self.addChild(cell);
self.grid[row][col] = null; // Will hold flower references
}
}
};
// Add a flower to the grid at specified position
self.addFlower = function (flower, row, col) {
if (row >= 0 && row < rows && col >= 0 && col < cols) {
self.grid[row][col] = flower;
flower.row = row;
flower.col = col;
// Position flower in grid
flower.x = col * cellSize - self.boardWidth / 2 + cellSize / 2;
flower.y = row * cellSize - self.boardHeight / 2 + cellSize / 2;
self.addChild(flower);
return true;
}
return false;
};
// Remove a flower from the grid
self.removeFlower = function (row, col) {
if (row >= 0 && row < rows && col >= 0 && col < cols) {
var flower = self.grid[row][col];
if (flower) {
self.grid[row][col] = null;
return flower;
}
}
return null;
};
// Swap two flowers in the grid
self.swapFlowers = function (row1, col1, row2, col2, animate, callback) {
if (row1 < 0 || row1 >= rows || col1 < 0 || col1 >= cols || row2 < 0 || row2 >= rows || col2 < 0 || col2 >= cols) {
return false;
}
var flower1 = self.grid[row1][col1];
var flower2 = self.grid[row2][col2];
if (!flower1 || !flower2) {
return false;
}
// Update grid references
self.grid[row1][col1] = flower2;
self.grid[row2][col2] = flower1;
// Update flower position properties
flower1.row = row2;
flower1.col = col2;
flower2.row = row1;
flower2.col = col1;
if (animate) {
// Calculate target positions
var pos1 = {
x: col2 * cellSize - self.boardWidth / 2 + cellSize / 2,
y: row2 * cellSize - self.boardHeight / 2 + cellSize / 2
};
var pos2 = {
x: col1 * cellSize - self.boardWidth / 2 + cellSize / 2,
y: row1 * cellSize - self.boardHeight / 2 + cellSize / 2
};
// Animate the swap
flower1.moveTo(pos1.x, pos1.y, 200);
flower2.moveTo(pos2.x, pos2.y, 200, callback);
} else {
// Instantly swap positions
flower1.x = col2 * cellSize - self.boardWidth / 2 + cellSize / 2;
flower1.y = row2 * cellSize - self.boardHeight / 2 + cellSize / 2;
flower2.x = col1 * cellSize - self.boardWidth / 2 + cellSize / 2;
flower2.y = row1 * cellSize - self.boardHeight / 2 + cellSize / 2;
if (callback) {
callback();
}
}
return true;
};
// Get flower at specific grid position
self.getFlower = function (row, col) {
if (row >= 0 && row < rows && col >= 0 && col < cols) {
return self.grid[row][col];
}
return null;
};
// Check if coordinates are adjacent
self.areAdjacent = function (row1, col1, row2, col2) {
var rowDiff = Math.abs(row1 - row2);
var colDiff = Math.abs(col1 - col2);
// Adjacent if exactly one coordinate differs by 1 and the other is the same
return rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1;
};
return self;
});
// LicoriceBall class for combo feature
var LicoriceBall = Container.expand(function (row, col) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.isExploded = false;
// Attach a licorice ball asset (use a unique color/shape for now)
var licoriceGraphics = self.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 90,
height: 90,
color: 0x222222,
tint: 0x222222
});
// Add a visual cue (e.g. a white "O" text)
var licoriceText = new Text2('🍬', {
size: 60,
fill: 0xffffff
});
licoriceText.anchor.set(0.5, 0.5);
self.addChild(licoriceText);
// Animate appearance
licoriceGraphics.scaleX = 0.2;
licoriceGraphics.scaleY = 0.2;
tween(licoriceGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.bounceOut
});
// Handle explosion on tap
self.down = function (x, y, obj) {
if (!self.isExploded && !isProcessingMatches && !gameOver) {
self.isExploded = true;
triggerLicoriceExplosion(self.row, self.col);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x4CAF50
});
/****
* Game Code
****/
// Add custom background
var customBackground = LK.getAsset('customBackground', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732
});
customBackground.x = 2048 / 2;
customBackground.y = 2732 / 2;
game.addChild(customBackground);
// Language system
var SUPPORTED_LANGUAGES = [{
code: 'fr',
name: 'Français'
}, {
code: 'en',
name: 'English'
}, {
code: 'de',
name: 'Deutsch'
}, {
code: 'ja',
name: '日本語'
}, {
code: 'it',
name: 'Italiano'
}, {
code: 'es',
name: 'Español'
}, {
code: 'zh',
name: '中文'
}, {
code: 'el',
name: 'Ελληνικά'
}, {
code: 'ro',
name: 'Română'
}, {
code: 'pl',
name: 'Polski'
}];
var TRANSLATIONS = {
fr: {
score: 'Score: ',
moves: 'Mouvements: ',
level: 'Niveau: ',
reset: 'RESET',
changeLanguage: 'Changer la langue',
chooseLanguage: 'Choisissez votre langue',
choice: 'Choisir',
back: 'Retour'
},
en: {
score: 'Score: ',
moves: 'Moves: ',
level: 'Level: ',
reset: 'RESET',
changeLanguage: 'Change the Game Language',
chooseLanguage: 'Choose your language',
choice: 'Choice',
back: 'Back'
},
de: {
score: 'Punkte: ',
moves: 'Züge: ',
level: 'Level: ',
reset: 'RESET',
changeLanguage: 'Spielsprache ändern',
chooseLanguage: 'Wählen Sie Ihre Sprache',
choice: 'Wählen',
back: 'Zurück'
},
ja: {
score: 'スコア: ',
moves: '手数: ',
level: 'レベル: ',
reset: 'リセット',
changeLanguage: 'ゲーム言語を変更',
chooseLanguage: '言語を選択してください',
choice: '選択',
back: '戻る'
},
it: {
score: 'Punteggio: ',
moves: 'Mosse: ',
level: 'Livello: ',
reset: 'RESET',
changeLanguage: 'Cambia lingua del gioco',
chooseLanguage: 'Scegli la tua lingua',
choice: 'Scegli',
back: 'Indietro'
},
es: {
score: 'Puntuación: ',
moves: 'Movimientos: ',
level: 'Nivel: ',
reset: 'RESET',
changeLanguage: 'Cambiar idioma del juego',
chooseLanguage: 'Elige tu idioma',
choice: 'Elegir',
back: 'Volver'
},
zh: {
score: '分数: ',
moves: '步数: ',
level: '等级: ',
reset: '重置',
changeLanguage: '更改游戏语言',
chooseLanguage: '选择您的语言',
choice: '选择',
back: '返回'
},
el: {
score: 'Σκορ: ',
moves: 'Κινήσεις: ',
level: 'Επίπεδο: ',
reset: 'ΕΠΑΝΑΦΟΡΑ',
changeLanguage: 'Αλλαγή γλώσσας παιχνιδιού',
chooseLanguage: 'Επιλέξτε τη γλώσσα σας',
choice: 'Επιλογή',
back: 'Πίσω'
},
ro: {
score: 'Scor: ',
moves: 'Mișcări: ',
level: 'Nivel: ',
reset: 'RESET',
changeLanguage: 'Schimbă limba jocului',
chooseLanguage: 'Alege limba ta',
choice: 'Alege',
back: 'Înapoi'
},
pl: {
score: 'Wynik: ',
moves: 'Ruchy: ',
level: 'Poziom: ',
reset: 'RESET',
changeLanguage: 'Zmień język gry',
chooseLanguage: 'Wybierz swój język',
choice: 'Wybierz',
back: 'Wstecz'
}
};
var currentLanguage = storage.language || 'fr';
var languageMenuOpen = false;
var languageMenuContainer = null;
function getText(key) {
return TRANSLATIONS[currentLanguage][key] || TRANSLATIONS['fr'][key] || key;
}
// Game constants
var ROWS = 7;
var COLS = 7;
var CELL_SIZE = 100;
var FLOWER_TYPES = ['tulip', 'poppy', 'rose', 'violet', 'lily', 'jasmine', 'lilac'];
var MAX_MOVES = 50;
// Game state variables
var gameBoard;
var selectedFlower = null;
var movesLeft = MAX_MOVES;
var currentScore = 0;
var currentLevel = storage.currentLevel || 1;
var highScore = storage.highScore || 0;
var remainingFlowers = ROWS * COLS;
var isProcessingMatches = false;
var gameOver = false;
var matchCounter = 0;
var matchesRequiredForNextLevel = 5;
// UI elements
var scoreText;
var movesText;
var levelText;
// Initialize game
function initGame() {
// Create game board
gameBoard = new GameBoard(ROWS, COLS, CELL_SIZE);
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2;
game.addChild(gameBoard);
// Initialize the grid cells
gameBoard.initializeGrid();
// Populate the board with flowers
populateBoard();
// Create UI
createUI();
// Set initial game state
movesLeft = MAX_MOVES;
currentScore = 0;
remainingFlowers = ROWS * COLS;
gameOver = false;
matchCounter = 0;
matchesRequiredForNextLevel = 5 + (currentLevel - 1) * 3; // 5 for level 1, +3 for each level
// Update UI
updateUI();
// Play background music
LK.playMusic('bgmusic');
// Play background music
LK.playMusic('bgmusic');
}
// Create UI elements
function createUI() {
// Create score text
scoreText = new Text2(getText('score') + '0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0); // Anchored to top-right
LK.gui.topRight.addChild(scoreText);
// Create moves text
movesText = new Text2(getText('moves') + movesLeft, {
size: 60,
fill: 0xFFFFFF
});
movesText.anchor.set(0, 0); // Anchored to top-left
// Don't place in top-left corner due to platform menu icon
movesText.x = 120;
LK.gui.topLeft.addChild(movesText);
// Create level text
levelText = new Text2(getText('level') + currentLevel + ' (' + matchCounter + '/' + matchesRequiredForNextLevel + ')', {
size: 60,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
// Create language button
var languageButton = new Container();
var languageBackground = languageButton.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 60,
tint: 0xFF0000
});
var languageText = new Text2(getText('changeLanguage'), {
size: 35,
fill: 0xFFFFFF
});
languageText.anchor.set(0.5, 0.5);
languageButton.addChild(languageText);
languageButton.x = 0;
languageButton.y = 120;
LK.gui.top.addChild(languageButton);
// Add event handler to language button
languageButton.down = function (x, y, obj) {
if (!languageMenuOpen) {
showLanguageMenu();
}
};
// Store reference for updates
languageButton.textElement = languageText;
// Create reset button
var resetButton = new Container();
var resetBackground = resetButton.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 120,
height: 60,
tint: 0xFF0000
});
var resetText = new Text2(getText('reset'), {
size: 40,
fill: 0xFFFFFF
});
resetText.anchor.set(0.5, 0.5);
resetButton.addChild(resetText);
resetButton.x = -150;
resetButton.y = 50;
LK.gui.topRight.addChild(resetButton);
// Store reference for updates
resetButton.textElement = resetText;
// Add event handler to reset button
resetButton.down = function (x, y, obj) {
// Reset level to 1
currentLevel = 1;
storage.currentLevel = 1;
// Reset match counter and requirements
matchCounter = 0;
matchesRequiredForNextLevel = 5;
// Reset game state and board
gameOver = true; // To stop game processing temporarily
// Clear the board
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
var flower = gameBoard.removeFlower(row, col);
if (flower) {
flower.destroy();
}
}
}
// Reset and restart the game
initGame();
};
}
// Update UI elements
function updateUI() {
scoreText.setText(getText('score') + currentScore);
movesText.setText(getText('moves') + movesLeft);
levelText.setText(getText('level') + currentLevel + ' (' + matchCounter + '/' + matchesRequiredForNextLevel + ')');
LK.setScore(currentScore);
}
// Populate the board with flowers
function populateBoard() {
// Create flowers and ensure no initial matches
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
addNewFlower(row, col);
}
}
// Check and resolve any initial matches
var initialMatches = findAllMatches();
while (initialMatches.length > 0) {
// If we have initial matches, regenerate those flowers
for (var i = 0; i < initialMatches.length; i++) {
var matchedFlower = gameBoard.removeFlower(initialMatches[i].row, initialMatches[i].col);
if (matchedFlower) {
matchedFlower.destroy();
addNewFlower(initialMatches[i].row, initialMatches[i].col);
}
}
initialMatches = findAllMatches();
}
}
// Add a new flower at the specified position
function addNewFlower(row, col) {
// Choose a random flower type that doesn't create a match
var validTypes = getValidFlowerTypes(row, col);
var randomType = validTypes[Math.floor(Math.random() * validTypes.length)];
// Create a new flower and add it to the board
var flower = new Flower(randomType);
gameBoard.addFlower(flower, row, col);
return flower;
}
// Get valid flower types that don't create a match at position
function getValidFlowerTypes(row, col) {
var invalidTypes = {}; // Use a regular object instead of Set
// Check horizontal matches to avoid
if (col >= 2) {
var flower1 = gameBoard.getFlower(row, col - 1);
var flower2 = gameBoard.getFlower(row, col - 2);
if (flower1 && flower2 && flower1.type === flower2.type) {
invalidTypes[flower1.type] = true; // Mark type as invalid
}
}
// Check vertical matches to avoid
if (row >= 2) {
var flower1 = gameBoard.getFlower(row - 1, col);
var flower2 = gameBoard.getFlower(row - 2, col);
if (flower1 && flower2 && flower1.type === flower2.type) {
invalidTypes[flower1.type] = true; // Mark type as invalid
}
}
// Return valid types (all types minus invalid ones)
return FLOWER_TYPES.filter(function (type) {
return !invalidTypes[type]; // Check if type is in invalidTypes object
});
}
// Handle flower selection
function selectFlower(flower) {
if (isProcessingMatches || gameOver) {
return;
}
if (selectedFlower === null) {
// First selection
selectedFlower = flower;
selectedFlower.setSelected(true);
LK.getSound('swap').play();
} else if (selectedFlower === flower) {
// Deselect
selectedFlower.setSelected(false);
selectedFlower = null;
} else {
// Second selection - check if adjacent
if (gameBoard.areAdjacent(selectedFlower.row, selectedFlower.col, flower.row, flower.col)) {
// Try to swap
trySwapFlowers(selectedFlower, flower);
} else {
// Not adjacent, switch selection
selectedFlower.setSelected(false);
selectedFlower = flower;
selectedFlower.setSelected(true);
LK.getSound('swap').play();
}
}
}
// Try to swap flowers and check for matches
function trySwapFlowers(flower1, flower2) {
// Swap flowers
var row1 = flower1.row;
var col1 = flower1.col;
var row2 = flower2.row;
var col2 = flower2.col;
// Deselect first flower
flower1.setSelected(false);
selectedFlower = null;
// Swap to check for matches
gameBoard.swapFlowers(row1, col1, row2, col2, true, function () {
// Check for matches after swap
var matches = findAllMatches();
if (matches.length > 0) {
// Valid move - process matches
decreaseMoves();
processMatches(matches);
} else {
// Invalid move - swap back
LK.getSound('invalid').play();
gameBoard.swapFlowers(row2, col2, row1, col1, true);
}
});
}
// Decrease moves and check game over
function decreaseMoves() {
movesLeft--;
updateUI();
// Check if game is over
if (movesLeft <= 0 && remainingFlowers > 0) {
endGame(false);
}
}
// Process all matches
function processMatches(matches) {
if (matches.length === 0) {
return;
}
isProcessingMatches = true;
// Calculate score based on match length
var matchScore = 0;
var matchCounts = {};
// Group matches by type
matches.forEach(function (match) {
var flower = gameBoard.getFlower(match.row, match.col);
if (flower) {
if (!matchCounts[flower.type]) {
matchCounts[flower.type] = 0;
}
matchCounts[flower.type]++;
}
});
// Calculate score based on matches
// Track licorice ball spawn positions
var licoriceToSpawn = [];
Object.keys(matchCounts).forEach(function (type) {
var count = matchCounts[type];
if (count >= 5) {
matchScore += count * 15;
LK.getSound('match5').play();
} else if (count === 4) {
matchScore += count * 10;
LK.getSound('match4').play();
// Find the 4-in-a-row and mark for licorice ball spawn
// Horizontal
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS - 3; col++) {
var f1 = gameBoard.getFlower(row, col);
var f2 = gameBoard.getFlower(row, col + 1);
var f3 = gameBoard.getFlower(row, col + 2);
var f4 = gameBoard.getFlower(row, col + 3);
if (f1 && f2 && f3 && f4 && f1.type === type && f2.type === type && f3.type === type && f4.type === type) {
// Place licorice ball at the second or third flower (centered)
licoriceToSpawn.push({
row: row,
col: col + 1
});
}
}
}
// Vertical
for (var col = 0; col < COLS; col++) {
for (var row = 0; row < ROWS - 3; row++) {
var f1 = gameBoard.getFlower(row, col);
var f2 = gameBoard.getFlower(row + 1, col);
var f3 = gameBoard.getFlower(row + 2, col);
var f4 = gameBoard.getFlower(row + 3, col);
if (f1 && f2 && f3 && f4 && f1.type === type && f2.type === type && f3.type === type && f4.type === type) {
licoriceToSpawn.push({
row: row + 1,
col: col
});
}
}
}
} else {
matchScore += count * 5;
LK.getSound('match3').play();
}
// Count this as a successful match for level progression
matchCounter++;
});
// Update score
currentScore += matchScore;
remainingFlowers -= matches.length;
// Animate matches and remove flowers
var animationsCompleted = 0;
matches.forEach(function (match) {
var flower = gameBoard.getFlower(match.row, match.col);
if (flower) {
gameBoard.removeFlower(match.row, match.col);
flower.animateMatch(function () {
flower.destroy();
animationsCompleted++;
// When all animations complete, make flowers fall and check for new matches
if (animationsCompleted === matches.length) {
// Spawn licorice balls if needed
if (typeof licoriceToSpawn !== "undefined" && licoriceToSpawn.length > 0) {
for (var i = 0; i < licoriceToSpawn.length; i++) {
var pos = licoriceToSpawn[i];
// Only spawn if cell is empty (flower was matched)
if (!gameBoard.getFlower(pos.row, pos.col)) {
var licorice = new LicoriceBall(pos.row, pos.col);
// Place licorice ball in grid (so it can be found for explosion)
gameBoard.grid[pos.row][pos.col] = licorice;
// Position licorice ball
licorice.x = pos.col * CELL_SIZE - gameBoard.boardWidth / 2 + CELL_SIZE / 2;
licorice.y = pos.row * CELL_SIZE - gameBoard.boardHeight / 2 + CELL_SIZE / 2;
gameBoard.addChild(licorice);
}
}
}
updateUI();
makeFlowersFall(function () {
fillEmptySpaces(function () {
// Check for new matches after filling
var newMatches = findAllMatches();
if (newMatches.length > 0) {
processMatches(newMatches);
} else {
isProcessingMatches = false;
// Check if player reached required match count
if (matchCounter >= matchesRequiredForNextLevel) {
// Level completed
endGame(true);
} else if (remainingFlowers <= 0) {
// All flowers cleared (alternative win condition)
endGame(true);
}
}
});
});
}
});
}
});
}
// Make flowers fall to fill empty spaces
function makeFlowersFall(callback) {
var animationsInProgress = 0;
// Process columns bottom to top
for (var col = 0; col < COLS; col++) {
var emptySpaces = 0;
// Process rows bottom to top
for (var row = ROWS - 1; row >= 0; row--) {
var flower = gameBoard.getFlower(row, col);
if (!flower) {
emptySpaces++;
} else if (emptySpaces > 0) {
// Move flower down by the number of empty spaces
var newRow = row + emptySpaces;
animationsInProgress++;
// Update grid reference
gameBoard.grid[row][col] = null;
gameBoard.grid[newRow][col] = flower;
// Update flower position properties
flower.row = newRow;
// Animate the fall
var targetY = newRow * CELL_SIZE - gameBoard.boardHeight / 2 + CELL_SIZE / 2;
if (typeof flower.moveTo === "function") {
flower.moveTo(flower.x, targetY, 300, function () {
animationsInProgress--;
if (animationsInProgress === 0 && callback) {
callback();
}
});
} else {
// Fallback for objects without moveTo (e.g. LicoriceBall)
flower.y = targetY;
animationsInProgress--;
if (animationsInProgress === 0 && callback) {
callback();
}
}
}
}
}
if (animationsInProgress === 0 && callback) {
callback();
}
}
// Fill empty spaces at the top with new flowers
function fillEmptySpaces(callback) {
var animationsInProgress = 0;
for (var col = 0; col < COLS; col++) {
// Find empty spaces at the top
for (var row = 0; row < ROWS; row++) {
if (!gameBoard.getFlower(row, col)) {
// Create a new flower above the board
var flower = new Flower(FLOWER_TYPES[Math.floor(Math.random() * FLOWER_TYPES.length)]);
gameBoard.addFlower(flower, row, col);
// Position it above the board
flower.y -= (row + 1) * CELL_SIZE;
// Animate it falling down
animationsInProgress++;
remainingFlowers++;
var targetY = row * CELL_SIZE - gameBoard.boardHeight / 2 + CELL_SIZE / 2;
flower.moveTo(flower.x, targetY, 300, function () {
animationsInProgress--;
if (animationsInProgress === 0 && callback) {
callback();
}
});
}
}
}
if (animationsInProgress === 0 && callback) {
callback();
}
}
// Find all matching flowers on the board
function findAllMatches() {
var matches = [];
var visited = {};
// Check horizontal matches
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS - 2; col++) {
var flower1 = gameBoard.getFlower(row, col);
var flower2 = gameBoard.getFlower(row, col + 1);
var flower3 = gameBoard.getFlower(row, col + 2);
if (flower1 && flower2 && flower3 && flower1.type === flower2.type && flower2.type === flower3.type) {
// Add match if not already added
var key1 = row + "," + col;
var key2 = row + "," + (col + 1);
var key3 = row + "," + (col + 2);
if (!visited[key1]) {
matches.push({
row: row,
col: col
});
visited[key1] = true;
}
if (!visited[key2]) {
matches.push({
row: row,
col: col + 1
});
visited[key2] = true;
}
if (!visited[key3]) {
matches.push({
row: row,
col: col + 2
});
visited[key3] = true;
}
}
}
}
// Check vertical matches
for (var row = 0; row < ROWS - 2; row++) {
for (var col = 0; col < COLS; col++) {
var flower1 = gameBoard.getFlower(row, col);
var flower2 = gameBoard.getFlower(row + 1, col);
var flower3 = gameBoard.getFlower(row + 2, col);
if (flower1 && flower2 && flower3 && flower1.type === flower2.type && flower2.type === flower3.type) {
// Add match if not already added
var key1 = row + "," + col;
var key2 = row + 1 + "," + col;
var key3 = row + 2 + "," + col;
if (!visited[key1]) {
matches.push({
row: row,
col: col
});
visited[key1] = true;
}
if (!visited[key2]) {
matches.push({
row: row + 1,
col: col
});
visited[key2] = true;
}
if (!visited[key3]) {
matches.push({
row: row + 2,
col: col
});
visited[key3] = true;
}
}
}
}
return matches;
}
// Check if any valid moves exist
function hasValidMoves() {
// Check each position for potential swaps that create matches
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
// Check right swap
if (col < COLS - 1) {
// Simulate swap right
var flower1 = gameBoard.getFlower(row, col);
var flower2 = gameBoard.getFlower(row, col + 1);
if (flower1 && flower2) {
// Swap
gameBoard.swapFlowers(row, col, row, col + 1, false);
// Check for matches
var matches = findAllMatches();
// Swap back
gameBoard.swapFlowers(row, col + 1, row, col, false);
if (matches.length > 0) {
return true;
}
}
}
// Check down swap
if (row < ROWS - 1) {
// Simulate swap down
var flower1 = gameBoard.getFlower(row, col);
var flower2 = gameBoard.getFlower(row + 1, col);
if (flower1 && flower2) {
// Swap
gameBoard.swapFlowers(row, col, row + 1, col, false);
// Check for matches
var matches = findAllMatches();
// Swap back
gameBoard.swapFlowers(row + 1, col, row, col, false);
if (matches.length > 0) {
return true;
}
}
}
}
}
return false;
}
// End the game
function endGame(success) {
gameOver = true;
if (success) {
// Level completed
currentLevel++;
storage.currentLevel = currentLevel;
// Reset match counter and increase requirements for next level
matchCounter = 0;
matchesRequiredForNextLevel = 5 + (currentLevel - 1) * 3; // 5 for level 1, +3 for each level
// Update high score
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Show you win screen
LK.getSound('levelup').play();
LK.showYouWin();
} else {
// Game over - ran out of moves
currentLevel = 1;
storage.currentLevel = currentLevel;
// Update high score
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Show game over screen
LK.showGameOver();
}
}
// Initialize the game
initGame();
// Main game update loop
game.update = function () {
// If no valid moves exist, shuffle the board
if (!isProcessingMatches && !gameOver && !hasValidMoves()) {
// Shuffle the board
var allFlowers = [];
// Collect all flowers
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
var flower = gameBoard.removeFlower(row, col);
if (flower) {
flower.destroy();
}
}
}
// Repopulate the board
populateBoard();
}
};
// Handle touch/mouse input on game board
game.down = function (x, y, obj) {
// This is handled in the Flower class
};
game.move = function (x, y, obj) {
// Handle dragging of selected flower
if (selectedFlower && !isProcessingMatches && !gameOver) {
// Get position in gameBoard coordinates
var localPos = gameBoard.toLocal({
x: x,
y: y
});
// Calculate which cell the player is hovering over
var hoverCol = Math.floor((localPos.x + gameBoard.boardWidth / 2) / CELL_SIZE);
var hoverRow = Math.floor((localPos.y + gameBoard.boardHeight / 2) / CELL_SIZE);
// Check if it's a valid cell and adjacent to the original position
if (hoverRow >= 0 && hoverRow < ROWS && hoverCol >= 0 && hoverCol < COLS && gameBoard.areAdjacent(selectedFlower.row, selectedFlower.col, hoverRow, hoverCol)) {
// Get the flower at the hover position
var targetFlower = gameBoard.getFlower(hoverRow, hoverCol);
if (targetFlower && targetFlower !== selectedFlower) {
// Try to swap the flowers
trySwapFlowers(selectedFlower, targetFlower);
}
}
}
};
game.up = function (x, y, obj) {
// No global up handling needed
};
// Show language selection menu
function showLanguageMenu() {
if (languageMenuOpen) return;
languageMenuOpen = true;
// Create language menu container
languageMenuContainer = new Container();
// Create background overlay
var overlay = languageMenuContainer.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732,
tint: 0x000000,
alpha: 0.8
});
// Create menu background
var menuBackground = languageMenuContainer.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 1200,
height: 1800,
tint: 0xFFFFFF
});
// Create title
var titleText = new Text2(getText('chooseLanguage'), {
size: 80,
fill: 0x000000
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -700;
languageMenuContainer.addChild(titleText);
// Create language buttons
var buttonStartY = -500;
var buttonSpacing = 120;
for (var i = 0; i < SUPPORTED_LANGUAGES.length; i++) {
var lang = SUPPORTED_LANGUAGES[i];
var langButton = new Container();
// Button background
var buttonBg = langButton.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 800,
height: 100,
tint: currentLanguage === lang.code ? 0x00FF00 : 0x808080
});
// Language name
var langName = new Text2(lang.name, {
size: 50,
fill: 0x000000
});
langName.anchor.set(0, 0.5);
langName.x = -300;
langButton.addChild(langName);
// Choice button
var choiceButton = new Container();
var choiceBg = choiceButton.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 80,
tint: 0x0066CC
});
var choiceText = new Text2(getText('choice'), {
size: 40,
fill: 0xFFFFFF
});
choiceText.anchor.set(0.5, 0.5);
choiceButton.addChild(choiceText);
choiceButton.x = 250;
// Add choice button event
choiceButton.languageCode = lang.code;
choiceButton.down = function (x, y, obj) {
selectLanguage(this.languageCode);
};
langButton.addChild(choiceButton);
langButton.y = buttonStartY + i * buttonSpacing;
languageMenuContainer.addChild(langButton);
}
// Create back button
var backButton = new Container();
var backBg = backButton.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 100,
tint: 0xFF0000
});
var backText = new Text2(getText('back'), {
size: 50,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backButton.addChild(backText);
backButton.y = 700;
backButton.down = function (x, y, obj) {
hideLanguageMenu();
};
languageMenuContainer.addChild(backButton);
// Position and add to game
languageMenuContainer.x = 2048 / 2;
languageMenuContainer.y = 2732 / 2;
game.addChild(languageMenuContainer);
}
// Hide language menu
function hideLanguageMenu() {
if (languageMenuContainer) {
languageMenuContainer.destroy();
languageMenuContainer = null;
}
languageMenuOpen = false;
}
// Select a language
function selectLanguage(languageCode) {
currentLanguage = languageCode;
storage.language = languageCode;
// Update all UI text
updateAllUIText();
// Hide language menu
hideLanguageMenu();
}
// Update all UI text with current language
function updateAllUIText() {
// Update main UI
updateUI();
// Update button texts
var topContainer = LK.gui.top;
for (var i = 0; i < topContainer.children.length; i++) {
var child = topContainer.children[i];
if (child.textElement && child.textElement.text) {
if (child.textElement.text.indexOf('Change') >= 0 || child.textElement.text.indexOf('Changer') >= 0 || child.textElement.text.indexOf('Cambiar') >= 0 || child.textElement.text.indexOf('Cambia') >= 0 || child.textElement.text.indexOf('Schimbă') >= 0 || child.textElement.text.indexOf('Zmień') >= 0 || child.textElement.text.indexOf('Αλλαγή') >= 0 || child.textElement.text.indexOf('Spielsprache') >= 0 || child.textElement.text.indexOf('更改') >= 0 || child.textElement.text.indexOf('ゲーム言語') >= 0) {
child.textElement.setText(getText('changeLanguage'));
}
}
}
var topRightContainer = LK.gui.topRight;
for (var i = 0; i < topRightContainer.children.length; i++) {
var child = topRightContainer.children[i];
if (child.textElement && child.textElement.text) {
if (child.textElement.text === 'RESET' || child.textElement.text === 'リセット' || child.textElement.text === 'ΕΠΑΝΑΦΟΡΑ' || child.textElement.text === '重置') {
child.textElement.setText(getText('reset'));
}
}
}
}
// Trigger the licorice explosion: remove all flowers in the column, trigger combo mode for 8 seconds
function triggerLicoriceExplosion(row, col) {
if (gameOver || isProcessingMatches) return;
isProcessingMatches = true;
// Remove all flowers in the column (except LicoriceBall itself)
for (var r = 0; r < ROWS; r++) {
var obj = gameBoard.getFlower(r, col);
if (obj && !(obj instanceof LicoriceBall)) {
gameBoard.removeFlower(r, col);
if (typeof obj.animateMatch === "function") {
obj.animateMatch(function () {
obj.destroy();
});
} else {
obj.destroy();
}
remainingFlowers--;
}
}
// Remove the LicoriceBall itself
var licorice = gameBoard.getFlower(row, col);
if (licorice && licorice instanceof LicoriceBall) {
gameBoard.removeFlower(row, col);
licorice.destroy();
}
// Combo mode: for 8 seconds, after each match, auto-match adjacent flowers
var comboEndTime = Date.now() + 8000;
var originalProcessMatches = processMatches;
var comboActive = true;
// Helper to auto-match adjacent flowers after each match
function comboProcessMatches(matches) {
originalProcessMatches(matches);
// After normal match processing, auto-match adjacent flowers if combo is still active
if (comboActive && !gameOver) {
LK.setTimeout(function () {
var newMatches = [];
// Find all adjacent pairs and add to matches
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
var f = gameBoard.getFlower(row, col);
if (!f || f instanceof LicoriceBall) continue;
// Check right
var fRight = gameBoard.getFlower(row, col + 1);
if (fRight && fRight.type === f.type && !(fRight instanceof LicoriceBall)) {
newMatches.push({
row: row,
col: col
});
newMatches.push({
row: row,
col: col + 1
});
}
// Check down
var fDown = gameBoard.getFlower(row + 1, col);
if (fDown && fDown.type === f.type && !(fDown instanceof LicoriceBall)) {
newMatches.push({
row: row,
col: col
});
newMatches.push({
row: row + 1,
col: col
});
}
}
}
// Remove duplicates
var seen = {};
var uniqueMatches = [];
for (var i = 0; i < newMatches.length; i++) {
var key = newMatches[i].row + "," + newMatches[i].col;
if (!seen[key]) {
uniqueMatches.push(newMatches[i]);
seen[key] = true;
}
}
if (uniqueMatches.length > 0 && comboActive) {
comboProcessMatches(uniqueMatches);
} else {
isProcessingMatches = false;
}
}, 350); // Wait for match animation
} else {
isProcessingMatches = false;
}
}
// Override processMatches for combo mode
processMatches = comboProcessMatches;
// End combo mode after 8 seconds
LK.setTimeout(function () {
comboActive = false;
processMatches = originalProcessMatches;
isProcessingMatches = false;
}, 8000);
// After explosion, let flowers fall and fill, then check for matches
makeFlowersFall(function () {
fillEmptySpaces(function () {
var matches = findAllMatches();
if (matches.length > 0) {
processMatches(matches);
} else {
isProcessingMatches = false;
}
});
});
} /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
currentLevel: 1
});
/****
* Classes
****/
var Flower = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
self.isMatched = false;
self.isMoving = false;
// Create flower asset based on type
var flowerGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.9,
scaleY: 0.9
});
// Method to handle selection/deselection visual effect
self.setSelected = function (selected) {
if (selected) {
tween(flowerGraphics, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut
});
} else {
tween(flowerGraphics, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 200,
easing: tween.easeOut
});
}
};
// Method to handle match animation
self.animateMatch = function (callback) {
self.isMatched = true;
tween(flowerGraphics, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (callback) {
callback();
}
}
});
};
// Method to move flower to a new position
self.moveTo = function (newX, newY, duration, callback) {
self.isMoving = true;
tween(self, {
x: newX,
y: newY
}, {
duration: duration || 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isMoving = false;
if (callback) {
callback();
}
}
});
};
// Event handlers
self.down = function (x, y, obj) {
// Handle touch/click in game logic
if (!self.isMatched && !self.isMoving && !gameOver) {
selectFlower(self);
}
};
return self;
});
var GameBoard = Container.expand(function (rows, cols, cellSize) {
var self = Container.call(this);
self.rows = rows;
self.cols = cols;
self.cellSize = cellSize;
self.boardWidth = cols * cellSize;
self.boardHeight = rows * cellSize;
self.grid = [];
// Create board background
var boardBackground = self.attachAsset('board', {
anchorX: 0.5,
anchorY: 0.5,
width: self.boardWidth,
height: self.boardHeight
});
// Initialize grid with cells
self.initializeGrid = function () {
// 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('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: cellSize - 10,
height: cellSize - 10
});
// Position cell
cell.x = col * cellSize - self.boardWidth / 2 + cellSize / 2;
cell.y = row * cellSize - self.boardHeight / 2 + cellSize / 2;
self.addChild(cell);
self.grid[row][col] = null; // Will hold flower references
}
}
};
// Add a flower to the grid at specified position
self.addFlower = function (flower, row, col) {
if (row >= 0 && row < rows && col >= 0 && col < cols) {
self.grid[row][col] = flower;
flower.row = row;
flower.col = col;
// Position flower in grid
flower.x = col * cellSize - self.boardWidth / 2 + cellSize / 2;
flower.y = row * cellSize - self.boardHeight / 2 + cellSize / 2;
self.addChild(flower);
return true;
}
return false;
};
// Remove a flower from the grid
self.removeFlower = function (row, col) {
if (row >= 0 && row < rows && col >= 0 && col < cols) {
var flower = self.grid[row][col];
if (flower) {
self.grid[row][col] = null;
return flower;
}
}
return null;
};
// Swap two flowers in the grid
self.swapFlowers = function (row1, col1, row2, col2, animate, callback) {
if (row1 < 0 || row1 >= rows || col1 < 0 || col1 >= cols || row2 < 0 || row2 >= rows || col2 < 0 || col2 >= cols) {
return false;
}
var flower1 = self.grid[row1][col1];
var flower2 = self.grid[row2][col2];
if (!flower1 || !flower2) {
return false;
}
// Update grid references
self.grid[row1][col1] = flower2;
self.grid[row2][col2] = flower1;
// Update flower position properties
flower1.row = row2;
flower1.col = col2;
flower2.row = row1;
flower2.col = col1;
if (animate) {
// Calculate target positions
var pos1 = {
x: col2 * cellSize - self.boardWidth / 2 + cellSize / 2,
y: row2 * cellSize - self.boardHeight / 2 + cellSize / 2
};
var pos2 = {
x: col1 * cellSize - self.boardWidth / 2 + cellSize / 2,
y: row1 * cellSize - self.boardHeight / 2 + cellSize / 2
};
// Animate the swap
flower1.moveTo(pos1.x, pos1.y, 200);
flower2.moveTo(pos2.x, pos2.y, 200, callback);
} else {
// Instantly swap positions
flower1.x = col2 * cellSize - self.boardWidth / 2 + cellSize / 2;
flower1.y = row2 * cellSize - self.boardHeight / 2 + cellSize / 2;
flower2.x = col1 * cellSize - self.boardWidth / 2 + cellSize / 2;
flower2.y = row1 * cellSize - self.boardHeight / 2 + cellSize / 2;
if (callback) {
callback();
}
}
return true;
};
// Get flower at specific grid position
self.getFlower = function (row, col) {
if (row >= 0 && row < rows && col >= 0 && col < cols) {
return self.grid[row][col];
}
return null;
};
// Check if coordinates are adjacent
self.areAdjacent = function (row1, col1, row2, col2) {
var rowDiff = Math.abs(row1 - row2);
var colDiff = Math.abs(col1 - col2);
// Adjacent if exactly one coordinate differs by 1 and the other is the same
return rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1;
};
return self;
});
// LicoriceBall class for combo feature
var LicoriceBall = Container.expand(function (row, col) {
var self = Container.call(this);
self.row = row;
self.col = col;
self.isExploded = false;
// Attach a licorice ball asset (use a unique color/shape for now)
var licoriceGraphics = self.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 90,
height: 90,
color: 0x222222,
tint: 0x222222
});
// Add a visual cue (e.g. a white "O" text)
var licoriceText = new Text2('🍬', {
size: 60,
fill: 0xffffff
});
licoriceText.anchor.set(0.5, 0.5);
self.addChild(licoriceText);
// Animate appearance
licoriceGraphics.scaleX = 0.2;
licoriceGraphics.scaleY = 0.2;
tween(licoriceGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.bounceOut
});
// Handle explosion on tap
self.down = function (x, y, obj) {
if (!self.isExploded && !isProcessingMatches && !gameOver) {
self.isExploded = true;
triggerLicoriceExplosion(self.row, self.col);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x4CAF50
});
/****
* Game Code
****/
// Add custom background
var customBackground = LK.getAsset('customBackground', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732
});
customBackground.x = 2048 / 2;
customBackground.y = 2732 / 2;
game.addChild(customBackground);
// Language system
var SUPPORTED_LANGUAGES = [{
code: 'fr',
name: 'Français'
}, {
code: 'en',
name: 'English'
}, {
code: 'de',
name: 'Deutsch'
}, {
code: 'ja',
name: '日本語'
}, {
code: 'it',
name: 'Italiano'
}, {
code: 'es',
name: 'Español'
}, {
code: 'zh',
name: '中文'
}, {
code: 'el',
name: 'Ελληνικά'
}, {
code: 'ro',
name: 'Română'
}, {
code: 'pl',
name: 'Polski'
}];
var TRANSLATIONS = {
fr: {
score: 'Score: ',
moves: 'Mouvements: ',
level: 'Niveau: ',
reset: 'RESET',
changeLanguage: 'Changer la langue',
chooseLanguage: 'Choisissez votre langue',
choice: 'Choisir',
back: 'Retour'
},
en: {
score: 'Score: ',
moves: 'Moves: ',
level: 'Level: ',
reset: 'RESET',
changeLanguage: 'Change the Game Language',
chooseLanguage: 'Choose your language',
choice: 'Choice',
back: 'Back'
},
de: {
score: 'Punkte: ',
moves: 'Züge: ',
level: 'Level: ',
reset: 'RESET',
changeLanguage: 'Spielsprache ändern',
chooseLanguage: 'Wählen Sie Ihre Sprache',
choice: 'Wählen',
back: 'Zurück'
},
ja: {
score: 'スコア: ',
moves: '手数: ',
level: 'レベル: ',
reset: 'リセット',
changeLanguage: 'ゲーム言語を変更',
chooseLanguage: '言語を選択してください',
choice: '選択',
back: '戻る'
},
it: {
score: 'Punteggio: ',
moves: 'Mosse: ',
level: 'Livello: ',
reset: 'RESET',
changeLanguage: 'Cambia lingua del gioco',
chooseLanguage: 'Scegli la tua lingua',
choice: 'Scegli',
back: 'Indietro'
},
es: {
score: 'Puntuación: ',
moves: 'Movimientos: ',
level: 'Nivel: ',
reset: 'RESET',
changeLanguage: 'Cambiar idioma del juego',
chooseLanguage: 'Elige tu idioma',
choice: 'Elegir',
back: 'Volver'
},
zh: {
score: '分数: ',
moves: '步数: ',
level: '等级: ',
reset: '重置',
changeLanguage: '更改游戏语言',
chooseLanguage: '选择您的语言',
choice: '选择',
back: '返回'
},
el: {
score: 'Σκορ: ',
moves: 'Κινήσεις: ',
level: 'Επίπεδο: ',
reset: 'ΕΠΑΝΑΦΟΡΑ',
changeLanguage: 'Αλλαγή γλώσσας παιχνιδιού',
chooseLanguage: 'Επιλέξτε τη γλώσσα σας',
choice: 'Επιλογή',
back: 'Πίσω'
},
ro: {
score: 'Scor: ',
moves: 'Mișcări: ',
level: 'Nivel: ',
reset: 'RESET',
changeLanguage: 'Schimbă limba jocului',
chooseLanguage: 'Alege limba ta',
choice: 'Alege',
back: 'Înapoi'
},
pl: {
score: 'Wynik: ',
moves: 'Ruchy: ',
level: 'Poziom: ',
reset: 'RESET',
changeLanguage: 'Zmień język gry',
chooseLanguage: 'Wybierz swój język',
choice: 'Wybierz',
back: 'Wstecz'
}
};
var currentLanguage = storage.language || 'fr';
var languageMenuOpen = false;
var languageMenuContainer = null;
function getText(key) {
return TRANSLATIONS[currentLanguage][key] || TRANSLATIONS['fr'][key] || key;
}
// Game constants
var ROWS = 7;
var COLS = 7;
var CELL_SIZE = 100;
var FLOWER_TYPES = ['tulip', 'poppy', 'rose', 'violet', 'lily', 'jasmine', 'lilac'];
var MAX_MOVES = 50;
// Game state variables
var gameBoard;
var selectedFlower = null;
var movesLeft = MAX_MOVES;
var currentScore = 0;
var currentLevel = storage.currentLevel || 1;
var highScore = storage.highScore || 0;
var remainingFlowers = ROWS * COLS;
var isProcessingMatches = false;
var gameOver = false;
var matchCounter = 0;
var matchesRequiredForNextLevel = 5;
// UI elements
var scoreText;
var movesText;
var levelText;
// Initialize game
function initGame() {
// Create game board
gameBoard = new GameBoard(ROWS, COLS, CELL_SIZE);
gameBoard.x = 2048 / 2;
gameBoard.y = 2732 / 2;
game.addChild(gameBoard);
// Initialize the grid cells
gameBoard.initializeGrid();
// Populate the board with flowers
populateBoard();
// Create UI
createUI();
// Set initial game state
movesLeft = MAX_MOVES;
currentScore = 0;
remainingFlowers = ROWS * COLS;
gameOver = false;
matchCounter = 0;
matchesRequiredForNextLevel = 5 + (currentLevel - 1) * 3; // 5 for level 1, +3 for each level
// Update UI
updateUI();
// Play background music
LK.playMusic('bgmusic');
// Play background music
LK.playMusic('bgmusic');
}
// Create UI elements
function createUI() {
// Create score text
scoreText = new Text2(getText('score') + '0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0); // Anchored to top-right
LK.gui.topRight.addChild(scoreText);
// Create moves text
movesText = new Text2(getText('moves') + movesLeft, {
size: 60,
fill: 0xFFFFFF
});
movesText.anchor.set(0, 0); // Anchored to top-left
// Don't place in top-left corner due to platform menu icon
movesText.x = 120;
LK.gui.topLeft.addChild(movesText);
// Create level text
levelText = new Text2(getText('level') + currentLevel + ' (' + matchCounter + '/' + matchesRequiredForNextLevel + ')', {
size: 60,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
// Create language button
var languageButton = new Container();
var languageBackground = languageButton.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 60,
tint: 0xFF0000
});
var languageText = new Text2(getText('changeLanguage'), {
size: 35,
fill: 0xFFFFFF
});
languageText.anchor.set(0.5, 0.5);
languageButton.addChild(languageText);
languageButton.x = 0;
languageButton.y = 120;
LK.gui.top.addChild(languageButton);
// Add event handler to language button
languageButton.down = function (x, y, obj) {
if (!languageMenuOpen) {
showLanguageMenu();
}
};
// Store reference for updates
languageButton.textElement = languageText;
// Create reset button
var resetButton = new Container();
var resetBackground = resetButton.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 120,
height: 60,
tint: 0xFF0000
});
var resetText = new Text2(getText('reset'), {
size: 40,
fill: 0xFFFFFF
});
resetText.anchor.set(0.5, 0.5);
resetButton.addChild(resetText);
resetButton.x = -150;
resetButton.y = 50;
LK.gui.topRight.addChild(resetButton);
// Store reference for updates
resetButton.textElement = resetText;
// Add event handler to reset button
resetButton.down = function (x, y, obj) {
// Reset level to 1
currentLevel = 1;
storage.currentLevel = 1;
// Reset match counter and requirements
matchCounter = 0;
matchesRequiredForNextLevel = 5;
// Reset game state and board
gameOver = true; // To stop game processing temporarily
// Clear the board
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
var flower = gameBoard.removeFlower(row, col);
if (flower) {
flower.destroy();
}
}
}
// Reset and restart the game
initGame();
};
}
// Update UI elements
function updateUI() {
scoreText.setText(getText('score') + currentScore);
movesText.setText(getText('moves') + movesLeft);
levelText.setText(getText('level') + currentLevel + ' (' + matchCounter + '/' + matchesRequiredForNextLevel + ')');
LK.setScore(currentScore);
}
// Populate the board with flowers
function populateBoard() {
// Create flowers and ensure no initial matches
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
addNewFlower(row, col);
}
}
// Check and resolve any initial matches
var initialMatches = findAllMatches();
while (initialMatches.length > 0) {
// If we have initial matches, regenerate those flowers
for (var i = 0; i < initialMatches.length; i++) {
var matchedFlower = gameBoard.removeFlower(initialMatches[i].row, initialMatches[i].col);
if (matchedFlower) {
matchedFlower.destroy();
addNewFlower(initialMatches[i].row, initialMatches[i].col);
}
}
initialMatches = findAllMatches();
}
}
// Add a new flower at the specified position
function addNewFlower(row, col) {
// Choose a random flower type that doesn't create a match
var validTypes = getValidFlowerTypes(row, col);
var randomType = validTypes[Math.floor(Math.random() * validTypes.length)];
// Create a new flower and add it to the board
var flower = new Flower(randomType);
gameBoard.addFlower(flower, row, col);
return flower;
}
// Get valid flower types that don't create a match at position
function getValidFlowerTypes(row, col) {
var invalidTypes = {}; // Use a regular object instead of Set
// Check horizontal matches to avoid
if (col >= 2) {
var flower1 = gameBoard.getFlower(row, col - 1);
var flower2 = gameBoard.getFlower(row, col - 2);
if (flower1 && flower2 && flower1.type === flower2.type) {
invalidTypes[flower1.type] = true; // Mark type as invalid
}
}
// Check vertical matches to avoid
if (row >= 2) {
var flower1 = gameBoard.getFlower(row - 1, col);
var flower2 = gameBoard.getFlower(row - 2, col);
if (flower1 && flower2 && flower1.type === flower2.type) {
invalidTypes[flower1.type] = true; // Mark type as invalid
}
}
// Return valid types (all types minus invalid ones)
return FLOWER_TYPES.filter(function (type) {
return !invalidTypes[type]; // Check if type is in invalidTypes object
});
}
// Handle flower selection
function selectFlower(flower) {
if (isProcessingMatches || gameOver) {
return;
}
if (selectedFlower === null) {
// First selection
selectedFlower = flower;
selectedFlower.setSelected(true);
LK.getSound('swap').play();
} else if (selectedFlower === flower) {
// Deselect
selectedFlower.setSelected(false);
selectedFlower = null;
} else {
// Second selection - check if adjacent
if (gameBoard.areAdjacent(selectedFlower.row, selectedFlower.col, flower.row, flower.col)) {
// Try to swap
trySwapFlowers(selectedFlower, flower);
} else {
// Not adjacent, switch selection
selectedFlower.setSelected(false);
selectedFlower = flower;
selectedFlower.setSelected(true);
LK.getSound('swap').play();
}
}
}
// Try to swap flowers and check for matches
function trySwapFlowers(flower1, flower2) {
// Swap flowers
var row1 = flower1.row;
var col1 = flower1.col;
var row2 = flower2.row;
var col2 = flower2.col;
// Deselect first flower
flower1.setSelected(false);
selectedFlower = null;
// Swap to check for matches
gameBoard.swapFlowers(row1, col1, row2, col2, true, function () {
// Check for matches after swap
var matches = findAllMatches();
if (matches.length > 0) {
// Valid move - process matches
decreaseMoves();
processMatches(matches);
} else {
// Invalid move - swap back
LK.getSound('invalid').play();
gameBoard.swapFlowers(row2, col2, row1, col1, true);
}
});
}
// Decrease moves and check game over
function decreaseMoves() {
movesLeft--;
updateUI();
// Check if game is over
if (movesLeft <= 0 && remainingFlowers > 0) {
endGame(false);
}
}
// Process all matches
function processMatches(matches) {
if (matches.length === 0) {
return;
}
isProcessingMatches = true;
// Calculate score based on match length
var matchScore = 0;
var matchCounts = {};
// Group matches by type
matches.forEach(function (match) {
var flower = gameBoard.getFlower(match.row, match.col);
if (flower) {
if (!matchCounts[flower.type]) {
matchCounts[flower.type] = 0;
}
matchCounts[flower.type]++;
}
});
// Calculate score based on matches
// Track licorice ball spawn positions
var licoriceToSpawn = [];
Object.keys(matchCounts).forEach(function (type) {
var count = matchCounts[type];
if (count >= 5) {
matchScore += count * 15;
LK.getSound('match5').play();
} else if (count === 4) {
matchScore += count * 10;
LK.getSound('match4').play();
// Find the 4-in-a-row and mark for licorice ball spawn
// Horizontal
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS - 3; col++) {
var f1 = gameBoard.getFlower(row, col);
var f2 = gameBoard.getFlower(row, col + 1);
var f3 = gameBoard.getFlower(row, col + 2);
var f4 = gameBoard.getFlower(row, col + 3);
if (f1 && f2 && f3 && f4 && f1.type === type && f2.type === type && f3.type === type && f4.type === type) {
// Place licorice ball at the second or third flower (centered)
licoriceToSpawn.push({
row: row,
col: col + 1
});
}
}
}
// Vertical
for (var col = 0; col < COLS; col++) {
for (var row = 0; row < ROWS - 3; row++) {
var f1 = gameBoard.getFlower(row, col);
var f2 = gameBoard.getFlower(row + 1, col);
var f3 = gameBoard.getFlower(row + 2, col);
var f4 = gameBoard.getFlower(row + 3, col);
if (f1 && f2 && f3 && f4 && f1.type === type && f2.type === type && f3.type === type && f4.type === type) {
licoriceToSpawn.push({
row: row + 1,
col: col
});
}
}
}
} else {
matchScore += count * 5;
LK.getSound('match3').play();
}
// Count this as a successful match for level progression
matchCounter++;
});
// Update score
currentScore += matchScore;
remainingFlowers -= matches.length;
// Animate matches and remove flowers
var animationsCompleted = 0;
matches.forEach(function (match) {
var flower = gameBoard.getFlower(match.row, match.col);
if (flower) {
gameBoard.removeFlower(match.row, match.col);
flower.animateMatch(function () {
flower.destroy();
animationsCompleted++;
// When all animations complete, make flowers fall and check for new matches
if (animationsCompleted === matches.length) {
// Spawn licorice balls if needed
if (typeof licoriceToSpawn !== "undefined" && licoriceToSpawn.length > 0) {
for (var i = 0; i < licoriceToSpawn.length; i++) {
var pos = licoriceToSpawn[i];
// Only spawn if cell is empty (flower was matched)
if (!gameBoard.getFlower(pos.row, pos.col)) {
var licorice = new LicoriceBall(pos.row, pos.col);
// Place licorice ball in grid (so it can be found for explosion)
gameBoard.grid[pos.row][pos.col] = licorice;
// Position licorice ball
licorice.x = pos.col * CELL_SIZE - gameBoard.boardWidth / 2 + CELL_SIZE / 2;
licorice.y = pos.row * CELL_SIZE - gameBoard.boardHeight / 2 + CELL_SIZE / 2;
gameBoard.addChild(licorice);
}
}
}
updateUI();
makeFlowersFall(function () {
fillEmptySpaces(function () {
// Check for new matches after filling
var newMatches = findAllMatches();
if (newMatches.length > 0) {
processMatches(newMatches);
} else {
isProcessingMatches = false;
// Check if player reached required match count
if (matchCounter >= matchesRequiredForNextLevel) {
// Level completed
endGame(true);
} else if (remainingFlowers <= 0) {
// All flowers cleared (alternative win condition)
endGame(true);
}
}
});
});
}
});
}
});
}
// Make flowers fall to fill empty spaces
function makeFlowersFall(callback) {
var animationsInProgress = 0;
// Process columns bottom to top
for (var col = 0; col < COLS; col++) {
var emptySpaces = 0;
// Process rows bottom to top
for (var row = ROWS - 1; row >= 0; row--) {
var flower = gameBoard.getFlower(row, col);
if (!flower) {
emptySpaces++;
} else if (emptySpaces > 0) {
// Move flower down by the number of empty spaces
var newRow = row + emptySpaces;
animationsInProgress++;
// Update grid reference
gameBoard.grid[row][col] = null;
gameBoard.grid[newRow][col] = flower;
// Update flower position properties
flower.row = newRow;
// Animate the fall
var targetY = newRow * CELL_SIZE - gameBoard.boardHeight / 2 + CELL_SIZE / 2;
if (typeof flower.moveTo === "function") {
flower.moveTo(flower.x, targetY, 300, function () {
animationsInProgress--;
if (animationsInProgress === 0 && callback) {
callback();
}
});
} else {
// Fallback for objects without moveTo (e.g. LicoriceBall)
flower.y = targetY;
animationsInProgress--;
if (animationsInProgress === 0 && callback) {
callback();
}
}
}
}
}
if (animationsInProgress === 0 && callback) {
callback();
}
}
// Fill empty spaces at the top with new flowers
function fillEmptySpaces(callback) {
var animationsInProgress = 0;
for (var col = 0; col < COLS; col++) {
// Find empty spaces at the top
for (var row = 0; row < ROWS; row++) {
if (!gameBoard.getFlower(row, col)) {
// Create a new flower above the board
var flower = new Flower(FLOWER_TYPES[Math.floor(Math.random() * FLOWER_TYPES.length)]);
gameBoard.addFlower(flower, row, col);
// Position it above the board
flower.y -= (row + 1) * CELL_SIZE;
// Animate it falling down
animationsInProgress++;
remainingFlowers++;
var targetY = row * CELL_SIZE - gameBoard.boardHeight / 2 + CELL_SIZE / 2;
flower.moveTo(flower.x, targetY, 300, function () {
animationsInProgress--;
if (animationsInProgress === 0 && callback) {
callback();
}
});
}
}
}
if (animationsInProgress === 0 && callback) {
callback();
}
}
// Find all matching flowers on the board
function findAllMatches() {
var matches = [];
var visited = {};
// Check horizontal matches
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS - 2; col++) {
var flower1 = gameBoard.getFlower(row, col);
var flower2 = gameBoard.getFlower(row, col + 1);
var flower3 = gameBoard.getFlower(row, col + 2);
if (flower1 && flower2 && flower3 && flower1.type === flower2.type && flower2.type === flower3.type) {
// Add match if not already added
var key1 = row + "," + col;
var key2 = row + "," + (col + 1);
var key3 = row + "," + (col + 2);
if (!visited[key1]) {
matches.push({
row: row,
col: col
});
visited[key1] = true;
}
if (!visited[key2]) {
matches.push({
row: row,
col: col + 1
});
visited[key2] = true;
}
if (!visited[key3]) {
matches.push({
row: row,
col: col + 2
});
visited[key3] = true;
}
}
}
}
// Check vertical matches
for (var row = 0; row < ROWS - 2; row++) {
for (var col = 0; col < COLS; col++) {
var flower1 = gameBoard.getFlower(row, col);
var flower2 = gameBoard.getFlower(row + 1, col);
var flower3 = gameBoard.getFlower(row + 2, col);
if (flower1 && flower2 && flower3 && flower1.type === flower2.type && flower2.type === flower3.type) {
// Add match if not already added
var key1 = row + "," + col;
var key2 = row + 1 + "," + col;
var key3 = row + 2 + "," + col;
if (!visited[key1]) {
matches.push({
row: row,
col: col
});
visited[key1] = true;
}
if (!visited[key2]) {
matches.push({
row: row + 1,
col: col
});
visited[key2] = true;
}
if (!visited[key3]) {
matches.push({
row: row + 2,
col: col
});
visited[key3] = true;
}
}
}
}
return matches;
}
// Check if any valid moves exist
function hasValidMoves() {
// Check each position for potential swaps that create matches
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
// Check right swap
if (col < COLS - 1) {
// Simulate swap right
var flower1 = gameBoard.getFlower(row, col);
var flower2 = gameBoard.getFlower(row, col + 1);
if (flower1 && flower2) {
// Swap
gameBoard.swapFlowers(row, col, row, col + 1, false);
// Check for matches
var matches = findAllMatches();
// Swap back
gameBoard.swapFlowers(row, col + 1, row, col, false);
if (matches.length > 0) {
return true;
}
}
}
// Check down swap
if (row < ROWS - 1) {
// Simulate swap down
var flower1 = gameBoard.getFlower(row, col);
var flower2 = gameBoard.getFlower(row + 1, col);
if (flower1 && flower2) {
// Swap
gameBoard.swapFlowers(row, col, row + 1, col, false);
// Check for matches
var matches = findAllMatches();
// Swap back
gameBoard.swapFlowers(row + 1, col, row, col, false);
if (matches.length > 0) {
return true;
}
}
}
}
}
return false;
}
// End the game
function endGame(success) {
gameOver = true;
if (success) {
// Level completed
currentLevel++;
storage.currentLevel = currentLevel;
// Reset match counter and increase requirements for next level
matchCounter = 0;
matchesRequiredForNextLevel = 5 + (currentLevel - 1) * 3; // 5 for level 1, +3 for each level
// Update high score
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Show you win screen
LK.getSound('levelup').play();
LK.showYouWin();
} else {
// Game over - ran out of moves
currentLevel = 1;
storage.currentLevel = currentLevel;
// Update high score
if (currentScore > highScore) {
highScore = currentScore;
storage.highScore = highScore;
}
// Show game over screen
LK.showGameOver();
}
}
// Initialize the game
initGame();
// Main game update loop
game.update = function () {
// If no valid moves exist, shuffle the board
if (!isProcessingMatches && !gameOver && !hasValidMoves()) {
// Shuffle the board
var allFlowers = [];
// Collect all flowers
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
var flower = gameBoard.removeFlower(row, col);
if (flower) {
flower.destroy();
}
}
}
// Repopulate the board
populateBoard();
}
};
// Handle touch/mouse input on game board
game.down = function (x, y, obj) {
// This is handled in the Flower class
};
game.move = function (x, y, obj) {
// Handle dragging of selected flower
if (selectedFlower && !isProcessingMatches && !gameOver) {
// Get position in gameBoard coordinates
var localPos = gameBoard.toLocal({
x: x,
y: y
});
// Calculate which cell the player is hovering over
var hoverCol = Math.floor((localPos.x + gameBoard.boardWidth / 2) / CELL_SIZE);
var hoverRow = Math.floor((localPos.y + gameBoard.boardHeight / 2) / CELL_SIZE);
// Check if it's a valid cell and adjacent to the original position
if (hoverRow >= 0 && hoverRow < ROWS && hoverCol >= 0 && hoverCol < COLS && gameBoard.areAdjacent(selectedFlower.row, selectedFlower.col, hoverRow, hoverCol)) {
// Get the flower at the hover position
var targetFlower = gameBoard.getFlower(hoverRow, hoverCol);
if (targetFlower && targetFlower !== selectedFlower) {
// Try to swap the flowers
trySwapFlowers(selectedFlower, targetFlower);
}
}
}
};
game.up = function (x, y, obj) {
// No global up handling needed
};
// Show language selection menu
function showLanguageMenu() {
if (languageMenuOpen) return;
languageMenuOpen = true;
// Create language menu container
languageMenuContainer = new Container();
// Create background overlay
var overlay = languageMenuContainer.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732,
tint: 0x000000,
alpha: 0.8
});
// Create menu background
var menuBackground = languageMenuContainer.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 1200,
height: 1800,
tint: 0xFFFFFF
});
// Create title
var titleText = new Text2(getText('chooseLanguage'), {
size: 80,
fill: 0x000000
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -700;
languageMenuContainer.addChild(titleText);
// Create language buttons
var buttonStartY = -500;
var buttonSpacing = 120;
for (var i = 0; i < SUPPORTED_LANGUAGES.length; i++) {
var lang = SUPPORTED_LANGUAGES[i];
var langButton = new Container();
// Button background
var buttonBg = langButton.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 800,
height: 100,
tint: currentLanguage === lang.code ? 0x00FF00 : 0x808080
});
// Language name
var langName = new Text2(lang.name, {
size: 50,
fill: 0x000000
});
langName.anchor.set(0, 0.5);
langName.x = -300;
langButton.addChild(langName);
// Choice button
var choiceButton = new Container();
var choiceBg = choiceButton.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 80,
tint: 0x0066CC
});
var choiceText = new Text2(getText('choice'), {
size: 40,
fill: 0xFFFFFF
});
choiceText.anchor.set(0.5, 0.5);
choiceButton.addChild(choiceText);
choiceButton.x = 250;
// Add choice button event
choiceButton.languageCode = lang.code;
choiceButton.down = function (x, y, obj) {
selectLanguage(this.languageCode);
};
langButton.addChild(choiceButton);
langButton.y = buttonStartY + i * buttonSpacing;
languageMenuContainer.addChild(langButton);
}
// Create back button
var backButton = new Container();
var backBg = backButton.attachAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 100,
tint: 0xFF0000
});
var backText = new Text2(getText('back'), {
size: 50,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backButton.addChild(backText);
backButton.y = 700;
backButton.down = function (x, y, obj) {
hideLanguageMenu();
};
languageMenuContainer.addChild(backButton);
// Position and add to game
languageMenuContainer.x = 2048 / 2;
languageMenuContainer.y = 2732 / 2;
game.addChild(languageMenuContainer);
}
// Hide language menu
function hideLanguageMenu() {
if (languageMenuContainer) {
languageMenuContainer.destroy();
languageMenuContainer = null;
}
languageMenuOpen = false;
}
// Select a language
function selectLanguage(languageCode) {
currentLanguage = languageCode;
storage.language = languageCode;
// Update all UI text
updateAllUIText();
// Hide language menu
hideLanguageMenu();
}
// Update all UI text with current language
function updateAllUIText() {
// Update main UI
updateUI();
// Update button texts
var topContainer = LK.gui.top;
for (var i = 0; i < topContainer.children.length; i++) {
var child = topContainer.children[i];
if (child.textElement && child.textElement.text) {
if (child.textElement.text.indexOf('Change') >= 0 || child.textElement.text.indexOf('Changer') >= 0 || child.textElement.text.indexOf('Cambiar') >= 0 || child.textElement.text.indexOf('Cambia') >= 0 || child.textElement.text.indexOf('Schimbă') >= 0 || child.textElement.text.indexOf('Zmień') >= 0 || child.textElement.text.indexOf('Αλλαγή') >= 0 || child.textElement.text.indexOf('Spielsprache') >= 0 || child.textElement.text.indexOf('更改') >= 0 || child.textElement.text.indexOf('ゲーム言語') >= 0) {
child.textElement.setText(getText('changeLanguage'));
}
}
}
var topRightContainer = LK.gui.topRight;
for (var i = 0; i < topRightContainer.children.length; i++) {
var child = topRightContainer.children[i];
if (child.textElement && child.textElement.text) {
if (child.textElement.text === 'RESET' || child.textElement.text === 'リセット' || child.textElement.text === 'ΕΠΑΝΑΦΟΡΑ' || child.textElement.text === '重置') {
child.textElement.setText(getText('reset'));
}
}
}
}
// Trigger the licorice explosion: remove all flowers in the column, trigger combo mode for 8 seconds
function triggerLicoriceExplosion(row, col) {
if (gameOver || isProcessingMatches) return;
isProcessingMatches = true;
// Remove all flowers in the column (except LicoriceBall itself)
for (var r = 0; r < ROWS; r++) {
var obj = gameBoard.getFlower(r, col);
if (obj && !(obj instanceof LicoriceBall)) {
gameBoard.removeFlower(r, col);
if (typeof obj.animateMatch === "function") {
obj.animateMatch(function () {
obj.destroy();
});
} else {
obj.destroy();
}
remainingFlowers--;
}
}
// Remove the LicoriceBall itself
var licorice = gameBoard.getFlower(row, col);
if (licorice && licorice instanceof LicoriceBall) {
gameBoard.removeFlower(row, col);
licorice.destroy();
}
// Combo mode: for 8 seconds, after each match, auto-match adjacent flowers
var comboEndTime = Date.now() + 8000;
var originalProcessMatches = processMatches;
var comboActive = true;
// Helper to auto-match adjacent flowers after each match
function comboProcessMatches(matches) {
originalProcessMatches(matches);
// After normal match processing, auto-match adjacent flowers if combo is still active
if (comboActive && !gameOver) {
LK.setTimeout(function () {
var newMatches = [];
// Find all adjacent pairs and add to matches
for (var row = 0; row < ROWS; row++) {
for (var col = 0; col < COLS; col++) {
var f = gameBoard.getFlower(row, col);
if (!f || f instanceof LicoriceBall) continue;
// Check right
var fRight = gameBoard.getFlower(row, col + 1);
if (fRight && fRight.type === f.type && !(fRight instanceof LicoriceBall)) {
newMatches.push({
row: row,
col: col
});
newMatches.push({
row: row,
col: col + 1
});
}
// Check down
var fDown = gameBoard.getFlower(row + 1, col);
if (fDown && fDown.type === f.type && !(fDown instanceof LicoriceBall)) {
newMatches.push({
row: row,
col: col
});
newMatches.push({
row: row + 1,
col: col
});
}
}
}
// Remove duplicates
var seen = {};
var uniqueMatches = [];
for (var i = 0; i < newMatches.length; i++) {
var key = newMatches[i].row + "," + newMatches[i].col;
if (!seen[key]) {
uniqueMatches.push(newMatches[i]);
seen[key] = true;
}
}
if (uniqueMatches.length > 0 && comboActive) {
comboProcessMatches(uniqueMatches);
} else {
isProcessingMatches = false;
}
}, 350); // Wait for match animation
} else {
isProcessingMatches = false;
}
}
// Override processMatches for combo mode
processMatches = comboProcessMatches;
// End combo mode after 8 seconds
LK.setTimeout(function () {
comboActive = false;
processMatches = originalProcessMatches;
isProcessingMatches = false;
}, 8000);
// After explosion, let flowers fall and fill, then check for matches
makeFlowersFall(function () {
fillEmptySpaces(function () {
var matches = findAllMatches();
if (matches.length > 0) {
processMatches(matches);
} else {
isProcessingMatches = false;
}
});
});
}