/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Firework = Container.expand(function (x, y) { var self = Container.call(this); var fireworkGraphics = self.attachAsset('firework', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; self.zIndex = 10; // Initial scale fireworkGraphics.scaleX = 0.1; fireworkGraphics.scaleY = 0.1; fireworkGraphics.alpha = 0.8; self.explode = function () { // Play firework sound LK.getSound('fireworkBurst').play(); // Main explosion animation - smaller size tween(fireworkGraphics, { scaleX: 5.0, scaleY: 5.0, rotation: Math.PI * 3 }, { duration: 1000, easing: tween.easeOut }); // Fade out effect tween(fireworkGraphics, { alpha: 0 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { if (self.parent) { self.parent.removeChild(self); } } }); // Create smaller colorful particle burst var particleCount = 25; var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff, 0xffffff, 0xff8000]; for (var i = 0; i < particleCount; i++) { var angle = i / particleCount * Math.PI * 2; var radius = 120 + Math.random() * 100; var targetX = self.x + Math.cos(angle) * radius; var targetY = self.y + Math.sin(angle) * radius; var color = colors[Math.floor(Math.random() * colors.length)]; var sparkle = new Particle(self.x, self.y, color); sparkle.velocityX = Math.cos(angle) * 300; sparkle.velocityY = Math.sin(angle) * 300; sparkle.life = 2.5; sparkle.scaleSpeed = 0.2; // Make particles smaller sparkle.scaleX = 1.5; sparkle.scaleY = 1.5; if (self.parent) { self.parent.addChild(sparkle); } } }; return self; }); var Gem = Container.expand(function (gemType, gridX, gridY) { var self = Container.call(this); self.gemType = gemType; self.gridX = gridX; self.gridY = gridY; self.isAnimating = false; var gemAssets = ['gemRed', 'gemBlue', 'gemGreen', 'gemYellow', 'gemPurple', 'gemOrange']; var gemGraphics = self.attachAsset(gemAssets[gemType], { anchorX: 0.5, anchorY: 0.5 }); self.setGridPosition = function (x, y) { self.gridX = x; self.gridY = y; }; self.animateToPosition = function (targetX, targetY, duration, onComplete) { if (!duration) duration = 200; self.isAnimating = true; tween(self, { x: targetX, y: targetY }, { duration: duration, easing: tween.easeOut, onFinish: function onFinish() { self.isAnimating = false; if (onComplete) onComplete(); } }); }; self.destroy = function () { // Play gem-specific destruction sound var gemSounds = ['gemRedDestroy', 'gemBlueDestroy', 'gemGreenDestroy', 'gemYellowDestroy', 'gemPurpleDestroy', 'gemOrangeDestroy']; if (self.gemType >= 0 && self.gemType < gemSounds.length) { try { LK.getSound(gemSounds[self.gemType]).play(); } catch (e) { console.log('Sound not found:', gemSounds[self.gemType]); } } // Special animation for red gems (gemType 0) if (self.gemType === 0) { self.createFlameExplosion(); } // Special animation for blue gems (gemType 1) if (self.gemType === 1) { self.createWaterExplosion(); } // Special animation for green gems (gemType 2) if (self.gemType === 2) { self.createLeafSpiral(); } // Special animation for yellow gems (gemType 3) if (self.gemType === 3) { self.createStarExplosion(); } // Special animation for purple gems (gemType 4) if (self.gemType === 4) { self.createPurpleImplosion(); } // Special animation for orange gems (gemType 5) if (self.gemType === 5) { self.createLavaMelting(); } // Create tween effect before actually destroying tween(self, { alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { if (self.parent) { self.parent.removeChild(self); } } }); }; self.createFlameExplosion = function () { var flameCount = 8; var radius = 100; for (var i = 0; i < flameCount; i++) { var angle = i / flameCount * Math.PI * 2; var flameParticle = LK.getAsset('flameParticle', { anchorX: 0.5, anchorY: 0.5 }); // Set initial position at gem center flameParticle.x = self.x; flameParticle.y = self.y; flameParticle.zIndex = 5; // Add to game if (self.parent) { self.parent.addChild(flameParticle); } // Calculate spiral target position var targetX = self.x + Math.cos(angle) * radius; var targetY = self.y + Math.sin(angle) * radius; // Create spiral rotation and movement animation tween(flameParticle, { x: targetX, y: targetY, rotation: Math.PI * 4, // 2 full rotations scaleX: 3.0, scaleY: 3.0 }, { duration: 400, easing: tween.easeOut }); // Fade out and disappear tween(flameParticle, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { if (flameParticle.parent) { flameParticle.parent.removeChild(flameParticle); } } }); // Add flame color transition from orange to red tween(flameParticle, { tint: 0xff0000 }, { duration: 300, easing: tween.easeInOut }); } }; self.createWaterExplosion = function () { var dropletCount = 12; var maxRadius = 120; for (var i = 0; i < dropletCount; i++) { var angle = i / dropletCount * Math.PI * 2; var waterDroplet = LK.getAsset('waterDroplet', { anchorX: 0.5, anchorY: 0.5 }); // Set initial position at gem center waterDroplet.x = self.x; waterDroplet.y = self.y; waterDroplet.zIndex = 5; // Random radius for scattered effect var radius = 60 + Math.random() * 60; // Add to game if (self.parent) { self.parent.addChild(waterDroplet); } // Calculate target position for water droplet var targetX = self.x + Math.cos(angle) * radius; var targetY = self.y + Math.sin(angle) * radius; // Create bouncing water droplet movement tween(waterDroplet, { x: targetX, y: targetY, scaleX: 2.5, scaleY: 2.5 }, { duration: 350, easing: tween.bounceOut }); // Create ripple effect with scaling tween(waterDroplet, { scaleX: 0.8, scaleY: 1.2 }, { duration: 200, easing: tween.easeInOut }); // Fade out like evaporating water tween(waterDroplet, { alpha: 0, scaleY: 0.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { if (waterDroplet.parent) { waterDroplet.parent.removeChild(waterDroplet); } } }); // Add water color transition from light blue to transparent tween(waterDroplet, { tint: 0x00aaff }, { duration: 250, easing: tween.easeInOut }); } }; self.createPurpleImplosion = function () { var vortexCount = 16; var initialRadius = 150; for (var i = 0; i < vortexCount; i++) { var angle = i / vortexCount * Math.PI * 2; var purpleVortex = LK.getAsset('purpleVortex', { anchorX: 0.5, anchorY: 0.5 }); // Set initial position in a circle around the gem var startX = self.x + Math.cos(angle) * initialRadius; var startY = self.y + Math.sin(angle) * initialRadius; purpleVortex.x = startX; purpleVortex.y = startY; purpleVortex.zIndex = 5; // Add purple tint and initial scale purpleVortex.tint = 0x8B00FF; purpleVortex.scaleX = 0.5; purpleVortex.scaleY = 0.5; // Add to game if (self.parent) { self.parent.addChild(purpleVortex); } // Create spiral inward movement animation tween(purpleVortex, { x: self.x, y: self.y, rotation: Math.PI * 6, // 3 full rotations inward scaleX: 3.5, scaleY: 3.5 }, { duration: 600, easing: tween.easeIn }); // Fade in then fade out with implosion effect tween(purpleVortex, { alpha: 1.0 }, { duration: 200, easing: tween.easeOut }); // Final implosion - scale down and disappear tween(purpleVortex, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { if (purpleVortex.parent) { purpleVortex.parent.removeChild(purpleVortex); } } }); // Color transition from purple to dark purple tween(purpleVortex, { tint: 0x4B0082 }, { duration: 300, easing: tween.easeInOut }); } }; self.createLeafSpiral = function () { var leafCount = 10; var spiralRadius = 140; for (var i = 0; i < leafCount; i++) { var angle = i / leafCount * Math.PI * 2; var leafParticle = LK.getAsset('leafParticle', { anchorX: 0.5, anchorY: 0.5 }); // Set initial position at gem center leafParticle.x = self.x; leafParticle.y = self.y; leafParticle.zIndex = 5; // Add natural green tint and initial scale leafParticle.tint = 0x228B22; leafParticle.scaleX = 0.8; leafParticle.scaleY = 0.8; leafParticle.rotation = angle; // Initial rotation based on position // Add to game if (self.parent) { self.parent.addChild(leafParticle); } // Create spiral outward movement with natural floating motion var targetX = self.x + Math.cos(angle) * spiralRadius; var targetY = self.y + Math.sin(angle) * spiralRadius; // Add some randomness for natural leaf movement targetX += (Math.random() - 0.5) * 60; targetY += (Math.random() - 0.5) * 60; // Create floating leaf movement animation tween(leafParticle, { x: targetX, y: targetY, rotation: angle + Math.PI * 3, // 1.5 full rotations scaleX: 2.8, scaleY: 2.8 }, { duration: 500, easing: tween.easeOut }); // Add gentle swaying motion like leaves in wind tween(leafParticle, { x: targetX + Math.sin(angle * 2) * 30, y: targetY + Math.cos(angle * 2) * 20 }, { duration: 800, easing: tween.easeInOut }); // Fade out like autumn leaves tween(leafParticle, { alpha: 0, scaleX: 0.3, scaleY: 0.3, rotation: angle + Math.PI * 5 // Continue rotating as it fades }, { duration: 700, easing: tween.easeInOut, onFinish: function onFinish() { if (leafParticle.parent) { leafParticle.parent.removeChild(leafParticle); } } }); // Add leaf color transition from green to brown tween(leafParticle, { tint: 0x8B4513 // Brown color }, { duration: 400, easing: tween.easeInOut }); } }; self.createStarExplosion = function () { var starCount = 14; var maxRadius = 160; for (var i = 0; i < starCount; i++) { var angle = i / starCount * Math.PI * 2; var starParticle = LK.getAsset('starParticle', { anchorX: 0.5, anchorY: 0.5 }); // Set initial position at gem center starParticle.x = self.x; starParticle.y = self.y; starParticle.zIndex = 5; // Add golden yellow tint and initial scale starParticle.tint = 0xFFD700; starParticle.scaleX = 0.3; starParticle.scaleY = 0.3; starParticle.rotation = angle; // Add to game if (self.parent) { self.parent.addChild(starParticle); } // Create spiral outward sun burst movement var radius = 80 + Math.random() * 80; var targetX = self.x + Math.cos(angle) * radius; var targetY = self.y + Math.sin(angle) * radius; // Add sparkle randomness for star effect targetX += (Math.random() - 0.5) * 40; targetY += (Math.random() - 0.5) * 40; // Create explosive star movement animation tween(starParticle, { x: targetX, y: targetY, rotation: angle + Math.PI * 4, // 2 full rotations scaleX: 5.0, scaleY: 5.0 }, { duration: 450, easing: tween.easeOut }); // Add pulsing sparkle effect tween(starParticle, { scaleX: 6.5, scaleY: 6.5 }, { duration: 150, easing: tween.easeInOut }); // Create secondary sparkle pulse tween(starParticle, { scaleX: 4.5, scaleY: 4.5, rotation: angle + Math.PI * 6 // Continue rotating }, { duration: 300, easing: tween.easeInOut }); // Fade out like fading starlight tween(starParticle, { alpha: 0, scaleX: 0.2, scaleY: 0.2, rotation: angle + Math.PI * 8 // Final rotation burst }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { if (starParticle.parent) { starParticle.parent.removeChild(starParticle); } } }); // Add star color transition from gold to bright white tween(starParticle, { tint: 0xFFFFFF // Bright white }, { duration: 350, easing: tween.easeInOut }); } }; self.createLavaMelting = function () { var lavaCount = 12; var meltRadius = 130; for (var i = 0; i < lavaCount; i++) { var angle = i / lavaCount * Math.PI * 2; var lavaParticle = LK.getAsset('lavaParticle', { anchorX: 0.5, anchorY: 0.5 }); // Set initial position at gem center lavaParticle.x = self.x; lavaParticle.y = self.y; lavaParticle.zIndex = 5; // Add molten orange-red tint and initial scale lavaParticle.tint = 0xFF4500; lavaParticle.scaleX = 0.6; lavaParticle.scaleY = 0.6; lavaParticle.rotation = Math.random() * Math.PI * 2; // Add to game if (self.parent) { self.parent.addChild(lavaParticle); } // Create melting downward movement with spiral motion var radius = 70 + Math.random() * 60; var targetX = self.x + Math.cos(angle) * radius; var targetY = self.y + Math.sin(angle) * radius; // Add downward melting bias targetY += Math.random() * 80 + 40; // Add some randomness for natural lava flow targetX += (Math.random() - 0.5) * 50; // Create lava melting movement animation tween(lavaParticle, { x: targetX, y: targetY, rotation: angle + Math.PI * 3, // 1.5 full rotations scaleX: 3.2, scaleY: 3.2 }, { duration: 550, easing: tween.easeOut }); // Add viscous lava dripping effect tween(lavaParticle, { y: targetY + 60, scaleY: 4.0 // Stretch vertically like dripping lava }, { duration: 400, easing: tween.easeIn }); // Create molten glow pulsing effect tween(lavaParticle, { scaleX: 4.0, tint: 0xFF6600 // Brighter orange }, { duration: 300, easing: tween.easeInOut }); // Cool down and solidify effect tween(lavaParticle, { alpha: 0, scaleX: 0.4, scaleY: 0.2, tint: 0x8B0000, // Dark red when cooling rotation: angle + Math.PI * 5 // Continue rotating as it cools }, { duration: 650, easing: tween.easeInOut, onFinish: function onFinish() { if (lavaParticle.parent) { lavaParticle.parent.removeChild(lavaParticle); } } }); // Add secondary heat shimmer effect tween(lavaParticle, { scaleX: 2.8, scaleY: 3.5 }, { duration: 200, easing: tween.easeInOut }); } }; return self; }); var Particle = Container.expand(function (x, y, color) { var self = Container.call(this); var particleGraphics = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); // Set particle color particleGraphics.tint = color; // Set initial position self.x = x; self.y = y; // Random velocity - increased for more dramatic effect self.velocityX = (Math.random() - 0.5) * 500; self.velocityY = (Math.random() - 0.5) * 500; self.gravity = 800; self.life = 1.5; self.fadeSpeed = 1.0; self.scale = 1.0; self.scaleSpeed = 0.5; // Add initial scale animation tween(self, { scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.easeOut }); self.update = function () { // Update position based on velocity self.x += self.velocityX * (1 / 60); self.y += self.velocityY * (1 / 60); // Apply gravity (positive for flipped board - particles fall down visually) self.velocityY += self.gravity * (1 / 60); // Fade out over time self.life -= self.fadeSpeed * (1 / 60); self.alpha = Math.max(0, self.life); // Scale down over time for dramatic effect self.scale -= self.scaleSpeed * (1 / 60); if (self.scale > 0) { self.scaleX = self.scale; self.scaleY = self.scale; } // Remove when fully faded if (self.life <= 0) { self.destroy(); } }; self.destroy = function () { if (self.parent) { self.parent.removeChild(self); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2C1810 }); /**** * Game Code ****/ var GRID_SIZE_X = 6; var GRID_SIZE_Y = 9; var CELL_SIZE = 170; var BOARD_OFFSET_X = (2048 - GRID_SIZE_X * CELL_SIZE) / 2; var BOARD_OFFSET_Y = (2732 - GRID_SIZE_Y * CELL_SIZE) / 2 + 600; var gameBoard = []; var selectedGem = null; var draggedGem = null; var dragStartPos = null; var isDragging = false; var isSwapping = false; var score = 0; var comboMultiplier = 1; var consecutiveGemsDestroyed = 0; var fireworkThreshold = 6; // Timer system - 7.5 minutes in seconds var gameTimeLimit = 7.5 * 60; // 7.5 minutes = 450 seconds var remainingTime = gameTimeLimit; var gameTimer = null; // Create brown background (scrolling) var k1 = game.attachAsset('brownBackground', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); k1.zIndex = 1; // Create second brown background positioned directly above the first one var k1_second = game.attachAsset('brownBackground', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 - 2732 }); k1_second.zIndex = 1; // Variables to track background animation state var backgroundSpeed = 2; var backgroundY1 = 2732 / 2; var backgroundY2 = 2732 / 2 - 2732; // Function to update background positions function updateBackgrounds() { // Move both backgrounds down backgroundY1 += backgroundSpeed; backgroundY2 += backgroundSpeed; k1.y = backgroundY1; k1_second.y = backgroundY2; // Reset positions when they move off screen if (backgroundY1 > 2732 / 2 + 2732) { backgroundY1 = backgroundY2 - 2732; } if (backgroundY2 > 2732 / 2 + 2732) { backgroundY2 = backgroundY1 - 2732; } } // Create board background var boardBg = game.attachAsset('boardBg', { anchorX: 0.5, anchorY: 1.0, x: 2048 / 2, y: 2732 + 850 }); boardBg.zIndex = 2; // Update timer display function updateTimer() { var minutes = Math.floor(remainingTime / 60); var seconds = remainingTime % 60; var timeText = minutes + ':' + (seconds < 10 ? '0' : '') + seconds; timerTxt.setText(timeText); } // Update health bar display function updateHealthBar() { // Calculate how many segments should be visible (10 segments total) var visibleSegments = Math.ceil(playerHealth / 10); // Update health text healthTxt.setText('HEALTH: ' + playerHealth); // Remove segments that should no longer be visible for (var i = 0; i < healthSegments.length; i++) { var segment = healthSegments[i]; if (i >= visibleSegments && segment.parent) { // Animate segment removal with cutting effect tween(segment, { scaleX: 0, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function (segmentToRemove) { return function () { if (segmentToRemove.parent) { segmentToRemove.parent.removeChild(segmentToRemove); } }; }(segment) }); } } // Add back segments that should be visible but are missing for (var i = 0; i < visibleSegments; i++) { var segment = healthSegments[i]; if (segment && !segment.parent) { // Re-add the segment to the GUI segment.scaleX = 1; segment.alpha = 1; segment.tint = 0x00ff00; LK.gui.top.addChild(segment); // Animate segment restoration with growing effect tween(segment, { scaleX: 1, alpha: 1 }, { duration: 300, easing: tween.easeOut }); } } // Flash remaining segments red when taking damage if (playerHealth < maxHealth) { for (var j = 0; j < visibleSegments; j++) { if (healthSegments[j] && healthSegments[j].parent) { tween(healthSegments[j], { tint: 0xff6666 }, { duration: 200, easing: tween.easeInOut, onFinish: function (segmentToFlash) { return function () { tween(segmentToFlash, { tint: 0x00ff00 }, { duration: 200, easing: tween.easeInOut }); }; }(healthSegments[j]) }); } } } } // Reduce player health function reduceHealth(amount) { playerHealth = Math.max(0, playerHealth - amount); updateHealthBar(); // Flash screen red when taking damage LK.effects.flashScreen(0xff0000, 300); // Check for game over if (playerHealth <= 0) { LK.showGameOver(); } } // Create explosion effect at gem position function createExplosion(x, y, gemColor) { var particleCount = 15; var gemColors = [0xff2222, 0x2222ff, 0x22ff22, 0xffff22, 0xff22ff, 0xff6622]; var color = gemColors[gemColor] || 0xffffff; for (var i = 0; i < particleCount; i++) { var particle = new Particle(x, y, color); game.addChild(particle); } } // Create firework celebration function createFirework() { // Create single firework in center upper position var fireworkX = BOARD_OFFSET_X + GRID_SIZE_X * CELL_SIZE / 2 - 150 + Math.random() * 300; var fireworkY = BOARD_OFFSET_Y - 500 + Math.random() * 200; var firework = new Firework(fireworkX, fireworkY); game.addChild(firework); // Start explosion immediately LK.setTimeout(function () { firework.explode(); }, 100); } // Create board frame var C1 = game.attachAsset('boardFrame', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 + 125 }); C1.zIndex = 3; // Create score display var scoreTxt = new Text2('SCORE: 0', { size: 60, fill: 0x000000 }); scoreTxt.anchor.set(0.5, 0); // Add red shadow effect and red border stroke if (scoreTxt.style) { scoreTxt.style.dropShadow = true; scoreTxt.style.dropShadowColor = "#ff0000"; scoreTxt.style.dropShadowBlur = 3; scoreTxt.style.dropShadowDistance = 3; scoreTxt.style.stroke = "#ff0000"; scoreTxt.style.strokeThickness = 4; } LK.gui.top.addChild(scoreTxt); scoreTxt.y = 100; // Create website text 300px below score var websiteTxt = new Text2('vayabi560.com', { size: 50, fill: 0x000000 }); websiteTxt.anchor.set(0.5, 0); // Add styling for better visibility and make bold if (websiteTxt.style) { websiteTxt.style.fontWeight = "bold"; websiteTxt.style.dropShadow = true; websiteTxt.style.dropShadowColor = "#ff0000"; websiteTxt.style.dropShadowBlur = 3; websiteTxt.style.dropShadowDistance = 3; websiteTxt.style.stroke = "#ff0000"; websiteTxt.style.strokeThickness = 2; } LK.gui.top.addChild(websiteTxt); websiteTxt.y = 400; // 100 + 300 = 400 (moved 300px up from original 700) // Create health system var playerHealth = 100; var maxHealth = 100; // Create health bar background (red) var healthBarBg = LK.getAsset('healthBarBg', { anchorX: 0, anchorY: 0.5 }); healthBarBg.x = 150; healthBarBg.y = 200; LK.gui.top.addChild(healthBarBg); // Create segmented health bar with 10 individual pieces var healthSegments = []; var segmentWidth = 40; // Each segment is 40px wide (400px total / 10 segments) for (var i = 0; i < 10; i++) { var healthSegment = LK.getAsset('healthBarFg', { anchorX: 0, anchorY: 0.5 }); healthSegment.width = segmentWidth; healthSegment.x = 150 + i * segmentWidth; healthSegment.y = 200; healthSegment.segmentIndex = i; healthSegments.push(healthSegment); LK.gui.top.addChild(healthSegment); } // Create health text var healthTxt = new Text2('HEALTH: 100', { size: 50, fill: 0x000000 }); // Make text bold and add styling for better visibility if (healthTxt.style) { healthTxt.style.fontWeight = "bold"; healthTxt.style.stroke = "#ffffff"; healthTxt.style.strokeThickness = 2; } healthTxt.anchor.set(0, 0.5); healthTxt.x = 150; healthTxt.y = 250; LK.gui.top.addChild(healthTxt); // Create timer display var timerTxt = new Text2('7:30', { size: 60, fill: 0x000000 }); // Make timer text bold and add styling for better visibility if (timerTxt.style) { timerTxt.style.fontWeight = "bold"; timerTxt.style.stroke = "#ffffff"; timerTxt.style.strokeThickness = 2; timerTxt.style.dropShadow = true; timerTxt.style.dropShadowColor = "#ff0000"; timerTxt.style.dropShadowBlur = 3; timerTxt.style.dropShadowDistance = 3; } timerTxt.anchor.set(0, 1); // Anchor to bottom-left for positioning LK.gui.bottomLeft.addChild(timerTxt); timerTxt.x = 160; // Move slightly to the right timerTxt.y = -900; // Move 35 squares (700px) up from previous position timerTxt.rotation = 340 * Math.PI / 180; // Point towards 340 degrees // Initialize empty board function initializeBoard() { gameBoard = []; for (var x = 0; x < GRID_SIZE_X; x++) { gameBoard[x] = []; for (var y = 0; y < GRID_SIZE_Y; y++) { gameBoard[x][y] = null; } } } // Fill board with random gems function fillBoard() { for (var x = 0; x < GRID_SIZE_X; x++) { for (var y = 0; y < GRID_SIZE_Y; y++) { if (!gameBoard[x][y]) { var gemType = Math.floor(Math.random() * 6); var gem = new Gem(gemType, x, y); var worldPos = gridToWorld(x, y); gem.x = worldPos.x; gem.y = worldPos.y; gem.zIndex = 4; gameBoard[x][y] = gem; game.addChild(gem); } } } } // Convert grid coordinates to world coordinates function gridToWorld(gridX, gridY) { return { x: BOARD_OFFSET_X + gridX * CELL_SIZE + CELL_SIZE / 2, y: BOARD_OFFSET_Y + (GRID_SIZE_Y - 1 - gridY) * CELL_SIZE + CELL_SIZE / 2 }; } // Convert world coordinates to grid coordinates function worldToGrid(worldX, worldY) { var gridX = Math.floor((worldX - BOARD_OFFSET_X) / CELL_SIZE); var gridY = GRID_SIZE_Y - 1 - Math.floor((worldY - BOARD_OFFSET_Y) / CELL_SIZE); if (gridX < 0 || gridX >= GRID_SIZE_X || gridY < 0 || gridY >= GRID_SIZE_Y) { return null; } return { x: gridX, y: gridY }; } // Check if two grid positions are adjacent function areAdjacent(pos1, pos2) { var dx = Math.abs(pos1.x - pos2.x); var dy = Math.abs(pos1.y - pos2.y); return dx === 1 && dy === 0 || dx === 0 && dy === 1; } // Check for matches starting at position (2x2 squares and 3-in-a-row) function checkMatches(startX, startY) { var gem = gameBoard[startX][startY]; if (!gem) return []; var matches = []; var gemType = gem.gemType; // Check horizontal 3-in-a-row matches var horizontalCount = 1; var horizontalGems = [gem]; // Check right direction for (var x = startX + 1; x < GRID_SIZE_X; x++) { var rightGem = gameBoard[x][startY]; if (rightGem && rightGem.gemType === gemType) { horizontalCount++; horizontalGems.push(rightGem); } else { break; } } // Check left direction for (var x = startX - 1; x >= 0; x--) { var leftGem = gameBoard[x][startY]; if (leftGem && leftGem.gemType === gemType) { horizontalCount++; horizontalGems.unshift(leftGem); } else { break; } } // Add horizontal matches if 3 or more if (horizontalCount >= 3) { for (var h = 0; h < horizontalGems.length; h++) { matches.push(horizontalGems[h]); } } // Check vertical 3-in-a-row matches var verticalCount = 1; var verticalGems = [gem]; // Check up direction for (var y = startY + 1; y < GRID_SIZE_Y; y++) { var upGem = gameBoard[startX][y]; if (upGem && upGem.gemType === gemType) { verticalCount++; verticalGems.push(upGem); } else { break; } } // Check down direction for (var y = startY - 1; y >= 0; y--) { var downGem = gameBoard[startX][y]; if (downGem && downGem.gemType === gemType) { verticalCount++; verticalGems.unshift(downGem); } else { break; } } // Add vertical matches if 3 or more if (verticalCount >= 3) { for (var v = 0; v < verticalGems.length; v++) { var found = false; for (var m = 0; m < matches.length; m++) { if (matches[m] === verticalGems[v]) { found = true; break; } } if (!found) { matches.push(verticalGems[v]); } } } // Check if this gem is part of a 2x2 square // Check all possible 2x2 squares this gem could be part of var squarePositions = [ // Top-left corner [{ x: startX, y: startY }, { x: startX + 1, y: startY }, { x: startX, y: startY + 1 }, { x: startX + 1, y: startY + 1 }], // Top-right corner [{ x: startX - 1, y: startY }, { x: startX, y: startY }, { x: startX - 1, y: startY + 1 }, { x: startX, y: startY + 1 }], // Bottom-left corner [{ x: startX, y: startY - 1 }, { x: startX + 1, y: startY - 1 }, { x: startX, y: startY }, { x: startX + 1, y: startY }], // Bottom-right corner [{ x: startX - 1, y: startY - 1 }, { x: startX, y: startY - 1 }, { x: startX - 1, y: startY }, { x: startX, y: startY }]]; for (var s = 0; s < squarePositions.length; s++) { var square = squarePositions[s]; var validSquare = true; var squareGems = []; // Check if all positions in this square are valid and contain same gem type for (var p = 0; p < square.length; p++) { var pos = square[p]; if (pos.x < 0 || pos.x >= GRID_SIZE_X || pos.y < 0 || pos.y >= GRID_SIZE_Y) { validSquare = false; break; } var squareGem = gameBoard[pos.x][pos.y]; if (!squareGem || squareGem.gemType !== gemType) { validSquare = false; break; } squareGems.push(squareGem); } // If we found a valid 2x2 square, add all gems to matches if (validSquare) { for (var g = 0; g < squareGems.length; g++) { var found = false; for (var m = 0; m < matches.length; m++) { if (matches[m] === squareGems[g]) { found = true; break; } } if (!found) { matches.push(squareGems[g]); } } } } return matches; } // Find all matches on the board function findAllMatches() { var allMatches = []; var processedGems = []; for (var x = 0; x < GRID_SIZE_X; x++) { for (var y = 0; y < GRID_SIZE_Y; y++) { if (gameBoard[x][y]) { var matches = checkMatches(x, y); for (var i = 0; i < matches.length; i++) { var gem = matches[i]; var found = false; for (var j = 0; j < processedGems.length; j++) { if (processedGems[j] === gem) { found = true; break; } } if (!found) { allMatches.push(gem); processedGems.push(gem); } } } } } return allMatches; } // Clear matched gems function clearMatches(matches) { if (matches.length === 0) return; LK.getSound('match').play(); var points = matches.length * 10 * comboMultiplier; var oldScore = score; score += points; scoreTxt.setText('SCORE: ' + score); // Health gain system - check if player should gain health every 1500 points var oldHealthGainLevel = Math.floor(oldScore / 1500); var newHealthGainLevel = Math.floor(score / 1500); if (newHealthGainLevel > oldHealthGainLevel && playerHealth < maxHealth) { // Player gains health, but don't exceed max health var healthToGain = Math.min(10, maxHealth - playerHealth); playerHealth += healthToGain; updateHealthBar(); // Flash screen green to show health gain LK.effects.flashScreen(0x00ff00, 500); } // Check for win condition - player reaches 10000 points if (score >= 10000) { LK.clearInterval(gameTimer); LK.showYouWin(); return; } // Track consecutive adjacent destroyed gems and trigger fireworks if (matches.length >= fireworkThreshold) { // Check if all gems are adjacent to each other (consecutive) var areConsecutive = true; if (matches.length >= fireworkThreshold) { // Sort matches by position to check adjacency for (var i = 0; i < matches.length - 1; i++) { var currentGem = matches[i]; var hasAdjacentMatch = false; for (var j = i + 1; j < matches.length; j++) { var nextGem = matches[j]; var dx = Math.abs(currentGem.gridX - nextGem.gridX); var dy = Math.abs(currentGem.gridY - nextGem.gridY); if (dx === 1 && dy === 0 || dx === 0 && dy === 1) { hasAdjacentMatch = true; break; } } if (!hasAdjacentMatch && i < matches.length - 1) { areConsecutive = false; break; } } } if (areConsecutive && matches.length >= fireworkThreshold) { // Create firework celebration createFirework(); } } for (var i = 0; i < matches.length; i++) { var gem = matches[i]; // Create explosion effect at gem position createExplosion(gem.x, gem.y, gem.gemType); gameBoard[gem.gridX][gem.gridY] = null; gem.destroy(); } // After clearing gems, apply gravity and fill empty spaces LK.setTimeout(function () { // Apply gravity repeatedly until no more gems fall function applyGravityRepeatedly() { var moved = applyGravity(); if (moved) { LK.setTimeout(applyGravityRepeatedly, 100); } else { fillEmptySpaces(); // Check for new matches after falling gems settle LK.setTimeout(function () { var newMatches = findAllMatches(); if (newMatches.length > 0) { comboMultiplier++; clearMatches(newMatches); } else { comboMultiplier = 1; consecutiveGemsDestroyed = 0; // Reset counter when no more matches isSwapping = false; } }, 500); } } applyGravityRepeatedly(); }, 200); } // Apply gravity to make gems fall function applyGravity() { var moved = false; for (var x = 0; x < GRID_SIZE_X; x++) { // Process each column from top to bottom (since board is flipped, gems fall upward) var writeIndex = 0; for (var y = 0; y < GRID_SIZE_Y; y++) { if (gameBoard[x][y]) { // If gem is not at the write position, move it up if (y !== writeIndex) { var gem = gameBoard[x][y]; gameBoard[x][writeIndex] = gem; gameBoard[x][y] = null; gem.setGridPosition(x, writeIndex); var worldPos = gridToWorld(x, writeIndex); gem.animateToPosition(worldPos.x, worldPos.y, 300); moved = true; } writeIndex++; } } } return moved; } // Fill empty spaces with new gems function fillEmptySpaces() { for (var x = 0; x < GRID_SIZE_X; x++) { for (var y = 0; y < GRID_SIZE_Y; y++) { if (!gameBoard[x][y]) { var gemType = Math.floor(Math.random() * 6); var gem = new Gem(gemType, x, y); var worldPos = gridToWorld(x, y); gem.x = worldPos.x; gem.y = BOARD_OFFSET_Y - CELL_SIZE * 2; // Start from above the top line of the flipped grid gem.zIndex = 4; gameBoard[x][y] = gem; game.addChild(gem); gem.animateToPosition(worldPos.x, worldPos.y, 150 + y * 30); // Faster staggered animation timing for flipped board } } } } // Swap two gems function swapGems(gem1, gem2) { if (isSwapping) return; isSwapping = true; // Store original positions var originalGem1X = gem1.gridX; var originalGem1Y = gem1.gridY; var originalGem2X = gem2.gridX; var originalGem2Y = gem2.gridY; // Update grid positions gem1.setGridPosition(gem2.gridX, gem2.gridY); gem2.setGridPosition(originalGem1X, originalGem1Y); gameBoard[gem1.gridX][gem1.gridY] = gem1; gameBoard[gem2.gridX][gem2.gridY] = gem2; var pos1 = gridToWorld(gem1.gridX, gem1.gridY); var pos2 = gridToWorld(gem2.gridX, gem2.gridY); // Animate the swap gem1.animateToPosition(pos1.x, pos1.y, 200); gem2.animateToPosition(pos2.x, pos2.y, 200); LK.setTimeout(function () { var matches = findAllMatches(); if (matches.length > 0) { // Valid swap - process matches clearMatches(matches); } else { // Invalid move - reduce health by 10 and animate back to original positions reduceHealth(10); gem1.setGridPosition(originalGem1X, originalGem1Y); gem2.setGridPosition(originalGem2X, originalGem2Y); gameBoard[gem1.gridX][gem1.gridY] = gem1; gameBoard[gem2.gridX][gem2.gridY] = gem2; var pos1 = gridToWorld(gem1.gridX, gem1.gridY); var pos2 = gridToWorld(gem2.gridX, gem2.gridY); gem1.animateToPosition(pos1.x, pos1.y, 200); gem2.animateToPosition(pos2.x, pos2.y, 200); LK.setTimeout(function () { isSwapping = false; }, 200); } }, 200); } // Game input handling game.down = function (x, y, obj) { if (isSwapping) return; var gridPos = worldToGrid(x, y); if (!gridPos) return; var clickedGem = gameBoard[gridPos.x][gridPos.y]; if (!clickedGem) return; draggedGem = clickedGem; dragStartPos = { x: x, y: y }; isDragging = true; draggedGem.alpha = 0.7; }; game.move = function (x, y, obj) { if (!isDragging || !draggedGem || isSwapping) return; // Calculate drag distance var dragDeltaX = x - dragStartPos.x; var dragDeltaY = y - dragStartPos.y; var dragDistance = Math.sqrt(dragDeltaX * dragDeltaX + dragDeltaY * dragDeltaY); // Only process if drag distance is significant enough if (dragDistance > CELL_SIZE * 0.3) { // Determine drag direction var targetGridX = draggedGem.gridX; var targetGridY = draggedGem.gridY; if (Math.abs(dragDeltaX) > Math.abs(dragDeltaY)) { // Horizontal drag if (dragDeltaX > 0) { targetGridX = Math.min(GRID_SIZE_X - 1, draggedGem.gridX + 1); } else { targetGridX = Math.max(0, draggedGem.gridX - 1); } } else { // Vertical drag (flipped coordinate system) if (dragDeltaY > 0) { targetGridY = Math.max(0, draggedGem.gridY - 1); } else { targetGridY = Math.min(GRID_SIZE_Y - 1, draggedGem.gridY + 1); } } // Update visual position to show intended swap var targetWorldPos = gridToWorld(targetGridX, targetGridY); draggedGem.x = targetWorldPos.x; draggedGem.y = targetWorldPos.y; // Store target position for later use draggedGem.targetGridX = targetGridX; draggedGem.targetGridY = targetGridY; } }; game.up = function (x, y, obj) { if (!isDragging || !draggedGem || isSwapping) return; var startGridPos = { x: draggedGem.gridX, y: draggedGem.gridY }; // Check if we have a target position from dragging if (draggedGem.targetGridX !== undefined && draggedGem.targetGridY !== undefined) { var targetGridPos = { x: draggedGem.targetGridX, y: draggedGem.targetGridY }; // Check if target position is valid and adjacent if (areAdjacent(startGridPos, targetGridPos) && gameBoard[targetGridPos.x][targetGridPos.y]) { var targetGem = gameBoard[targetGridPos.x][targetGridPos.y]; // Attempt swap swapGems(draggedGem, targetGem); } else { // Invalid swap - animate back to original position var worldPos = gridToWorld(draggedGem.gridX, draggedGem.gridY); draggedGem.animateToPosition(worldPos.x, worldPos.y, 200); } // Clean up target position draggedGem.targetGridX = undefined; draggedGem.targetGridY = undefined; } else { // No significant drag - just return to original position var worldPos = gridToWorld(draggedGem.gridX, draggedGem.gridY); draggedGem.animateToPosition(worldPos.x, worldPos.y, 200); } draggedGem.alpha = 1.0; draggedGem = null; dragStartPos = null; isDragging = false; }; // Create grid lines around gems function createGridLines() { // Create vertical lines for (var x = 0; x <= GRID_SIZE_X; x++) { for (var lineY = 0; lineY < GRID_SIZE_Y * CELL_SIZE; lineY += 20) { var verticalLine = LK.getAsset('particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.05, scaleY: 0.5 }); verticalLine.tint = 0x8B4513; verticalLine.x = BOARD_OFFSET_X + x * CELL_SIZE; verticalLine.y = BOARD_OFFSET_Y + lineY + 10; verticalLine.zIndex = 3; game.addChild(verticalLine); } } // Create horizontal lines for (var y = 0; y <= GRID_SIZE_Y; y++) { for (var lineX = 0; lineX < GRID_SIZE_X * CELL_SIZE; lineX += 20) { var horizontalLine = LK.getAsset('particle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.05 }); horizontalLine.tint = 0x8B4513; horizontalLine.x = BOARD_OFFSET_X + lineX + 10; horizontalLine.y = BOARD_OFFSET_Y + y * CELL_SIZE; horizontalLine.zIndex = 3; game.addChild(horizontalLine); } } } // Initialize the game initializeBoard(); fillBoard(); createGridLines(); // Add game update loop for continuous animations game.update = function () { // Update scrolling backgrounds updateBackgrounds(); // Add gentle rotation animation to all gems for (var x = 0; x < GRID_SIZE_X; x++) { for (var y = 0; y < GRID_SIZE_Y; y++) { var gem = gameBoard[x][y]; if (gem && !gem.isAnimating && !gem.rotationStarted) { // Create gentle rotation effect gem.rotationStarted = true; var delay = (x + y) * 30; // Stagger the rotation animation LK.setTimeout(function (currentGem) { return function () { function createRotation() { if (currentGem.parent && !currentGem.isAnimating) { var currentRotation = currentGem.rotation || 0; tween(currentGem, { rotation: currentRotation + Math.PI * 2 }, { duration: 6000, easing: tween.linear, onFinish: function onFinish() { if (currentGem.parent && !currentGem.isAnimating) { createRotation(); // Continue rotating } } }); } } createRotation(); }; }(gem), delay); } } } }; // Start countdown timer gameTimer = LK.setInterval(function () { remainingTime--; updateTimer(); // Check if time is running out (last 30 seconds) - flash timer red if (remainingTime <= 30 && remainingTime > 0) { tween(timerTxt, { tint: 0xff0000 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(timerTxt, { tint: 0x000000 }, { duration: 500, easing: tween.easeInOut }); } }); } // Game over when time runs out if (remainingTime <= 0) { LK.clearInterval(gameTimer); LK.showGameOver(); } }, 1000); // Update every 1000ms (1 second) // Create logo in top-right corner var logo = LK.getAsset('logo', { anchorX: 1, anchorY: 0, scaleX: 0.15, scaleY: 0.15 }); logo.x = -50; logo.y = 0; logo.zIndex = 15; LK.gui.topRight.addChild(logo); // Add logo glow effect tween(logo, { scaleX: 1.1, scaleY: 1.1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { tween(logo, { scaleX: 1.0, scaleY: 1.0 }, { duration: 1500, easing: tween.easeInOut }); } }); // Start background music LK.playMusic('background'); // Initialize timer display updateTimer(); // Initial match clearing LK.setTimeout(function () { var matches = findAllMatches(); if (matches.length > 0) { clearMatches(matches); } }, 100);
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Firework = Container.expand(function (x, y) {
var self = Container.call(this);
var fireworkGraphics = self.attachAsset('firework', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.zIndex = 10;
// Initial scale
fireworkGraphics.scaleX = 0.1;
fireworkGraphics.scaleY = 0.1;
fireworkGraphics.alpha = 0.8;
self.explode = function () {
// Play firework sound
LK.getSound('fireworkBurst').play();
// Main explosion animation - smaller size
tween(fireworkGraphics, {
scaleX: 5.0,
scaleY: 5.0,
rotation: Math.PI * 3
}, {
duration: 1000,
easing: tween.easeOut
});
// Fade out effect
tween(fireworkGraphics, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
// Create smaller colorful particle burst
var particleCount = 25;
var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff, 0xffffff, 0xff8000];
for (var i = 0; i < particleCount; i++) {
var angle = i / particleCount * Math.PI * 2;
var radius = 120 + Math.random() * 100;
var targetX = self.x + Math.cos(angle) * radius;
var targetY = self.y + Math.sin(angle) * radius;
var color = colors[Math.floor(Math.random() * colors.length)];
var sparkle = new Particle(self.x, self.y, color);
sparkle.velocityX = Math.cos(angle) * 300;
sparkle.velocityY = Math.sin(angle) * 300;
sparkle.life = 2.5;
sparkle.scaleSpeed = 0.2;
// Make particles smaller
sparkle.scaleX = 1.5;
sparkle.scaleY = 1.5;
if (self.parent) {
self.parent.addChild(sparkle);
}
}
};
return self;
});
var Gem = Container.expand(function (gemType, gridX, gridY) {
var self = Container.call(this);
self.gemType = gemType;
self.gridX = gridX;
self.gridY = gridY;
self.isAnimating = false;
var gemAssets = ['gemRed', 'gemBlue', 'gemGreen', 'gemYellow', 'gemPurple', 'gemOrange'];
var gemGraphics = self.attachAsset(gemAssets[gemType], {
anchorX: 0.5,
anchorY: 0.5
});
self.setGridPosition = function (x, y) {
self.gridX = x;
self.gridY = y;
};
self.animateToPosition = function (targetX, targetY, duration, onComplete) {
if (!duration) duration = 200;
self.isAnimating = true;
tween(self, {
x: targetX,
y: targetY
}, {
duration: duration,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isAnimating = false;
if (onComplete) onComplete();
}
});
};
self.destroy = function () {
// Play gem-specific destruction sound
var gemSounds = ['gemRedDestroy', 'gemBlueDestroy', 'gemGreenDestroy', 'gemYellowDestroy', 'gemPurpleDestroy', 'gemOrangeDestroy'];
if (self.gemType >= 0 && self.gemType < gemSounds.length) {
try {
LK.getSound(gemSounds[self.gemType]).play();
} catch (e) {
console.log('Sound not found:', gemSounds[self.gemType]);
}
}
// Special animation for red gems (gemType 0)
if (self.gemType === 0) {
self.createFlameExplosion();
}
// Special animation for blue gems (gemType 1)
if (self.gemType === 1) {
self.createWaterExplosion();
}
// Special animation for green gems (gemType 2)
if (self.gemType === 2) {
self.createLeafSpiral();
}
// Special animation for yellow gems (gemType 3)
if (self.gemType === 3) {
self.createStarExplosion();
}
// Special animation for purple gems (gemType 4)
if (self.gemType === 4) {
self.createPurpleImplosion();
}
// Special animation for orange gems (gemType 5)
if (self.gemType === 5) {
self.createLavaMelting();
}
// Create tween effect before actually destroying
tween(self, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
};
self.createFlameExplosion = function () {
var flameCount = 8;
var radius = 100;
for (var i = 0; i < flameCount; i++) {
var angle = i / flameCount * Math.PI * 2;
var flameParticle = LK.getAsset('flameParticle', {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial position at gem center
flameParticle.x = self.x;
flameParticle.y = self.y;
flameParticle.zIndex = 5;
// Add to game
if (self.parent) {
self.parent.addChild(flameParticle);
}
// Calculate spiral target position
var targetX = self.x + Math.cos(angle) * radius;
var targetY = self.y + Math.sin(angle) * radius;
// Create spiral rotation and movement animation
tween(flameParticle, {
x: targetX,
y: targetY,
rotation: Math.PI * 4,
// 2 full rotations
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 400,
easing: tween.easeOut
});
// Fade out and disappear
tween(flameParticle, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (flameParticle.parent) {
flameParticle.parent.removeChild(flameParticle);
}
}
});
// Add flame color transition from orange to red
tween(flameParticle, {
tint: 0xff0000
}, {
duration: 300,
easing: tween.easeInOut
});
}
};
self.createWaterExplosion = function () {
var dropletCount = 12;
var maxRadius = 120;
for (var i = 0; i < dropletCount; i++) {
var angle = i / dropletCount * Math.PI * 2;
var waterDroplet = LK.getAsset('waterDroplet', {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial position at gem center
waterDroplet.x = self.x;
waterDroplet.y = self.y;
waterDroplet.zIndex = 5;
// Random radius for scattered effect
var radius = 60 + Math.random() * 60;
// Add to game
if (self.parent) {
self.parent.addChild(waterDroplet);
}
// Calculate target position for water droplet
var targetX = self.x + Math.cos(angle) * radius;
var targetY = self.y + Math.sin(angle) * radius;
// Create bouncing water droplet movement
tween(waterDroplet, {
x: targetX,
y: targetY,
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 350,
easing: tween.bounceOut
});
// Create ripple effect with scaling
tween(waterDroplet, {
scaleX: 0.8,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeInOut
});
// Fade out like evaporating water
tween(waterDroplet, {
alpha: 0,
scaleY: 0.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (waterDroplet.parent) {
waterDroplet.parent.removeChild(waterDroplet);
}
}
});
// Add water color transition from light blue to transparent
tween(waterDroplet, {
tint: 0x00aaff
}, {
duration: 250,
easing: tween.easeInOut
});
}
};
self.createPurpleImplosion = function () {
var vortexCount = 16;
var initialRadius = 150;
for (var i = 0; i < vortexCount; i++) {
var angle = i / vortexCount * Math.PI * 2;
var purpleVortex = LK.getAsset('purpleVortex', {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial position in a circle around the gem
var startX = self.x + Math.cos(angle) * initialRadius;
var startY = self.y + Math.sin(angle) * initialRadius;
purpleVortex.x = startX;
purpleVortex.y = startY;
purpleVortex.zIndex = 5;
// Add purple tint and initial scale
purpleVortex.tint = 0x8B00FF;
purpleVortex.scaleX = 0.5;
purpleVortex.scaleY = 0.5;
// Add to game
if (self.parent) {
self.parent.addChild(purpleVortex);
}
// Create spiral inward movement animation
tween(purpleVortex, {
x: self.x,
y: self.y,
rotation: Math.PI * 6,
// 3 full rotations inward
scaleX: 3.5,
scaleY: 3.5
}, {
duration: 600,
easing: tween.easeIn
});
// Fade in then fade out with implosion effect
tween(purpleVortex, {
alpha: 1.0
}, {
duration: 200,
easing: tween.easeOut
});
// Final implosion - scale down and disappear
tween(purpleVortex, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (purpleVortex.parent) {
purpleVortex.parent.removeChild(purpleVortex);
}
}
});
// Color transition from purple to dark purple
tween(purpleVortex, {
tint: 0x4B0082
}, {
duration: 300,
easing: tween.easeInOut
});
}
};
self.createLeafSpiral = function () {
var leafCount = 10;
var spiralRadius = 140;
for (var i = 0; i < leafCount; i++) {
var angle = i / leafCount * Math.PI * 2;
var leafParticle = LK.getAsset('leafParticle', {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial position at gem center
leafParticle.x = self.x;
leafParticle.y = self.y;
leafParticle.zIndex = 5;
// Add natural green tint and initial scale
leafParticle.tint = 0x228B22;
leafParticle.scaleX = 0.8;
leafParticle.scaleY = 0.8;
leafParticle.rotation = angle; // Initial rotation based on position
// Add to game
if (self.parent) {
self.parent.addChild(leafParticle);
}
// Create spiral outward movement with natural floating motion
var targetX = self.x + Math.cos(angle) * spiralRadius;
var targetY = self.y + Math.sin(angle) * spiralRadius;
// Add some randomness for natural leaf movement
targetX += (Math.random() - 0.5) * 60;
targetY += (Math.random() - 0.5) * 60;
// Create floating leaf movement animation
tween(leafParticle, {
x: targetX,
y: targetY,
rotation: angle + Math.PI * 3,
// 1.5 full rotations
scaleX: 2.8,
scaleY: 2.8
}, {
duration: 500,
easing: tween.easeOut
});
// Add gentle swaying motion like leaves in wind
tween(leafParticle, {
x: targetX + Math.sin(angle * 2) * 30,
y: targetY + Math.cos(angle * 2) * 20
}, {
duration: 800,
easing: tween.easeInOut
});
// Fade out like autumn leaves
tween(leafParticle, {
alpha: 0,
scaleX: 0.3,
scaleY: 0.3,
rotation: angle + Math.PI * 5 // Continue rotating as it fades
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (leafParticle.parent) {
leafParticle.parent.removeChild(leafParticle);
}
}
});
// Add leaf color transition from green to brown
tween(leafParticle, {
tint: 0x8B4513 // Brown color
}, {
duration: 400,
easing: tween.easeInOut
});
}
};
self.createStarExplosion = function () {
var starCount = 14;
var maxRadius = 160;
for (var i = 0; i < starCount; i++) {
var angle = i / starCount * Math.PI * 2;
var starParticle = LK.getAsset('starParticle', {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial position at gem center
starParticle.x = self.x;
starParticle.y = self.y;
starParticle.zIndex = 5;
// Add golden yellow tint and initial scale
starParticle.tint = 0xFFD700;
starParticle.scaleX = 0.3;
starParticle.scaleY = 0.3;
starParticle.rotation = angle;
// Add to game
if (self.parent) {
self.parent.addChild(starParticle);
}
// Create spiral outward sun burst movement
var radius = 80 + Math.random() * 80;
var targetX = self.x + Math.cos(angle) * radius;
var targetY = self.y + Math.sin(angle) * radius;
// Add sparkle randomness for star effect
targetX += (Math.random() - 0.5) * 40;
targetY += (Math.random() - 0.5) * 40;
// Create explosive star movement animation
tween(starParticle, {
x: targetX,
y: targetY,
rotation: angle + Math.PI * 4,
// 2 full rotations
scaleX: 5.0,
scaleY: 5.0
}, {
duration: 450,
easing: tween.easeOut
});
// Add pulsing sparkle effect
tween(starParticle, {
scaleX: 6.5,
scaleY: 6.5
}, {
duration: 150,
easing: tween.easeInOut
});
// Create secondary sparkle pulse
tween(starParticle, {
scaleX: 4.5,
scaleY: 4.5,
rotation: angle + Math.PI * 6 // Continue rotating
}, {
duration: 300,
easing: tween.easeInOut
});
// Fade out like fading starlight
tween(starParticle, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2,
rotation: angle + Math.PI * 8 // Final rotation burst
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (starParticle.parent) {
starParticle.parent.removeChild(starParticle);
}
}
});
// Add star color transition from gold to bright white
tween(starParticle, {
tint: 0xFFFFFF // Bright white
}, {
duration: 350,
easing: tween.easeInOut
});
}
};
self.createLavaMelting = function () {
var lavaCount = 12;
var meltRadius = 130;
for (var i = 0; i < lavaCount; i++) {
var angle = i / lavaCount * Math.PI * 2;
var lavaParticle = LK.getAsset('lavaParticle', {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial position at gem center
lavaParticle.x = self.x;
lavaParticle.y = self.y;
lavaParticle.zIndex = 5;
// Add molten orange-red tint and initial scale
lavaParticle.tint = 0xFF4500;
lavaParticle.scaleX = 0.6;
lavaParticle.scaleY = 0.6;
lavaParticle.rotation = Math.random() * Math.PI * 2;
// Add to game
if (self.parent) {
self.parent.addChild(lavaParticle);
}
// Create melting downward movement with spiral motion
var radius = 70 + Math.random() * 60;
var targetX = self.x + Math.cos(angle) * radius;
var targetY = self.y + Math.sin(angle) * radius;
// Add downward melting bias
targetY += Math.random() * 80 + 40;
// Add some randomness for natural lava flow
targetX += (Math.random() - 0.5) * 50;
// Create lava melting movement animation
tween(lavaParticle, {
x: targetX,
y: targetY,
rotation: angle + Math.PI * 3,
// 1.5 full rotations
scaleX: 3.2,
scaleY: 3.2
}, {
duration: 550,
easing: tween.easeOut
});
// Add viscous lava dripping effect
tween(lavaParticle, {
y: targetY + 60,
scaleY: 4.0 // Stretch vertically like dripping lava
}, {
duration: 400,
easing: tween.easeIn
});
// Create molten glow pulsing effect
tween(lavaParticle, {
scaleX: 4.0,
tint: 0xFF6600 // Brighter orange
}, {
duration: 300,
easing: tween.easeInOut
});
// Cool down and solidify effect
tween(lavaParticle, {
alpha: 0,
scaleX: 0.4,
scaleY: 0.2,
tint: 0x8B0000,
// Dark red when cooling
rotation: angle + Math.PI * 5 // Continue rotating as it cools
}, {
duration: 650,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (lavaParticle.parent) {
lavaParticle.parent.removeChild(lavaParticle);
}
}
});
// Add secondary heat shimmer effect
tween(lavaParticle, {
scaleX: 2.8,
scaleY: 3.5
}, {
duration: 200,
easing: tween.easeInOut
});
}
};
return self;
});
var Particle = Container.expand(function (x, y, color) {
var self = Container.call(this);
var particleGraphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
// Set particle color
particleGraphics.tint = color;
// Set initial position
self.x = x;
self.y = y;
// Random velocity - increased for more dramatic effect
self.velocityX = (Math.random() - 0.5) * 500;
self.velocityY = (Math.random() - 0.5) * 500;
self.gravity = 800;
self.life = 1.5;
self.fadeSpeed = 1.0;
self.scale = 1.0;
self.scaleSpeed = 0.5;
// Add initial scale animation
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut
});
self.update = function () {
// Update position based on velocity
self.x += self.velocityX * (1 / 60);
self.y += self.velocityY * (1 / 60);
// Apply gravity (positive for flipped board - particles fall down visually)
self.velocityY += self.gravity * (1 / 60);
// Fade out over time
self.life -= self.fadeSpeed * (1 / 60);
self.alpha = Math.max(0, self.life);
// Scale down over time for dramatic effect
self.scale -= self.scaleSpeed * (1 / 60);
if (self.scale > 0) {
self.scaleX = self.scale;
self.scaleY = self.scale;
}
// Remove when fully faded
if (self.life <= 0) {
self.destroy();
}
};
self.destroy = function () {
if (self.parent) {
self.parent.removeChild(self);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2C1810
});
/****
* Game Code
****/
var GRID_SIZE_X = 6;
var GRID_SIZE_Y = 9;
var CELL_SIZE = 170;
var BOARD_OFFSET_X = (2048 - GRID_SIZE_X * CELL_SIZE) / 2;
var BOARD_OFFSET_Y = (2732 - GRID_SIZE_Y * CELL_SIZE) / 2 + 600;
var gameBoard = [];
var selectedGem = null;
var draggedGem = null;
var dragStartPos = null;
var isDragging = false;
var isSwapping = false;
var score = 0;
var comboMultiplier = 1;
var consecutiveGemsDestroyed = 0;
var fireworkThreshold = 6;
// Timer system - 7.5 minutes in seconds
var gameTimeLimit = 7.5 * 60; // 7.5 minutes = 450 seconds
var remainingTime = gameTimeLimit;
var gameTimer = null;
// Create brown background (scrolling)
var k1 = game.attachAsset('brownBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
k1.zIndex = 1;
// Create second brown background positioned directly above the first one
var k1_second = game.attachAsset('brownBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2 - 2732
});
k1_second.zIndex = 1;
// Variables to track background animation state
var backgroundSpeed = 2;
var backgroundY1 = 2732 / 2;
var backgroundY2 = 2732 / 2 - 2732;
// Function to update background positions
function updateBackgrounds() {
// Move both backgrounds down
backgroundY1 += backgroundSpeed;
backgroundY2 += backgroundSpeed;
k1.y = backgroundY1;
k1_second.y = backgroundY2;
// Reset positions when they move off screen
if (backgroundY1 > 2732 / 2 + 2732) {
backgroundY1 = backgroundY2 - 2732;
}
if (backgroundY2 > 2732 / 2 + 2732) {
backgroundY2 = backgroundY1 - 2732;
}
}
// Create board background
var boardBg = game.attachAsset('boardBg', {
anchorX: 0.5,
anchorY: 1.0,
x: 2048 / 2,
y: 2732 + 850
});
boardBg.zIndex = 2;
// Update timer display
function updateTimer() {
var minutes = Math.floor(remainingTime / 60);
var seconds = remainingTime % 60;
var timeText = minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
timerTxt.setText(timeText);
}
// Update health bar display
function updateHealthBar() {
// Calculate how many segments should be visible (10 segments total)
var visibleSegments = Math.ceil(playerHealth / 10);
// Update health text
healthTxt.setText('HEALTH: ' + playerHealth);
// Remove segments that should no longer be visible
for (var i = 0; i < healthSegments.length; i++) {
var segment = healthSegments[i];
if (i >= visibleSegments && segment.parent) {
// Animate segment removal with cutting effect
tween(segment, {
scaleX: 0,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function (segmentToRemove) {
return function () {
if (segmentToRemove.parent) {
segmentToRemove.parent.removeChild(segmentToRemove);
}
};
}(segment)
});
}
}
// Add back segments that should be visible but are missing
for (var i = 0; i < visibleSegments; i++) {
var segment = healthSegments[i];
if (segment && !segment.parent) {
// Re-add the segment to the GUI
segment.scaleX = 1;
segment.alpha = 1;
segment.tint = 0x00ff00;
LK.gui.top.addChild(segment);
// Animate segment restoration with growing effect
tween(segment, {
scaleX: 1,
alpha: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Flash remaining segments red when taking damage
if (playerHealth < maxHealth) {
for (var j = 0; j < visibleSegments; j++) {
if (healthSegments[j] && healthSegments[j].parent) {
tween(healthSegments[j], {
tint: 0xff6666
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function (segmentToFlash) {
return function () {
tween(segmentToFlash, {
tint: 0x00ff00
}, {
duration: 200,
easing: tween.easeInOut
});
};
}(healthSegments[j])
});
}
}
}
}
// Reduce player health
function reduceHealth(amount) {
playerHealth = Math.max(0, playerHealth - amount);
updateHealthBar();
// Flash screen red when taking damage
LK.effects.flashScreen(0xff0000, 300);
// Check for game over
if (playerHealth <= 0) {
LK.showGameOver();
}
}
// Create explosion effect at gem position
function createExplosion(x, y, gemColor) {
var particleCount = 15;
var gemColors = [0xff2222, 0x2222ff, 0x22ff22, 0xffff22, 0xff22ff, 0xff6622];
var color = gemColors[gemColor] || 0xffffff;
for (var i = 0; i < particleCount; i++) {
var particle = new Particle(x, y, color);
game.addChild(particle);
}
}
// Create firework celebration
function createFirework() {
// Create single firework in center upper position
var fireworkX = BOARD_OFFSET_X + GRID_SIZE_X * CELL_SIZE / 2 - 150 + Math.random() * 300;
var fireworkY = BOARD_OFFSET_Y - 500 + Math.random() * 200;
var firework = new Firework(fireworkX, fireworkY);
game.addChild(firework);
// Start explosion immediately
LK.setTimeout(function () {
firework.explode();
}, 100);
}
// Create board frame
var C1 = game.attachAsset('boardFrame', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2 + 125
});
C1.zIndex = 3;
// Create score display
var scoreTxt = new Text2('SCORE: 0', {
size: 60,
fill: 0x000000
});
scoreTxt.anchor.set(0.5, 0);
// Add red shadow effect and red border stroke
if (scoreTxt.style) {
scoreTxt.style.dropShadow = true;
scoreTxt.style.dropShadowColor = "#ff0000";
scoreTxt.style.dropShadowBlur = 3;
scoreTxt.style.dropShadowDistance = 3;
scoreTxt.style.stroke = "#ff0000";
scoreTxt.style.strokeThickness = 4;
}
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 100;
// Create website text 300px below score
var websiteTxt = new Text2('vayabi560.com', {
size: 50,
fill: 0x000000
});
websiteTxt.anchor.set(0.5, 0);
// Add styling for better visibility and make bold
if (websiteTxt.style) {
websiteTxt.style.fontWeight = "bold";
websiteTxt.style.dropShadow = true;
websiteTxt.style.dropShadowColor = "#ff0000";
websiteTxt.style.dropShadowBlur = 3;
websiteTxt.style.dropShadowDistance = 3;
websiteTxt.style.stroke = "#ff0000";
websiteTxt.style.strokeThickness = 2;
}
LK.gui.top.addChild(websiteTxt);
websiteTxt.y = 400; // 100 + 300 = 400 (moved 300px up from original 700)
// Create health system
var playerHealth = 100;
var maxHealth = 100;
// Create health bar background (red)
var healthBarBg = LK.getAsset('healthBarBg', {
anchorX: 0,
anchorY: 0.5
});
healthBarBg.x = 150;
healthBarBg.y = 200;
LK.gui.top.addChild(healthBarBg);
// Create segmented health bar with 10 individual pieces
var healthSegments = [];
var segmentWidth = 40; // Each segment is 40px wide (400px total / 10 segments)
for (var i = 0; i < 10; i++) {
var healthSegment = LK.getAsset('healthBarFg', {
anchorX: 0,
anchorY: 0.5
});
healthSegment.width = segmentWidth;
healthSegment.x = 150 + i * segmentWidth;
healthSegment.y = 200;
healthSegment.segmentIndex = i;
healthSegments.push(healthSegment);
LK.gui.top.addChild(healthSegment);
}
// Create health text
var healthTxt = new Text2('HEALTH: 100', {
size: 50,
fill: 0x000000
});
// Make text bold and add styling for better visibility
if (healthTxt.style) {
healthTxt.style.fontWeight = "bold";
healthTxt.style.stroke = "#ffffff";
healthTxt.style.strokeThickness = 2;
}
healthTxt.anchor.set(0, 0.5);
healthTxt.x = 150;
healthTxt.y = 250;
LK.gui.top.addChild(healthTxt);
// Create timer display
var timerTxt = new Text2('7:30', {
size: 60,
fill: 0x000000
});
// Make timer text bold and add styling for better visibility
if (timerTxt.style) {
timerTxt.style.fontWeight = "bold";
timerTxt.style.stroke = "#ffffff";
timerTxt.style.strokeThickness = 2;
timerTxt.style.dropShadow = true;
timerTxt.style.dropShadowColor = "#ff0000";
timerTxt.style.dropShadowBlur = 3;
timerTxt.style.dropShadowDistance = 3;
}
timerTxt.anchor.set(0, 1); // Anchor to bottom-left for positioning
LK.gui.bottomLeft.addChild(timerTxt);
timerTxt.x = 160; // Move slightly to the right
timerTxt.y = -900; // Move 35 squares (700px) up from previous position
timerTxt.rotation = 340 * Math.PI / 180; // Point towards 340 degrees
// Initialize empty board
function initializeBoard() {
gameBoard = [];
for (var x = 0; x < GRID_SIZE_X; x++) {
gameBoard[x] = [];
for (var y = 0; y < GRID_SIZE_Y; y++) {
gameBoard[x][y] = null;
}
}
}
// Fill board with random gems
function fillBoard() {
for (var x = 0; x < GRID_SIZE_X; x++) {
for (var y = 0; y < GRID_SIZE_Y; y++) {
if (!gameBoard[x][y]) {
var gemType = Math.floor(Math.random() * 6);
var gem = new Gem(gemType, x, y);
var worldPos = gridToWorld(x, y);
gem.x = worldPos.x;
gem.y = worldPos.y;
gem.zIndex = 4;
gameBoard[x][y] = gem;
game.addChild(gem);
}
}
}
}
// Convert grid coordinates to world coordinates
function gridToWorld(gridX, gridY) {
return {
x: BOARD_OFFSET_X + gridX * CELL_SIZE + CELL_SIZE / 2,
y: BOARD_OFFSET_Y + (GRID_SIZE_Y - 1 - gridY) * CELL_SIZE + CELL_SIZE / 2
};
}
// Convert world coordinates to grid coordinates
function worldToGrid(worldX, worldY) {
var gridX = Math.floor((worldX - BOARD_OFFSET_X) / CELL_SIZE);
var gridY = GRID_SIZE_Y - 1 - Math.floor((worldY - BOARD_OFFSET_Y) / CELL_SIZE);
if (gridX < 0 || gridX >= GRID_SIZE_X || gridY < 0 || gridY >= GRID_SIZE_Y) {
return null;
}
return {
x: gridX,
y: gridY
};
}
// Check if two grid positions are adjacent
function areAdjacent(pos1, pos2) {
var dx = Math.abs(pos1.x - pos2.x);
var dy = Math.abs(pos1.y - pos2.y);
return dx === 1 && dy === 0 || dx === 0 && dy === 1;
}
// Check for matches starting at position (2x2 squares and 3-in-a-row)
function checkMatches(startX, startY) {
var gem = gameBoard[startX][startY];
if (!gem) return [];
var matches = [];
var gemType = gem.gemType;
// Check horizontal 3-in-a-row matches
var horizontalCount = 1;
var horizontalGems = [gem];
// Check right direction
for (var x = startX + 1; x < GRID_SIZE_X; x++) {
var rightGem = gameBoard[x][startY];
if (rightGem && rightGem.gemType === gemType) {
horizontalCount++;
horizontalGems.push(rightGem);
} else {
break;
}
}
// Check left direction
for (var x = startX - 1; x >= 0; x--) {
var leftGem = gameBoard[x][startY];
if (leftGem && leftGem.gemType === gemType) {
horizontalCount++;
horizontalGems.unshift(leftGem);
} else {
break;
}
}
// Add horizontal matches if 3 or more
if (horizontalCount >= 3) {
for (var h = 0; h < horizontalGems.length; h++) {
matches.push(horizontalGems[h]);
}
}
// Check vertical 3-in-a-row matches
var verticalCount = 1;
var verticalGems = [gem];
// Check up direction
for (var y = startY + 1; y < GRID_SIZE_Y; y++) {
var upGem = gameBoard[startX][y];
if (upGem && upGem.gemType === gemType) {
verticalCount++;
verticalGems.push(upGem);
} else {
break;
}
}
// Check down direction
for (var y = startY - 1; y >= 0; y--) {
var downGem = gameBoard[startX][y];
if (downGem && downGem.gemType === gemType) {
verticalCount++;
verticalGems.unshift(downGem);
} else {
break;
}
}
// Add vertical matches if 3 or more
if (verticalCount >= 3) {
for (var v = 0; v < verticalGems.length; v++) {
var found = false;
for (var m = 0; m < matches.length; m++) {
if (matches[m] === verticalGems[v]) {
found = true;
break;
}
}
if (!found) {
matches.push(verticalGems[v]);
}
}
}
// Check if this gem is part of a 2x2 square
// Check all possible 2x2 squares this gem could be part of
var squarePositions = [
// Top-left corner
[{
x: startX,
y: startY
}, {
x: startX + 1,
y: startY
}, {
x: startX,
y: startY + 1
}, {
x: startX + 1,
y: startY + 1
}],
// Top-right corner
[{
x: startX - 1,
y: startY
}, {
x: startX,
y: startY
}, {
x: startX - 1,
y: startY + 1
}, {
x: startX,
y: startY + 1
}],
// Bottom-left corner
[{
x: startX,
y: startY - 1
}, {
x: startX + 1,
y: startY - 1
}, {
x: startX,
y: startY
}, {
x: startX + 1,
y: startY
}],
// Bottom-right corner
[{
x: startX - 1,
y: startY - 1
}, {
x: startX,
y: startY - 1
}, {
x: startX - 1,
y: startY
}, {
x: startX,
y: startY
}]];
for (var s = 0; s < squarePositions.length; s++) {
var square = squarePositions[s];
var validSquare = true;
var squareGems = [];
// Check if all positions in this square are valid and contain same gem type
for (var p = 0; p < square.length; p++) {
var pos = square[p];
if (pos.x < 0 || pos.x >= GRID_SIZE_X || pos.y < 0 || pos.y >= GRID_SIZE_Y) {
validSquare = false;
break;
}
var squareGem = gameBoard[pos.x][pos.y];
if (!squareGem || squareGem.gemType !== gemType) {
validSquare = false;
break;
}
squareGems.push(squareGem);
}
// If we found a valid 2x2 square, add all gems to matches
if (validSquare) {
for (var g = 0; g < squareGems.length; g++) {
var found = false;
for (var m = 0; m < matches.length; m++) {
if (matches[m] === squareGems[g]) {
found = true;
break;
}
}
if (!found) {
matches.push(squareGems[g]);
}
}
}
}
return matches;
}
// Find all matches on the board
function findAllMatches() {
var allMatches = [];
var processedGems = [];
for (var x = 0; x < GRID_SIZE_X; x++) {
for (var y = 0; y < GRID_SIZE_Y; y++) {
if (gameBoard[x][y]) {
var matches = checkMatches(x, y);
for (var i = 0; i < matches.length; i++) {
var gem = matches[i];
var found = false;
for (var j = 0; j < processedGems.length; j++) {
if (processedGems[j] === gem) {
found = true;
break;
}
}
if (!found) {
allMatches.push(gem);
processedGems.push(gem);
}
}
}
}
}
return allMatches;
}
// Clear matched gems
function clearMatches(matches) {
if (matches.length === 0) return;
LK.getSound('match').play();
var points = matches.length * 10 * comboMultiplier;
var oldScore = score;
score += points;
scoreTxt.setText('SCORE: ' + score);
// Health gain system - check if player should gain health every 1500 points
var oldHealthGainLevel = Math.floor(oldScore / 1500);
var newHealthGainLevel = Math.floor(score / 1500);
if (newHealthGainLevel > oldHealthGainLevel && playerHealth < maxHealth) {
// Player gains health, but don't exceed max health
var healthToGain = Math.min(10, maxHealth - playerHealth);
playerHealth += healthToGain;
updateHealthBar();
// Flash screen green to show health gain
LK.effects.flashScreen(0x00ff00, 500);
}
// Check for win condition - player reaches 10000 points
if (score >= 10000) {
LK.clearInterval(gameTimer);
LK.showYouWin();
return;
}
// Track consecutive adjacent destroyed gems and trigger fireworks
if (matches.length >= fireworkThreshold) {
// Check if all gems are adjacent to each other (consecutive)
var areConsecutive = true;
if (matches.length >= fireworkThreshold) {
// Sort matches by position to check adjacency
for (var i = 0; i < matches.length - 1; i++) {
var currentGem = matches[i];
var hasAdjacentMatch = false;
for (var j = i + 1; j < matches.length; j++) {
var nextGem = matches[j];
var dx = Math.abs(currentGem.gridX - nextGem.gridX);
var dy = Math.abs(currentGem.gridY - nextGem.gridY);
if (dx === 1 && dy === 0 || dx === 0 && dy === 1) {
hasAdjacentMatch = true;
break;
}
}
if (!hasAdjacentMatch && i < matches.length - 1) {
areConsecutive = false;
break;
}
}
}
if (areConsecutive && matches.length >= fireworkThreshold) {
// Create firework celebration
createFirework();
}
}
for (var i = 0; i < matches.length; i++) {
var gem = matches[i];
// Create explosion effect at gem position
createExplosion(gem.x, gem.y, gem.gemType);
gameBoard[gem.gridX][gem.gridY] = null;
gem.destroy();
}
// After clearing gems, apply gravity and fill empty spaces
LK.setTimeout(function () {
// Apply gravity repeatedly until no more gems fall
function applyGravityRepeatedly() {
var moved = applyGravity();
if (moved) {
LK.setTimeout(applyGravityRepeatedly, 100);
} else {
fillEmptySpaces();
// Check for new matches after falling gems settle
LK.setTimeout(function () {
var newMatches = findAllMatches();
if (newMatches.length > 0) {
comboMultiplier++;
clearMatches(newMatches);
} else {
comboMultiplier = 1;
consecutiveGemsDestroyed = 0; // Reset counter when no more matches
isSwapping = false;
}
}, 500);
}
}
applyGravityRepeatedly();
}, 200);
}
// Apply gravity to make gems fall
function applyGravity() {
var moved = false;
for (var x = 0; x < GRID_SIZE_X; x++) {
// Process each column from top to bottom (since board is flipped, gems fall upward)
var writeIndex = 0;
for (var y = 0; y < GRID_SIZE_Y; y++) {
if (gameBoard[x][y]) {
// If gem is not at the write position, move it up
if (y !== writeIndex) {
var gem = gameBoard[x][y];
gameBoard[x][writeIndex] = gem;
gameBoard[x][y] = null;
gem.setGridPosition(x, writeIndex);
var worldPos = gridToWorld(x, writeIndex);
gem.animateToPosition(worldPos.x, worldPos.y, 300);
moved = true;
}
writeIndex++;
}
}
}
return moved;
}
// Fill empty spaces with new gems
function fillEmptySpaces() {
for (var x = 0; x < GRID_SIZE_X; x++) {
for (var y = 0; y < GRID_SIZE_Y; y++) {
if (!gameBoard[x][y]) {
var gemType = Math.floor(Math.random() * 6);
var gem = new Gem(gemType, x, y);
var worldPos = gridToWorld(x, y);
gem.x = worldPos.x;
gem.y = BOARD_OFFSET_Y - CELL_SIZE * 2; // Start from above the top line of the flipped grid
gem.zIndex = 4;
gameBoard[x][y] = gem;
game.addChild(gem);
gem.animateToPosition(worldPos.x, worldPos.y, 150 + y * 30); // Faster staggered animation timing for flipped board
}
}
}
}
// Swap two gems
function swapGems(gem1, gem2) {
if (isSwapping) return;
isSwapping = true;
// Store original positions
var originalGem1X = gem1.gridX;
var originalGem1Y = gem1.gridY;
var originalGem2X = gem2.gridX;
var originalGem2Y = gem2.gridY;
// Update grid positions
gem1.setGridPosition(gem2.gridX, gem2.gridY);
gem2.setGridPosition(originalGem1X, originalGem1Y);
gameBoard[gem1.gridX][gem1.gridY] = gem1;
gameBoard[gem2.gridX][gem2.gridY] = gem2;
var pos1 = gridToWorld(gem1.gridX, gem1.gridY);
var pos2 = gridToWorld(gem2.gridX, gem2.gridY);
// Animate the swap
gem1.animateToPosition(pos1.x, pos1.y, 200);
gem2.animateToPosition(pos2.x, pos2.y, 200);
LK.setTimeout(function () {
var matches = findAllMatches();
if (matches.length > 0) {
// Valid swap - process matches
clearMatches(matches);
} else {
// Invalid move - reduce health by 10 and animate back to original positions
reduceHealth(10);
gem1.setGridPosition(originalGem1X, originalGem1Y);
gem2.setGridPosition(originalGem2X, originalGem2Y);
gameBoard[gem1.gridX][gem1.gridY] = gem1;
gameBoard[gem2.gridX][gem2.gridY] = gem2;
var pos1 = gridToWorld(gem1.gridX, gem1.gridY);
var pos2 = gridToWorld(gem2.gridX, gem2.gridY);
gem1.animateToPosition(pos1.x, pos1.y, 200);
gem2.animateToPosition(pos2.x, pos2.y, 200);
LK.setTimeout(function () {
isSwapping = false;
}, 200);
}
}, 200);
}
// Game input handling
game.down = function (x, y, obj) {
if (isSwapping) return;
var gridPos = worldToGrid(x, y);
if (!gridPos) return;
var clickedGem = gameBoard[gridPos.x][gridPos.y];
if (!clickedGem) return;
draggedGem = clickedGem;
dragStartPos = {
x: x,
y: y
};
isDragging = true;
draggedGem.alpha = 0.7;
};
game.move = function (x, y, obj) {
if (!isDragging || !draggedGem || isSwapping) return;
// Calculate drag distance
var dragDeltaX = x - dragStartPos.x;
var dragDeltaY = y - dragStartPos.y;
var dragDistance = Math.sqrt(dragDeltaX * dragDeltaX + dragDeltaY * dragDeltaY);
// Only process if drag distance is significant enough
if (dragDistance > CELL_SIZE * 0.3) {
// Determine drag direction
var targetGridX = draggedGem.gridX;
var targetGridY = draggedGem.gridY;
if (Math.abs(dragDeltaX) > Math.abs(dragDeltaY)) {
// Horizontal drag
if (dragDeltaX > 0) {
targetGridX = Math.min(GRID_SIZE_X - 1, draggedGem.gridX + 1);
} else {
targetGridX = Math.max(0, draggedGem.gridX - 1);
}
} else {
// Vertical drag (flipped coordinate system)
if (dragDeltaY > 0) {
targetGridY = Math.max(0, draggedGem.gridY - 1);
} else {
targetGridY = Math.min(GRID_SIZE_Y - 1, draggedGem.gridY + 1);
}
}
// Update visual position to show intended swap
var targetWorldPos = gridToWorld(targetGridX, targetGridY);
draggedGem.x = targetWorldPos.x;
draggedGem.y = targetWorldPos.y;
// Store target position for later use
draggedGem.targetGridX = targetGridX;
draggedGem.targetGridY = targetGridY;
}
};
game.up = function (x, y, obj) {
if (!isDragging || !draggedGem || isSwapping) return;
var startGridPos = {
x: draggedGem.gridX,
y: draggedGem.gridY
};
// Check if we have a target position from dragging
if (draggedGem.targetGridX !== undefined && draggedGem.targetGridY !== undefined) {
var targetGridPos = {
x: draggedGem.targetGridX,
y: draggedGem.targetGridY
};
// Check if target position is valid and adjacent
if (areAdjacent(startGridPos, targetGridPos) && gameBoard[targetGridPos.x][targetGridPos.y]) {
var targetGem = gameBoard[targetGridPos.x][targetGridPos.y];
// Attempt swap
swapGems(draggedGem, targetGem);
} else {
// Invalid swap - animate back to original position
var worldPos = gridToWorld(draggedGem.gridX, draggedGem.gridY);
draggedGem.animateToPosition(worldPos.x, worldPos.y, 200);
}
// Clean up target position
draggedGem.targetGridX = undefined;
draggedGem.targetGridY = undefined;
} else {
// No significant drag - just return to original position
var worldPos = gridToWorld(draggedGem.gridX, draggedGem.gridY);
draggedGem.animateToPosition(worldPos.x, worldPos.y, 200);
}
draggedGem.alpha = 1.0;
draggedGem = null;
dragStartPos = null;
isDragging = false;
};
// Create grid lines around gems
function createGridLines() {
// Create vertical lines
for (var x = 0; x <= GRID_SIZE_X; x++) {
for (var lineY = 0; lineY < GRID_SIZE_Y * CELL_SIZE; lineY += 20) {
var verticalLine = LK.getAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.05,
scaleY: 0.5
});
verticalLine.tint = 0x8B4513;
verticalLine.x = BOARD_OFFSET_X + x * CELL_SIZE;
verticalLine.y = BOARD_OFFSET_Y + lineY + 10;
verticalLine.zIndex = 3;
game.addChild(verticalLine);
}
}
// Create horizontal lines
for (var y = 0; y <= GRID_SIZE_Y; y++) {
for (var lineX = 0; lineX < GRID_SIZE_X * CELL_SIZE; lineX += 20) {
var horizontalLine = LK.getAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.05
});
horizontalLine.tint = 0x8B4513;
horizontalLine.x = BOARD_OFFSET_X + lineX + 10;
horizontalLine.y = BOARD_OFFSET_Y + y * CELL_SIZE;
horizontalLine.zIndex = 3;
game.addChild(horizontalLine);
}
}
}
// Initialize the game
initializeBoard();
fillBoard();
createGridLines();
// Add game update loop for continuous animations
game.update = function () {
// Update scrolling backgrounds
updateBackgrounds();
// Add gentle rotation animation to all gems
for (var x = 0; x < GRID_SIZE_X; x++) {
for (var y = 0; y < GRID_SIZE_Y; y++) {
var gem = gameBoard[x][y];
if (gem && !gem.isAnimating && !gem.rotationStarted) {
// Create gentle rotation effect
gem.rotationStarted = true;
var delay = (x + y) * 30; // Stagger the rotation animation
LK.setTimeout(function (currentGem) {
return function () {
function createRotation() {
if (currentGem.parent && !currentGem.isAnimating) {
var currentRotation = currentGem.rotation || 0;
tween(currentGem, {
rotation: currentRotation + Math.PI * 2
}, {
duration: 6000,
easing: tween.linear,
onFinish: function onFinish() {
if (currentGem.parent && !currentGem.isAnimating) {
createRotation(); // Continue rotating
}
}
});
}
}
createRotation();
};
}(gem), delay);
}
}
}
};
// Start countdown timer
gameTimer = LK.setInterval(function () {
remainingTime--;
updateTimer();
// Check if time is running out (last 30 seconds) - flash timer red
if (remainingTime <= 30 && remainingTime > 0) {
tween(timerTxt, {
tint: 0xff0000
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(timerTxt, {
tint: 0x000000
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
}
// Game over when time runs out
if (remainingTime <= 0) {
LK.clearInterval(gameTimer);
LK.showGameOver();
}
}, 1000); // Update every 1000ms (1 second)
// Create logo in top-right corner
var logo = LK.getAsset('logo', {
anchorX: 1,
anchorY: 0,
scaleX: 0.15,
scaleY: 0.15
});
logo.x = -50;
logo.y = 0;
logo.zIndex = 15;
LK.gui.topRight.addChild(logo);
// Add logo glow effect
tween(logo, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(logo, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1500,
easing: tween.easeInOut
});
}
});
// Start background music
LK.playMusic('background');
// Initialize timer display
updateTimer();
// Initial match clearing
LK.setTimeout(function () {
var matches = findAllMatches();
if (matches.length > 0) {
clearMatches(matches);
}
}, 100);
Rengi acık mavi gökyüzümsü olsun küçük bulutlar olsun
Su damlası patlama parçacık. In-Game asset. 2d. High contrast. No shadows
Karadelik vortex parçacık spiral. In-Game asset. 2d. High contrast. No shadows
Yeşil yaprak süzülen. In-Game asset. 2d. High contrast. No shadows
Yıldız patlama parlak. In-Game asset. 2d. High contrast. No shadows
Lav topu erimiş. In-Game asset. 2d. High contrast. No shadows
Health bar, grandyan, green,. In-Game asset. 2d. High contrast. No shadows
Hiç birşeye dokunma sadece yeşil olan kısımları kırmızı ile yer değiştir
Make it with candy