/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Candy = Container.expand(function (type) {
var self = Container.call(this);
self.candyType = type || 1;
self.gridX = 0;
self.gridY = 0;
self.isAnimating = false;
self.isMatched = false;
self.idleAnimationActive = false;
self.bobOffset = Math.random() * Math.PI * 2; // Random starting phase for bobbing
self.sparkleTimer = Math.random() * 120; // Random timing for sparkle effect
var candyGraphics = self.attachAsset('candy' + self.candyType, {
anchorX: 0.5,
anchorY: 0.5
});
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = GRID_START_X + gridX * CELL_SIZE + CELL_SIZE / 2;
self.y = GRID_START_Y + gridY * CELL_SIZE + CELL_SIZE / 2;
};
self.animateToPosition = function (targetX, targetY, callback) {
self.isAnimating = true;
self.stopIdleAnimation(); // Stop idle animation during move animation
tween(self, {
x: targetX,
y: targetY
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
if (callback) callback();
// Restart idle animation after move completes
LK.setTimeout(function () {
if (!self.isMatched) {
self.startIdleAnimation();
}
}, 200);
}
});
};
self.animateFall = function (newGridY, callback) {
var targetY = GRID_START_Y + newGridY * CELL_SIZE + CELL_SIZE / 2;
self.gridY = newGridY;
self.stopIdleAnimation(); // Stop idle animation during fall
self.animateToPosition(self.x, targetY, function () {
if (callback) callback();
// Restart idle animation after fall completes
LK.setTimeout(function () {
if (!self.isMatched) {
self.startIdleAnimation();
}
}, 300);
});
};
self.startIdleAnimation = function () {
if (self.idleAnimationActive || self.isAnimating || self.isMatched) return;
self.idleAnimationActive = true;
// Gentle bobbing animation
function bobAnimation() {
if (!self.idleAnimationActive || self.isAnimating || self.isMatched) return;
tween(self, {
y: self.y - 8
}, {
duration: 1500 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!self.idleAnimationActive || self.isAnimating || self.isMatched) return;
tween(self, {
y: self.y + 8
}, {
duration: 1500 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.idleAnimationActive && !self.isAnimating && !self.isMatched) {
bobAnimation();
}
}
});
}
});
}
// Start bobbing with a random delay
LK.setTimeout(function () {
if (self.idleAnimationActive && !self.isAnimating && !self.isMatched) {
bobAnimation();
}
}, Math.random() * 3000);
};
self.stopIdleAnimation = function () {
self.idleAnimationActive = false;
tween.stop(self, {
y: true
}); // Stop any y-position tweens
};
self.sparkle = function () {
if (self.isAnimating || self.isMatched) return;
// Create sparkle effect with scale and alpha animation
tween(candyGraphics, {
scaleX: 1.15,
scaleY: 1.15,
alpha: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(candyGraphics, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
};
self.markForRemoval = function () {
self.isMatched = true;
self.stopIdleAnimation(); // Stop idle animations when marked for removal
tween(self, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 200,
easing: tween.easeIn
});
};
return self;
});
var ComboText = Container.expand(function (message, x, y, color) {
var self = Container.call(this);
self.x = x;
self.y = y;
var comboTextDisplay = new Text2(message, {
size: 120,
fill: color || 0xFFFFFF
});
comboTextDisplay.anchor.set(0.5, 0.5);
self.addChild(comboTextDisplay);
// Initial state
self.alpha = 0;
self.scaleX = 0.5;
self.scaleY = 0.5;
// Animate in
tween(self, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold for a moment
LK.setTimeout(function () {
// Animate out
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5,
y: self.y - 100
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.destroy();
var index = comboTexts.indexOf(self);
if (index > -1) {
comboTexts.splice(index, 1);
}
}
});
}, 800);
}
});
return self;
});
var FloatingParticle = Container.expand(function (color, size) {
var self = Container.call(this);
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.tint = color;
particleGraphics.scaleX = size;
particleGraphics.scaleY = size;
self.speed = Math.random() * 2 + 1;
self.direction = Math.random() * Math.PI * 2;
self.bobOffset = Math.random() * Math.PI * 2;
self.bobSpeed = Math.random() * 0.02 + 0.01;
self.update = function () {
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed + Math.sin(LK.ticks * self.bobSpeed + self.bobOffset) * 0.5;
// Wrap around screen
if (self.x < -50) self.x = 2098;
if (self.x > 2098) self.x = -50;
if (self.y < -50) self.y = 2782;
if (self.y > 2782) self.y = -50;
// Gentle rotation
self.rotation += 0.01;
};
return self;
});
var HowToPlayScreen = Container.expand(function () {
var self = Container.call(this);
// Background
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
background.x = 2048 / 2;
background.y = 2732 / 2;
background.scaleX = 12;
background.scaleY = 16;
background.alpha = 0.9;
background.tint = 0x000000;
self.addChild(background);
// Title
var titleText = new Text2('HOW TO PLAY', {
size: 120,
fill: 0xFF69B4
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 400;
self.addChild(titleText);
// Instructions
var instructions = ['1. Swap adjacent candies to create matches', '2. Match 3-6 connected candies of same type', '3. Candies can connect horizontally & vertically', '4. Scoring: 3 blocks=50, 4=150, 5=200, 6=300', '5. Create combos for bonus points!'];
for (var i = 0; i < instructions.length; i++) {
var instructionText = new Text2(instructions[i], {
size: 55,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 2048 / 2;
instructionText.y = 700 + i * 140;
self.addChild(instructionText);
}
// Back button
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
backButton.x = 2048 / 2;
backButton.y = 2200;
backButton.scaleX = 1.5;
backButton.scaleY = 0.6;
backButton.tint = 0xEB4D4B;
self.addChild(backButton);
var backButtonText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
self.addChild(backButtonText);
backButton.down = function (x, y, obj) {
// Button press animation
tween(backButton, {
scaleX: 1.3,
scaleY: 0.5
}, {
duration: 100,
onFinish: function onFinish() {
tween(backButton, {
scaleX: 1.5,
scaleY: 0.6
}, {
duration: 100
});
}
});
// Go back to main menu
if (typeof showMainMenu === 'function') {
showMainMenu();
}
};
return self;
});
var MainMenu = Container.expand(function () {
var self = Container.call(this);
// Create gradient background overlay
var gradientOverlay = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
gradientOverlay.x = 2048 / 2;
gradientOverlay.y = 2732 / 2;
gradientOverlay.scaleX = 12;
gradientOverlay.scaleY = 16;
gradientOverlay.alpha = 0.1;
gradientOverlay.tint = 0x000000;
self.addChild(gradientOverlay);
// Add menu decorations
var decorations = new MenuDecoration();
self.addChild(decorations);
// Create floating background particles
var particles = [];
for (var i = 0; i < 20; i++) {
var colors = [0xFF69B4, 0x4ECDC4, 0xF9CA24, 0xEB4D4B, 0x45B7D1, 0xF0932B];
var color = colors[Math.floor(Math.random() * colors.length)];
var size = Math.random() * 0.8 + 0.3;
var particle = new FloatingParticle(color, size);
particle.x = Math.random() * 2048;
particle.y = Math.random() * 2732;
particle.alpha = 0.6;
particles.push(particle);
self.addChild(particle);
}
// Game title with enhanced styling
var titleText = new Text2('🍭 CANDY MATCH 🍭', {
size: 200,
fill: 0xFF69B4
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 500;
self.addChild(titleText);
// Add title shadow effect
var titleShadow = new Text2('🍭 CANDY MATCH 🍭', {
size: 200,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 2048 / 2 + 5;
titleShadow.y = 505;
titleShadow.alpha = 0.3;
self.addChildAt(titleShadow, self.getChildIndex(titleText));
// Subtitle
var subtitleText = new Text2('✨ Match candies and create sweet combos! ✨', {
size: 60,
fill: 0xFFFFFF
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 2048 / 2;
subtitleText.y = 650;
subtitleText.alpha = 0.8;
self.addChild(subtitleText);
// Enhanced start button with glow effect
var startButtonGlow = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
startButtonGlow.x = 2048 / 2;
startButtonGlow.y = 1200;
startButtonGlow.scaleX = 3.4;
startButtonGlow.scaleY = 1.4;
startButtonGlow.tint = 0x4ECDC4;
startButtonGlow.alpha = 0.3;
self.addChild(startButtonGlow);
var startButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
startButton.x = 2048 / 2;
startButton.y = 1200;
startButton.scaleX = 3.2;
startButton.scaleY = 1.3;
startButton.tint = 0x4ECDC4;
self.addChild(startButton);
var startButtonText = new Text2('🚀 START GAME', {
size: 90,
fill: 0xFFFFFF
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = startButton.x;
startButtonText.y = startButton.y;
self.addChild(startButtonText);
// Enhanced how to play button with glow effect
var howToPlayButtonGlow = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
howToPlayButtonGlow.x = 2048 / 2;
howToPlayButtonGlow.y = 1500;
howToPlayButtonGlow.scaleX = 3.2;
howToPlayButtonGlow.scaleY = 1.3;
howToPlayButtonGlow.tint = 0xF9CA24;
howToPlayButtonGlow.alpha = 0.3;
self.addChild(howToPlayButtonGlow);
var howToPlayButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
howToPlayButton.x = 2048 / 2;
howToPlayButton.y = 1500;
howToPlayButton.scaleX = 3.0;
howToPlayButton.scaleY = 1.2;
howToPlayButton.tint = 0xF9CA24;
self.addChild(howToPlayButton);
var howToPlayButtonText = new Text2('❓ HOW TO PLAY', {
size: 90,
fill: 0xFFFFFF
});
howToPlayButtonText.anchor.set(0.5, 0.5);
howToPlayButtonText.x = howToPlayButton.x;
howToPlayButtonText.y = howToPlayButton.y;
self.addChild(howToPlayButtonText);
// Sound control buttons
var soundButtonGlow = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
soundButtonGlow.x = 2048 / 2;
soundButtonGlow.y = 1800;
soundButtonGlow.scaleX = 3.0;
soundButtonGlow.scaleY = 1.2;
soundButtonGlow.tint = 0xEB4D4B;
soundButtonGlow.alpha = 0.3;
self.addChild(soundButtonGlow);
var soundButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
soundButton.x = 2048 / 2;
soundButton.y = 1800;
soundButton.scaleX = 2.8;
soundButton.scaleY = 1.1;
soundButton.tint = 0xEB4D4B;
self.addChild(soundButton);
// Get current sound state from storage
var soundEnabled = storage.soundEnabled;
if (soundEnabled === null || soundEnabled === undefined) {
soundEnabled = true;
storage.soundEnabled = soundEnabled;
}
var soundButtonText = new Text2(soundEnabled ? '🔊 SOUND ON' : '🔇 SOUND OFF', {
size: 90,
fill: 0xFFFFFF
});
soundButtonText.anchor.set(0.5, 0.5);
soundButtonText.x = soundButton.x;
soundButtonText.y = soundButton.y;
self.addChild(soundButtonText);
// Apply current sound settings
if (!soundEnabled) {
LK.stopMusic();
}
// Animate title with rainbow effect
self.animateTitle = function () {
// Scale animation
tween(titleText, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
scaleX: 1,
scaleY: 1
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.animateTitle();
}
});
}
});
// Color cycle animation
var colors = [0xFF69B4, 0x4ECDC4, 0xF9CA24, 0xEB4D4B, 0x45B7D1];
var currentColorIndex = 0;
function cycleColors() {
currentColorIndex = (currentColorIndex + 1) % colors.length;
tween(titleText, {
tint: colors[currentColorIndex]
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
LK.setTimeout(cycleColors, 500);
}
});
}
cycleColors();
};
// Animate subtitle
function animateSubtitle() {
tween(subtitleText, {
alpha: 1,
y: subtitleText.y - 10
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(subtitleText, {
alpha: 0.6,
y: 650
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: animateSubtitle
});
}
});
}
animateSubtitle();
// Animate button glows
function animateGlows() {
tween(startButtonGlow, {
alpha: 0.6,
scaleX: 3.6,
scaleY: 1.5
}, {
duration: 1200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(startButtonGlow, {
alpha: 0.3,
scaleX: 3.4,
scaleY: 1.4
}, {
duration: 1200,
easing: tween.easeInOut
});
}
});
tween(howToPlayButtonGlow, {
alpha: 0.6,
scaleX: 3.4,
scaleY: 1.4
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(howToPlayButtonGlow, {
alpha: 0.3,
scaleX: 3.2,
scaleY: 1.3
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: animateGlows
});
}
});
}
animateGlows();
// Update particles
self.update = function () {
for (var i = 0; i < particles.length; i++) {
particles[i].update();
}
};
// Enhanced button click handlers
startButton.down = function (x, y, obj) {
// Button press animation with enhanced effects
tween(startButton, {
scaleX: 2.8,
scaleY: 1.0
}, {
duration: 80,
onFinish: function onFinish() {
tween(startButton, {
scaleX: 3.4,
scaleY: 1.4
}, {
duration: 80,
onFinish: function onFinish() {
tween(startButton, {
scaleX: 3.2,
scaleY: 1.3
}, {
duration: 80
});
}
});
}
});
// Animate button glow on press
tween(startButtonGlow, {
alpha: 0.8,
scaleX: 3.8,
scaleY: 1.7
}, {
duration: 150,
onFinish: function onFinish() {
tween(startButtonGlow, {
alpha: 0.3,
scaleX: 3.4,
scaleY: 1.4
}, {
duration: 200
});
}
});
// Start the game
startGame();
};
howToPlayButton.down = function (x, y, obj) {
// Button press animation with enhanced effects
tween(howToPlayButton, {
scaleX: 2.6,
scaleY: 0.9
}, {
duration: 80,
onFinish: function onFinish() {
tween(howToPlayButton, {
scaleX: 3.2,
scaleY: 1.3
}, {
duration: 80,
onFinish: function onFinish() {
tween(howToPlayButton, {
scaleX: 3.0,
scaleY: 1.2
}, {
duration: 80
});
}
});
}
});
// Animate button glow on press
tween(howToPlayButtonGlow, {
alpha: 0.8,
scaleX: 3.6,
scaleY: 1.6
}, {
duration: 150,
onFinish: function onFinish() {
tween(howToPlayButtonGlow, {
alpha: 0.3,
scaleX: 3.2,
scaleY: 1.3
}, {
duration: 200
});
}
});
// Show how to play
if (typeof showHowToPlay === 'function') {
showHowToPlay();
}
};
soundButton.down = function (x, y, obj) {
// Button press animation with enhanced effects
tween(soundButton, {
scaleX: 2.4,
scaleY: 0.9
}, {
duration: 80,
onFinish: function onFinish() {
tween(soundButton, {
scaleX: 3.0,
scaleY: 1.2
}, {
duration: 80,
onFinish: function onFinish() {
tween(soundButton, {
scaleX: 2.8,
scaleY: 1.1
}, {
duration: 80
});
}
});
}
});
// Animate button glow on press
tween(soundButtonGlow, {
alpha: 0.8,
scaleX: 3.4,
scaleY: 1.5
}, {
duration: 150,
onFinish: function onFinish() {
tween(soundButtonGlow, {
alpha: 0.3,
scaleX: 3.0,
scaleY: 1.2
}, {
duration: 200
});
}
});
// Toggle sound
soundEnabled = !soundEnabled;
storage.soundEnabled = soundEnabled;
soundButtonText.setText(soundEnabled ? '🔊 SOUND ON' : '🔇 SOUND OFF');
// Apply sound settings
if (soundEnabled) {
LK.playMusic('rahat');
} else {
LK.stopMusic();
}
};
return self;
});
var MenuDecoration = Container.expand(function () {
var self = Container.call(this);
// Create candy decorations around the screen
var decorationCount = 12;
var _loop = function _loop() {
candyType = Math.floor(Math.random() * 6) + 1;
decoration = LK.getAsset('candy' + candyType, {
anchorX: 0.5,
anchorY: 0.5
});
angle = i / decorationCount * Math.PI * 2;
radius = 800 + Math.random() * 200;
decoration.x = 2048 / 2 + Math.cos(angle) * radius;
decoration.y = 2732 / 2 + Math.sin(angle) * radius;
decoration.alpha = 0.3;
decoration.scaleX = 0.8 + Math.random() * 0.4;
decoration.scaleY = decoration.scaleX;
self.addChild(decoration);
// Animate decorations
animDuration = 3000 + Math.random() * 2000;
targetScale = decoration.scaleX + 0.2;
function animateDecoration(dec) {
tween(dec, {
rotation: dec.rotation + Math.PI * 2,
scaleX: targetScale,
scaleY: targetScale
}, {
duration: animDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
targetScale = 0.8 + Math.random() * 0.4;
animDuration = 3000 + Math.random() * 2000;
animateDecoration(dec);
}
});
}
animateDecoration(decoration);
},
candyType,
decoration,
angle,
radius,
animDuration,
targetScale;
for (var i = 0; i < decorationCount; i++) {
_loop();
}
return self;
});
var Particle = Container.expand(function (color, startX, startY) {
var self = Container.call(this);
self.x = startX;
self.y = startY;
self.velocityX = (Math.random() - 0.5) * 400;
self.velocityY = (Math.random() - 0.5) * 400;
self.gravity = 300;
self.life = 1.0;
self.maxLife = 1.0;
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.tint = color;
particleGraphics.scaleX = Math.random() * 0.8 + 0.4;
particleGraphics.scaleY = particleGraphics.scaleX;
self.update = function () {
self.x += self.velocityX * (1 / 60);
self.y += self.velocityY * (1 / 60);
self.velocityY += self.gravity * (1 / 60);
self.life -= 1 / 60 * 2;
var alpha = self.life / self.maxLife;
self.alpha = alpha;
self.scaleX = alpha * particleGraphics.scaleX;
self.scaleY = alpha * particleGraphics.scaleY;
if (self.life <= 0) {
self.destroy();
var index = particles.indexOf(self);
if (index > -1) {
particles.splice(index, 1);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xff69b4
});
/****
* Game Code
****/
function _createForOfIteratorHelper(r, e) {
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (!t) {
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {
t && (r = t);
var _n = 0,
F = function F() {};
return {
s: F,
n: function n() {
return _n >= r.length ? {
done: !0
} : {
done: !1,
value: r[_n++]
};
},
e: function e(r) {
throw r;
},
f: F
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var o,
a = !0,
u = !1;
return {
s: function s() {
t = t.call(r);
},
n: function n() {
var r = t.next();
return a = r.done, r;
},
e: function e(r) {
u = !0, o = r;
},
f: function f() {
try {
a || null == t["return"] || t["return"]();
} finally {
if (u) throw o;
}
}
};
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
var GRID_SIZE = 8;
var CELL_SIZE = 200;
var GRID_START_X = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var GRID_START_Y = 400;
var gameGrid = [];
var candyTypes = [1, 2, 3, 4, 5, 6];
var selectedCandy = null;
var isProcessing = false;
var movesLeft = 999999;
var score = 0;
var animatingCandies = 0;
var particles = [];
var comboTexts = [];
var currentCombo = 0;
// Power-up variables
var rowCleanerButton = null;
var colorChangerButton = null;
var clearAllButton = null;
var hintButton = null;
var rowCleanerCost = 2000;
var colorChangerCost = 500;
var clearAllCost = 7000;
var hintCost = 4000;
var selectedPowerUp = null; // 'rowCleaner', 'colorChanger', or 'clearAll'
var hintArrows = []; // Array to store hint arrow elements
// Game state variables
var gameState = 'menu'; // 'menu', 'game', 'howtoplay'
var mainMenu = null;
var howToPlayScreen = null;
var gameElements = [];
// Sound control variables
var soundEnabled = true;
// Initialize grid array
for (var x = 0; x < GRID_SIZE; x++) {
gameGrid[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
gameGrid[x][y] = null;
}
}
// Menu control functions
function startGame() {
gameState = 'game';
hideAllScreens();
initializeGameElements();
}
function showMainMenu() {
gameState = 'menu';
// Clear game grid and destroy candies
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y]) {
gameGrid[x][y].destroy();
gameGrid[x][y] = null;
}
}
}
// Clear particles
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].destroy();
}
particles = [];
// Clear combo texts
for (var i = comboTexts.length - 1; i >= 0; i--) {
comboTexts[i].destroy();
}
comboTexts = [];
// Reset game state
selectedCandy = null;
isProcessing = false;
animatingCandies = 0;
currentCombo = 0;
hideAllScreens();
if (!mainMenu) {
mainMenu = new MainMenu();
game.addChild(mainMenu);
} else {
// Recursively re-enable all menu interactions
var _enableMenuInteractions = function enableMenuInteractions(container) {
container.interactive = true;
if (container.children) {
for (var i = 0; i < container.children.length; i++) {
var child = container.children[i];
child.interactive = true;
if (child.children && child.children.length > 0) {
_enableMenuInteractions(child);
}
}
}
};
mainMenu.alpha = 1;
mainMenu.visible = true;
mainMenu.interactive = true;
_enableMenuInteractions(mainMenu);
}
mainMenu.animateTitle();
}
function showHowToPlay() {
gameState = 'howtoplay';
hideAllScreens();
// Completely hide main menu to prevent interactions
if (mainMenu) {
// Recursively disable all child interactions
var _disableMenuInteractions = function disableMenuInteractions(container) {
container.interactive = false;
if (container.children) {
for (var i = 0; i < container.children.length; i++) {
var child = container.children[i];
child.interactive = false;
if (child.down) {
child.down = null;
}
if (child.children && child.children.length > 0) {
_disableMenuInteractions(child);
}
}
}
};
mainMenu.alpha = 0;
mainMenu.visible = false;
mainMenu.interactive = false;
_disableMenuInteractions(mainMenu);
}
if (!howToPlayScreen) {
howToPlayScreen = new HowToPlayScreen();
game.addChild(howToPlayScreen);
// Ensure how-to-play screen is on top
game.setChildIndex(howToPlayScreen, game.children.length - 1);
} else {
howToPlayScreen.alpha = 1;
howToPlayScreen.visible = true;
// Bring to front when showing
game.setChildIndex(howToPlayScreen, game.children.length - 1);
}
}
function hideAllScreens() {
if (mainMenu) {
mainMenu.alpha = 0;
mainMenu.visible = false;
}
if (howToPlayScreen) {
howToPlayScreen.alpha = 0;
howToPlayScreen.visible = false;
}
// Hide game elements (except persistent ones like scoreText and backgroundImage)
for (var i = 0; i < gameElements.length; i++) {
if (gameElements[i] !== scoreText && gameElements[i] !== backgroundImage) {
gameElements[i].alpha = 0;
}
}
}
function initializeGameElements() {
// Clear existing game elements first
for (var i = gameElements.length - 1; i >= 0; i--) {
if (gameElements[i] !== scoreText && gameElements[i] !== backgroundImage) {
gameElements[i].destroy();
gameElements.splice(i, 1);
}
}
// Show persistent game elements
for (var i = 0; i < gameElements.length; i++) {
gameElements[i].alpha = 1;
}
// Grid background removed - candies display directly on background image
// Add 7000 points to initial score
score += 7000;
scoreText.setText('Score: ' + score);
// Create power-up buttons
createPowerUpButtons();
// Clear hint arrows
clearHintArrows();
// Reset game state
score = 0;
currentCombo = 0;
selectedCandy = null;
isProcessing = false;
scoreText.setText('Score: 0');
// Clear existing candies and initialize fresh grid
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y]) {
gameGrid[x][y].destroy();
gameGrid[x][y] = null;
}
}
}
// Initialize the game grid
initializeGrid();
}
// UI Elements
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
gameElements.push(scoreText);
// Create background image
var backgroundImage = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5
});
backgroundImage.x = 2048 / 2;
backgroundImage.y = 2732 / 2;
backgroundImage.scaleX = 2048 / backgroundImage.width;
backgroundImage.scaleY = 2732 / backgroundImage.height;
game.addChild(backgroundImage);
gameElements.push(backgroundImage);
// Grid cells will be created when game starts
function getRandomCandyType() {
return candyTypes[Math.floor(Math.random() * candyTypes.length)];
}
function createCandy(x, y, type) {
if (!type) type = getRandomCandyType();
var candy = new Candy(type);
candy.setGridPosition(x, y);
gameGrid[x][y] = candy;
game.addChild(candy);
// Start idle animations after a short delay to let positioning settle
LK.setTimeout(function () {
if (candy && !candy.isMatched) {
candy.startIdleAnimation();
}
}, 500);
return candy;
}
function initializeGrid() {
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
var type;
var attempts = 0;
do {
type = getRandomCandyType();
attempts++;
} while (attempts < 10 && wouldCreateMatch(x, y, type));
createCandy(x, y, type);
}
}
}
function wouldCreateMatch(x, y, type) {
var horizontalCount = 1;
var verticalCount = 1;
// Check horizontal
for (var i = x - 1; i >= 0 && gameGrid[i][y] && gameGrid[i][y].candyType === type; i--) {
horizontalCount++;
}
for (var i = x + 1; i < GRID_SIZE && gameGrid[i][y] && gameGrid[i][y].candyType === type; i++) {
horizontalCount++;
}
// Check vertical
for (var i = y - 1; i >= 0 && gameGrid[x][i] && gameGrid[x][i].candyType === type; i--) {
verticalCount++;
}
for (var i = y + 1; i < GRID_SIZE && gameGrid[x][i] && gameGrid[x][i].candyType === type; i++) {
verticalCount++;
}
return horizontalCount >= 3 || verticalCount >= 3;
}
function getCandyAt(worldX, worldY) {
var gridX = Math.floor((worldX - GRID_START_X) / CELL_SIZE);
var gridY = Math.floor((worldY - GRID_START_Y) / CELL_SIZE);
if (gridX >= 0 && gridX < GRID_SIZE && gridY >= 0 && gridY < GRID_SIZE) {
return gameGrid[gridX][gridY];
}
return null;
}
function areAdjacent(candy1, candy2) {
var dx = Math.abs(candy1.gridX - candy2.gridX);
var dy = Math.abs(candy1.gridY - candy2.gridY);
return dx === 1 && dy === 0 || dx === 0 && dy === 1;
}
function swapCandies(candy1, candy2) {
var tempX = candy1.gridX;
var tempY = candy1.gridY;
var tempPosX = candy1.x;
var tempPosY = candy1.y;
gameGrid[candy1.gridX][candy1.gridY] = candy2;
gameGrid[candy2.gridX][candy2.gridY] = candy1;
candy1.gridX = candy2.gridX;
candy1.gridY = candy2.gridY;
candy2.gridX = tempX;
candy2.gridY = tempY;
animatingCandies += 2;
// Play boing sound when candies are swapped
LK.getSound('boing').play();
candy1.animateToPosition(candy2.x, candy2.y, function () {
animatingCandies--;
});
candy2.animateToPosition(tempPosX, tempPosY, function () {
animatingCandies--;
});
}
function findMatches() {
var allMatches = [];
var visited = [];
// Initialize visited array
for (var x = 0; x < GRID_SIZE; x++) {
visited[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
visited[x][y] = false;
}
}
// Flood fill function to find all connected candies of same type (max 6 blocks)
function floodFill(startX, startY, candyType, connectedGroup) {
// Check bounds and if already visited
if (startX < 0 || startX >= GRID_SIZE || startY < 0 || startY >= GRID_SIZE) {
return;
}
if (visited[startX][startY]) {
return;
}
var candy = gameGrid[startX][startY];
if (!candy || candy.candyType !== candyType) {
return;
}
// Limit group size to maximum 6 blocks
if (connectedGroup.length >= 6) {
return;
}
// Mark as visited and add to group
visited[startX][startY] = true;
connectedGroup.push(candy);
// Check all 4 adjacent directions (up, down, left, right) only if we haven't reached the limit
if (connectedGroup.length < 6) {
floodFill(startX - 1, startY, candyType, connectedGroup); // left
floodFill(startX + 1, startY, candyType, connectedGroup); // right
floodFill(startX, startY - 1, candyType, connectedGroup); // up
floodFill(startX, startY + 1, candyType, connectedGroup); // down
}
}
// Check each cell for connected groups
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (!visited[x][y] && gameGrid[x][y]) {
var connectedGroup = [];
var candyType = gameGrid[x][y].candyType;
// Find all connected candies of the same type
floodFill(x, y, candyType, connectedGroup);
// If we found 3 or more connected candies, add this group to allMatches
if (connectedGroup.length >= 3) {
allMatches.push(connectedGroup);
}
}
}
}
return allMatches;
}
function removeMatches(matchGroups) {
if (matchGroups.length === 0) return;
LK.getSound('match').play();
// Flatten all matches for candy removal and particle effects
var allMatches = [];
for (var g = 0; g < matchGroups.length; g++) {
for (var c = 0; c < matchGroups[g].length; c++) {
allMatches.push(matchGroups[g][c]);
}
}
for (var i = 0; i < allMatches.length; i++) {
var candy = allMatches[i];
// Create particle explosion for each candy
var candyColors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0xf9ca24, 0xf0932b, 0xeb4d4b];
var particleColor = candyColors[candy.candyType - 1] || 0xffffff;
// Create 8-12 particles per candy
var particleCount = Math.floor(Math.random() * 5) + 8;
for (var p = 0; p < particleCount; p++) {
var particle = new Particle(particleColor, candy.x, candy.y);
particles.push(particle);
game.addChild(particle);
}
candy.markForRemoval();
gameGrid[candy.gridX][candy.gridY] = null;
}
// Update score based on each group's size with new scoring system
var oldScore = score;
var largestGroupSize = 0;
for (var g = 0; g < matchGroups.length; g++) {
var groupSize = Math.min(matchGroups[g].length, 6); // Cap at 6 blocks maximum
if (groupSize > largestGroupSize) {
largestGroupSize = groupSize;
}
if (groupSize === 3) {
score += 50;
} else if (groupSize === 4) {
score += 150;
} else if (groupSize === 5) {
score += 200;
} else if (groupSize >= 6) {
score += 300;
}
}
// Check for score milestones and celebrate
var milestones = [1000, 5000, 10000, 25000, 50000];
for (var m = 0; m < milestones.length; m++) {
if (oldScore < milestones[m] && score >= milestones[m]) {
// Milestone reached! Create celebration effect
var milestoneText = new Text2('MILESTONE!\n' + milestones[m] + ' POINTS!', {
size: 80,
fill: 0xFF69B4
});
milestoneText.anchor.set(0.5, 0.5);
milestoneText.x = 2048 / 2;
milestoneText.y = 400;
milestoneText.alpha = 0;
milestoneText.scaleX = 0.5;
milestoneText.scaleY = 0.5;
game.addChild(milestoneText);
// Animate milestone text
tween(milestoneText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(milestoneText, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5,
y: milestoneText.y - 100
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
milestoneText.destroy();
}
});
}, 1500);
}
});
// Screen flash for milestone
LK.effects.flashScreen(0xFF69B4, 800);
break;
}
}
// Create floating score text for each match group
var scoreIncrease = score - oldScore;
var floatingScoreText = new Text2('+' + scoreIncrease, {
size: 100,
fill: 0xF9CA24
});
floatingScoreText.anchor.set(0.5, 0.5);
// Position above the grid
floatingScoreText.x = 2048 / 2;
floatingScoreText.y = GRID_START_Y - 100;
floatingScoreText.alpha = 0;
// Set initial rotation to be horizontal
floatingScoreText.rotation = 0;
game.addChild(floatingScoreText);
// Animate floating score - first appear, then move towards score display
tween(floatingScoreText, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Convert score text position to game coordinates
var scoreWorldPos = game.toLocal(LK.gui.top.toGlobal({
x: scoreText.x,
y: scoreText.y
}));
tween(floatingScoreText, {
alpha: 0,
x: scoreWorldPos.x,
y: scoreWorldPos.y,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
floatingScoreText.destroy();
}
});
}
});
// Animate main score counter with pulsing effect
tween(scoreText, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xF9CA24
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(scoreText, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut
});
}
});
// Animate score increase with smooth counting
var tempScore = oldScore;
var scoreAnimationTimer = LK.setInterval(function () {
tempScore += scoreIncrease / 30; // 30 steps over 500ms
if (tempScore >= score) {
tempScore = score;
LK.clearInterval(scoreAnimationTimer);
}
scoreText.setText('Score: ' + Math.floor(tempScore));
}, 16); // ~60fps
// Increment combo counter
currentCombo++;
// Play celebration sound for large matches
if (largestGroupSize >= 5) {
// Celebration sound removed
}
// Show combo text based on largest match size and combo count
var comboMessage = "";
var comboColor = 0xFFFFFF;
if (largestGroupSize >= 5) {
comboMessage = "Fantastic!";
comboColor = 0xFF6B6B;
// Screen shake for 5+ matches
var shakeIntensity = 15;
var originalX = game.x;
var originalY = game.y;
tween(game, {
x: originalX + shakeIntensity,
y: originalY + shakeIntensity * 0.5
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: originalX - shakeIntensity,
y: originalY - shakeIntensity * 0.5
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: originalX + shakeIntensity * 0.5,
y: originalY + shakeIntensity
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: originalX,
y: originalY
}, {
duration: 50
});
}
});
}
});
}
});
} else if (largestGroupSize >= 4) {
comboMessage = "Awesome!";
comboColor = 0x4ECDC4;
// Screen shake for 4+ matches
var shakeIntensity = 10;
var originalX = game.x;
var originalY = game.y;
tween(game, {
x: originalX + shakeIntensity,
y: originalY
}, {
duration: 75,
onFinish: function onFinish() {
tween(game, {
x: originalX - shakeIntensity,
y: originalY
}, {
duration: 75,
onFinish: function onFinish() {
tween(game, {
x: originalX,
y: originalY
}, {
duration: 75
});
}
});
}
});
} else if (currentCombo >= 3) {
comboMessage = "Great Combo!";
comboColor = 0xF9CA24;
// Light screen shake for combo
var shakeIntensity = 5;
var originalX = game.x;
var originalY = game.y;
tween(game, {
x: originalX + shakeIntensity
}, {
duration: 100,
onFinish: function onFinish() {
tween(game, {
x: originalX - shakeIntensity
}, {
duration: 100,
onFinish: function onFinish() {
tween(game, {
x: originalX
}, {
duration: 100
});
}
});
}
});
} else if (currentCombo >= 2) {
comboMessage = "Great!";
comboColor = 0x45B7D1;
}
if (comboMessage !== "") {
var comboX = 2048 / 2;
var comboY = GRID_START_Y + GRID_SIZE * CELL_SIZE / 2;
var comboText = new ComboText(comboMessage, comboX, comboY, comboColor);
comboTexts.push(comboText);
game.addChild(comboText);
}
// Remove candies after animation
LK.setTimeout(function () {
for (var i = 0; i < allMatches.length; i++) {
allMatches[i].destroy();
}
applyGravity();
}, 250);
}
function applyGravity() {
var needsGravity = false;
// Apply gravity physics - make candies fall to fill empty spaces
for (var x = 0; x < GRID_SIZE; x++) {
// Collect all candies in this column that are not null
var candiesInColumn = [];
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y] !== null) {
candiesInColumn.push({
candy: gameGrid[x][y],
originalY: y
});
}
gameGrid[x][y] = null; // Clear the column
}
// Place candies back starting from bottom
var newY = GRID_SIZE - 1;
for (var i = candiesInColumn.length - 1; i >= 0; i--) {
var candyData = candiesInColumn[i];
var candy = candyData.candy;
var originalY = candyData.originalY;
gameGrid[x][newY] = candy;
// Immediately update grid position to prevent overlaps
candy.gridX = x;
candy.gridY = newY;
// Check if candy needs to fall
if (newY !== originalY) {
var targetY = GRID_START_Y + newY * CELL_SIZE + CELL_SIZE / 2;
candy.isAnimating = true;
animatingCandies++;
tween(candy, {
y: targetY
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
candy.isAnimating = false;
animatingCandies--;
}
});
needsGravity = true;
} else {
// Ensure position is correct even if not animating
candy.x = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2;
candy.y = GRID_START_Y + newY * CELL_SIZE + CELL_SIZE / 2;
}
newY--;
}
// Fill remaining empty spaces at top with new candies
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y] === null) {
var newCandy = createCandy(x, y, getRandomCandyType());
newCandy.y = GRID_START_Y - CELL_SIZE + CELL_SIZE / 2;
newCandy.isAnimating = true;
animatingCandies++;
var targetY = GRID_START_Y + y * CELL_SIZE + CELL_SIZE / 2;
tween(newCandy, {
y: targetY
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
newCandy.isAnimating = false;
animatingCandies--;
}
});
needsGravity = true;
}
}
}
if (needsGravity) {
LK.getSound('cascade').play();
LK.setTimeout(function () {
checkForNewMatches();
}, 400);
}
}
function checkForNewMatches() {
if (animatingCandies > 0) {
LK.setTimeout(checkForNewMatches, 100);
return;
}
var matchGroups = findMatches();
if (matchGroups.length > 0) {
removeMatches(matchGroups);
} else {
isProcessing = false;
currentCombo = 0; // Reset combo counter when no more matches
}
}
function findBestMove() {
var bestMove = null;
var bestScore = 0;
// Check all possible moves
for (var x1 = 0; x1 < GRID_SIZE; x1++) {
for (var y1 = 0; y1 < GRID_SIZE; y1++) {
var candy1 = gameGrid[x1][y1];
if (!candy1) continue;
// Check adjacent positions
var adjacentPositions = [{
x: x1 - 1,
y: y1
},
// left
{
x: x1 + 1,
y: y1
},
// right
{
x: x1,
y: y1 - 1
},
// up
{
x: x1,
y: y1 + 1
} // down
];
for (var i = 0; i < adjacentPositions.length; i++) {
var pos = adjacentPositions[i];
if (pos.x < 0 || pos.x >= GRID_SIZE || pos.y < 0 || pos.y >= GRID_SIZE) continue;
var candy2 = gameGrid[pos.x][pos.y];
if (!candy2) continue;
// Temporarily swap to check for matches
var tempX1 = candy1.gridX,
tempY1 = candy1.gridY;
var tempX2 = candy2.gridX,
tempY2 = candy2.gridY;
gameGrid[tempX1][tempY1] = candy2;
gameGrid[tempX2][tempY2] = candy1;
candy1.gridX = tempX2;
candy1.gridY = tempY2;
candy2.gridX = tempX1;
candy2.gridY = tempY1;
var matchGroups = findMatches();
var moveScore = 0;
for (var g = 0; g < matchGroups.length; g++) {
var groupSize = Math.min(matchGroups[g].length, 6);
if (groupSize === 3) moveScore += 50;else if (groupSize === 4) moveScore += 150;else if (groupSize === 5) moveScore += 200;else if (groupSize >= 6) moveScore += 300;
}
// Revert the swap
gameGrid[tempX1][tempY1] = candy1;
gameGrid[tempX2][tempY2] = candy2;
candy1.gridX = tempX1;
candy1.gridY = tempY1;
candy2.gridX = tempX2;
candy2.gridY = tempY2;
if (moveScore > bestScore) {
bestScore = moveScore;
bestMove = {
candy1: candy1,
candy2: candy2,
score: moveScore
};
}
}
}
}
return bestMove;
}
function showHintArrow(candy1, candy2) {
// Clear existing hint arrows
clearHintArrows();
// Create arrow from candy1 to candy2
var arrow = new Container();
var arrowBody = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0.5
});
arrowBody.scaleX = 0.8;
arrowBody.scaleY = 0.1;
arrowBody.tint = 0xFFFF00;
arrow.addChild(arrowBody);
var arrowHead = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
arrowHead.scaleX = 0.15;
arrowHead.scaleY = 0.15;
arrowHead.tint = 0xFFFF00;
arrowHead.rotation = Math.PI / 4; // 45 degrees
arrow.addChild(arrowHead);
// Position arrow between the two candies
var midX = (candy1.x + candy2.x) / 2;
var midY = (candy1.y + candy2.y) / 2;
arrow.x = midX;
arrow.y = midY;
// Calculate angle from candy1 to candy2
var dx = candy2.x - candy1.x;
var dy = candy2.y - candy1.y;
var angle = Math.atan2(dy, dx);
arrow.rotation = angle;
// Position arrow body and head
arrowBody.x = -80;
arrowHead.x = 80;
// Add pulsing animation
arrow.alpha = 0;
game.addChild(arrow);
hintArrows.push(arrow);
tween(arrow, {
alpha: 1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Pulse animation
function pulseArrow() {
tween(arrow, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(arrow, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (hintArrows.indexOf(arrow) !== -1) {
pulseArrow();
}
}
});
}
});
}
pulseArrow();
}
});
// Auto-hide hint after 5 seconds
LK.setTimeout(function () {
clearHintArrows();
}, 5000);
}
function clearHintArrows() {
for (var i = hintArrows.length - 1; i >= 0; i--) {
hintArrows[i].destroy();
}
hintArrows = [];
}
function checkGameState() {
// Endless game - no win/lose conditions
}
function processMove(candy1, candy2) {
if (isProcessing || !candy1 || !candy2 || candy1 === candy2) return false;
if (!areAdjacent(candy1, candy2)) return false;
isProcessing = true;
// Endless game - no move limit
// Temporarily swap to check for matches
var tempX1 = candy1.gridX,
tempY1 = candy1.gridY;
var tempX2 = candy2.gridX,
tempY2 = candy2.gridY;
gameGrid[tempX1][tempY1] = candy2;
gameGrid[tempX2][tempY2] = candy1;
candy1.gridX = tempX2;
candy1.gridY = tempY2;
candy2.gridX = tempX1;
candy2.gridY = tempY1;
var matchGroups = findMatches();
if (matchGroups.length > 0) {
// Valid move - perform the swap animation
swapCandies(candy2, candy1); // Swap back positions for animation
LK.setTimeout(function () {
removeMatches(matchGroups);
}, 350);
} else {
// Invalid move - revert
gameGrid[tempX1][tempY1] = candy1;
gameGrid[tempX2][tempY2] = candy2;
candy1.gridX = tempX1;
candy1.gridY = tempY1;
candy2.gridX = tempX2;
candy2.gridY = tempY2;
// Animate back to original positions
swapCandies(candy1, candy2);
LK.setTimeout(function () {
isProcessing = false;
}, 350);
// Endless game - no move limit
}
return true;
}
game.down = function (x, y, obj) {
if (gameState !== 'game') return;
if (isProcessing) return;
// Check if a power-up is selected
if (selectedPowerUp) {
var candy = getCandyAt(x, y);
if (!candy) {
selectedPowerUp = null;
return;
}
if (selectedPowerUp === 'rowCleaner' && score >= rowCleanerCost) {
// Clear entire row
score -= rowCleanerCost;
scoreText.setText('Score: ' + score);
var row = candy.gridY;
var clearedCandies = [];
for (var x = 0; x < GRID_SIZE; x++) {
if (gameGrid[x][row]) {
clearedCandies.push(gameGrid[x][row]);
gameGrid[x][row] = null;
}
}
// Animate cleared candies
for (var i = 0; i < clearedCandies.length; i++) {
clearedCandies[i].markForRemoval();
}
LK.setTimeout(function () {
for (var i = 0; i < clearedCandies.length; i++) {
clearedCandies[i].destroy();
}
applyGravity();
}, 250);
selectedPowerUp = null;
return;
}
if (selectedPowerUp === 'colorChanger' && score >= colorChangerCost) {
// Change candy color to random type
score -= colorChangerCost;
scoreText.setText('Score: ' + score);
var newType = getRandomCandyType();
var oldCandy = candy;
var x = oldCandy.gridX;
var y = oldCandy.gridY;
// Remove old candy
oldCandy.destroy();
// Create new candy with different type
var newCandy = createCandy(x, y, newType);
// Flash effect
tween(newCandy, {
alpha: 0.3
}, {
duration: 100,
onFinish: function onFinish() {
tween(newCandy, {
alpha: 1
}, {
duration: 100
});
}
});
selectedPowerUp = null;
return;
}
if (selectedPowerUp === 'clearAll' && score >= clearAllCost) {
// Clear all candies from the grid
score -= clearAllCost;
score += 5000; // Bonus points
scoreText.setText('Score: ' + score);
var allCandies = [];
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y]) {
allCandies.push(gameGrid[x][y]);
gameGrid[x][y] = null;
}
}
}
// Animate all candies removal
for (var i = 0; i < allCandies.length; i++) {
allCandies[i].markForRemoval();
}
// Create spectacular particle effects
for (var i = 0; i < allCandies.length; i++) {
var candy = allCandies[i];
var candyColors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0xf9ca24, 0xf0932b, 0xeb4d4b];
var particleColor = candyColors[candy.candyType - 1] || 0xffffff;
// Create more particles for spectacular effect
var particleCount = Math.floor(Math.random() * 8) + 12;
for (var p = 0; p < particleCount; p++) {
var particle = new Particle(particleColor, candy.x, candy.y);
particles.push(particle);
game.addChild(particle);
}
}
// Screen flash effect
LK.effects.flashScreen(0xFFFFFF, 500);
// Remove candies and refill grid
LK.setTimeout(function () {
for (var i = 0; i < allCandies.length; i++) {
allCandies[i].destroy();
}
applyGravity();
}, 250);
selectedPowerUp = null;
return;
}
selectedPowerUp = null;
return;
}
var candy = getCandyAt(x, y);
if (!candy) return;
if (selectedCandy) {
if (selectedCandy === candy) {
// Deselect same candy
tween(selectedCandy, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
selectedCandy = null;
} else if (areAdjacent(selectedCandy, candy)) {
// Try to make move if candies are adjacent
var success = processMove(selectedCandy, candy);
if (success) {
tween(selectedCandy, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
selectedCandy = null;
}
} else {
// Deselect current candy and select new candy
tween(selectedCandy, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
selectedCandy = candy;
tween(candy, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150
});
}
} else {
// Select candy
selectedCandy = candy;
tween(candy, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150
});
}
};
// Create menu button (hamburger style with candy theme)
var menuButton = null;
function createPowerUpButtons() {
// Calculate position below coffee cup
var coffeeY = GRID_START_Y + GRID_SIZE * CELL_SIZE + 150;
var powerUpStartY = coffeeY + 200;
// Row Cleaner Button
rowCleanerButton = new Container();
var rowCleanerBg = LK.getAsset('powerupButton', {
anchorX: 0.5,
anchorY: 0.5
});
rowCleanerBg.x = 0;
rowCleanerBg.y = 0;
rowCleanerBg.tint = 0xFF6B6B;
rowCleanerButton.addChild(rowCleanerBg);
var rowCleanerIcon = LK.getAsset('rowCleaner', {
anchorX: 0.5,
anchorY: 0.5
});
rowCleanerIcon.x = 0;
rowCleanerIcon.y = -30;
rowCleanerButton.addChild(rowCleanerIcon);
// Progress bar background for row cleaner
var rowCleanerProgressBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
rowCleanerProgressBg.x = 0;
rowCleanerProgressBg.y = 50;
rowCleanerProgressBg.scaleX = 0.6;
rowCleanerProgressBg.scaleY = 0.15;
rowCleanerProgressBg.tint = 0x000000;
rowCleanerProgressBg.alpha = 0.3;
rowCleanerButton.addChild(rowCleanerProgressBg);
// Progress bar fill for row cleaner
var rowCleanerProgressFill = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0.5
});
rowCleanerProgressFill.x = -60;
rowCleanerProgressFill.y = 50;
rowCleanerProgressFill.scaleX = 0;
rowCleanerProgressFill.scaleY = 0.15;
rowCleanerProgressFill.tint = 0x00FF00;
rowCleanerButton.addChild(rowCleanerProgressFill);
rowCleanerButton.progressFill = rowCleanerProgressFill;
var rowCleanerText = new Text2('2000', {
size: 40,
fill: 0xFFFFFF
});
rowCleanerText.anchor.set(0.5, 0.5);
rowCleanerText.x = 0;
rowCleanerText.y = 10;
rowCleanerButton.addChild(rowCleanerText);
var rowCleanerDesc = new Text2('Clear Row', {
size: 30,
fill: 0xFFFFFF
});
rowCleanerDesc.anchor.set(0.5, 0.5);
rowCleanerDesc.x = 0;
rowCleanerDesc.y = 40;
rowCleanerButton.addChild(rowCleanerDesc);
rowCleanerButton.x = 2048 / 2 - 200;
rowCleanerButton.y = powerUpStartY;
// Color Changer Button
colorChangerButton = new Container();
var colorChangerBg = LK.getAsset('powerupButton', {
anchorX: 0.5,
anchorY: 0.5
});
colorChangerBg.x = 0;
colorChangerBg.y = 0;
colorChangerBg.tint = 0xF9CA24;
colorChangerButton.addChild(colorChangerBg);
var colorChangerIcon = LK.getAsset('colorChanger', {
anchorX: 0.5,
anchorY: 0.5
});
colorChangerIcon.x = 0;
colorChangerIcon.y = -30;
colorChangerButton.addChild(colorChangerIcon);
// Progress bar background for color changer
var colorChangerProgressBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
colorChangerProgressBg.x = 0;
colorChangerProgressBg.y = 50;
colorChangerProgressBg.scaleX = 0.6;
colorChangerProgressBg.scaleY = 0.15;
colorChangerProgressBg.tint = 0x000000;
colorChangerProgressBg.alpha = 0.3;
colorChangerButton.addChild(colorChangerProgressBg);
// Progress bar fill for color changer
var colorChangerProgressFill = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0.5
});
colorChangerProgressFill.x = -60;
colorChangerProgressFill.y = 50;
colorChangerProgressFill.scaleX = 0;
colorChangerProgressFill.scaleY = 0.15;
colorChangerProgressFill.tint = 0x00FF00;
colorChangerButton.addChild(colorChangerProgressFill);
colorChangerButton.progressFill = colorChangerProgressFill;
var colorChangerText = new Text2('500', {
size: 40,
fill: 0xFFFFFF
});
colorChangerText.anchor.set(0.5, 0.5);
colorChangerText.x = 0;
colorChangerText.y = 10;
colorChangerButton.addChild(colorChangerText);
var colorChangerDesc = new Text2('Change Color', {
size: 30,
fill: 0xFFFFFF
});
colorChangerDesc.anchor.set(0.5, 0.5);
colorChangerDesc.x = 0;
colorChangerDesc.y = 40;
colorChangerButton.addChild(colorChangerDesc);
colorChangerButton.x = 2048 / 2;
colorChangerButton.y = powerUpStartY;
// Clear All Button
clearAllButton = new Container();
var clearAllBg = LK.getAsset('powerupButton', {
anchorX: 0.5,
anchorY: 0.5
});
clearAllBg.x = 0;
clearAllBg.y = 0;
clearAllBg.tint = 0x9B59B6;
clearAllButton.addChild(clearAllBg);
var clearAllIcon = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
clearAllIcon.scaleX = 0.4;
clearAllIcon.scaleY = 0.4;
clearAllIcon.x = 0;
clearAllIcon.y = -30;
clearAllButton.addChild(clearAllIcon);
// Progress bar background for clear all
var clearAllProgressBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
clearAllProgressBg.x = 0;
clearAllProgressBg.y = 50;
clearAllProgressBg.scaleX = 0.6;
clearAllProgressBg.scaleY = 0.15;
clearAllProgressBg.tint = 0x000000;
clearAllProgressBg.alpha = 0.3;
clearAllButton.addChild(clearAllProgressBg);
// Progress bar fill for clear all
var clearAllProgressFill = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0.5
});
clearAllProgressFill.x = -60;
clearAllProgressFill.y = 50;
clearAllProgressFill.scaleX = 0;
clearAllProgressFill.scaleY = 0.15;
clearAllProgressFill.tint = 0x00FF00;
clearAllButton.addChild(clearAllProgressFill);
clearAllButton.progressFill = clearAllProgressFill;
var clearAllText = new Text2('7000', {
size: 40,
fill: 0xFFFFFF
});
clearAllText.anchor.set(0.5, 0.5);
clearAllText.x = 0;
clearAllText.y = 10;
clearAllButton.addChild(clearAllText);
var clearAllDesc = new Text2('Clear All', {
size: 30,
fill: 0xFFFFFF
});
clearAllDesc.anchor.set(0.5, 0.5);
clearAllDesc.x = 0;
clearAllDesc.y = 40;
clearAllButton.addChild(clearAllDesc);
clearAllButton.x = 2048 / 2 + 200;
clearAllButton.y = powerUpStartY;
// Add click handlers
// Hint Button
var hintButton = new Container();
var hintBg = LK.getAsset('powerupButton', {
anchorX: 0.5,
anchorY: 0.5
});
hintBg.x = 0;
hintBg.y = 0;
hintBg.tint = 0x45B7D1;
hintButton.addChild(hintBg);
var hintIcon = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
hintIcon.x = 0;
hintIcon.y = -30;
hintIcon.scaleX = 0.25;
hintIcon.scaleY = 0.25;
hintIcon.tint = 0xFFFFFF;
hintButton.addChild(hintIcon);
// Progress bar background for hint
var hintProgressBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
hintProgressBg.x = 0;
hintProgressBg.y = 50;
hintProgressBg.scaleX = 0.6;
hintProgressBg.scaleY = 0.15;
hintProgressBg.tint = 0x000000;
hintProgressBg.alpha = 0.3;
hintButton.addChild(hintProgressBg);
// Progress bar fill for hint
var hintProgressFill = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0.5
});
hintProgressFill.x = -60;
hintProgressFill.y = 50;
hintProgressFill.scaleX = 0;
hintProgressFill.scaleY = 0.15;
hintProgressFill.tint = 0x00FF00;
hintButton.addChild(hintProgressFill);
hintButton.progressFill = hintProgressFill;
var hintText = new Text2('4000', {
size: 40,
fill: 0xFFFFFF
});
hintText.anchor.set(0.5, 0.5);
hintText.x = 0;
hintText.y = 10;
hintButton.addChild(hintText);
var hintDesc = new Text2('Hint', {
size: 30,
fill: 0xFFFFFF
});
hintDesc.anchor.set(0.5, 0.5);
hintDesc.x = 0;
hintDesc.y = 40;
hintButton.addChild(hintDesc);
hintButton.x = 2048 / 2 - 400;
hintButton.y = powerUpStartY;
rowCleanerButton.down = function (x, y, obj) {
if (score >= rowCleanerCost) {
selectedPowerUp = 'rowCleaner';
// Visual feedback
tween(rowCleanerBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(rowCleanerBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
colorChangerButton.down = function (x, y, obj) {
if (score >= colorChangerCost) {
selectedPowerUp = 'colorChanger';
// Visual feedback
tween(colorChangerBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(colorChangerBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
// Add hint button click handler
hintButton.down = function (x, y, obj) {
if (score >= 4000) {
score -= 4000;
scoreText.setText('Score: ' + score);
// Find best move
var bestMove = findBestMove();
if (bestMove) {
showHintArrow(bestMove.candy1, bestMove.candy2);
}
// Visual feedback
tween(hintBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(hintBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
// Add clear all button click handler
clearAllButton.down = function (x, y, obj) {
if (score >= clearAllCost) {
// Execute clear all immediately instead of setting selectedPowerUp
score -= clearAllCost;
score += 5000; // Bonus points
scoreText.setText('Score: ' + score);
var allCandies = [];
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y]) {
allCandies.push(gameGrid[x][y]);
gameGrid[x][y] = null;
}
}
}
// Animate all candies removal
for (var i = 0; i < allCandies.length; i++) {
allCandies[i].markForRemoval();
}
// Create spectacular particle effects
for (var i = 0; i < allCandies.length; i++) {
var candy = allCandies[i];
var candyColors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0xf9ca24, 0xf0932b, 0xeb4d4b];
var particleColor = candyColors[candy.candyType - 1] || 0xffffff;
// Create more particles for spectacular effect
var particleCount = Math.floor(Math.random() * 8) + 12;
for (var p = 0; p < particleCount; p++) {
var particle = new Particle(particleColor, candy.x, candy.y);
particles.push(particle);
game.addChild(particle);
}
}
// Screen flash effect
LK.effects.flashScreen(0xFFFFFF, 500);
// Remove candies and refill grid
LK.setTimeout(function () {
for (var i = 0; i < allCandies.length; i++) {
allCandies[i].destroy();
}
applyGravity();
}, 250);
// Visual feedback
tween(clearAllBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(clearAllBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
game.addChild(hintButton);
game.addChild(rowCleanerButton);
game.addChild(colorChangerButton);
game.addChild(clearAllButton);
gameElements.push(hintButton);
gameElements.push(rowCleanerButton);
gameElements.push(colorChangerButton);
gameElements.push(clearAllButton);
}
// Menu button functionality removed
// Coffee cup will be created when game starts
// Initialize sound settings from storage
try {
var storedSoundSetting = storage.soundEnabled;
if (storedSoundSetting === null || storedSoundSetting === undefined) {
soundEnabled = true;
storage.soundEnabled = soundEnabled;
} else {
soundEnabled = storedSoundSetting;
}
} catch (e) {
// Fallback if storage is not available
soundEnabled = true;
}
// Start background music if sound is enabled
if (soundEnabled) {
LK.playMusic('rahat');
}
// Show main menu instead of initializing game immediately
showMainMenu();
game.update = function () {
// Update menu elements when in menu state
if (gameState === 'menu' && mainMenu) {
mainMenu.update();
}
// Update particles during game
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].update();
}
// Combo texts are automatically managed by their own animations
// No manual update needed as they self-destruct
// Background is now static image - no animation needed
// Update power-up button states and progress bars based on score
if (gameState === 'game' && hintButton && rowCleanerButton && colorChangerButton && clearAllButton) {
// Update hint button and progress bar
var hintProgress = Math.min(score / hintCost, 1);
if (hintButton.progressFill) {
tween(hintButton.progressFill, {
scaleX: hintProgress * 0.6
}, {
duration: 200,
easing: tween.easeOut
});
// Change color based on availability
var fillColor = score >= hintCost ? 0x00FF00 : 0xFFAA00;
tween(hintButton.progressFill, {
tint: fillColor
}, {
duration: 200
});
}
if (score >= hintCost) {
hintButton.alpha = 1;
} else {
hintButton.alpha = 0.8;
}
// Update row cleaner button and progress bar
var rowCleanerProgress = Math.min(score / rowCleanerCost, 1);
if (rowCleanerButton.progressFill) {
tween(rowCleanerButton.progressFill, {
scaleX: rowCleanerProgress * 0.6
}, {
duration: 200,
easing: tween.easeOut
});
// Change color based on availability
var fillColor = score >= rowCleanerCost ? 0x00FF00 : 0xFFAA00;
tween(rowCleanerButton.progressFill, {
tint: fillColor
}, {
duration: 200
});
}
if (score >= rowCleanerCost) {
rowCleanerButton.alpha = 1;
} else {
rowCleanerButton.alpha = 0.8;
}
// Update color changer button and progress bar
var colorChangerProgress = Math.min(score / colorChangerCost, 1);
if (colorChangerButton.progressFill) {
tween(colorChangerButton.progressFill, {
scaleX: colorChangerProgress * 0.6
}, {
duration: 200,
easing: tween.easeOut
});
// Change color based on availability
var fillColor = score >= colorChangerCost ? 0x00FF00 : 0xFFAA00;
tween(colorChangerButton.progressFill, {
tint: fillColor
}, {
duration: 200
});
}
if (score >= colorChangerCost) {
colorChangerButton.alpha = 1;
} else {
colorChangerButton.alpha = 0.8;
}
// Update clear all button and progress bar
var clearAllProgress = Math.min(score / clearAllCost, 1);
if (clearAllButton.progressFill) {
tween(clearAllButton.progressFill, {
scaleX: clearAllProgress * 0.6
}, {
duration: 200,
easing: tween.easeOut
});
// Change color based on availability
var fillColor = score >= clearAllCost ? 0x00FF00 : 0xFFAA00;
tween(clearAllButton.progressFill, {
tint: fillColor
}, {
duration: 200
});
}
if (score >= clearAllCost) {
clearAllButton.alpha = 1;
} else {
clearAllButton.alpha = 0.8;
}
}
// Only run game physics during game state
if (gameState === 'game') {
// Add sparkle effects to random candies
if (LK.ticks % 180 === 0) {
// Every 3 seconds at 60fps
// Pick a random candy to sparkle
var validCandies = [];
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y] && !gameGrid[x][y].isAnimating && !gameGrid[x][y].isMatched) {
validCandies.push(gameGrid[x][y]);
}
}
}
if (validCandies.length > 0) {
var randomCandy = validCandies[Math.floor(Math.random() * validCandies.length)];
randomCandy.sparkle();
}
}
// Continuously check for physics updates when not processing moves
if (!isProcessing && animatingCandies === 0) {
// Check if any candies need to fall due to physics
var needsPhysicsUpdate = false;
for (var x = 0; x < GRID_SIZE && !needsPhysicsUpdate; x++) {
// Check entire column from bottom to top for any candy that should fall
for (var y = GRID_SIZE - 1; y >= 0; y--) {
if (gameGrid[x][y] === null) {
// Found empty space, check if there's a candy above it
for (var checkY = y - 1; checkY >= 0; checkY--) {
if (gameGrid[x][checkY] !== null) {
needsPhysicsUpdate = true;
break;
}
}
if (needsPhysicsUpdate) break;
}
}
}
if (needsPhysicsUpdate) {
applyGravity();
}
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Candy = Container.expand(function (type) {
var self = Container.call(this);
self.candyType = type || 1;
self.gridX = 0;
self.gridY = 0;
self.isAnimating = false;
self.isMatched = false;
self.idleAnimationActive = false;
self.bobOffset = Math.random() * Math.PI * 2; // Random starting phase for bobbing
self.sparkleTimer = Math.random() * 120; // Random timing for sparkle effect
var candyGraphics = self.attachAsset('candy' + self.candyType, {
anchorX: 0.5,
anchorY: 0.5
});
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = GRID_START_X + gridX * CELL_SIZE + CELL_SIZE / 2;
self.y = GRID_START_Y + gridY * CELL_SIZE + CELL_SIZE / 2;
};
self.animateToPosition = function (targetX, targetY, callback) {
self.isAnimating = true;
self.stopIdleAnimation(); // Stop idle animation during move animation
tween(self, {
x: targetX,
y: targetY
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
if (callback) callback();
// Restart idle animation after move completes
LK.setTimeout(function () {
if (!self.isMatched) {
self.startIdleAnimation();
}
}, 200);
}
});
};
self.animateFall = function (newGridY, callback) {
var targetY = GRID_START_Y + newGridY * CELL_SIZE + CELL_SIZE / 2;
self.gridY = newGridY;
self.stopIdleAnimation(); // Stop idle animation during fall
self.animateToPosition(self.x, targetY, function () {
if (callback) callback();
// Restart idle animation after fall completes
LK.setTimeout(function () {
if (!self.isMatched) {
self.startIdleAnimation();
}
}, 300);
});
};
self.startIdleAnimation = function () {
if (self.idleAnimationActive || self.isAnimating || self.isMatched) return;
self.idleAnimationActive = true;
// Gentle bobbing animation
function bobAnimation() {
if (!self.idleAnimationActive || self.isAnimating || self.isMatched) return;
tween(self, {
y: self.y - 8
}, {
duration: 1500 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!self.idleAnimationActive || self.isAnimating || self.isMatched) return;
tween(self, {
y: self.y + 8
}, {
duration: 1500 + Math.random() * 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.idleAnimationActive && !self.isAnimating && !self.isMatched) {
bobAnimation();
}
}
});
}
});
}
// Start bobbing with a random delay
LK.setTimeout(function () {
if (self.idleAnimationActive && !self.isAnimating && !self.isMatched) {
bobAnimation();
}
}, Math.random() * 3000);
};
self.stopIdleAnimation = function () {
self.idleAnimationActive = false;
tween.stop(self, {
y: true
}); // Stop any y-position tweens
};
self.sparkle = function () {
if (self.isAnimating || self.isMatched) return;
// Create sparkle effect with scale and alpha animation
tween(candyGraphics, {
scaleX: 1.15,
scaleY: 1.15,
alpha: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(candyGraphics, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
};
self.markForRemoval = function () {
self.isMatched = true;
self.stopIdleAnimation(); // Stop idle animations when marked for removal
tween(self, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 200,
easing: tween.easeIn
});
};
return self;
});
var ComboText = Container.expand(function (message, x, y, color) {
var self = Container.call(this);
self.x = x;
self.y = y;
var comboTextDisplay = new Text2(message, {
size: 120,
fill: color || 0xFFFFFF
});
comboTextDisplay.anchor.set(0.5, 0.5);
self.addChild(comboTextDisplay);
// Initial state
self.alpha = 0;
self.scaleX = 0.5;
self.scaleY = 0.5;
// Animate in
tween(self, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Hold for a moment
LK.setTimeout(function () {
// Animate out
tween(self, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5,
y: self.y - 100
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.destroy();
var index = comboTexts.indexOf(self);
if (index > -1) {
comboTexts.splice(index, 1);
}
}
});
}, 800);
}
});
return self;
});
var FloatingParticle = Container.expand(function (color, size) {
var self = Container.call(this);
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.tint = color;
particleGraphics.scaleX = size;
particleGraphics.scaleY = size;
self.speed = Math.random() * 2 + 1;
self.direction = Math.random() * Math.PI * 2;
self.bobOffset = Math.random() * Math.PI * 2;
self.bobSpeed = Math.random() * 0.02 + 0.01;
self.update = function () {
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed + Math.sin(LK.ticks * self.bobSpeed + self.bobOffset) * 0.5;
// Wrap around screen
if (self.x < -50) self.x = 2098;
if (self.x > 2098) self.x = -50;
if (self.y < -50) self.y = 2782;
if (self.y > 2782) self.y = -50;
// Gentle rotation
self.rotation += 0.01;
};
return self;
});
var HowToPlayScreen = Container.expand(function () {
var self = Container.call(this);
// Background
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
background.x = 2048 / 2;
background.y = 2732 / 2;
background.scaleX = 12;
background.scaleY = 16;
background.alpha = 0.9;
background.tint = 0x000000;
self.addChild(background);
// Title
var titleText = new Text2('HOW TO PLAY', {
size: 120,
fill: 0xFF69B4
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 400;
self.addChild(titleText);
// Instructions
var instructions = ['1. Swap adjacent candies to create matches', '2. Match 3-6 connected candies of same type', '3. Candies can connect horizontally & vertically', '4. Scoring: 3 blocks=50, 4=150, 5=200, 6=300', '5. Create combos for bonus points!'];
for (var i = 0; i < instructions.length; i++) {
var instructionText = new Text2(instructions[i], {
size: 55,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 2048 / 2;
instructionText.y = 700 + i * 140;
self.addChild(instructionText);
}
// Back button
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
backButton.x = 2048 / 2;
backButton.y = 2200;
backButton.scaleX = 1.5;
backButton.scaleY = 0.6;
backButton.tint = 0xEB4D4B;
self.addChild(backButton);
var backButtonText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backButtonText.anchor.set(0.5, 0.5);
backButtonText.x = backButton.x;
backButtonText.y = backButton.y;
self.addChild(backButtonText);
backButton.down = function (x, y, obj) {
// Button press animation
tween(backButton, {
scaleX: 1.3,
scaleY: 0.5
}, {
duration: 100,
onFinish: function onFinish() {
tween(backButton, {
scaleX: 1.5,
scaleY: 0.6
}, {
duration: 100
});
}
});
// Go back to main menu
if (typeof showMainMenu === 'function') {
showMainMenu();
}
};
return self;
});
var MainMenu = Container.expand(function () {
var self = Container.call(this);
// Create gradient background overlay
var gradientOverlay = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
gradientOverlay.x = 2048 / 2;
gradientOverlay.y = 2732 / 2;
gradientOverlay.scaleX = 12;
gradientOverlay.scaleY = 16;
gradientOverlay.alpha = 0.1;
gradientOverlay.tint = 0x000000;
self.addChild(gradientOverlay);
// Add menu decorations
var decorations = new MenuDecoration();
self.addChild(decorations);
// Create floating background particles
var particles = [];
for (var i = 0; i < 20; i++) {
var colors = [0xFF69B4, 0x4ECDC4, 0xF9CA24, 0xEB4D4B, 0x45B7D1, 0xF0932B];
var color = colors[Math.floor(Math.random() * colors.length)];
var size = Math.random() * 0.8 + 0.3;
var particle = new FloatingParticle(color, size);
particle.x = Math.random() * 2048;
particle.y = Math.random() * 2732;
particle.alpha = 0.6;
particles.push(particle);
self.addChild(particle);
}
// Game title with enhanced styling
var titleText = new Text2('🍭 CANDY MATCH 🍭', {
size: 200,
fill: 0xFF69B4
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 500;
self.addChild(titleText);
// Add title shadow effect
var titleShadow = new Text2('🍭 CANDY MATCH 🍭', {
size: 200,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 2048 / 2 + 5;
titleShadow.y = 505;
titleShadow.alpha = 0.3;
self.addChildAt(titleShadow, self.getChildIndex(titleText));
// Subtitle
var subtitleText = new Text2('✨ Match candies and create sweet combos! ✨', {
size: 60,
fill: 0xFFFFFF
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 2048 / 2;
subtitleText.y = 650;
subtitleText.alpha = 0.8;
self.addChild(subtitleText);
// Enhanced start button with glow effect
var startButtonGlow = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
startButtonGlow.x = 2048 / 2;
startButtonGlow.y = 1200;
startButtonGlow.scaleX = 3.4;
startButtonGlow.scaleY = 1.4;
startButtonGlow.tint = 0x4ECDC4;
startButtonGlow.alpha = 0.3;
self.addChild(startButtonGlow);
var startButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
startButton.x = 2048 / 2;
startButton.y = 1200;
startButton.scaleX = 3.2;
startButton.scaleY = 1.3;
startButton.tint = 0x4ECDC4;
self.addChild(startButton);
var startButtonText = new Text2('🚀 START GAME', {
size: 90,
fill: 0xFFFFFF
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = startButton.x;
startButtonText.y = startButton.y;
self.addChild(startButtonText);
// Enhanced how to play button with glow effect
var howToPlayButtonGlow = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
howToPlayButtonGlow.x = 2048 / 2;
howToPlayButtonGlow.y = 1500;
howToPlayButtonGlow.scaleX = 3.2;
howToPlayButtonGlow.scaleY = 1.3;
howToPlayButtonGlow.tint = 0xF9CA24;
howToPlayButtonGlow.alpha = 0.3;
self.addChild(howToPlayButtonGlow);
var howToPlayButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
howToPlayButton.x = 2048 / 2;
howToPlayButton.y = 1500;
howToPlayButton.scaleX = 3.0;
howToPlayButton.scaleY = 1.2;
howToPlayButton.tint = 0xF9CA24;
self.addChild(howToPlayButton);
var howToPlayButtonText = new Text2('❓ HOW TO PLAY', {
size: 90,
fill: 0xFFFFFF
});
howToPlayButtonText.anchor.set(0.5, 0.5);
howToPlayButtonText.x = howToPlayButton.x;
howToPlayButtonText.y = howToPlayButton.y;
self.addChild(howToPlayButtonText);
// Sound control buttons
var soundButtonGlow = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
soundButtonGlow.x = 2048 / 2;
soundButtonGlow.y = 1800;
soundButtonGlow.scaleX = 3.0;
soundButtonGlow.scaleY = 1.2;
soundButtonGlow.tint = 0xEB4D4B;
soundButtonGlow.alpha = 0.3;
self.addChild(soundButtonGlow);
var soundButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
soundButton.x = 2048 / 2;
soundButton.y = 1800;
soundButton.scaleX = 2.8;
soundButton.scaleY = 1.1;
soundButton.tint = 0xEB4D4B;
self.addChild(soundButton);
// Get current sound state from storage
var soundEnabled = storage.soundEnabled;
if (soundEnabled === null || soundEnabled === undefined) {
soundEnabled = true;
storage.soundEnabled = soundEnabled;
}
var soundButtonText = new Text2(soundEnabled ? '🔊 SOUND ON' : '🔇 SOUND OFF', {
size: 90,
fill: 0xFFFFFF
});
soundButtonText.anchor.set(0.5, 0.5);
soundButtonText.x = soundButton.x;
soundButtonText.y = soundButton.y;
self.addChild(soundButtonText);
// Apply current sound settings
if (!soundEnabled) {
LK.stopMusic();
}
// Animate title with rainbow effect
self.animateTitle = function () {
// Scale animation
tween(titleText, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
scaleX: 1,
scaleY: 1
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.animateTitle();
}
});
}
});
// Color cycle animation
var colors = [0xFF69B4, 0x4ECDC4, 0xF9CA24, 0xEB4D4B, 0x45B7D1];
var currentColorIndex = 0;
function cycleColors() {
currentColorIndex = (currentColorIndex + 1) % colors.length;
tween(titleText, {
tint: colors[currentColorIndex]
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
LK.setTimeout(cycleColors, 500);
}
});
}
cycleColors();
};
// Animate subtitle
function animateSubtitle() {
tween(subtitleText, {
alpha: 1,
y: subtitleText.y - 10
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(subtitleText, {
alpha: 0.6,
y: 650
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: animateSubtitle
});
}
});
}
animateSubtitle();
// Animate button glows
function animateGlows() {
tween(startButtonGlow, {
alpha: 0.6,
scaleX: 3.6,
scaleY: 1.5
}, {
duration: 1200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(startButtonGlow, {
alpha: 0.3,
scaleX: 3.4,
scaleY: 1.4
}, {
duration: 1200,
easing: tween.easeInOut
});
}
});
tween(howToPlayButtonGlow, {
alpha: 0.6,
scaleX: 3.4,
scaleY: 1.4
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(howToPlayButtonGlow, {
alpha: 0.3,
scaleX: 3.2,
scaleY: 1.3
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: animateGlows
});
}
});
}
animateGlows();
// Update particles
self.update = function () {
for (var i = 0; i < particles.length; i++) {
particles[i].update();
}
};
// Enhanced button click handlers
startButton.down = function (x, y, obj) {
// Button press animation with enhanced effects
tween(startButton, {
scaleX: 2.8,
scaleY: 1.0
}, {
duration: 80,
onFinish: function onFinish() {
tween(startButton, {
scaleX: 3.4,
scaleY: 1.4
}, {
duration: 80,
onFinish: function onFinish() {
tween(startButton, {
scaleX: 3.2,
scaleY: 1.3
}, {
duration: 80
});
}
});
}
});
// Animate button glow on press
tween(startButtonGlow, {
alpha: 0.8,
scaleX: 3.8,
scaleY: 1.7
}, {
duration: 150,
onFinish: function onFinish() {
tween(startButtonGlow, {
alpha: 0.3,
scaleX: 3.4,
scaleY: 1.4
}, {
duration: 200
});
}
});
// Start the game
startGame();
};
howToPlayButton.down = function (x, y, obj) {
// Button press animation with enhanced effects
tween(howToPlayButton, {
scaleX: 2.6,
scaleY: 0.9
}, {
duration: 80,
onFinish: function onFinish() {
tween(howToPlayButton, {
scaleX: 3.2,
scaleY: 1.3
}, {
duration: 80,
onFinish: function onFinish() {
tween(howToPlayButton, {
scaleX: 3.0,
scaleY: 1.2
}, {
duration: 80
});
}
});
}
});
// Animate button glow on press
tween(howToPlayButtonGlow, {
alpha: 0.8,
scaleX: 3.6,
scaleY: 1.6
}, {
duration: 150,
onFinish: function onFinish() {
tween(howToPlayButtonGlow, {
alpha: 0.3,
scaleX: 3.2,
scaleY: 1.3
}, {
duration: 200
});
}
});
// Show how to play
if (typeof showHowToPlay === 'function') {
showHowToPlay();
}
};
soundButton.down = function (x, y, obj) {
// Button press animation with enhanced effects
tween(soundButton, {
scaleX: 2.4,
scaleY: 0.9
}, {
duration: 80,
onFinish: function onFinish() {
tween(soundButton, {
scaleX: 3.0,
scaleY: 1.2
}, {
duration: 80,
onFinish: function onFinish() {
tween(soundButton, {
scaleX: 2.8,
scaleY: 1.1
}, {
duration: 80
});
}
});
}
});
// Animate button glow on press
tween(soundButtonGlow, {
alpha: 0.8,
scaleX: 3.4,
scaleY: 1.5
}, {
duration: 150,
onFinish: function onFinish() {
tween(soundButtonGlow, {
alpha: 0.3,
scaleX: 3.0,
scaleY: 1.2
}, {
duration: 200
});
}
});
// Toggle sound
soundEnabled = !soundEnabled;
storage.soundEnabled = soundEnabled;
soundButtonText.setText(soundEnabled ? '🔊 SOUND ON' : '🔇 SOUND OFF');
// Apply sound settings
if (soundEnabled) {
LK.playMusic('rahat');
} else {
LK.stopMusic();
}
};
return self;
});
var MenuDecoration = Container.expand(function () {
var self = Container.call(this);
// Create candy decorations around the screen
var decorationCount = 12;
var _loop = function _loop() {
candyType = Math.floor(Math.random() * 6) + 1;
decoration = LK.getAsset('candy' + candyType, {
anchorX: 0.5,
anchorY: 0.5
});
angle = i / decorationCount * Math.PI * 2;
radius = 800 + Math.random() * 200;
decoration.x = 2048 / 2 + Math.cos(angle) * radius;
decoration.y = 2732 / 2 + Math.sin(angle) * radius;
decoration.alpha = 0.3;
decoration.scaleX = 0.8 + Math.random() * 0.4;
decoration.scaleY = decoration.scaleX;
self.addChild(decoration);
// Animate decorations
animDuration = 3000 + Math.random() * 2000;
targetScale = decoration.scaleX + 0.2;
function animateDecoration(dec) {
tween(dec, {
rotation: dec.rotation + Math.PI * 2,
scaleX: targetScale,
scaleY: targetScale
}, {
duration: animDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
targetScale = 0.8 + Math.random() * 0.4;
animDuration = 3000 + Math.random() * 2000;
animateDecoration(dec);
}
});
}
animateDecoration(decoration);
},
candyType,
decoration,
angle,
radius,
animDuration,
targetScale;
for (var i = 0; i < decorationCount; i++) {
_loop();
}
return self;
});
var Particle = Container.expand(function (color, startX, startY) {
var self = Container.call(this);
self.x = startX;
self.y = startY;
self.velocityX = (Math.random() - 0.5) * 400;
self.velocityY = (Math.random() - 0.5) * 400;
self.gravity = 300;
self.life = 1.0;
self.maxLife = 1.0;
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.tint = color;
particleGraphics.scaleX = Math.random() * 0.8 + 0.4;
particleGraphics.scaleY = particleGraphics.scaleX;
self.update = function () {
self.x += self.velocityX * (1 / 60);
self.y += self.velocityY * (1 / 60);
self.velocityY += self.gravity * (1 / 60);
self.life -= 1 / 60 * 2;
var alpha = self.life / self.maxLife;
self.alpha = alpha;
self.scaleX = alpha * particleGraphics.scaleX;
self.scaleY = alpha * particleGraphics.scaleY;
if (self.life <= 0) {
self.destroy();
var index = particles.indexOf(self);
if (index > -1) {
particles.splice(index, 1);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xff69b4
});
/****
* Game Code
****/
function _createForOfIteratorHelper(r, e) {
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (!t) {
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {
t && (r = t);
var _n = 0,
F = function F() {};
return {
s: F,
n: function n() {
return _n >= r.length ? {
done: !0
} : {
done: !1,
value: r[_n++]
};
},
e: function e(r) {
throw r;
},
f: F
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var o,
a = !0,
u = !1;
return {
s: function s() {
t = t.call(r);
},
n: function n() {
var r = t.next();
return a = r.done, r;
},
e: function e(r) {
u = !0, o = r;
},
f: function f() {
try {
a || null == t["return"] || t["return"]();
} finally {
if (u) throw o;
}
}
};
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
var GRID_SIZE = 8;
var CELL_SIZE = 200;
var GRID_START_X = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var GRID_START_Y = 400;
var gameGrid = [];
var candyTypes = [1, 2, 3, 4, 5, 6];
var selectedCandy = null;
var isProcessing = false;
var movesLeft = 999999;
var score = 0;
var animatingCandies = 0;
var particles = [];
var comboTexts = [];
var currentCombo = 0;
// Power-up variables
var rowCleanerButton = null;
var colorChangerButton = null;
var clearAllButton = null;
var hintButton = null;
var rowCleanerCost = 2000;
var colorChangerCost = 500;
var clearAllCost = 7000;
var hintCost = 4000;
var selectedPowerUp = null; // 'rowCleaner', 'colorChanger', or 'clearAll'
var hintArrows = []; // Array to store hint arrow elements
// Game state variables
var gameState = 'menu'; // 'menu', 'game', 'howtoplay'
var mainMenu = null;
var howToPlayScreen = null;
var gameElements = [];
// Sound control variables
var soundEnabled = true;
// Initialize grid array
for (var x = 0; x < GRID_SIZE; x++) {
gameGrid[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
gameGrid[x][y] = null;
}
}
// Menu control functions
function startGame() {
gameState = 'game';
hideAllScreens();
initializeGameElements();
}
function showMainMenu() {
gameState = 'menu';
// Clear game grid and destroy candies
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y]) {
gameGrid[x][y].destroy();
gameGrid[x][y] = null;
}
}
}
// Clear particles
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].destroy();
}
particles = [];
// Clear combo texts
for (var i = comboTexts.length - 1; i >= 0; i--) {
comboTexts[i].destroy();
}
comboTexts = [];
// Reset game state
selectedCandy = null;
isProcessing = false;
animatingCandies = 0;
currentCombo = 0;
hideAllScreens();
if (!mainMenu) {
mainMenu = new MainMenu();
game.addChild(mainMenu);
} else {
// Recursively re-enable all menu interactions
var _enableMenuInteractions = function enableMenuInteractions(container) {
container.interactive = true;
if (container.children) {
for (var i = 0; i < container.children.length; i++) {
var child = container.children[i];
child.interactive = true;
if (child.children && child.children.length > 0) {
_enableMenuInteractions(child);
}
}
}
};
mainMenu.alpha = 1;
mainMenu.visible = true;
mainMenu.interactive = true;
_enableMenuInteractions(mainMenu);
}
mainMenu.animateTitle();
}
function showHowToPlay() {
gameState = 'howtoplay';
hideAllScreens();
// Completely hide main menu to prevent interactions
if (mainMenu) {
// Recursively disable all child interactions
var _disableMenuInteractions = function disableMenuInteractions(container) {
container.interactive = false;
if (container.children) {
for (var i = 0; i < container.children.length; i++) {
var child = container.children[i];
child.interactive = false;
if (child.down) {
child.down = null;
}
if (child.children && child.children.length > 0) {
_disableMenuInteractions(child);
}
}
}
};
mainMenu.alpha = 0;
mainMenu.visible = false;
mainMenu.interactive = false;
_disableMenuInteractions(mainMenu);
}
if (!howToPlayScreen) {
howToPlayScreen = new HowToPlayScreen();
game.addChild(howToPlayScreen);
// Ensure how-to-play screen is on top
game.setChildIndex(howToPlayScreen, game.children.length - 1);
} else {
howToPlayScreen.alpha = 1;
howToPlayScreen.visible = true;
// Bring to front when showing
game.setChildIndex(howToPlayScreen, game.children.length - 1);
}
}
function hideAllScreens() {
if (mainMenu) {
mainMenu.alpha = 0;
mainMenu.visible = false;
}
if (howToPlayScreen) {
howToPlayScreen.alpha = 0;
howToPlayScreen.visible = false;
}
// Hide game elements (except persistent ones like scoreText and backgroundImage)
for (var i = 0; i < gameElements.length; i++) {
if (gameElements[i] !== scoreText && gameElements[i] !== backgroundImage) {
gameElements[i].alpha = 0;
}
}
}
function initializeGameElements() {
// Clear existing game elements first
for (var i = gameElements.length - 1; i >= 0; i--) {
if (gameElements[i] !== scoreText && gameElements[i] !== backgroundImage) {
gameElements[i].destroy();
gameElements.splice(i, 1);
}
}
// Show persistent game elements
for (var i = 0; i < gameElements.length; i++) {
gameElements[i].alpha = 1;
}
// Grid background removed - candies display directly on background image
// Add 7000 points to initial score
score += 7000;
scoreText.setText('Score: ' + score);
// Create power-up buttons
createPowerUpButtons();
// Clear hint arrows
clearHintArrows();
// Reset game state
score = 0;
currentCombo = 0;
selectedCandy = null;
isProcessing = false;
scoreText.setText('Score: 0');
// Clear existing candies and initialize fresh grid
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y]) {
gameGrid[x][y].destroy();
gameGrid[x][y] = null;
}
}
}
// Initialize the game grid
initializeGrid();
}
// UI Elements
var scoreText = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
gameElements.push(scoreText);
// Create background image
var backgroundImage = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5
});
backgroundImage.x = 2048 / 2;
backgroundImage.y = 2732 / 2;
backgroundImage.scaleX = 2048 / backgroundImage.width;
backgroundImage.scaleY = 2732 / backgroundImage.height;
game.addChild(backgroundImage);
gameElements.push(backgroundImage);
// Grid cells will be created when game starts
function getRandomCandyType() {
return candyTypes[Math.floor(Math.random() * candyTypes.length)];
}
function createCandy(x, y, type) {
if (!type) type = getRandomCandyType();
var candy = new Candy(type);
candy.setGridPosition(x, y);
gameGrid[x][y] = candy;
game.addChild(candy);
// Start idle animations after a short delay to let positioning settle
LK.setTimeout(function () {
if (candy && !candy.isMatched) {
candy.startIdleAnimation();
}
}, 500);
return candy;
}
function initializeGrid() {
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
var type;
var attempts = 0;
do {
type = getRandomCandyType();
attempts++;
} while (attempts < 10 && wouldCreateMatch(x, y, type));
createCandy(x, y, type);
}
}
}
function wouldCreateMatch(x, y, type) {
var horizontalCount = 1;
var verticalCount = 1;
// Check horizontal
for (var i = x - 1; i >= 0 && gameGrid[i][y] && gameGrid[i][y].candyType === type; i--) {
horizontalCount++;
}
for (var i = x + 1; i < GRID_SIZE && gameGrid[i][y] && gameGrid[i][y].candyType === type; i++) {
horizontalCount++;
}
// Check vertical
for (var i = y - 1; i >= 0 && gameGrid[x][i] && gameGrid[x][i].candyType === type; i--) {
verticalCount++;
}
for (var i = y + 1; i < GRID_SIZE && gameGrid[x][i] && gameGrid[x][i].candyType === type; i++) {
verticalCount++;
}
return horizontalCount >= 3 || verticalCount >= 3;
}
function getCandyAt(worldX, worldY) {
var gridX = Math.floor((worldX - GRID_START_X) / CELL_SIZE);
var gridY = Math.floor((worldY - GRID_START_Y) / CELL_SIZE);
if (gridX >= 0 && gridX < GRID_SIZE && gridY >= 0 && gridY < GRID_SIZE) {
return gameGrid[gridX][gridY];
}
return null;
}
function areAdjacent(candy1, candy2) {
var dx = Math.abs(candy1.gridX - candy2.gridX);
var dy = Math.abs(candy1.gridY - candy2.gridY);
return dx === 1 && dy === 0 || dx === 0 && dy === 1;
}
function swapCandies(candy1, candy2) {
var tempX = candy1.gridX;
var tempY = candy1.gridY;
var tempPosX = candy1.x;
var tempPosY = candy1.y;
gameGrid[candy1.gridX][candy1.gridY] = candy2;
gameGrid[candy2.gridX][candy2.gridY] = candy1;
candy1.gridX = candy2.gridX;
candy1.gridY = candy2.gridY;
candy2.gridX = tempX;
candy2.gridY = tempY;
animatingCandies += 2;
// Play boing sound when candies are swapped
LK.getSound('boing').play();
candy1.animateToPosition(candy2.x, candy2.y, function () {
animatingCandies--;
});
candy2.animateToPosition(tempPosX, tempPosY, function () {
animatingCandies--;
});
}
function findMatches() {
var allMatches = [];
var visited = [];
// Initialize visited array
for (var x = 0; x < GRID_SIZE; x++) {
visited[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
visited[x][y] = false;
}
}
// Flood fill function to find all connected candies of same type (max 6 blocks)
function floodFill(startX, startY, candyType, connectedGroup) {
// Check bounds and if already visited
if (startX < 0 || startX >= GRID_SIZE || startY < 0 || startY >= GRID_SIZE) {
return;
}
if (visited[startX][startY]) {
return;
}
var candy = gameGrid[startX][startY];
if (!candy || candy.candyType !== candyType) {
return;
}
// Limit group size to maximum 6 blocks
if (connectedGroup.length >= 6) {
return;
}
// Mark as visited and add to group
visited[startX][startY] = true;
connectedGroup.push(candy);
// Check all 4 adjacent directions (up, down, left, right) only if we haven't reached the limit
if (connectedGroup.length < 6) {
floodFill(startX - 1, startY, candyType, connectedGroup); // left
floodFill(startX + 1, startY, candyType, connectedGroup); // right
floodFill(startX, startY - 1, candyType, connectedGroup); // up
floodFill(startX, startY + 1, candyType, connectedGroup); // down
}
}
// Check each cell for connected groups
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (!visited[x][y] && gameGrid[x][y]) {
var connectedGroup = [];
var candyType = gameGrid[x][y].candyType;
// Find all connected candies of the same type
floodFill(x, y, candyType, connectedGroup);
// If we found 3 or more connected candies, add this group to allMatches
if (connectedGroup.length >= 3) {
allMatches.push(connectedGroup);
}
}
}
}
return allMatches;
}
function removeMatches(matchGroups) {
if (matchGroups.length === 0) return;
LK.getSound('match').play();
// Flatten all matches for candy removal and particle effects
var allMatches = [];
for (var g = 0; g < matchGroups.length; g++) {
for (var c = 0; c < matchGroups[g].length; c++) {
allMatches.push(matchGroups[g][c]);
}
}
for (var i = 0; i < allMatches.length; i++) {
var candy = allMatches[i];
// Create particle explosion for each candy
var candyColors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0xf9ca24, 0xf0932b, 0xeb4d4b];
var particleColor = candyColors[candy.candyType - 1] || 0xffffff;
// Create 8-12 particles per candy
var particleCount = Math.floor(Math.random() * 5) + 8;
for (var p = 0; p < particleCount; p++) {
var particle = new Particle(particleColor, candy.x, candy.y);
particles.push(particle);
game.addChild(particle);
}
candy.markForRemoval();
gameGrid[candy.gridX][candy.gridY] = null;
}
// Update score based on each group's size with new scoring system
var oldScore = score;
var largestGroupSize = 0;
for (var g = 0; g < matchGroups.length; g++) {
var groupSize = Math.min(matchGroups[g].length, 6); // Cap at 6 blocks maximum
if (groupSize > largestGroupSize) {
largestGroupSize = groupSize;
}
if (groupSize === 3) {
score += 50;
} else if (groupSize === 4) {
score += 150;
} else if (groupSize === 5) {
score += 200;
} else if (groupSize >= 6) {
score += 300;
}
}
// Check for score milestones and celebrate
var milestones = [1000, 5000, 10000, 25000, 50000];
for (var m = 0; m < milestones.length; m++) {
if (oldScore < milestones[m] && score >= milestones[m]) {
// Milestone reached! Create celebration effect
var milestoneText = new Text2('MILESTONE!\n' + milestones[m] + ' POINTS!', {
size: 80,
fill: 0xFF69B4
});
milestoneText.anchor.set(0.5, 0.5);
milestoneText.x = 2048 / 2;
milestoneText.y = 400;
milestoneText.alpha = 0;
milestoneText.scaleX = 0.5;
milestoneText.scaleY = 0.5;
game.addChild(milestoneText);
// Animate milestone text
tween(milestoneText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(milestoneText, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5,
y: milestoneText.y - 100
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
milestoneText.destroy();
}
});
}, 1500);
}
});
// Screen flash for milestone
LK.effects.flashScreen(0xFF69B4, 800);
break;
}
}
// Create floating score text for each match group
var scoreIncrease = score - oldScore;
var floatingScoreText = new Text2('+' + scoreIncrease, {
size: 100,
fill: 0xF9CA24
});
floatingScoreText.anchor.set(0.5, 0.5);
// Position above the grid
floatingScoreText.x = 2048 / 2;
floatingScoreText.y = GRID_START_Y - 100;
floatingScoreText.alpha = 0;
// Set initial rotation to be horizontal
floatingScoreText.rotation = 0;
game.addChild(floatingScoreText);
// Animate floating score - first appear, then move towards score display
tween(floatingScoreText, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Convert score text position to game coordinates
var scoreWorldPos = game.toLocal(LK.gui.top.toGlobal({
x: scoreText.x,
y: scoreText.y
}));
tween(floatingScoreText, {
alpha: 0,
x: scoreWorldPos.x,
y: scoreWorldPos.y,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
floatingScoreText.destroy();
}
});
}
});
// Animate main score counter with pulsing effect
tween(scoreText, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xF9CA24
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(scoreText, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut
});
}
});
// Animate score increase with smooth counting
var tempScore = oldScore;
var scoreAnimationTimer = LK.setInterval(function () {
tempScore += scoreIncrease / 30; // 30 steps over 500ms
if (tempScore >= score) {
tempScore = score;
LK.clearInterval(scoreAnimationTimer);
}
scoreText.setText('Score: ' + Math.floor(tempScore));
}, 16); // ~60fps
// Increment combo counter
currentCombo++;
// Play celebration sound for large matches
if (largestGroupSize >= 5) {
// Celebration sound removed
}
// Show combo text based on largest match size and combo count
var comboMessage = "";
var comboColor = 0xFFFFFF;
if (largestGroupSize >= 5) {
comboMessage = "Fantastic!";
comboColor = 0xFF6B6B;
// Screen shake for 5+ matches
var shakeIntensity = 15;
var originalX = game.x;
var originalY = game.y;
tween(game, {
x: originalX + shakeIntensity,
y: originalY + shakeIntensity * 0.5
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: originalX - shakeIntensity,
y: originalY - shakeIntensity * 0.5
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: originalX + shakeIntensity * 0.5,
y: originalY + shakeIntensity
}, {
duration: 50,
onFinish: function onFinish() {
tween(game, {
x: originalX,
y: originalY
}, {
duration: 50
});
}
});
}
});
}
});
} else if (largestGroupSize >= 4) {
comboMessage = "Awesome!";
comboColor = 0x4ECDC4;
// Screen shake for 4+ matches
var shakeIntensity = 10;
var originalX = game.x;
var originalY = game.y;
tween(game, {
x: originalX + shakeIntensity,
y: originalY
}, {
duration: 75,
onFinish: function onFinish() {
tween(game, {
x: originalX - shakeIntensity,
y: originalY
}, {
duration: 75,
onFinish: function onFinish() {
tween(game, {
x: originalX,
y: originalY
}, {
duration: 75
});
}
});
}
});
} else if (currentCombo >= 3) {
comboMessage = "Great Combo!";
comboColor = 0xF9CA24;
// Light screen shake for combo
var shakeIntensity = 5;
var originalX = game.x;
var originalY = game.y;
tween(game, {
x: originalX + shakeIntensity
}, {
duration: 100,
onFinish: function onFinish() {
tween(game, {
x: originalX - shakeIntensity
}, {
duration: 100,
onFinish: function onFinish() {
tween(game, {
x: originalX
}, {
duration: 100
});
}
});
}
});
} else if (currentCombo >= 2) {
comboMessage = "Great!";
comboColor = 0x45B7D1;
}
if (comboMessage !== "") {
var comboX = 2048 / 2;
var comboY = GRID_START_Y + GRID_SIZE * CELL_SIZE / 2;
var comboText = new ComboText(comboMessage, comboX, comboY, comboColor);
comboTexts.push(comboText);
game.addChild(comboText);
}
// Remove candies after animation
LK.setTimeout(function () {
for (var i = 0; i < allMatches.length; i++) {
allMatches[i].destroy();
}
applyGravity();
}, 250);
}
function applyGravity() {
var needsGravity = false;
// Apply gravity physics - make candies fall to fill empty spaces
for (var x = 0; x < GRID_SIZE; x++) {
// Collect all candies in this column that are not null
var candiesInColumn = [];
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y] !== null) {
candiesInColumn.push({
candy: gameGrid[x][y],
originalY: y
});
}
gameGrid[x][y] = null; // Clear the column
}
// Place candies back starting from bottom
var newY = GRID_SIZE - 1;
for (var i = candiesInColumn.length - 1; i >= 0; i--) {
var candyData = candiesInColumn[i];
var candy = candyData.candy;
var originalY = candyData.originalY;
gameGrid[x][newY] = candy;
// Immediately update grid position to prevent overlaps
candy.gridX = x;
candy.gridY = newY;
// Check if candy needs to fall
if (newY !== originalY) {
var targetY = GRID_START_Y + newY * CELL_SIZE + CELL_SIZE / 2;
candy.isAnimating = true;
animatingCandies++;
tween(candy, {
y: targetY
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
candy.isAnimating = false;
animatingCandies--;
}
});
needsGravity = true;
} else {
// Ensure position is correct even if not animating
candy.x = GRID_START_X + x * CELL_SIZE + CELL_SIZE / 2;
candy.y = GRID_START_Y + newY * CELL_SIZE + CELL_SIZE / 2;
}
newY--;
}
// Fill remaining empty spaces at top with new candies
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y] === null) {
var newCandy = createCandy(x, y, getRandomCandyType());
newCandy.y = GRID_START_Y - CELL_SIZE + CELL_SIZE / 2;
newCandy.isAnimating = true;
animatingCandies++;
var targetY = GRID_START_Y + y * CELL_SIZE + CELL_SIZE / 2;
tween(newCandy, {
y: targetY
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
newCandy.isAnimating = false;
animatingCandies--;
}
});
needsGravity = true;
}
}
}
if (needsGravity) {
LK.getSound('cascade').play();
LK.setTimeout(function () {
checkForNewMatches();
}, 400);
}
}
function checkForNewMatches() {
if (animatingCandies > 0) {
LK.setTimeout(checkForNewMatches, 100);
return;
}
var matchGroups = findMatches();
if (matchGroups.length > 0) {
removeMatches(matchGroups);
} else {
isProcessing = false;
currentCombo = 0; // Reset combo counter when no more matches
}
}
function findBestMove() {
var bestMove = null;
var bestScore = 0;
// Check all possible moves
for (var x1 = 0; x1 < GRID_SIZE; x1++) {
for (var y1 = 0; y1 < GRID_SIZE; y1++) {
var candy1 = gameGrid[x1][y1];
if (!candy1) continue;
// Check adjacent positions
var adjacentPositions = [{
x: x1 - 1,
y: y1
},
// left
{
x: x1 + 1,
y: y1
},
// right
{
x: x1,
y: y1 - 1
},
// up
{
x: x1,
y: y1 + 1
} // down
];
for (var i = 0; i < adjacentPositions.length; i++) {
var pos = adjacentPositions[i];
if (pos.x < 0 || pos.x >= GRID_SIZE || pos.y < 0 || pos.y >= GRID_SIZE) continue;
var candy2 = gameGrid[pos.x][pos.y];
if (!candy2) continue;
// Temporarily swap to check for matches
var tempX1 = candy1.gridX,
tempY1 = candy1.gridY;
var tempX2 = candy2.gridX,
tempY2 = candy2.gridY;
gameGrid[tempX1][tempY1] = candy2;
gameGrid[tempX2][tempY2] = candy1;
candy1.gridX = tempX2;
candy1.gridY = tempY2;
candy2.gridX = tempX1;
candy2.gridY = tempY1;
var matchGroups = findMatches();
var moveScore = 0;
for (var g = 0; g < matchGroups.length; g++) {
var groupSize = Math.min(matchGroups[g].length, 6);
if (groupSize === 3) moveScore += 50;else if (groupSize === 4) moveScore += 150;else if (groupSize === 5) moveScore += 200;else if (groupSize >= 6) moveScore += 300;
}
// Revert the swap
gameGrid[tempX1][tempY1] = candy1;
gameGrid[tempX2][tempY2] = candy2;
candy1.gridX = tempX1;
candy1.gridY = tempY1;
candy2.gridX = tempX2;
candy2.gridY = tempY2;
if (moveScore > bestScore) {
bestScore = moveScore;
bestMove = {
candy1: candy1,
candy2: candy2,
score: moveScore
};
}
}
}
}
return bestMove;
}
function showHintArrow(candy1, candy2) {
// Clear existing hint arrows
clearHintArrows();
// Create arrow from candy1 to candy2
var arrow = new Container();
var arrowBody = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0.5
});
arrowBody.scaleX = 0.8;
arrowBody.scaleY = 0.1;
arrowBody.tint = 0xFFFF00;
arrow.addChild(arrowBody);
var arrowHead = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
arrowHead.scaleX = 0.15;
arrowHead.scaleY = 0.15;
arrowHead.tint = 0xFFFF00;
arrowHead.rotation = Math.PI / 4; // 45 degrees
arrow.addChild(arrowHead);
// Position arrow between the two candies
var midX = (candy1.x + candy2.x) / 2;
var midY = (candy1.y + candy2.y) / 2;
arrow.x = midX;
arrow.y = midY;
// Calculate angle from candy1 to candy2
var dx = candy2.x - candy1.x;
var dy = candy2.y - candy1.y;
var angle = Math.atan2(dy, dx);
arrow.rotation = angle;
// Position arrow body and head
arrowBody.x = -80;
arrowHead.x = 80;
// Add pulsing animation
arrow.alpha = 0;
game.addChild(arrow);
hintArrows.push(arrow);
tween(arrow, {
alpha: 1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Pulse animation
function pulseArrow() {
tween(arrow, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(arrow, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (hintArrows.indexOf(arrow) !== -1) {
pulseArrow();
}
}
});
}
});
}
pulseArrow();
}
});
// Auto-hide hint after 5 seconds
LK.setTimeout(function () {
clearHintArrows();
}, 5000);
}
function clearHintArrows() {
for (var i = hintArrows.length - 1; i >= 0; i--) {
hintArrows[i].destroy();
}
hintArrows = [];
}
function checkGameState() {
// Endless game - no win/lose conditions
}
function processMove(candy1, candy2) {
if (isProcessing || !candy1 || !candy2 || candy1 === candy2) return false;
if (!areAdjacent(candy1, candy2)) return false;
isProcessing = true;
// Endless game - no move limit
// Temporarily swap to check for matches
var tempX1 = candy1.gridX,
tempY1 = candy1.gridY;
var tempX2 = candy2.gridX,
tempY2 = candy2.gridY;
gameGrid[tempX1][tempY1] = candy2;
gameGrid[tempX2][tempY2] = candy1;
candy1.gridX = tempX2;
candy1.gridY = tempY2;
candy2.gridX = tempX1;
candy2.gridY = tempY1;
var matchGroups = findMatches();
if (matchGroups.length > 0) {
// Valid move - perform the swap animation
swapCandies(candy2, candy1); // Swap back positions for animation
LK.setTimeout(function () {
removeMatches(matchGroups);
}, 350);
} else {
// Invalid move - revert
gameGrid[tempX1][tempY1] = candy1;
gameGrid[tempX2][tempY2] = candy2;
candy1.gridX = tempX1;
candy1.gridY = tempY1;
candy2.gridX = tempX2;
candy2.gridY = tempY2;
// Animate back to original positions
swapCandies(candy1, candy2);
LK.setTimeout(function () {
isProcessing = false;
}, 350);
// Endless game - no move limit
}
return true;
}
game.down = function (x, y, obj) {
if (gameState !== 'game') return;
if (isProcessing) return;
// Check if a power-up is selected
if (selectedPowerUp) {
var candy = getCandyAt(x, y);
if (!candy) {
selectedPowerUp = null;
return;
}
if (selectedPowerUp === 'rowCleaner' && score >= rowCleanerCost) {
// Clear entire row
score -= rowCleanerCost;
scoreText.setText('Score: ' + score);
var row = candy.gridY;
var clearedCandies = [];
for (var x = 0; x < GRID_SIZE; x++) {
if (gameGrid[x][row]) {
clearedCandies.push(gameGrid[x][row]);
gameGrid[x][row] = null;
}
}
// Animate cleared candies
for (var i = 0; i < clearedCandies.length; i++) {
clearedCandies[i].markForRemoval();
}
LK.setTimeout(function () {
for (var i = 0; i < clearedCandies.length; i++) {
clearedCandies[i].destroy();
}
applyGravity();
}, 250);
selectedPowerUp = null;
return;
}
if (selectedPowerUp === 'colorChanger' && score >= colorChangerCost) {
// Change candy color to random type
score -= colorChangerCost;
scoreText.setText('Score: ' + score);
var newType = getRandomCandyType();
var oldCandy = candy;
var x = oldCandy.gridX;
var y = oldCandy.gridY;
// Remove old candy
oldCandy.destroy();
// Create new candy with different type
var newCandy = createCandy(x, y, newType);
// Flash effect
tween(newCandy, {
alpha: 0.3
}, {
duration: 100,
onFinish: function onFinish() {
tween(newCandy, {
alpha: 1
}, {
duration: 100
});
}
});
selectedPowerUp = null;
return;
}
if (selectedPowerUp === 'clearAll' && score >= clearAllCost) {
// Clear all candies from the grid
score -= clearAllCost;
score += 5000; // Bonus points
scoreText.setText('Score: ' + score);
var allCandies = [];
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y]) {
allCandies.push(gameGrid[x][y]);
gameGrid[x][y] = null;
}
}
}
// Animate all candies removal
for (var i = 0; i < allCandies.length; i++) {
allCandies[i].markForRemoval();
}
// Create spectacular particle effects
for (var i = 0; i < allCandies.length; i++) {
var candy = allCandies[i];
var candyColors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0xf9ca24, 0xf0932b, 0xeb4d4b];
var particleColor = candyColors[candy.candyType - 1] || 0xffffff;
// Create more particles for spectacular effect
var particleCount = Math.floor(Math.random() * 8) + 12;
for (var p = 0; p < particleCount; p++) {
var particle = new Particle(particleColor, candy.x, candy.y);
particles.push(particle);
game.addChild(particle);
}
}
// Screen flash effect
LK.effects.flashScreen(0xFFFFFF, 500);
// Remove candies and refill grid
LK.setTimeout(function () {
for (var i = 0; i < allCandies.length; i++) {
allCandies[i].destroy();
}
applyGravity();
}, 250);
selectedPowerUp = null;
return;
}
selectedPowerUp = null;
return;
}
var candy = getCandyAt(x, y);
if (!candy) return;
if (selectedCandy) {
if (selectedCandy === candy) {
// Deselect same candy
tween(selectedCandy, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
selectedCandy = null;
} else if (areAdjacent(selectedCandy, candy)) {
// Try to make move if candies are adjacent
var success = processMove(selectedCandy, candy);
if (success) {
tween(selectedCandy, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
selectedCandy = null;
}
} else {
// Deselect current candy and select new candy
tween(selectedCandy, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
selectedCandy = candy;
tween(candy, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150
});
}
} else {
// Select candy
selectedCandy = candy;
tween(candy, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150
});
}
};
// Create menu button (hamburger style with candy theme)
var menuButton = null;
function createPowerUpButtons() {
// Calculate position below coffee cup
var coffeeY = GRID_START_Y + GRID_SIZE * CELL_SIZE + 150;
var powerUpStartY = coffeeY + 200;
// Row Cleaner Button
rowCleanerButton = new Container();
var rowCleanerBg = LK.getAsset('powerupButton', {
anchorX: 0.5,
anchorY: 0.5
});
rowCleanerBg.x = 0;
rowCleanerBg.y = 0;
rowCleanerBg.tint = 0xFF6B6B;
rowCleanerButton.addChild(rowCleanerBg);
var rowCleanerIcon = LK.getAsset('rowCleaner', {
anchorX: 0.5,
anchorY: 0.5
});
rowCleanerIcon.x = 0;
rowCleanerIcon.y = -30;
rowCleanerButton.addChild(rowCleanerIcon);
// Progress bar background for row cleaner
var rowCleanerProgressBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
rowCleanerProgressBg.x = 0;
rowCleanerProgressBg.y = 50;
rowCleanerProgressBg.scaleX = 0.6;
rowCleanerProgressBg.scaleY = 0.15;
rowCleanerProgressBg.tint = 0x000000;
rowCleanerProgressBg.alpha = 0.3;
rowCleanerButton.addChild(rowCleanerProgressBg);
// Progress bar fill for row cleaner
var rowCleanerProgressFill = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0.5
});
rowCleanerProgressFill.x = -60;
rowCleanerProgressFill.y = 50;
rowCleanerProgressFill.scaleX = 0;
rowCleanerProgressFill.scaleY = 0.15;
rowCleanerProgressFill.tint = 0x00FF00;
rowCleanerButton.addChild(rowCleanerProgressFill);
rowCleanerButton.progressFill = rowCleanerProgressFill;
var rowCleanerText = new Text2('2000', {
size: 40,
fill: 0xFFFFFF
});
rowCleanerText.anchor.set(0.5, 0.5);
rowCleanerText.x = 0;
rowCleanerText.y = 10;
rowCleanerButton.addChild(rowCleanerText);
var rowCleanerDesc = new Text2('Clear Row', {
size: 30,
fill: 0xFFFFFF
});
rowCleanerDesc.anchor.set(0.5, 0.5);
rowCleanerDesc.x = 0;
rowCleanerDesc.y = 40;
rowCleanerButton.addChild(rowCleanerDesc);
rowCleanerButton.x = 2048 / 2 - 200;
rowCleanerButton.y = powerUpStartY;
// Color Changer Button
colorChangerButton = new Container();
var colorChangerBg = LK.getAsset('powerupButton', {
anchorX: 0.5,
anchorY: 0.5
});
colorChangerBg.x = 0;
colorChangerBg.y = 0;
colorChangerBg.tint = 0xF9CA24;
colorChangerButton.addChild(colorChangerBg);
var colorChangerIcon = LK.getAsset('colorChanger', {
anchorX: 0.5,
anchorY: 0.5
});
colorChangerIcon.x = 0;
colorChangerIcon.y = -30;
colorChangerButton.addChild(colorChangerIcon);
// Progress bar background for color changer
var colorChangerProgressBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
colorChangerProgressBg.x = 0;
colorChangerProgressBg.y = 50;
colorChangerProgressBg.scaleX = 0.6;
colorChangerProgressBg.scaleY = 0.15;
colorChangerProgressBg.tint = 0x000000;
colorChangerProgressBg.alpha = 0.3;
colorChangerButton.addChild(colorChangerProgressBg);
// Progress bar fill for color changer
var colorChangerProgressFill = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0.5
});
colorChangerProgressFill.x = -60;
colorChangerProgressFill.y = 50;
colorChangerProgressFill.scaleX = 0;
colorChangerProgressFill.scaleY = 0.15;
colorChangerProgressFill.tint = 0x00FF00;
colorChangerButton.addChild(colorChangerProgressFill);
colorChangerButton.progressFill = colorChangerProgressFill;
var colorChangerText = new Text2('500', {
size: 40,
fill: 0xFFFFFF
});
colorChangerText.anchor.set(0.5, 0.5);
colorChangerText.x = 0;
colorChangerText.y = 10;
colorChangerButton.addChild(colorChangerText);
var colorChangerDesc = new Text2('Change Color', {
size: 30,
fill: 0xFFFFFF
});
colorChangerDesc.anchor.set(0.5, 0.5);
colorChangerDesc.x = 0;
colorChangerDesc.y = 40;
colorChangerButton.addChild(colorChangerDesc);
colorChangerButton.x = 2048 / 2;
colorChangerButton.y = powerUpStartY;
// Clear All Button
clearAllButton = new Container();
var clearAllBg = LK.getAsset('powerupButton', {
anchorX: 0.5,
anchorY: 0.5
});
clearAllBg.x = 0;
clearAllBg.y = 0;
clearAllBg.tint = 0x9B59B6;
clearAllButton.addChild(clearAllBg);
var clearAllIcon = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
clearAllIcon.scaleX = 0.4;
clearAllIcon.scaleY = 0.4;
clearAllIcon.x = 0;
clearAllIcon.y = -30;
clearAllButton.addChild(clearAllIcon);
// Progress bar background for clear all
var clearAllProgressBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
clearAllProgressBg.x = 0;
clearAllProgressBg.y = 50;
clearAllProgressBg.scaleX = 0.6;
clearAllProgressBg.scaleY = 0.15;
clearAllProgressBg.tint = 0x000000;
clearAllProgressBg.alpha = 0.3;
clearAllButton.addChild(clearAllProgressBg);
// Progress bar fill for clear all
var clearAllProgressFill = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0.5
});
clearAllProgressFill.x = -60;
clearAllProgressFill.y = 50;
clearAllProgressFill.scaleX = 0;
clearAllProgressFill.scaleY = 0.15;
clearAllProgressFill.tint = 0x00FF00;
clearAllButton.addChild(clearAllProgressFill);
clearAllButton.progressFill = clearAllProgressFill;
var clearAllText = new Text2('7000', {
size: 40,
fill: 0xFFFFFF
});
clearAllText.anchor.set(0.5, 0.5);
clearAllText.x = 0;
clearAllText.y = 10;
clearAllButton.addChild(clearAllText);
var clearAllDesc = new Text2('Clear All', {
size: 30,
fill: 0xFFFFFF
});
clearAllDesc.anchor.set(0.5, 0.5);
clearAllDesc.x = 0;
clearAllDesc.y = 40;
clearAllButton.addChild(clearAllDesc);
clearAllButton.x = 2048 / 2 + 200;
clearAllButton.y = powerUpStartY;
// Add click handlers
// Hint Button
var hintButton = new Container();
var hintBg = LK.getAsset('powerupButton', {
anchorX: 0.5,
anchorY: 0.5
});
hintBg.x = 0;
hintBg.y = 0;
hintBg.tint = 0x45B7D1;
hintButton.addChild(hintBg);
var hintIcon = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
hintIcon.x = 0;
hintIcon.y = -30;
hintIcon.scaleX = 0.25;
hintIcon.scaleY = 0.25;
hintIcon.tint = 0xFFFFFF;
hintButton.addChild(hintIcon);
// Progress bar background for hint
var hintProgressBg = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5
});
hintProgressBg.x = 0;
hintProgressBg.y = 50;
hintProgressBg.scaleX = 0.6;
hintProgressBg.scaleY = 0.15;
hintProgressBg.tint = 0x000000;
hintProgressBg.alpha = 0.3;
hintButton.addChild(hintProgressBg);
// Progress bar fill for hint
var hintProgressFill = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0.5
});
hintProgressFill.x = -60;
hintProgressFill.y = 50;
hintProgressFill.scaleX = 0;
hintProgressFill.scaleY = 0.15;
hintProgressFill.tint = 0x00FF00;
hintButton.addChild(hintProgressFill);
hintButton.progressFill = hintProgressFill;
var hintText = new Text2('4000', {
size: 40,
fill: 0xFFFFFF
});
hintText.anchor.set(0.5, 0.5);
hintText.x = 0;
hintText.y = 10;
hintButton.addChild(hintText);
var hintDesc = new Text2('Hint', {
size: 30,
fill: 0xFFFFFF
});
hintDesc.anchor.set(0.5, 0.5);
hintDesc.x = 0;
hintDesc.y = 40;
hintButton.addChild(hintDesc);
hintButton.x = 2048 / 2 - 400;
hintButton.y = powerUpStartY;
rowCleanerButton.down = function (x, y, obj) {
if (score >= rowCleanerCost) {
selectedPowerUp = 'rowCleaner';
// Visual feedback
tween(rowCleanerBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(rowCleanerBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
colorChangerButton.down = function (x, y, obj) {
if (score >= colorChangerCost) {
selectedPowerUp = 'colorChanger';
// Visual feedback
tween(colorChangerBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(colorChangerBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
// Add hint button click handler
hintButton.down = function (x, y, obj) {
if (score >= 4000) {
score -= 4000;
scoreText.setText('Score: ' + score);
// Find best move
var bestMove = findBestMove();
if (bestMove) {
showHintArrow(bestMove.candy1, bestMove.candy2);
}
// Visual feedback
tween(hintBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(hintBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
// Add clear all button click handler
clearAllButton.down = function (x, y, obj) {
if (score >= clearAllCost) {
// Execute clear all immediately instead of setting selectedPowerUp
score -= clearAllCost;
score += 5000; // Bonus points
scoreText.setText('Score: ' + score);
var allCandies = [];
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y]) {
allCandies.push(gameGrid[x][y]);
gameGrid[x][y] = null;
}
}
}
// Animate all candies removal
for (var i = 0; i < allCandies.length; i++) {
allCandies[i].markForRemoval();
}
// Create spectacular particle effects
for (var i = 0; i < allCandies.length; i++) {
var candy = allCandies[i];
var candyColors = [0xff6b6b, 0x4ecdc4, 0x45b7d1, 0xf9ca24, 0xf0932b, 0xeb4d4b];
var particleColor = candyColors[candy.candyType - 1] || 0xffffff;
// Create more particles for spectacular effect
var particleCount = Math.floor(Math.random() * 8) + 12;
for (var p = 0; p < particleCount; p++) {
var particle = new Particle(particleColor, candy.x, candy.y);
particles.push(particle);
game.addChild(particle);
}
}
// Screen flash effect
LK.effects.flashScreen(0xFFFFFF, 500);
// Remove candies and refill grid
LK.setTimeout(function () {
for (var i = 0; i < allCandies.length; i++) {
allCandies[i].destroy();
}
applyGravity();
}, 250);
// Visual feedback
tween(clearAllBg, {
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 100,
onFinish: function onFinish() {
tween(clearAllBg, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
};
game.addChild(hintButton);
game.addChild(rowCleanerButton);
game.addChild(colorChangerButton);
game.addChild(clearAllButton);
gameElements.push(hintButton);
gameElements.push(rowCleanerButton);
gameElements.push(colorChangerButton);
gameElements.push(clearAllButton);
}
// Menu button functionality removed
// Coffee cup will be created when game starts
// Initialize sound settings from storage
try {
var storedSoundSetting = storage.soundEnabled;
if (storedSoundSetting === null || storedSoundSetting === undefined) {
soundEnabled = true;
storage.soundEnabled = soundEnabled;
} else {
soundEnabled = storedSoundSetting;
}
} catch (e) {
// Fallback if storage is not available
soundEnabled = true;
}
// Start background music if sound is enabled
if (soundEnabled) {
LK.playMusic('rahat');
}
// Show main menu instead of initializing game immediately
showMainMenu();
game.update = function () {
// Update menu elements when in menu state
if (gameState === 'menu' && mainMenu) {
mainMenu.update();
}
// Update particles during game
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].update();
}
// Combo texts are automatically managed by their own animations
// No manual update needed as they self-destruct
// Background is now static image - no animation needed
// Update power-up button states and progress bars based on score
if (gameState === 'game' && hintButton && rowCleanerButton && colorChangerButton && clearAllButton) {
// Update hint button and progress bar
var hintProgress = Math.min(score / hintCost, 1);
if (hintButton.progressFill) {
tween(hintButton.progressFill, {
scaleX: hintProgress * 0.6
}, {
duration: 200,
easing: tween.easeOut
});
// Change color based on availability
var fillColor = score >= hintCost ? 0x00FF00 : 0xFFAA00;
tween(hintButton.progressFill, {
tint: fillColor
}, {
duration: 200
});
}
if (score >= hintCost) {
hintButton.alpha = 1;
} else {
hintButton.alpha = 0.8;
}
// Update row cleaner button and progress bar
var rowCleanerProgress = Math.min(score / rowCleanerCost, 1);
if (rowCleanerButton.progressFill) {
tween(rowCleanerButton.progressFill, {
scaleX: rowCleanerProgress * 0.6
}, {
duration: 200,
easing: tween.easeOut
});
// Change color based on availability
var fillColor = score >= rowCleanerCost ? 0x00FF00 : 0xFFAA00;
tween(rowCleanerButton.progressFill, {
tint: fillColor
}, {
duration: 200
});
}
if (score >= rowCleanerCost) {
rowCleanerButton.alpha = 1;
} else {
rowCleanerButton.alpha = 0.8;
}
// Update color changer button and progress bar
var colorChangerProgress = Math.min(score / colorChangerCost, 1);
if (colorChangerButton.progressFill) {
tween(colorChangerButton.progressFill, {
scaleX: colorChangerProgress * 0.6
}, {
duration: 200,
easing: tween.easeOut
});
// Change color based on availability
var fillColor = score >= colorChangerCost ? 0x00FF00 : 0xFFAA00;
tween(colorChangerButton.progressFill, {
tint: fillColor
}, {
duration: 200
});
}
if (score >= colorChangerCost) {
colorChangerButton.alpha = 1;
} else {
colorChangerButton.alpha = 0.8;
}
// Update clear all button and progress bar
var clearAllProgress = Math.min(score / clearAllCost, 1);
if (clearAllButton.progressFill) {
tween(clearAllButton.progressFill, {
scaleX: clearAllProgress * 0.6
}, {
duration: 200,
easing: tween.easeOut
});
// Change color based on availability
var fillColor = score >= clearAllCost ? 0x00FF00 : 0xFFAA00;
tween(clearAllButton.progressFill, {
tint: fillColor
}, {
duration: 200
});
}
if (score >= clearAllCost) {
clearAllButton.alpha = 1;
} else {
clearAllButton.alpha = 0.8;
}
}
// Only run game physics during game state
if (gameState === 'game') {
// Add sparkle effects to random candies
if (LK.ticks % 180 === 0) {
// Every 3 seconds at 60fps
// Pick a random candy to sparkle
var validCandies = [];
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (gameGrid[x][y] && !gameGrid[x][y].isAnimating && !gameGrid[x][y].isMatched) {
validCandies.push(gameGrid[x][y]);
}
}
}
if (validCandies.length > 0) {
var randomCandy = validCandies[Math.floor(Math.random() * validCandies.length)];
randomCandy.sparkle();
}
}
// Continuously check for physics updates when not processing moves
if (!isProcessing && animatingCandies === 0) {
// Check if any candies need to fall due to physics
var needsPhysicsUpdate = false;
for (var x = 0; x < GRID_SIZE && !needsPhysicsUpdate; x++) {
// Check entire column from bottom to top for any candy that should fall
for (var y = GRID_SIZE - 1; y >= 0; y--) {
if (gameGrid[x][y] === null) {
// Found empty space, check if there's a candy above it
for (var checkY = y - 1; checkY >= 0; checkY--) {
if (gameGrid[x][checkY] !== null) {
needsPhysicsUpdate = true;
break;
}
}
if (needsPhysicsUpdate) break;
}
}
}
if (needsPhysicsUpdate) {
applyGravity();
}
}
}
};
gerçekçi bir şeker. yuvarlak bir şeker ve kırmızı renk. In-Game asset. 2d. High contrast. No shadows
gerçekçi bir şeker. yuvarlak bir şeker ve pembe renk.. In-Game asset. 2d. High contrast. No shadows
gerçekçi bir şeker. yuvarlak bir şeker ve koyu mavi renk. aynı candy2 deki gibi In-Game asset. 2d. High contrast. No shadows
gerçekçi bir şeker. yuvarlak bir şeker ve sarı renk. aynı candy2 deki gibi olsun. In-Game asset. 2d. High contrast. No shadows
gerçekçi bir şeker. yuvarlak bir şeker ve kahverengi renk. aynı candy2 deki gibi olsun. In-Game asset. 2d. High contrast. No shadows
gerçekçi bir şeker. yuvarlak bir şeker ve yeşil renk. aynı candy2 deki gibi olsun. In-Game asset. 2d. High contrast. No shadows
a coffee. In-Game asset. 2d. High contrast. No shadows
Bir satırda oyundaki tüm şekerler yan yana dizilsin. In-Game asset. 2d. High contrast. No shadows
Yuvarlak bir şeker ve üstünde ona ucu değen sağ üstte bir kalem. In-Game asset. 2d. High contrast. No shadows