/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScoreClassic: 0, highScoreFast: 0, highScoreSeek: 0 }); /**** * Classes ****/ var CosmeticsScreen = Container.expand(function () { var self = Container.call(this); var title = new Text2("Cosmetics", { size: 150, fill: 0x000000 }); title.anchor.set(0.5, 0); title.x = 2048 / 2; title.y = 300; self.addChild(title); title.y = 200; self.cosmeticsGrid = new Container(); self.addChild(self.cosmeticsGrid); var cosmeticSets = [{ id: 1, name: "Default", memePrefix: 'meme' }, { id: 2, name: "Variant 1", memePrefix: 'meme1-2' }, { id: 3, name: "Variant 2", memePrefix: 'meme1-3' }]; var cardWidth = 550; var cardHeight = 680; var cardsPerRow = 3; var cardSpacing = 50; var startX = (2048 - (cardsPerRow * cardWidth + (cardsPerRow - 1) * cardSpacing)) / 2 + cardWidth / 2; var startY = 700; cosmeticSets.forEach(function (set, index) { var row = Math.floor(index / cardsPerRow); var col = index % cardsPerRow; var card = new Container(); card.x = startX + col * (cardWidth + cardSpacing); card.y = startY + row * (cardHeight + 80); var cardShadow = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.6, scaleY: 3.6 }); cardShadow.tint = 0x000000; cardShadow.alpha = 0.3; cardShadow.x = 10; cardShadow.y = 10; var cardBg = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 3.5 }); cardBg.tint = 0xFFFFFF; var nameText = new Text2(set.name, { size: 70, fill: 0x000000 }); nameText.anchor.set(0.5, 0); nameText.y = -cardHeight / 2 + 80; var setLabel = new Text2("MEME SET", { size: 40, fill: 0x555555 }); setLabel.anchor.set(0.5, 1); setLabel.y = cardHeight / 2 - 40; function animateCard() { tween(card, { y: card.y - 10 }, { duration: 800, easing: tween.easeInOutQuad, onComplete: function onComplete() { tween(card, { y: card.y + 10 }, { duration: 800, easing: tween.easeInOutQuad, onComplete: animateCard }); } }); } LK.setTimeout(animateCard, Math.random() * 1000); var previewContainer = new Container(); var previewSize = 120; var previewSpacing = 20; var previewScale = 0.6; var previewsPerRow = 3; var previewGridWidth = previewsPerRow * previewSize + (previewsPerRow - 1) * previewSpacing; var previewStartX = -previewGridWidth / 2 + previewSize / 2; var previewStartY = -40; for (var i = 1; i <= 5; i++) { var row = Math.floor((i - 1) / previewsPerRow); var col = (i - 1) % previewsPerRow; var preview; if (set.id === 1) { preview = LK.getAsset('meme' + i, { anchorX: 0.5, anchorY: 0.5, scaleX: previewScale, scaleY: previewScale }); } else if (set.id === 2) { preview = LK.getAsset('meme' + i + '-2', { anchorX: 0.5, anchorY: 0.5, scaleX: previewScale, scaleY: previewScale }); } else { preview = LK.getAsset('meme' + i + '-3', { anchorX: 0.5, anchorY: 0.5, scaleX: previewScale, scaleY: previewScale }); } preview.x = previewStartX + col * (previewSize + previewSpacing); preview.y = previewStartY + row * (previewSize + previewSpacing); var previewBg = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6, alpha: 0.1 }); previewBg.tint = 0x888888; previewBg.x = preview.x; previewBg.y = preview.y; previewContainer.addChild(previewBg); previewContainer.addChild(preview); } var statusOverlay = new Container(); card.statusOverlay = statusOverlay; var isSelected = set.id === 1; // Default always to set 1 if (isSelected) { var _pulseBorder = function pulseBorder() { tween(selectedBorder, { alpha: 0.2 }, { duration: 700, easing: tween.easeInOutQuad, onComplete: function onComplete() { tween(selectedBorder, { alpha: 0.5 }, { duration: 700, easing: tween.easeInOutQuad, onComplete: _pulseBorder }); } }); }; var selectedBorder = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.6, scaleY: 3.6 }); selectedBorder.tint = 0x00FF00; selectedBorder.alpha = 0.5; _pulseBorder(); var checkmark = LK.getAsset('check', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); checkmark.x = cardWidth / 2 - 70; checkmark.y = -cardHeight / 2 + 70; card.addChildAt(selectedBorder, 0); statusOverlay.addChild(checkmark); } card.addChild(cardShadow); card.addChild(cardBg); card.addChild(nameText); card.addChild(setLabel); card.addChild(previewContainer); card.addChild(statusOverlay); card.interactive = true; card.setId = set.id; card.isUnlocked = true; card.price = set.price; card.down = function (x, y, obj) { LK.effects.flashObject(cardBg, 0xFFFFFF, 200); LK.getSound('ButtonSound').play(); // Update selectedSet and persist to storage selectedSet = card.setId; storage.selectedSet = selectedSet; self.cosmeticsGrid.children.forEach(function (otherCard) { otherCard.children.forEach(function (child) { if (child.tint === 0x00FF00 && child !== cardBg) { otherCard.removeChild(child); } }); if (otherCard.statusOverlay) { while (otherCard.statusOverlay.children.length > 0) { otherCard.statusOverlay.removeChild(otherCard.statusOverlay.children[0]); } } }); var selectedBorder = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.6, scaleY: 3.6 }); selectedBorder.tint = 0x00FF00; var checkmark = LK.getAsset('check', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); checkmark.x = cardWidth / 2 - 70; checkmark.y = -cardHeight / 2 + 70; checkmark.isCheckmark = true; card.addChildAt(selectedBorder, 0); statusOverlay.addChild(checkmark); }; self.cosmeticsGrid.addChild(card); }); var backButton = createButton("BACK TO MENU", 0xFF5500, 3, 1.5, function () { game.removeChild(self); initializeMenu(); }, 2048 / 2, 2732 - 200); self.addChild(backButton); return self; }); var GridCell = Container.expand(function () { var self = Container.call(this); self.init = function () { self.value = 0; self.sprite = null; self.row = -1; self.col = -1; self.isSpecial = false; self.specialType = null; return self; }; self.setValue = function (newValue) { if (self.value === newValue) { return; } self.value = newValue; if (self.sprite) { self.removeChild(self.sprite); } var spriteId; if (game && game.gameMode === 'seekmode' && game.seekModeForceSeekSprite) { spriteId = 'seek'; } else { // Default to meme set 1 // Use global selectedSet for cosmetics if (typeof selectedSet !== "number") selectedSet = 1; if (selectedSet === 1) { spriteId = 'meme' + newValue; } else if (selectedSet === 2) { spriteId = 'meme' + newValue + '-2'; } else if (selectedSet === 3) { spriteId = 'meme' + newValue + '-3'; } else { spriteId = 'meme' + newValue; } } self.sprite = LK.getAsset(spriteId, { anchorX: 0.5, anchorY: 0.5 }); var padding = 20; var maxWidth = cellSize - padding * 2; var maxHeight = cellSize - padding * 2; var scaleX = maxWidth / self.sprite.width; var scaleY = maxHeight / self.sprite.height; var scale = Math.min(scaleX, scaleY); self.sprite.scale.set(scale, scale); self.addChild(self.sprite); if (self.isSpecial) { // Remove any previous overlays if (self.specialOverlay && self.children.indexOf(self.specialOverlay) !== -1) { self.removeChild(self.specialOverlay); self.specialOverlay = null; } // --- Special overlays for green+bomb and green+clear/line combos --- // If this cell was created as a result of a green+bomb or green+clear/line combo, show the overlay on top of the meme asset if (self.specialType === 'bomb') { // Overlay habilidadBomba sprite self.specialOverlay = LK.getAsset('habilidadBomba', { anchorX: 0.5, anchorY: 0.5 }); // Scale overlay to fit cell var padding = 20; var maxWidth = cellSize - padding * 2; var maxHeight = cellSize - padding * 2; var scaleX = maxWidth / self.specialOverlay.width; var scaleY = maxHeight / self.specialOverlay.height; var scale = Math.min(scaleX, scaleY); self.specialOverlay.scale.set(scale, scale); self.addChild(self.specialOverlay); // --- Color sync: animate overlay tint with background --- if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") { var _tweenBombOverlayColor = function tweenBombOverlayColor() { if (!self.specialOverlay) { return; } var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length; var nextColor = colorTweenTargets[nextIndex]; self._specialOverlayTween = tween(self.specialOverlay, { tint: nextColor }, { duration: 14000, easing: tween.easeInOutQuad, onFinish: function onFinish() { if (self.specialOverlay) { _tweenBombOverlayColor(); } } }); }; // Remove any previous color tween if (self._specialOverlayTween) { self._specialOverlayTween.stop && self._specialOverlayTween.stop(); self._specialOverlayTween = null; } self.specialOverlay.tint = colorTweenTargets[currentColorIndex]; _tweenBombOverlayColor(); } } else if (self.specialType === 'green') { // Use the 'habilidadVerde' asset for green special ability if (self.sprite) { self.removeChild(self.sprite); } self.sprite = LK.getAsset('habilidadVerde', { anchorX: 0.5, anchorY: 0.5 }); // Scale to fit cell var padding = 20; var maxWidth = cellSize - padding * 2; var maxHeight = cellSize - padding * 2; var scaleX = maxWidth / self.sprite.width; var scaleY = maxHeight / self.sprite.height; var scale = Math.min(scaleX, scaleY); self.sprite.scale.set(scale, scale); self.addChild(self.sprite); } else if (self.specialType === 'clear' || self.specialType === 'vertical' || self.specialType === 'horizontal') { // Overlay habilidadClear sprite for clear/line specials self.specialOverlay = LK.getAsset('HabilidadClear', { anchorX: 0.5, anchorY: 0.5 }); // Rotate overlay to match direction if (self.specialType === 'horizontal') { self.specialOverlay.rotation = 0; // Horizontal: no rotation } else if (self.specialType === 'vertical') { self.specialOverlay.rotation = Math.PI / 2; // Vertical: 90 degrees } else { self.specialOverlay.rotation = 0; // Default for 'clear' } // Scale overlay to fit cell var padding = 20; var maxWidth = cellSize - padding * 2; var maxHeight = cellSize - padding * 2; var scaleX = maxWidth / self.specialOverlay.width; var scaleY = maxHeight / self.specialOverlay.height; var scale = Math.min(scaleX, scaleY); self.specialOverlay.scale.set(scale, scale); self.addChild(self.specialOverlay); // --- Color sync: animate overlay tint with background --- if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") { var _tweenClearOverlayColor = function tweenClearOverlayColor() { if (!self.specialOverlay) { return; } var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length; var nextColor = colorTweenTargets[nextIndex]; self._specialOverlayTween = tween(self.specialOverlay, { tint: nextColor }, { duration: 14000, easing: tween.easeInOutQuad, onFinish: function onFinish() { if (self.specialOverlay) { _tweenClearOverlayColor(); } } }); }; // Remove any previous color tween if (self._specialOverlayTween) { self._specialOverlayTween.stop && self._specialOverlayTween.stop(); self._specialOverlayTween = null; } self.specialOverlay.tint = colorTweenTargets[currentColorIndex]; _tweenClearOverlayColor(); } } else { self.sprite.tint = 0xFF8800; } } }; self.activateSpecialPower = function () { if (!self.isSpecial) { return; } var cellsToDestroy = []; var bonusPoints = 0; if (self.specialType === 'horizontal') { for (var row = extraRows; row < gridSize + extraRows; row++) { if (gridCells[row][self.col] && gridCells[row][self.col] !== self) { cellsToDestroy.push(gridCells[row][self.col]); } } bonusPoints = 100; } else if (self.specialType === 'vertical') { for (var col = 0; col < gridSize; col++) { if (gridCells[self.row][col] && gridCells[self.row][col] !== self) { cellsToDestroy.push(gridCells[self.row][col]); } } bonusPoints = 100; } else if (self.specialType === 'bomb') { for (var row = Math.max(extraRows, self.row - 1); row <= Math.min(gridSize + extraRows - 1, self.row + 1); row++) { for (var col = Math.max(0, self.col - 1); col <= Math.min(gridSize - 1, self.col + 1); col++) { if (gridCells[row][col] && gridCells[row][col] !== self) { cellsToDestroy.push(gridCells[row][col]); } } } bonusPoints = 150; } else if (self.specialType === 'green') { self.value = 99; // Ensure green special always has value 99 bonusPoints = 200; } // No score pop-up needed if (cellsToDestroy.length > 0) { destroyCells(cellsToDestroy); self.beingDestroyed = true; LK.getSound('Explosion').play(); LK.effects.flashObject(self, 0xFFFFFF, 200); gridContainer.removeChild(self); self.destroy(); gridCells[self.row][self.col] = null; } }; self.showSelection = function () { if (self.selectionHighlight) { return; } self.selectionHighlight = new Container(); var highlight = LK.getAsset('cuadricula', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.15, scaleY: 1.15, alpha: 0.6 }); highlight.tint = 0x00FFFF; self.selectionHighlight.addChild(highlight); self.addChildAt(self.selectionHighlight, 0); function pulseAnimation() { tween(highlight, { alpha: 0.3, scaleX: 1.2, scaleY: 1.2 }, { duration: 500, easing: tween.easeInOutQuad, onComplete: function onComplete() { tween(highlight, { alpha: 0.6, scaleX: 1.15, scaleY: 1.15 }, { duration: 500, easing: tween.easeInOutQuad, onComplete: pulseAnimation }); } }); } pulseAnimation(); }; self.hideSelection = function () { if (self.selectionHighlight) { self.removeChild(self.selectionHighlight); self.selectionHighlight = null; } }; self.down = function (x, y, obj) { handleCellTap(self); }; self.init(); return self; }); var MemeParticle = Container.expand(function () { var self = Container.call(this); self.init = function (textureId) { self.textureId = textureId; if (self.sprite) { self.removeChild(self.sprite); } self.sprite = self.attachAsset(self.textureId, { anchorX: 0.5, anchorY: 0.5 }); self.alpha = 0.8; // Aumenta el tamaño un 20% self.scale.set(0.6); // Aumenta la velocidad (dobla la velocidad base) self.speedX = (Math.random() - 0.5) * 2; self.speedY = 1.0 + Math.random() * 1.0; self.rotationSpeed = (Math.random() - 0.5) * 0.05; return self; }; self.update = function () { self.x += self.speedX; self.y += self.speedY; self.rotation += self.rotationSpeed; if (self.y > 2732 + 100 || self.x < -100 || self.x > 2048 + 100) { self.destroy(); } }; return self; }); var MenuScreen = Container.expand(function () { var self = Container.call(this); // Initialize title touch counter self.titleTouchCount = 0; var title = self.attachAsset('titulo', { anchorX: 0.5, anchorY: 0, scaleX: 3, scaleY: 3 }); title.x = 2048 / 2; title.y = 300; title.interactive = true; // Add touch functionality to title title.down = function (x, y, obj) { self.titleTouchCount++; // Flash title when touched LK.effects.flashObject(title, 0xFFFFFF, 200); // After 10 touches, change title to easter egg 2 if (self.titleTouchCount >= 10) { // Remove the current title self.removeChild(title); // Create new title with easterEgg2 asset title = self.attachAsset('easterEgg2', { anchorX: 0.5, anchorY: 0, scaleX: 3, scaleY: 3 }); title.x = 2048 / 2; title.y = 300; // Play a sound effect for the easter egg LK.getSound('Explosion').play(); } }; function pulseTitle() { tween(title, { scaleX: 3.2, scaleY: 3.4, y: 200 }, { duration: 400, easing: tween.easeOutQuad, onFinish: function onFinish() { tween(title, { scaleX: 3.5, scaleY: 2.7, y: 350 }, { duration: 300, easing: tween.easeInQuad, onFinish: function onFinish() { tween(title, { rotation: -0.05, scaleX: 3.1, scaleY: 3.2, y: 320 }, { duration: 180, easing: tween.easeOutQuad, onFinish: function onFinish() { tween(title, { rotation: 0.05, scaleX: 3.2, scaleY: 3.1 }, { duration: 180, easing: tween.easeInOutQuad, onFinish: function onFinish() { tween(title, { rotation: 0, scaleX: 3, scaleY: 3, y: 300 }, { duration: 250, easing: tween.easeOutElastic, onFinish: function onFinish() { LK.setTimeout(pulseTitle, 800); } }); } }); } }); } }); } }); } pulseTitle(); // First column var classicButton = createButton("ZEN MODE", 0x00AA00, 3, 1.5, function () { self.startGame('classic'); }, 2048 / 4, 2732 / 2 + 400); self.addChild(classicButton); var seekModeButton = createButton("SEEK MODE", 0xDD5500, 3, 1.5, function () { self.startGame('seekmode'); }, 2048 / 4, 2732 / 2 + 700); self.addChild(seekModeButton); var fastButton = createButton("FAST MODE", 0x0088FF, 3, 1.5, function () { self.startGame('fast'); }, 2048 / 4, 2732 / 2 + 1000); self.addChild(fastButton); // Second column var cosmeticsButton = createButton("COSMETICS", 0x9932CC, 3, 1.5, function () { game.removeChild(self); showCosmeticsScreen(); }, 2048 * 3 / 4, 2732 / 2 + 400); self.addChild(cosmeticsButton); var recordsButton = createButton("RECORDS", 0xFFA500, 3, 1.5, function () { game.removeChild(self); showRecordsScreen(); }, 2048 * 3 / 4, 2732 / 2 + 700); self.addChild(recordsButton); var settingsButton = createButton("SETTINGS", 0x800080, 3, 1.5, function () { game.removeChild(self); showSettingsScreen(); }, 2048 * 3 / 4, 2732 / 2 + 1000); self.addChild(settingsButton); self.startGame = function (mode) { game.gameMode = mode || 'classic'; game.removeChild(self); game.initializeGame(); }; self.particleContainer = new Container(); self.addChildAt(self.particleContainer, 0); self.memeParticles = ['meme1', 'meme2', 'meme3', 'meme4', 'meme5', 'meme1-2', 'meme2-2', 'meme3-2', 'meme4-2', 'meme5-2', 'meme1-3', 'meme2-3', 'meme3-3', 'meme4-3', 'meme5-3']; self.particleTimer = LK.setInterval(function () { self.emitParticle(); }, 500); self.emitParticle = function () { if (!particlesEnabled) return; var randomMemeId = self.memeParticles[Math.floor(Math.random() * self.memeParticles.length)]; var particle = new MemeParticle().init(randomMemeId); particle.x = Math.random() * 2048; particle.y = -50; self.particleContainer.addChild(particle); }; self.update = function () { self.particleContainer.children.forEach(function (particle) { if (particle && particle.update) { // Check collision with pointerSprite (global) if (typeof pointerSprite !== "undefined" && pointerSprite.visible && particle.x !== undefined && particle.y !== undefined && pointerSprite.x !== undefined && pointerSprite.y !== undefined) { // Use simple distance check (circle collision) var dx = particle.x - pointerSprite.x; var dy = particle.y - pointerSprite.y; var dist = Math.sqrt(dx * dx + dy * dy); // Use average radius (particle is 100x100, pointer is 10x10) if (dist < 55) { // Trigger explosion: emit particles at this position if (particlesEnabled) { var numParticles = 10 + Math.floor(Math.random() * 5); for (var p = 0; p < numParticles; p++) { var part = LK.getAsset('particula', { anchorX: 0.5, anchorY: 0.5 }); // Randomize color (RGB) var r = Math.floor(128 + Math.random() * 127); var g = Math.floor(128 + Math.random() * 127); var b = Math.floor(128 + Math.random() * 127); var rgb = r << 16 | g << 8 | b; part.tint = rgb; part.x = particle.x; part.y = particle.y; // Random velocity and direction var angle = Math.random() * Math.PI * 2; var speed = 7 + Math.random() * 7; var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; // Animate particle (function (part, vx, vy) { var lifetime = 180 + Math.random() * 120; var startAlpha = 0.9 + Math.random() * 0.1; part.alpha = startAlpha; self.particleContainer.addChild(part); var elapsed = 0; var updateFn = function updateFn() { part.x += vx; part.y += vy; part.alpha -= 0.05 + Math.random() * 0.03; part.scale.x *= 0.92; part.scale.y *= 0.92; elapsed += 16; if (elapsed > lifetime || part.alpha <= 0.05) { if (part.parent) { part.parent.removeChild(part); } LK.clearInterval(intervalId); } }; var intervalId = LK.setInterval(updateFn, 16); })(part, vx, vy); } } // Play explosion sound LK.getSound('Explosion').play(); // --- Easter Egg: cuenta partículas meme destruidas en menú --- if (!easterEggActive) { memeParticlesDestroyedInMenu++; if (memeParticlesDestroyedInMenu >= 20) { // Mostrar easter egg solo si no está activo easterEggActive = true; memeParticlesDestroyedInMenu = 0; // Crear contenedor del easter egg easterEggContainer = new Container(); var egg = LK.getAsset('easterEgg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 1.1, scaleY: 1.1, alpha: 0.0 }); easterEggContainer.addChild(egg); // Fade in tween(egg, { alpha: 1 }, { duration: 600 }); // Añadir al menú self.addChild(easterEggContainer); easterEggTimeout = LK.setTimeout(function () { if (easterEggContainer && easterEggContainer.parent) { // Fade out tween(egg, { alpha: 0 }, { duration: 500 }); // Removed tween on eggText (no eggText exists) LK.setTimeout(function () { if (easterEggContainer.parent) { self.removeChild(easterEggContainer); } easterEggContainer = null; easterEggActive = false; }, 600); } else { easterEggActive = false; easterEggContainer = null; } }, 5000); } } // Remove the meme particle if (particle.parent) { particle.parent.removeChild(particle); } if (typeof particle.destroy === "function") { particle.destroy(); } return; // Only destroy one per frame } } particle.update(); } }); }; self.destroy = function () { LK.clearInterval(self.particleTimer); self.particleTimer = null; self.particleContainer.destroy({ children: true }); self.particleContainer = null; Container.prototype.destroy.call(self); }; return self; }); var PauseButton = Container.expand(function () { var self = Container.call(this); var pauseIcon = self.attachAsset('Pause', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, // 0.8 * 1.5 = 1.2 scaleY: 1.2 }); var background = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.05, // 0.7 * 1.5 = 1.05 scaleY: 1.05 }); background.tint = 0x444444; background.alpha = 0.7; self.addChildAt(background, 0); self.down = function (x, y, obj) { LK.effects.flashObject(self, 0xFFFFFF, 200); LK.getSound('ButtonSound').play(); showPausePopup(); }; return self; }); var PausePopup = Container.expand(function () { var self = Container.call(this); var popupBg = self.attachAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 3.5, scaleY: 4.5 }); popupBg.tint = 0x222222; popupBg.alpha = 0.9; var popupTitle = new Text2("PAUSED", { size: 100, fill: 0xFFFFFF }); popupTitle.anchor.set(0.5, 0); popupTitle.y = -250; self.addChild(popupTitle); var continueButton = createButton("CONTINUE", 0x00AA00, 2.5, 1.2, function () { game.removeChild(self); }, 0, -20); self.addChild(continueButton); var surrenderButton = createButton("SURRENDER", 0xFF0000, 2.5, 1.2, function () { game.removeChild(self); game.onGameOver(); }, 0, 200); self.addChild(surrenderButton); return self; }); var RecordsScreen = Container.expand(function () { var self = Container.call(this); // Create title var title = new Text2("High Scores", { size: 150, fill: 0x000000 }); title.anchor.set(0.5, 0); title.x = 2048 / 2; title.y = 300; self.addChild(title); // Refresh high scores when shown self.refreshHighScores = function () { // Clear existing score texts if any if (self.classicScoreText) { self.removeChild(self.classicScoreText); self.classicScoreText = null; } if (self.fastScoreText) { self.removeChild(self.fastScoreText); self.fastScoreText = null; } if (self.seekScoreText) { self.removeChild(self.seekScoreText); self.seekScoreText = null; } // Create mode buttons with high scores function createModeButton(modeName, score, color, posY) { var button = new Container(); var buttonBg = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 1.5 }); buttonBg.tint = color; var modeText = new Text2(modeName, { size: 90, fill: 0xFFFFFF }); modeText.anchor.set(0.5, 0); modeText.y = -100; var scoreText = new Text2(score.toString(), { size: 70, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); scoreText.y = 10; button.addChild(buttonBg); button.addChild(modeText); button.addChild(scoreText); button.x = 2048 / 2; button.y = posY; // --- Easter Egg 3 logic --- if (typeof window.easterEgg3Counters === "undefined") { window.easterEgg3Counters = { zen: 0, fast: 0, seek: 0 }; } if (typeof window.easterEgg3Active === "undefined") { window.easterEgg3Active = false; } if (typeof window.easterEgg3Container === "undefined") { window.easterEgg3Container = null; } button.interactive = true; button.down = function (x, y, obj) { // Identify which button was pressed by modeName if (modeName === "Zen Mode") { window.easterEgg3Counters.zen++; if (window.easterEgg3Counters.zen > 2) window.easterEgg3Counters.zen = 2; } else if (modeName === "Fast Mode") { window.easterEgg3Counters.fast++; if (window.easterEgg3Counters.fast > 3) window.easterEgg3Counters.fast = 3; } else if (modeName === "Seek Mode") { window.easterEgg3Counters.seek++; if (window.easterEgg3Counters.seek > 1) window.easterEgg3Counters.seek = 1; } // Check if all conditions are met if (window.easterEgg3Counters.zen === 2 && window.easterEgg3Counters.fast === 3 && window.easterEgg3Counters.seek === 1 && !window.easterEgg3Active) { window.easterEgg3Active = true; // Show easterEgg3 asset centered and above all if (window.easterEgg3Container && window.easterEgg3Container.parent) { window.easterEgg3Container.parent.removeChild(window.easterEgg3Container); } window.easterEgg3Container = new Container(); var egg3 = LK.getAsset('easterEgg3', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 1, scaleY: 1, alpha: 0 }); window.easterEgg3Container.addChild(egg3); // Fade in tween(egg3, { alpha: 1 }, { duration: 600 }); // Add to records screen if (typeof game !== "undefined" && game.children && game.children.indexOf(self) !== -1) { self.addChild(window.easterEgg3Container); } else if (typeof game !== "undefined") { game.addChild(window.easterEgg3Container); } // Fade out and remove after 5 seconds LK.setTimeout(function () { tween(egg3, { alpha: 0 }, { duration: 500 }); LK.setTimeout(function () { if (window.easterEgg3Container && window.easterEgg3Container.parent) { window.easterEgg3Container.parent.removeChild(window.easterEgg3Container); } window.easterEgg3Container = null; window.easterEgg3Active = false; // Reset counters so it can be triggered again window.easterEgg3Counters.zen = 0; window.easterEgg3Counters.fast = 0; window.easterEgg3Counters.seek = 0; }, 600); }, 5000); } }; return button; } self.addChild(createModeButton("Zen Mode", highScoreClassic, 0x00AA00, 800)); self.addChild(createModeButton("Fast Mode", highScoreFast, 0x0088FF, 1100)); self.addChild(createModeButton("Seek Mode", highScoreSeek, 0xDD5500, 1400)); }; // Refresh scores when created self.refreshHighScores(); // Back button var backButton = createButton("BACK TO MENU", 0xFF5500, 3, 1.5, function () { game.removeChild(self); initializeMenu(); }, 2048 / 2, 2732 - 200); self.addChild(backButton); return self; }); var SeekMode = Container.expand(function () { var self = Container.call(this); // Similar to classic mode logic var gridContainer = new Container(); self.addChild(gridContainer); return self; }); var SettingsScreen = Container.expand(function () { var self = Container.call(this); var title = new Text2("Settings", { size: 150, fill: 0x000000 }); title.anchor.set(0.5, 0); title.x = 2048 / 2; title.y = 300; self.addChild(title); // --- Improved vertical separation and position for all settings buttons --- var settingsButtonSpacing = 320; // Increased spacing var firstButtonY = 800; // Lowered starting Y // RESET PROGRESS button (first, red, prominent) var resetButton = createButton("RESET PROGRESS", 0xFF0000, 3.7, 1.5, function () { // Show confirmation popup var confirmPopup = new Container(); // Enlarged popup background var popupBg = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 5.5, scaleY: 5.7 }); popupBg.tint = 0x222222; popupBg.alpha = 0.9; var popupTitle = new Text2("Reset all progress?", { size: 80, fill: 0xFFFFFF }); popupTitle.anchor.set(0.5, 0); popupTitle.y = -440; var popupText = new Text2("This will reset all high scores\nThis cannot be undone.", { size: 60, fill: 0xFFFFFF }); popupText.anchor.set(0.5, 0); popupText.y = -300; // Confirm and Cancel buttons, separated vertically var confirmButton = createButton("CONFIRM", 0xFF0000, 2.5, 1.2, function () { // Reset high scores highScoreClassic = 0; highScoreFast = 0; highScoreSeek = 0; storage.highScoreClassic = 0; storage.highScoreFast = 0; storage.highScoreSeek = 0; // Show confirmation message var messagePopup = new Container(); var messageBg = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 5.5, scaleY: 1.5 }); messageBg.tint = 0x00AA00; var messageText = new Text2("Progress reset successfully", { size: 80, fill: 0xFFFFFF }); messageText.anchor.set(0.5, 0.5); messagePopup.addChild(messageBg); messagePopup.addChild(messageText); messagePopup.x = 2048 / 2; messagePopup.y = 2732 / 2; game.addChild(messagePopup); // Remove message after 2 seconds LK.setTimeout(function () { game.removeChild(messagePopup); }, 2000); // Remove confirmation popup game.removeChild(confirmPopup); }, 0, 60); var cancelButton = createButton("CANCEL", 0x00AA00, 2.5, 1.2, function () { game.removeChild(confirmPopup); }, 0, 280); // Increased vertical separation confirmPopup.addChild(popupBg); confirmPopup.addChild(popupTitle); confirmPopup.addChild(popupText); confirmPopup.addChild(confirmButton); confirmPopup.addChild(cancelButton); confirmPopup.x = 2048 / 2; confirmPopup.y = 2732 / 2; game.addChild(confirmPopup); }, 2048 / 2, firstButtonY); self.addChild(resetButton); // DRAG MEME button var dragButtonLabel = function dragButtonLabel() { return dragMemeEnabled ? "DRAG MEME: ON" : "DRAG MEME: OFF"; }; var dragButton = createButton(dragButtonLabel(), 0x0088FF, 3.7, 1.5, function () { dragMemeEnabled = !dragMemeEnabled; storage.dragMemeEnabled = dragMemeEnabled; dragButton.label.setText(dragButtonLabel()); }, 2048 / 2, firstButtonY + settingsButtonSpacing * 1); self.addChild(dragButton); // INTRO ENABLE/DISABLE button var introButtonLabel = function introButtonLabel() { return introEnabled ? "SHOW INTRO: ON" : "SHOW INTRO: OFF"; }; var introButton = createButton(introButtonLabel(), 0x222222, 3.7, 1.5, function () { introEnabled = !introEnabled; storage.introEnabled = introEnabled; introButton.label.setText(introButtonLabel()); }, 2048 / 2, firstButtonY + settingsButtonSpacing * 2); self.addChild(introButton); // BACKGROUND COLOR ANIMATION button var bgColorAnimButtonLabel = function bgColorAnimButtonLabel() { return backgroundColorAnimEnabled ? "BG COLOR: ON" : "BG COLOR: OFF"; }; var bgColorAnimButton = createButton(bgColorAnimButtonLabel(), 0x7ec8e3, 3.7, 1.5, function () { backgroundColorAnimEnabled = !backgroundColorAnimEnabled; storage.backgroundColorAnimEnabled = backgroundColorAnimEnabled; bgColorAnimButton.label.setText(bgColorAnimButtonLabel()); // If disabling, set background and UiBar to static color if (!backgroundColorAnimEnabled) { if (typeof backgroundAsset !== "undefined" && backgroundAsset) { backgroundAsset.tint = colorTweenTargets[0]; } if (game.uiBarBg) { game.uiBarBg.tint = colorTweenTargets[0]; } } else { // If enabling, restart color tween if (typeof tweenBackgroundToNextColor === "function") { tweenBackgroundToNextColor(); } if (typeof _tweenUiBarToNextColor === "function") { _tweenUiBarToNextColor(); } } }, 2048 / 2, firstButtonY + settingsButtonSpacing * 3); self.addChild(bgColorAnimButton); // PARTICLES ENABLE/DISABLE button var particlesEnabledLabel = function particlesEnabledLabel() { return particlesEnabled ? "PARTICLES: ON" : "PARTICLES: OFF"; }; var particlesButton = createButton(particlesEnabledLabel(), 0x00bfff, 3.7, 1.5, function () { particlesEnabled = !particlesEnabled; storage.particlesEnabled = particlesEnabled; particlesButton.label.setText(particlesEnabledLabel()); }, 2048 / 2, firstButtonY + settingsButtonSpacing * 4); self.addChild(particlesButton); // BACK TO MENU button (bottom, with extra margin) var backButton = createButton("BACK TO MENU", 0xFF5500, 3, 1.5, function () { game.removeChild(self); initializeMenu(); }, 2048 / 2, 2732 - 200); self.addChild(backButton); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // --- PARTICULAS AL COLISIONAR checkPointer CON UN MEME --- game.update = function () { // Solo si la grilla está inicializada y checkPointers existen if (typeof gridCells !== "undefined" && Array.isArray(checkPointers) && checkPointers.length > 0) { // Recorre solo la zona visible for (var row = extraRows; row < gridSize + extraRows; row++) { for (var col = 0; col < gridSize; col++) { if (!gridCells[row]) { continue; } var cell = gridCells[row][col]; if (cell && cell.sprite && !cell.beingDestroyed) { // Chequea colisión simple (círculo) entre cada checkPointer y el centro de la celda for (var i = 0; i < checkPointers.length; i++) { var checkPointer = checkPointers[i]; if (!checkPointer.visible) { continue; } var dx = checkPointer.x - cell.x; var dy = checkPointer.y - cell.y; var dist = Math.sqrt(dx * dx + dy * dy); // Usa radio generoso (meme ~100, checkPointer ~20) if (dist < 80) { // Solo una vez por colisión: marca la celda como "siendo destruida por check" if (!cell._checkCollisionActive) { cell._checkCollisionActive = true; // Efecto de partículas: 10-16 partículas if (particlesEnabled) { var numParticles = 10 + Math.floor(Math.random() * 7); for (var p = 0; p < numParticles; p++) { var part = LK.getAsset('particula', { anchorX: 0.5, anchorY: 0.5 }); // Color aleatorio // Usa el color actual del fondo para las partículas var bgColor = backgroundAsset.tint; part.tint = bgColor; part.x = cell.x; part.y = cell.y; // Aumenta el tamaño un 10% part.scale.x = 1.7; part.scale.y = 1.7; // Velocidad y dirección aleatoria, pero nunca hacia arriba (vy >= 0), y velocidad reducida var angle = Math.random() * Math.PI; // Solo de 0 a PI (derecha a izquierda, abajo) var speed = 5.5 + Math.random() * 2.5; // Más lento que antes var vx = Math.cos(angle) * speed; var vy = Math.abs(Math.sin(angle) * speed); // Siempre hacia abajo o lateral // Animación de partícula (function (part, vx, vy) { var lifetime = 180 + Math.random() * 120; var startAlpha = 0.9 + Math.random() * 0.1; part.alpha = startAlpha; if (gridContainer && typeof gridContainer.addChild === "function") { gridContainer.addChild(part); } else { game.addChild(part); } var elapsed = 0; var updateFn = function updateFn() { part.x += vx; part.y += vy; part.alpha -= 0.05 + Math.random() * 0.03; part.scale.x *= 0.92; part.scale.y *= 0.92; elapsed += 16; if (elapsed > lifetime || part.alpha <= 0.05) { if (part.parent) { part.parent.removeChild(part); } LK.clearInterval(intervalId); } }; var intervalId = LK.setInterval(updateFn, 16); })(part, vx, vy); } } // Efecto visual (sin sonido) LK.effects.flashObject(cell, 0xFFFFFF, 200); // (Eliminado sonido de explosión) // Opcional: puedes marcar la celda para que no repita el efecto hasta que se mueva el checkPointer // Elimina la marca después de un breve tiempo para permitir nuevas colisiones LK.setTimeout(function () { cell._checkCollisionActive = false; }, 400); } } } } } } } }; // --- INTRO OPTION: persistently store intro enabled/disabled --- // Load dragMemeEnabled from storage if available, otherwise default to true var dragMemeEnabled = typeof storage.dragMemeEnabled === "boolean" ? storage.dragMemeEnabled : true; storage.dragMemeEnabled = dragMemeEnabled; // Load introEnabled from storage if available, otherwise default to true var introEnabled = typeof storage.introEnabled === "boolean" ? storage.introEnabled : true; storage.introEnabled = introEnabled; // Load backgroundColorAnimEnabled from storage if available, otherwise default to true var backgroundColorAnimEnabled = typeof storage.backgroundColorAnimEnabled === "boolean" ? storage.backgroundColorAnimEnabled : true; storage.backgroundColorAnimEnabled = backgroundColorAnimEnabled; // Load particlesEnabled from storage if available, otherwise default to true var particlesEnabled = typeof storage.particlesEnabled === "boolean" ? storage.particlesEnabled : true; storage.particlesEnabled = particlesEnabled; var backgroundAsset = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChildAt(backgroundAsset, 0); // --- Easter Egg: contador de partículas meme destruidas en menú --- var memeParticlesDestroyedInMenu = 0; var easterEggActive = false; var easterEggTimeout = null; var easterEggContainer = null; // --- Smoothly tween background color forever --- var colorTweenTargets = [0xffb347, 0x7ec8e3, 0x8aff80, 0xff80c0, 0xf9ff80, 0x80ffd9, 0xd1b3ff, 0xffffff, 0xffe066, 0x6ec6ff, 0xff6ec7, 0x6effb3, 0xff6666, 0x66ffea, 0x9d66ff, 0x66ff66, 0xffe6b3, 0xb3e6ff, 0xe6b3ff, 0xb3ffb3]; var currentColorIndex = 0; function tweenBackgroundToNextColor() { var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length; var nextColor = colorTweenTargets[nextIndex]; if (!backgroundColorAnimEnabled) { // If disabled, set to static color and do not continue tweening backgroundAsset.tint = colorTweenTargets[0]; return; } tween(backgroundAsset, { tint: nextColor }, { duration: 14000, // much slower and smoother (was 7000) easing: tween.easeInOutQuad, onFinish: function onFinish() { currentColorIndex = nextIndex; tweenBackgroundToNextColor(); } }); } // Start the color tween loop backgroundAsset.tint = colorTweenTargets[0]; if (backgroundColorAnimEnabled) { tweenBackgroundToNextColor(); } // Game state and initialization var menuScreen; var cosmeticsScreen; var recordsScreen; var settingsScreen; var pausePopup; var gameInitialized = false; var score = 0; var scoreText; var gridContainer; var gameMode = 'classic'; // Track current game mode var timerText; var timerSeconds = 10; // 10 seconds timer for Fast Mode var timerInterval; var saveScoreInterval; // Interval for saving classic mode high score var seekMode; // Reference to seek mode instance // Game tracks scores in local variables, no global persistence var highScoreClassic = typeof storage.highScoreClassic === "number" ? storage.highScoreClassic : 0; // Zen Mode high score var highScoreFast = typeof storage.highScoreFast === "number" ? storage.highScoreFast : 0; // Fast Mode high score var highScoreSeek = typeof storage.highScoreSeek === "number" ? storage.highScoreSeek : 0; // Seek Mode high score // --- SEEK MODE: Block meme movement for 5 seconds and show countdown --- var seekModeBlockActive = false; var seekModeBlockTimer = null; var seekModeBlockSeconds = 5; var seekModeBlockText = null; // --- SEEK MODE: Combo tracking and visualization --- var seekModeComboCount = 0; var seekModeComboText = null; var seekModeShowAllTimer = null; // Function to show settings screen function showSettingsScreen() { if (!settingsScreen) { settingsScreen = new SettingsScreen(); } game.addChild(settingsScreen); } // Create a reusable button function function createButton(text, bgColor, width, height, callback, buttonX, buttonY) { var button = new Container(); var buttonBg = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: width || 3, scaleY: height || 1.5 }); buttonBg.tint = bgColor || 0x00AA00; var buttonText = new Text2(text, { size: 70, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); button.addChild(buttonBg); button.addChild(buttonText); // Add button interaction button.interactive = true; button.down = function (x, y, obj) { LK.effects.flashObject(buttonBg, 0xFFFFFF, 200); LK.getSound('ButtonSound').play(); if (callback) { LK.setTimeout(function () { callback(); }, 300); } }; // Store references for later access button.background = buttonBg; button.label = buttonText; // Set position if provided if (buttonX !== undefined) { button.x = buttonX; } if (buttonY !== undefined) { button.y = buttonY; } return button; } // --- UiBar initialization: always present, not just in game modes --- if (!game.uiBarBg) { var _tweenUiBarToNextColor = function tweenUiBarToNextColor() { var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length; var nextColor = colorTweenTargets[nextIndex]; if (!backgroundColorAnimEnabled) { // If disabled, set to static color and do not continue tweening uiBar.tint = colorTweenTargets[0]; return; } tween(uiBar, { tint: nextColor }, { duration: 14000, easing: tween.easeInOutQuad, onFinish: function onFinish() { // No need to update currentColorIndex here, backgroundAsset already does it _tweenUiBarToNextColor(); } }); }; // Calculate bar height as 1/6 of screen height var barHeight = Math.floor(2732 / 6); var barWidth = 2400; // Get UiBar asset and scale to fit width and height var uiBar = LK.getAsset('UiBar', { anchorX: 0, anchorY: 0, x: -30, y: -30, scaleX: barWidth / 100, scaleY: barHeight / 100, alpha: 1 }); // Place at top of screen, behind score text game.uiBarBg = uiBar; // Insert at zIndex 1 (above background, below score text) game.addChildAt(uiBar, 1); // --- Smoothly tween UiBar color forever, synchronized with backgroundAsset --- uiBar.tint = colorTweenTargets[0]; if (backgroundColorAnimEnabled) { _tweenUiBarToNextColor(); } } // Centralized initialization function for adding elements to the game function initializeGameElements() { // Create and add grid container if it doesn't exist if (!gridContainer) { gridContainer = new Container(); game.addChild(gridContainer); } // --- Move UiBar and UI texts above memes --- // Remove and re-add UiBar to ensure it's above memes if (game.uiBarBg && game.uiBarBg.parent) { game.uiBarBg.parent.removeChild(game.uiBarBg); } if (game.uiBarBg) { game.addChild(game.uiBarBg); } // Create score text for all modes if (!scoreText) { scoreText = new Text2("Score: 0", { size: 120, fill: 0x222222 }); scoreText.anchor.set(0.5, 0); scoreText.x = 2048 / 2; scoreText.y = 80; } game.addChild(scoreText); // Create timer text for fast mode if needed if (game.gameMode === 'fast' && !timerText) { timerText = new Text2("Time: " + timerSeconds.toFixed(1), { size: 140, fill: 0xFF0000 }); timerText.anchor.set(0.5, 1); timerText.x = 2048 / 2; timerText.y = 2732 - 100; } if (timerText) { game.addChild(timerText); } // Add mode indicator text var modeTextContent = "Zen Mode"; var modeTextColor = 0x00AA00; if (game.gameMode === 'fast') { modeTextContent = "Fast Mode"; modeTextColor = 0x0088FF; } else if (game.gameMode === 'seekmode') { modeTextContent = "Seek Mode"; modeTextColor = 0xDD5500; } if (game.modeText && game.modeText.parent) { game.modeText.parent.removeChild(game.modeText); } game.modeText = new Text2(modeTextContent, { size: 80, fill: modeTextColor }); game.modeText.anchor.set(0.5, 0); game.modeText.x = 2048 / 2; game.modeText.y = 180; game.addChild(game.modeText); // Add pause button to the top right corner in zen mode and seek mode if (game.gameMode === 'classic' || game.gameMode === 'seekmode') { if (game.pauseButton && game.pauseButton.parent) { game.pauseButton.parent.removeChild(game.pauseButton); } game.pauseButton = new PauseButton(); // Add a 20px margin from the top and right edges game.pauseButton.x = 2048 - 100 - 45; game.pauseButton.y = 100 + 45; game.pauseButton.interactive = true; game.addChild(game.pauseButton); } } // Function to show pause popup function showPausePopup() { if (!pausePopup) { pausePopup = new PausePopup(); pausePopup.x = 2048 / 2; pausePopup.y = 2732 / 2; } game.addChild(pausePopup); // Fast Mode doesn't have pause functionality, so no need to pause the timer } // Show records screen function showRecordsScreen() { if (!recordsScreen) { recordsScreen = new RecordsScreen(); } // Make sure to refresh high scores every time the screen is shown if (recordsScreen.refreshHighScores) { recordsScreen.refreshHighScores(); } game.addChild(recordsScreen); } // --- COSMETICS: Track selected cosmetic set globally and persistently --- // Load selectedSet from storage if available, otherwise default to 1 var selectedSet = typeof storage.selectedSet === "number" ? storage.selectedSet : 1; storage.selectedSet = selectedSet; // Ensure it's always persisted on load // Show cosmetics screen function showCosmeticsScreen() { if (!cosmeticsScreen) { cosmeticsScreen = new CosmeticsScreen(); } game.addChild(cosmeticsScreen); } // Initialize the menu function initializeMenu() { if (menuScreen) { // Clean up meme particles and intervals to prevent duplicates if (typeof menuScreen.destroy === "function") { menuScreen.destroy(); } menuScreen = null; } menuScreen = new MenuScreen(); game.addChild(menuScreen); } // Function to periodically save high score and manage music function startPeriodicSave() { // Clear any existing save interval if (saveScoreInterval) { LK.clearInterval(saveScoreInterval); saveScoreInterval = null; } // Save high score every second if it's better saveScoreInterval = LK.setInterval(function () { if (game.gameMode === 'classic' && score > highScoreClassic) { highScoreClassic = score; storage.highScoreClassic = highScoreClassic; } else if (game.gameMode === 'seekmode' && score > highScoreSeek) { highScoreSeek = score; storage.highScoreSeek = highScoreSeek; } }, 1000); // Start background music startBackgroundMusic(); } // Function to manage background music function startBackgroundMusic() { // Clear existing music interval if it exists if (game.musicInterval) { LK.clearInterval(game.musicInterval); game.musicInterval = null; } // Array of available music tracks var musicTracks = ['Backgroundmusic', 'backgroundmusic2']; // Track the last played track to avoid repeats var lastTrackIndex = -1; // Function to play the next track (not repeating the same one) function playNextTrack() { var nextIndex; if (musicTracks.length === 1) { nextIndex = 0; } else { // Pick a different track than the last one do { nextIndex = Math.floor(Math.random() * musicTracks.length); } while (nextIndex === lastTrackIndex); } var selectedTrack = musicTracks[nextIndex]; lastTrackIndex = nextIndex; // Play the selected track, and set up onFinish to play the next one LK.playMusic(selectedTrack, { loop: false, onFinish: function onFinish() { // When the song finishes, play the next one playNextTrack(); } }); console.log("Now playing: " + selectedTrack); } // Play the first track playNextTrack(); // Remove the old interval-based music change (not needed anymore) } // Initialize the game game.initializeGame = function () { // Initialize score system score = 0; // Clear existing grid container if it exists if (gridContainer) { while (gridContainer.children.length > 0) { var child = gridContainer.children[0]; gridContainer.removeChild(child); child.destroy(); } } // Initialize timer for Fast Mode if (game.gameMode === 'fast') { // Clear any existing timer if (timerInterval) { LK.clearInterval(timerInterval); timerInterval = null; } // Calculate initial max timer value based on score var maxTimer = 10; // Use exponential thresholds for slower decrease if (score >= 100) { maxTimer = 9; } if (score >= 500) { maxTimer = 8; } if (score >= 1000) { maxTimer = 7; } if (score >= 3500) { maxTimer = 6; } if (score >= 10000) { maxTimer = 5; } if (score >= 25000) { maxTimer = 4; } if (score >= 50000) { maxTimer = 3; } // Reset timer value timerSeconds = maxTimer; if (timerText) { timerText.setText("Time: " + maxTimer.toFixed(1)); } // Start timer countdown startTimer(); } // Centralize initialization of all game elements initializeGameElements(); // Initialize score initializeScore(); // Initialize the game grid initializeGrid(); ensureNoInitialMatches(); gameInitialized = true; // Start periodic save of high score for classic mode startPeriodicSave(); // --- SEEK MODE: Block meme movement for 5 seconds and show countdown --- if (game.gameMode === 'seekmode') { // Block meme movement seekModeBlockActive = true; seekModeBlockSeconds = 5; // Reset combo counter seekModeComboCount = 0; // Remove previous combo text if any if (seekModeComboText) { game.removeChild(seekModeComboText); seekModeComboText.destroy(); seekModeComboText = null; } // Remove previous block text if any if (seekModeBlockText) { game.removeChild(seekModeBlockText); seekModeBlockText.destroy(); seekModeBlockText = null; } // Create and show countdown text at bottom center, but a bit higher to leave space for combo text below seekModeBlockText = new Text2("Wait: " + seekModeBlockSeconds, { size: 120, fill: 0xDD5500, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); seekModeBlockText.anchor.set(0.5, 1); seekModeBlockText.x = 2048 / 2; seekModeBlockText.y = 2732 - 220; // Move up to leave space for combo text below game.addChild(seekModeBlockText); // Start countdown timer if (seekModeBlockTimer) { LK.clearInterval(seekModeBlockTimer); seekModeBlockTimer = null; } seekModeBlockTimer = LK.setInterval(function () { seekModeBlockSeconds--; if (seekModeBlockText) { seekModeBlockText.setText("Wait: " + seekModeBlockSeconds); } if (seekModeBlockSeconds <= 0) { seekModeBlockActive = false; if (seekModeBlockTimer) { LK.clearInterval(seekModeBlockTimer); seekModeBlockTimer = null; } if (seekModeBlockText) { game.removeChild(seekModeBlockText); seekModeBlockText.destroy(); seekModeBlockText = null; } // --- Set all current memes to use the 'seek' sprite --- for (var row = 0; row < gridSize + extraRows; row++) { for (var col = 0; col < gridSize; col++) { var cell = gridCells[row][col]; if (cell && cell.sprite) { // Remove old sprite cell.removeChild(cell.sprite); // Attach new 'seek' sprite cell.sprite = LK.getAsset('seek', { anchorX: 0.5, anchorY: 0.5 }); // Scale to fit cell var padding = 20; var maxWidth = cellSize - padding * 2; var maxHeight = cellSize - padding * 2; var scaleX = maxWidth / cell.sprite.width; var scaleY = maxHeight / cell.sprite.height; var scale = Math.min(scaleX, scaleY); cell.sprite.scale.set(scale, scale); cell.addChild(cell.sprite); } } } // --- Mark that new memes should use 'seek' sprite --- game.seekModeForceSeekSprite = true; // Create combo counter text seekModeComboCount = 0; if (seekModeComboText) { game.removeChild(seekModeComboText); seekModeComboText.destroy(); } seekModeComboText = new Text2("Combos: 0/3", { size: 120, fill: 0xDD5500, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); seekModeComboText.anchor.set(0.5, 0); seekModeComboText.x = 2048 / 2; seekModeComboText.y = 2732 - 120; // Place just below the wait text game.addChild(seekModeComboText); } }, 1000); } else { // Clean up if not in seek mode seekModeBlockActive = false; if (seekModeBlockTimer) { LK.clearInterval(seekModeBlockTimer); seekModeBlockTimer = null; } if (seekModeBlockText) { game.removeChild(seekModeBlockText); seekModeBlockText.destroy(); seekModeBlockText = null; } // --- FIX: Reset seekModeForceSeekSprite so memes use correct sprite on re-enter --- game.seekModeForceSeekSprite = false; } }; // Create and position score text function initializeScore() { score = 0; if (scoreText) { scoreText.setText("Score: 0"); } } // Update score and score text function updateScore(points) { if (typeof points === "number" && points > 0) { score += points; if (scoreText) { scoreText.setText("Score: " + score); } } } var cellPool = []; function getGridCell() { if (cellPool.length > 0) { return cellPool.pop().init(); } return new GridCell(); } function recycleGridCell(cell) { if (cell) { cellPool.push(cell); } } var selectedCell = null; var isAnimating = false; window.gravityInProgress = false; window.fillInProgress = false; // Track combo counts for sound effects var comboCounter = 0; var comboInProgress = false; function handleCellTap(tappedCell) { // Bloquea romper y mover memes durante el periodo visible en seekmode if (isAnimating || window.gravityInProgress || window.fillInProgress || pausePopup && game.children.includes(pausePopup) || game.gameMode === 'seekmode' && (seekModeBlockActive || game.seekModeForceSeekSprite && seekModeComboText && seekModeComboText.text && seekModeComboText.text.indexOf('MEMES VISIBLE') === 0)) { return; } if (!tappedCell || !gridCells[tappedCell.row] || gridCells[tappedCell.row][tappedCell.col] !== tappedCell || tappedCell.beingDestroyed) { return; } if (selectedCell !== null) { // If it's valid if (selectedCell && gridCells[selectedCell.row] && gridCells[selectedCell.row][selectedCell.col] === selectedCell) { // If we tap the same cell, deselect it if (selectedCell === tappedCell) { selectedCell.hideSelection(); selectedCell = null; return; } // Check if they're adjacent var isAdjacent = Math.abs(selectedCell.row - tappedCell.row) === 1 && selectedCell.col === tappedCell.col || Math.abs(selectedCell.col - tappedCell.col) === 1 && selectedCell.row === tappedCell.row; if (!isAdjacent) { // Not adjacent, switch selection selectedCell.hideSelection(); selectedCell = tappedCell; selectedCell.showSelection(); return; } // They are adjacent, continue with swap var cell1 = selectedCell; var cell2 = tappedCell; // Hide selection before swap cell1.hideSelection(); selectedCell = null; var isAdjacent = Math.abs(cell1.row - cell2.row) === 1 && cell1.col === cell2.col || Math.abs(cell1.col - cell2.col) === 1 && cell1.row === cell2.row; if (!isAdjacent) { return; } } else { // Invalid selected cell, select the new one if (selectedCell) { selectedCell.hideSelection(); } selectedCell = tappedCell; selectedCell.showSelection(); return; } } else { // No selection yet, select this cell selectedCell = tappedCell; selectedCell.showSelection(); return; } isAnimating = true; var pos1_x = cell1.x; var pos1_y = cell1.y; var pos2_x = cell2.x; var pos2_y = cell2.y; var row1 = cell1.row; var col1 = cell1.col; var row2 = cell2.row; var col2 = cell2.col; // Check if cells are special memes var greenSpecialCell = null; var regularCell = null; var lineSpecialCells = []; var twoGreenSpecials = false; // Check for two green special memes if (cell1.isSpecial && cell1.specialType === 'green' && cell2.isSpecial && cell2.specialType === 'green') { twoGreenSpecials = true; } // Check for green special meme else if (cell1.isSpecial && cell1.specialType === 'green') { greenSpecialCell = cell1; regularCell = cell2; } else if (cell2.isSpecial && cell2.specialType === 'green') { greenSpecialCell = cell2; regularCell = cell1; } // Assign a unique value to green special so it doesn't break when combined if (greenSpecialCell) { greenSpecialCell.value = 99; // Use a value outside the normal meme range (1-5) } if (twoGreenSpecials) { cell1.value = 99; cell2.value = 99; } // Check for line-clearing special memes (horizontal/vertical) if (cell1.isSpecial && (cell1.specialType === 'horizontal' || cell1.specialType === 'vertical')) { lineSpecialCells.push(cell1); } if (cell2.isSpecial && (cell2.specialType === 'horizontal' || cell2.specialType === 'vertical')) { lineSpecialCells.push(cell2); } gridCells[row1][col1] = cell2; gridCells[row2][col2] = cell1; cell1.row = row2; cell1.col = col2; cell2.row = row1; cell2.col = col1; // Make sure any selection highlights are removed if (cell1.selectionHighlight) { cell1.hideSelection(); } if (cell2.selectionHighlight) { cell2.hideSelection(); } // Check for special combinations: two green special memes, two bombs, line-clearing + bomb, or two line special memes if (twoGreenSpecials) { // Two green special memes combination - destroys all visible memes var cellsToDestroy = []; // Collect all visible cells on the board for (var row = extraRows; row < gridSize + extraRows; row++) { for (var col = 0; col < gridSize; col++) { if (gridCells[row][col] && gridCells[row][col] !== cell1 && gridCells[row][col] !== cell2) { cellsToDestroy.push(gridCells[row][col]); } } } // Award massive bonus for two green special meme combination updateScore(1000); // Show mega score popup var megaScorePopup = new Text2("+1000", { size: 100, fill: 0x00FF00 }); megaScorePopup.anchor.set(0.5, 0.5); megaScorePopup.x = 2048 / 2; megaScorePopup.y = 2732 / 2; game.addChild(megaScorePopup); // Animate mega score popup tween(megaScorePopup, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 1000, onComplete: function onComplete() { game.removeChild(megaScorePopup); megaScorePopup.destroy(); } }); // Mark special memes for destruction cell1.beingDestroyed = true; cell2.beingDestroyed = true; // Schedule destruction after the swap animation completes LK.setTimeout(function () { LK.getSound('Explosion').play(); // Flash screen effect for this powerful combination LK.effects.flashScreen(0x00FF00, 500); // Destroy the collected cells if (cellsToDestroy.length > 0) { destroyCells(cellsToDestroy); } // Destroy the special memes LK.effects.flashObject(cell1, 0xFFFFFF, 200); LK.effects.flashObject(cell2, 0xFFFFFF, 200); gridContainer.removeChild(cell1); gridContainer.removeChild(cell2); cell1.destroy(); cell2.destroy(); gridCells[cell1.row][cell1.col] = null; gridCells[cell2.row][cell2.col] = null; }, 250); } else if (cell1.isSpecial && cell1.specialType === 'bomb' && cell2.isSpecial && cell2.specialType === 'bomb') { // Two bomb special memes combination - creates a 5x5 explosion var cellsToDestroy = []; // Determine which cell was moved by the player var activatedCell = cell1; // Create a 5x5 area effect centered on the first bomb for (var r = Math.max(extraRows, cell1.row - 2); r <= Math.min(gridSize + extraRows - 1, cell1.row + 2); r++) { for (var c = Math.max(0, cell1.col - 2); c <= Math.min(gridSize - 1, cell1.col + 2); c++) { if (gridCells[r][c] && gridCells[r][c] !== cell1 && gridCells[r][c] !== cell2) { cellsToDestroy.push(gridCells[r][c]); } } } // Mark special memes for destruction cell1.beingDestroyed = true; cell2.beingDestroyed = true; // Schedule destruction after the swap animation completes LK.setTimeout(function () { LK.getSound('Explosion').play(); // Destroy the collected cells if (cellsToDestroy.length > 0) { destroyCells(cellsToDestroy); } // Destroy the special memes LK.effects.flashObject(cell1, 0xFFFFFF, 200); LK.effects.flashObject(cell2, 0xFFFFFF, 200); gridContainer.removeChild(cell1); gridContainer.removeChild(cell2); cell1.destroy(); cell2.destroy(); gridCells[cell1.row][cell1.col] = null; gridCells[cell2.row][cell2.col] = null; }, 250); } else if (cell1.isSpecial && cell1.specialType === 'bomb' && cell2.isSpecial && (cell2.specialType === 'horizontal' || cell2.specialType === 'vertical') || cell2.isSpecial && cell2.specialType === 'bomb' && cell1.isSpecial && (cell1.specialType === 'horizontal' || cell1.specialType === 'vertical')) { // Line-clearing + bomb special combination var bombCell = cell1.specialType === 'bomb' ? cell1 : cell2; var lineCell = cell1.specialType === 'horizontal' || cell1.specialType === 'vertical' ? cell1 : cell2; var cellsToDestroy = []; var affectedRows = []; // Determine which cell was moved by the player var activatedCell = cell1; // Enhanced effect: destroy 3 rows or columns based on the line special type if (lineCell.specialType === 'horizontal') { // Destroy 3 rows centered on the bomb's row for (var r = Math.max(extraRows, bombCell.row - 1); r <= Math.min(gridSize + extraRows - 1, bombCell.row + 1); r++) { affectedRows.push(r); for (var c = 0; c < gridSize; c++) { if (gridCells[r][c] && gridCells[r][c] !== bombCell && gridCells[r][c] !== lineCell) { cellsToDestroy.push(gridCells[r][c]); } } } } else { // vertical // Destroy 3 columns centered on the bomb's column for (var r = extraRows; r < gridSize + extraRows; r++) { for (var c = Math.max(0, bombCell.col - 1); c <= Math.min(gridSize - 1, bombCell.col + 1); c++) { if (gridCells[r][c] && gridCells[r][c] !== bombCell && gridCells[r][c] !== lineCell) { cellsToDestroy.push(gridCells[r][c]); } } } } // Mark special memes for destruction bombCell.beingDestroyed = true; lineCell.beingDestroyed = true; // Schedule destruction after the swap animation completes LK.setTimeout(function () { LK.getSound('Explosion').play(); // Destroy the collected cells if (cellsToDestroy.length > 0) { destroyCells(cellsToDestroy); } // Destroy the special memes LK.effects.flashObject(bombCell, 0xFFFFFF, 200); LK.effects.flashObject(lineCell, 0xFFFFFF, 200); gridContainer.removeChild(bombCell); gridContainer.removeChild(lineCell); bombCell.destroy(); lineCell.destroy(); gridCells[bombCell.row][bombCell.col] = null; gridCells[lineCell.row][lineCell.col] = null; }, 250); } // Check if two line special memes were swapped (horizontal and vertical) else if (lineSpecialCells.length === 2) { var cellsToDestroy = []; // Collect cells in both row and column var row1, col1, row2, col2; var activatedSpecial; // Determine which special meme was moved by the player (based on original positions) if (lineSpecialCells[0] === cell1) { activatedSpecial = lineSpecialCells[0]; row1 = lineSpecialCells[0].row; col1 = lineSpecialCells[0].col; } else { activatedSpecial = lineSpecialCells[1]; row1 = lineSpecialCells[1].row; col1 = lineSpecialCells[1].col; } // Add cells from the activated special's row or column for (var row = extraRows; row < gridSize + extraRows; row++) { // Add cells from column if (gridCells[row][col1] && gridCells[row][col1] !== lineSpecialCells[0] && gridCells[row][col1] !== lineSpecialCells[1]) { cellsToDestroy.push(gridCells[row][col1]); } } // Add cells from the activated special's row for (var col = 0; col < gridSize; col++) { // Add cells from row if (col !== col1 && gridCells[row1][col] && gridCells[row1][col] !== lineSpecialCells[0] && gridCells[row1][col] !== lineSpecialCells[1]) { cellsToDestroy.push(gridCells[row1][col]); } } // Mark special memes for destruction lineSpecialCells.forEach(function (specialMeme) { specialMeme.beingDestroyed = true; }); // Schedule destruction after the swap animation completes LK.setTimeout(function () { LK.getSound('Explosion').play(); // Destroy the collected cells if (cellsToDestroy.length > 0) { destroyCells(cellsToDestroy); } // Destroy the special memes lineSpecialCells.forEach(function (specialMeme) { LK.effects.flashObject(specialMeme, 0xFFFFFF, 200); gridContainer.removeChild(specialMeme); specialMeme.destroy(); gridCells[specialMeme.row][specialMeme.col] = null; }); }, 250); } // Check if greenSpecialCell was swapped with a line-clearing special meme or bomb else if (greenSpecialCell && regularCell) { // Check if regularCell is a line-clearing special meme if (regularCell.isSpecial && (regularCell.specialType === 'horizontal' || regularCell.specialType === 'vertical')) { var targetType = regularCell.value; var cellsToTransform = []; // Find all cells with the same type as the swapped line-clearing meme for (var row = extraRows; row < gridSize + extraRows; row++) { for (var col = 0; col < gridSize; col++) { var cell = gridCells[row][col]; if (cell && cell.value === targetType && cell !== greenSpecialCell && cell !== regularCell && !cell.isSpecial) { cellsToTransform.push(cell); } } } // Mark special memes for destruction greenSpecialCell.beingDestroyed = true; // Schedule transformation and activation after the swap animation completes LK.setTimeout(function () { LK.getSound('Explosion').play(); LK.effects.flashObject(greenSpecialCell, 0xFFFFFF, 200); // Transform all cells of the same type into line-clearing memes cellsToTransform.forEach(function (cell) { cell.isSpecial = true; // Randomly assign horizontal or vertical cell.specialType = Math.random() < 0.5 ? 'horizontal' : 'vertical'; // Apply tint cell.sprite.tint = 0xFF8800; // Orange tint for line special memes LK.effects.flashObject(cell, 0xFFFFFF, 200); // Activate each special meme with a slight delay LK.setTimeout(function () { cell.activateSpecialPower(); }, 100 + Math.random() * 300); }); // Remove the green special meme gridContainer.removeChild(greenSpecialCell); greenSpecialCell.destroy(); gridCells[greenSpecialCell.row][greenSpecialCell.col] = null; // Activate the original line-clearing meme last LK.setTimeout(function () { regularCell.activateSpecialPower(); }, 500); }, 250); } else if (regularCell.isSpecial && regularCell.specialType === 'bomb') { // Green special + bomb special interaction var targetType = regularCell.value; var cellsToTransform = []; // Find all cells with the same type as the bomb meme for (var row = extraRows; row < gridSize + extraRows; row++) { for (var col = 0; col < gridSize; col++) { var cell = gridCells[row][col]; if (cell && cell.value === targetType && cell !== greenSpecialCell && cell !== regularCell && !cell.isSpecial) { cellsToTransform.push(cell); } } } // Mark green special meme for destruction greenSpecialCell.beingDestroyed = true; // Schedule transformation and activation after swap animation LK.setTimeout(function () { LK.getSound('Explosion').play(); LK.effects.flashObject(greenSpecialCell, 0xFFFFFF, 200); // Transform all cells of same type into bomb memes cellsToTransform.forEach(function (cell) { cell.isSpecial = true; cell.specialType = 'bomb'; // Apply blue tint for bomb memes cell.sprite.tint = 0x0088FF; LK.effects.flashObject(cell, 0xFFFFFF, 200); // Activate each bomb with slight delay LK.setTimeout(function () { cell.activateSpecialPower(); }, 100 + Math.random() * 300); }); // Remove the green special meme gridContainer.removeChild(greenSpecialCell); greenSpecialCell.destroy(); gridCells[greenSpecialCell.row][greenSpecialCell.col] = null; // Activate the original bomb last LK.setTimeout(function () { regularCell.activateSpecialPower(); }, 500); }, 250); } else { var targetType = regularCell.value; var cellsToDestroy = []; // Find all cells with the same type as the swapped meme for (var row = extraRows; row < gridSize + extraRows; row++) { for (var col = 0; col < gridSize; col++) { var cell = gridCells[row][col]; if (cell && cell.value === targetType && cell !== greenSpecialCell) { cellsToDestroy.push(cell); } } } // Also destroy the green special meme itself greenSpecialCell.beingDestroyed = true; // Schedule destruction after the swap animation completes LK.setTimeout(function () { if (cellsToDestroy.length > 0) { LK.getSound('Explosion').play(); LK.effects.flashObject(greenSpecialCell, 0xFFFFFF, 200); destroyCells(cellsToDestroy); gridContainer.removeChild(greenSpecialCell); greenSpecialCell.destroy(); gridCells[greenSpecialCell.row][greenSpecialCell.col] = null; } }, 250); } } tween(cell1, { x: pos2_x, y: pos2_y }, { duration: 200 }); tween(cell2, { x: pos1_x, y: pos1_y }, { duration: 200 }); selectedCell = null; LK.setTimeout(function () { checkForAndDestroyMatches(cell1, cell2); if (!window.destructionInProgress && !window.gravityInProgress && !window.fillInProgress) { isAnimating = false; } }, 250); } function getMatches() { var matches = []; function findDirectionalMatches(isHorizontal) { var primary, secondary; var primaryMax = isHorizontal ? gridSize + extraRows : gridSize; var secondaryMax = isHorizontal ? gridSize : gridSize + extraRows; for (primary = isHorizontal ? extraRows : 0; primary < primaryMax; primary++) { if (!gridCells[primary]) { continue; } for (secondary = 0; secondary < secondaryMax;) { if (secondary > secondaryMax - 3) { secondary++; continue; } var cell1 = isHorizontal ? gridCells[primary] ? gridCells[primary][secondary] : null : gridCells[secondary] ? gridCells[secondary][primary] : null; if (!cell1 || !cell1.value || cell1.value === 99) { secondary++; continue; } var currentMatchValue = cell1.value; var currentMatchCells = [cell1]; for (var k = secondary + 1; k < secondaryMax; k++) { var nextCell = isHorizontal ? gridCells[primary] ? gridCells[primary][k] : null : gridCells[k] ? gridCells[k][primary] : null; if (nextCell && nextCell.value === currentMatchValue && nextCell.value !== 99) { currentMatchCells.push(nextCell); } else { // If no new matches found, reset combo counter LK.setTimeout(function () { if (!window.gravityInProgress && !window.fillInProgress) { comboInProgress = false; comboCounter = 0; } }, 500); break; } } if (currentMatchCells.length >= 3) { var validCells = currentMatchCells.filter(function (cell) { return cell && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell; }); if (validCells.length >= 3) { var visibleCells = validCells.filter(function (cell) { return cell.row >= extraRows; }); var invisibleCells = validCells.filter(function (cell) { return cell.row < extraRows; }); // Either all cells are visible, or all cells are invisible, or there are at least 3 visible cells if (visibleCells.length === 0 || invisibleCells.length === 0 || visibleCells.length >= 3) { // Add isHorizontal and matchType to the match data validCells.isHorizontal = isHorizontal; validCells.matchType = currentMatchValue; matches.push(validCells); } } } secondary += currentMatchCells.length > 0 ? currentMatchCells.length : 1; } } } findDirectionalMatches(true); findDirectionalMatches(false); return matches; } function destroyCells(cellsToDestroy) { isAnimating = true; // Create a unique ID for this destruction process var destructionId = Date.now() + Math.random(); // Check if match crosses visible/invisible boundary var visibleCells = cellsToDestroy.filter(function (cell) { return cell.row >= extraRows; }); var invisibleCells = cellsToDestroy.filter(function (cell) { return cell.row < extraRows; }); // If mixed visibility and only one cell is visible, don't destroy any if (visibleCells.length > 0 && invisibleCells.length > 0 && (visibleCells.length === 1 || invisibleCells.length === 1)) { isAnimating = false; return; } // Reset timer if we're in Fast Mode and destroying visible cells if (game.gameMode === 'fast' && visibleCells.length > 0) { resetTimer(); } // Award points for visible matches var pointsToAdd = 0; var visibleCellsToDestroy = cellsToDestroy.filter(function (cell) { return cell.row >= extraRows && cell && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell; }); if (visibleCellsToDestroy.length > 0) { // 10 points per cell, bonus for combos pointsToAdd = visibleCellsToDestroy.length * 10; if (visibleCellsToDestroy.length >= 4) { pointsToAdd += 10; } if (visibleCellsToDestroy.length >= 5) { pointsToAdd += 20; } if (visibleCellsToDestroy.length >= 6) { pointsToAdd += 30; } updateScore(pointsToAdd); } // Find the center cell (the one that triggered the match) var centerCell = null; // If we have the cells from a swap, use one of those as the center if (arguments.length > 1 && arguments[1]) { centerCell = arguments[1]; } else if (cellsToDestroy.length > 0) { // Otherwise use the middle cell of the match centerCell = cellsToDestroy[Math.floor(cellsToDestroy.length / 2)]; } // Group cells by meme type var cellsByType = {}; visibleCellsToDestroy.forEach(function (cell) { if (!cellsByType[cell.value]) { cellsByType[cell.value] = []; } cellsByType[cell.value].push(cell); }); // Count unique groups for combo sound var uniqueGroupCount = Object.keys(cellsByType).length; if (uniqueGroupCount > 0) { // Increment combo counter only if this is part of a continuous combo if (!comboInProgress) { comboCounter = uniqueGroupCount; comboInProgress = true; } else { comboCounter += uniqueGroupCount; } // Update seek mode combo counter if we're in seek mode if (game && game.gameMode === 'seekmode' && !seekModeBlockActive && game.seekModeForceSeekSprite) { seekModeComboCount++; // Create or update combo text if (!seekModeComboText) { seekModeComboText = new Text2("Combos: " + seekModeComboCount + "/3", { size: 120, fill: 0xDD5500, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); seekModeComboText.anchor.set(0.5, 0); seekModeComboText.x = 2048 / 2; seekModeComboText.y = 2732 - 120; // Place just below the wait text game.addChild(seekModeComboText); } else { seekModeComboText.setText("Combos: " + seekModeComboCount + "/3"); } // Check if we've reached 3 combos if (seekModeComboCount >= 3) { // Reset combo counter seekModeComboCount = 0; // Show all memes for 3 seconds for (var row = 0; row < gridSize + extraRows; row++) { var _loop = function _loop() { cell = gridCells[row][col]; if (cell && cell.sprite) { // Remove old seek sprite cell.removeChild(cell.sprite); // Attach original meme sprite based on value // Use global selectedSet for cosmetics if (typeof selectedSet !== "number") selectedSet = 1; if (selectedSet === 1) { spriteId = 'meme' + cell.value; } else if (selectedSet === 2) { spriteId = 'meme' + cell.value + '-2'; } else if (selectedSet === 3) { spriteId = 'meme' + cell.value + '-3'; } else { spriteId = 'meme' + cell.value; } cell.sprite = LK.getAsset(spriteId, { anchorX: 0.5, anchorY: 0.5 }); // Scale to fit cell padding = 20; maxWidth = cellSize - padding * 2; maxHeight = cellSize - padding * 2; scaleX = maxWidth / cell.sprite.width; scaleY = maxHeight / cell.sprite.height; scale = Math.min(scaleX, scaleY); cell.sprite.scale.set(scale, scale); cell.addChild(cell.sprite); if (cell.isSpecial) { // Remove any previous overlays if (cell.specialOverlay && cell.children.indexOf(cell.specialOverlay) !== -1) { cell.removeChild(cell.specialOverlay); cell.specialOverlay = null; } if (cell.specialType === 'bomb') { // Overlay habilidadBomba sprite cell.specialOverlay = LK.getAsset('habilidadBomba', { anchorX: 0.5, anchorY: 0.5 }); padding = 20; maxWidth = cellSize - padding * 2; maxHeight = cellSize - padding * 2; scaleX = maxWidth / cell.specialOverlay.width; scaleY = maxHeight / cell.specialOverlay.height; scale = Math.min(scaleX, scaleY); cell.specialOverlay.scale.set(scale, scale); cell.addChild(cell.specialOverlay); // --- Color sync: animate overlay tint with background --- if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") { var _tweenBombOverlayColor2 = function tweenBombOverlayColor() { if (!cell.specialOverlay) { return; } var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length; var nextColor = colorTweenTargets[nextIndex]; cell._specialOverlayTween = tween(cell.specialOverlay, { tint: nextColor }, { duration: 14000, easing: tween.easeInOutQuad, onFinish: function onFinish() { if (cell.specialOverlay) { _tweenBombOverlayColor2(); } } }); }; if (cell._specialOverlayTween) { cell._specialOverlayTween.stop && cell._specialOverlayTween.stop(); cell._specialOverlayTween = null; } cell.specialOverlay.tint = colorTweenTargets[currentColorIndex]; _tweenBombOverlayColor2(); } } else if (cell.specialType === 'green') { // Use 'habilidadVerde' asset for green special meme if (cell.sprite) { cell.removeChild(cell.sprite); } if (game.seekModeForceSeekSprite) { // Si estamos en modo seek ocultando memes, no mostrar habilidad verde cell.sprite = null; } else { cell.sprite = LK.getAsset('habilidadVerde', { anchorX: 0.5, anchorY: 0.5 }); padding = 20; maxWidth = cellSize - padding * 2; maxHeight = cellSize - padding * 2; scaleX = maxWidth / cell.sprite.width; scaleY = maxHeight / cell.sprite.height; scale = Math.min(scaleX, scaleY); cell.sprite.scale.set(scale, scale); cell.addChild(cell.sprite); } } else if (cell.specialType === 'clear' || cell.specialType === 'vertical' || cell.specialType === 'horizontal') { // Overlay habilidadClear sprite for clear/line specials cell.specialOverlay = LK.getAsset('HabilidadClear', { anchorX: 0.5, anchorY: 0.5 }); // Rotate overlay to match direction if (cell.specialType === 'horizontal') { cell.specialOverlay.rotation = 0; } else if (cell.specialType === 'vertical') { cell.specialOverlay.rotation = Math.PI / 2; } else { cell.specialOverlay.rotation = 0; } padding = 20; maxWidth = cellSize - padding * 2; maxHeight = cellSize - padding * 2; scaleX = maxWidth / cell.specialOverlay.width; scaleY = maxHeight / cell.specialOverlay.height; scale = Math.min(scaleX, scaleY); cell.specialOverlay.scale.set(scale, scale); cell.addChild(cell.specialOverlay); // --- Color sync: animate overlay tint with background --- if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") { var _tweenClearOverlayColor2 = function tweenClearOverlayColor() { if (!cell.specialOverlay) { return; } var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length; var nextColor = colorTweenTargets[nextIndex]; cell._specialOverlayTween = tween(cell.specialOverlay, { tint: nextColor }, { duration: 14000, easing: tween.easeInOutQuad, onFinish: function onFinish() { if (cell.specialOverlay) { _tweenClearOverlayColor2(); } } }); }; if (cell._specialOverlayTween) { cell._specialOverlayTween.stop && cell._specialOverlayTween.stop(); cell._specialOverlayTween = null; } cell.specialOverlay.tint = colorTweenTargets[currentColorIndex]; _tweenClearOverlayColor2(); } } else { cell.sprite.tint = 0xFF8800; } } } }, cell, selectedSet, spriteId, padding, maxWidth, maxHeight, scaleX, scaleY, scale, padding, maxWidth, maxHeight, scaleX, scaleY, scale, padding, maxWidth, maxHeight, scaleX, scaleY, scale, padding, maxWidth, maxHeight, scaleX, scaleY, scale; for (var col = 0; col < gridSize; col++) { _loop(); } } // Display countdown text if (seekModeComboText) { seekModeComboText.setText("MEMES VISIBLE: 3"); if (seekModeComboText.style) { seekModeComboText.style.size = 120; seekModeComboText.style.fill = 0xDD5500; seekModeComboText.style.font = "'GillSans-Bold',Impact,'Arial Black',Tahoma"; } seekModeComboText.y = 2732 - 120; // Ensure position is below wait text } // Start countdown var showAllSeconds = 3; var countdownInterval = LK.setInterval(function () { showAllSeconds--; if (seekModeComboText) { seekModeComboText.setText("MEMES VISIBLE: " + showAllSeconds); if (seekModeComboText.style) { seekModeComboText.style.size = 120; seekModeComboText.style.fill = 0xDD5500; seekModeComboText.style.font = "'GillSans-Bold',Impact,'Arial Black',Tahoma"; } seekModeComboText.y = 2732 - 120; // Ensure position is below wait text } if (showAllSeconds <= 0) { // Clear interval LK.clearInterval(countdownInterval); // Set all memes back to seek sprite for (var row = 0; row < gridSize + extraRows; row++) { var _loop2 = function _loop2() { cell = gridCells[row][col]; if (cell && cell.sprite) { // Remove old sprite cell.removeChild(cell.sprite); // Attach new 'seek' sprite cell.sprite = LK.getAsset('seek', { anchorX: 0.5, anchorY: 0.5 }); // Scale to fit cell padding = 20; maxWidth = cellSize - padding * 2; maxHeight = cellSize - padding * 2; scaleX = maxWidth / cell.sprite.width; scaleY = maxHeight / cell.sprite.height; scale = Math.min(scaleX, scaleY); cell.sprite.scale.set(scale, scale); cell.addChild(cell.sprite); // Preserve special tints if needed if (cell.isSpecial) { // Remove any previous overlays if (cell.specialOverlay && cell.children.indexOf(cell.specialOverlay) !== -1) { cell.removeChild(cell.specialOverlay); cell.specialOverlay = null; } if (cell.specialType === 'bomb') { // Overlay habilidadBomba sprite cell.specialOverlay = LK.getAsset('habilidadBomba', { anchorX: 0.5, anchorY: 0.5 }); padding = 20; maxWidth = cellSize - padding * 2; maxHeight = cellSize - padding * 2; scaleX = maxWidth / cell.specialOverlay.width; scaleY = maxHeight / cell.specialOverlay.height; scale = Math.min(scaleX, scaleY); cell.specialOverlay.scale.set(scale, scale); cell.addChild(cell.specialOverlay); // --- Color sync: animate overlay tint with background --- if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") { var _tweenBombOverlayColor3 = function tweenBombOverlayColor() { if (!cell.specialOverlay) { return; } var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length; var nextColor = colorTweenTargets[nextIndex]; cell._specialOverlayTween = tween(cell.specialOverlay, { tint: nextColor }, { duration: 14000, easing: tween.easeInOutQuad, onFinish: function onFinish() { if (cell.specialOverlay) { _tweenBombOverlayColor3(); } } }); }; if (cell._specialOverlayTween) { cell._specialOverlayTween.stop && cell._specialOverlayTween.stop(); cell._specialOverlayTween = null; } cell.specialOverlay.tint = colorTweenTargets[currentColorIndex]; _tweenBombOverlayColor3(); } } else if (cell.specialType === 'green') { // Use 'habilidadVerde' asset for green special meme if (cell.sprite) { cell.removeChild(cell.sprite); } if (game.seekModeForceSeekSprite) { // Si estamos en modo seek ocultando memes, no mostrar habilidad verde cell.sprite = null; } else { cell.sprite = LK.getAsset('habilidadVerde', { anchorX: 0.5, anchorY: 0.5 }); padding = 20; maxWidth = cellSize - padding * 2; maxHeight = cellSize - padding * 2; scaleX = maxWidth / cell.sprite.width; scaleY = maxHeight / cell.sprite.height; scale = Math.min(scaleX, scaleY); cell.sprite.scale.set(scale, scale); cell.addChild(cell.sprite); } } else if (cell.specialType === 'clear' || cell.specialType === 'vertical' || cell.specialType === 'horizontal') { // Overlay habilidadClear sprite for clear/line specials cell.specialOverlay = LK.getAsset('HabilidadClear', { anchorX: 0.5, anchorY: 0.5 }); // Rotate overlay to match direction if (cell.specialType === 'horizontal') { cell.specialOverlay.rotation = 0; } else if (cell.specialType === 'vertical') { cell.specialOverlay.rotation = Math.PI / 2; } else { cell.specialOverlay.rotation = 0; } padding = 20; maxWidth = cellSize - padding * 2; maxHeight = cellSize - padding * 2; scaleX = maxWidth / cell.specialOverlay.width; scaleY = maxHeight / cell.specialOverlay.height; scale = Math.min(scaleX, scaleY); cell.specialOverlay.scale.set(scale, scale); cell.addChild(cell.specialOverlay); // --- Color sync: animate overlay tint with background --- if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") { var _tweenClearOverlayColor3 = function tweenClearOverlayColor() { if (!cell.specialOverlay) { return; } var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length; var nextColor = colorTweenTargets[nextIndex]; cell._specialOverlayTween = tween(cell.specialOverlay, { tint: nextColor }, { duration: 14000, easing: tween.easeInOutQuad, onFinish: function onFinish() { if (cell.specialOverlay) { _tweenClearOverlayColor3(); } } }); }; if (cell._specialOverlayTween) { cell._specialOverlayTween.stop && cell._specialOverlayTween.stop(); cell._specialOverlayTween = null; } cell.specialOverlay.tint = colorTweenTargets[currentColorIndex]; _tweenClearOverlayColor3(); } } else { cell.sprite.tint = 0xFF8800; } } } }, cell, padding, maxWidth, maxHeight, scaleX, scaleY, scale, padding, maxWidth, maxHeight, scaleX, scaleY, scale, padding, maxWidth, maxHeight, scaleX, scaleY, scale, padding, maxWidth, maxHeight, scaleX, scaleY, scale; for (var col = 0; col < gridSize; col++) { _loop2(); } } // Reset combo text if (seekModeComboText) { seekModeComboText.setText("Combos: 0/3"); if (seekModeComboText.style) { seekModeComboText.style.size = 120; seekModeComboText.style.fill = 0xDD5500; seekModeComboText.style.font = "'GillSans-Bold',Impact,'Arial Black',Tahoma"; } seekModeComboText.y = 2732 - 120; // Ensure position is below wait text } } }, 1000); } } // Stop any currently playing combo sounds before playing a new one LK.getSound('Combo1').stop(); LK.getSound('Combo2').stop(); LK.getSound('Combo3').stop(); LK.getSound('Combo4').stop(); LK.getSound('Combo5').stop(); if (comboCounter >= 9) { LK.getSound('Combo5').play(); } else if (comboCounter >= 6) { LK.getSound('Combo4').play(); } else if (comboCounter >= 4) { LK.getSound('Combo3').play(); } else if (comboCounter >= 3) { LK.getSound('Combo2').play(); } else if (comboCounter >= 2) { LK.getSound('Combo1').play(); } } var specialCell = null; var isHorizontalMatch = false; var isVerticalMatch = false; for (var type in cellsByType) { var typeCells = cellsByType[type]; // --- Prevent special abilities from creating new special abilities --- // If any cell in this group is a special, do not create a new special meme var groupHasSpecial = false; for (var i = 0; i < typeCells.length; i++) { if (typeCells[i].isSpecial) { groupHasSpecial = true; break; } } if (groupHasSpecial) { // Do not create a new special meme for this group continue; } if (typeCells.length >= 6) { // Check if a special ability is currently being used before creating a green special var specialAbilityInUse = false; // Loop through all cells to check if any special is activated for (var r = 0; r < gridSize + extraRows; r++) { for (var c = 0; c < gridSize; c++) { if (gridCells[r] && gridCells[r][c] && gridCells[r][c].isSpecial && gridCells[r][c].beingDestroyed) { specialAbilityInUse = true; break; } } if (specialAbilityInUse) { break; } } // Only create a green special meme if no other special ability is in use if (!specialAbilityInUse) { // Create green special meme for 6 or more matches if (centerCell && centerCell.value === parseInt(type)) { specialCell = centerCell; } else { specialCell = typeCells[0]; } specialCell.isSpecial = true; specialCell.specialType = 'green'; specialCell.value = 99; // Assign unique value for green special specialCell.value = 99; // Assign unique value for green special specialCell.beingDestroyed = false; // Award bonus points for creating a green special meme updateScore(100); // Remove special cell from cells to destroy visibleCellsToDestroy = visibleCellsToDestroy.filter(function (cell) { return cell !== specialCell; }); } break; // Only create one special meme } else if (typeCells.length === 5) { // Create bomb special meme (blue) if (centerCell && centerCell.value === parseInt(type)) { specialCell = centerCell; } else { specialCell = typeCells[0]; } specialCell.isSpecial = true; specialCell.specialType = 'bomb'; specialCell.beingDestroyed = false; // Award bonus points for creating a bomb special meme updateScore(75); // Remove special cell from cells to destroy visibleCellsToDestroy = visibleCellsToDestroy.filter(function (cell) { return cell !== specialCell; }); break; // Only create one special meme } else if (typeCells.length === 4) { // Determine if match is horizontal or vertical var allInSameRow = true; var allInSameCol = true; var firstCell = typeCells[0]; for (var i = 1; i < typeCells.length; i++) { if (typeCells[i].row !== firstCell.row) { allInSameRow = false; } if (typeCells[i].col !== firstCell.col) { allInSameCol = false; } } isHorizontalMatch = allInSameRow; isVerticalMatch = allInSameCol; // Create special meme only if it's a clean horizontal or vertical match if (isHorizontalMatch || isVerticalMatch) { // Use center cell if it matches the type, otherwise use the first cell of this type if (centerCell && centerCell.value === parseInt(type)) { specialCell = centerCell; } else { specialCell = typeCells[0]; } specialCell.isSpecial = true; specialCell.specialType = isHorizontalMatch ? 'horizontal' : 'vertical'; specialCell.beingDestroyed = false; // Award bonus points for creating a line-clearing special meme updateScore(50); // Remove special cell from cells to destroy visibleCellsToDestroy = visibleCellsToDestroy.filter(function (cell) { return cell !== specialCell; }); break; // Only create one special meme } } } // Sort cells by distance from center cell for ripple effect if (centerCell && visibleCellsToDestroy.length > 0) { visibleCellsToDestroy.sort(function (a, b) { var distA = Math.abs(a.row - centerCell.row) + Math.abs(a.col - centerCell.col); var distB = Math.abs(b.row - centerCell.row) + Math.abs(b.col - centerCell.col); return distA - distB; }); } var delay = 0; var delayIncrement = 50; var totalDestructionTime = visibleCellsToDestroy.length * delayIncrement; // Mark cells as being destroyed to prevent them from being part of new matches visibleCellsToDestroy.forEach(function (cell) { if (cell) { cell.beingDestroyed = true; // Check if any of these cells are matched with a special meme if (cell.isSpecial) { // Special meme is being destroyed, activate its power LK.setTimeout(function () { cell.activateSpecialPower(); }, 100); } } }); // If we have a special meme, apply the appropriate overlay or tint if (specialCell && specialCell.isSpecial) { // Remove any previous overlays if (specialCell.specialOverlay && specialCell.children.indexOf(specialCell.specialOverlay) !== -1) { specialCell.removeChild(specialCell.specialOverlay); specialCell.specialOverlay = null; } if (specialCell.specialType === 'bomb') { // Overlay habilidadBomba sprite specialCell.specialOverlay = LK.getAsset('habilidadBomba', { anchorX: 0.5, anchorY: 0.5 }); var padding = 20; var maxWidth = cellSize - padding * 2; var maxHeight = cellSize - padding * 2; var scaleX = maxWidth / specialCell.specialOverlay.width; var scaleY = maxHeight / specialCell.specialOverlay.height; var scale = Math.min(scaleX, scaleY); specialCell.specialOverlay.scale.set(scale, scale); specialCell.addChild(specialCell.specialOverlay); // --- Color sync: animate overlay tint with background --- if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") { var _tweenBombOverlayColor4 = function tweenBombOverlayColor() { if (!specialCell.specialOverlay) { return; } var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length; var nextColor = colorTweenTargets[nextIndex]; specialCell._specialOverlayTween = tween(specialCell.specialOverlay, { tint: nextColor }, { duration: 14000, easing: tween.easeInOutQuad, onFinish: function onFinish() { if (specialCell.specialOverlay) { _tweenBombOverlayColor4(); } } }); }; if (specialCell._specialOverlayTween) { specialCell._specialOverlayTween.stop && specialCell._specialOverlayTween.stop(); specialCell._specialOverlayTween = null; } specialCell.specialOverlay.tint = colorTweenTargets[currentColorIndex]; _tweenBombOverlayColor4(); } } else if (specialCell.specialType === 'green') { // Use 'habilidadVerde' asset for green special meme if (specialCell.sprite) { specialCell.removeChild(specialCell.sprite); } specialCell.sprite = LK.getAsset('habilidadVerde', { anchorX: 0.5, anchorY: 0.5 }); var padding = 20; var maxWidth = cellSize - padding * 2; var maxHeight = cellSize - padding * 2; var scaleX = maxWidth / specialCell.sprite.width; var scaleY = maxHeight / specialCell.sprite.height; var scale = Math.min(scaleX, scaleY); specialCell.sprite.scale.set(scale, scale); specialCell.addChild(specialCell.sprite); } else if (specialCell.specialType === 'clear' || specialCell.specialType === 'vertical' || specialCell.specialType === 'horizontal') { // Overlay habilidadClear sprite for clear/line specials specialCell.specialOverlay = LK.getAsset('HabilidadClear', { anchorX: 0.5, anchorY: 0.5 }); // Rotate overlay to match direction if (specialCell.specialType === 'horizontal') { specialCell.specialOverlay.rotation = 0; } else if (specialCell.specialType === 'vertical') { specialCell.specialOverlay.rotation = Math.PI / 2; } else { specialCell.specialOverlay.rotation = 0; } var padding = 20; var maxWidth = cellSize - padding * 2; var maxHeight = cellSize - padding * 2; var scaleX = maxWidth / specialCell.specialOverlay.width; var scaleY = maxHeight / specialCell.specialOverlay.height; var scale = Math.min(scaleX, scaleY); specialCell.specialOverlay.scale.set(scale, scale); specialCell.addChild(specialCell.specialOverlay); // --- Color sync: animate overlay tint with background --- if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") { var _tweenClearOverlayColor4 = function tweenClearOverlayColor() { if (!specialCell.specialOverlay) { return; } var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length; var nextColor = colorTweenTargets[nextIndex]; specialCell._specialOverlayTween = tween(specialCell.specialOverlay, { tint: nextColor }, { duration: 14000, easing: tween.easeInOutQuad, onFinish: function onFinish() { if (specialCell.specialOverlay) { _tweenClearOverlayColor4(); } } }); }; if (specialCell._specialOverlayTween) { specialCell._specialOverlayTween.stop && specialCell._specialOverlayTween.stop(); specialCell._specialOverlayTween = null; } specialCell.specialOverlay.tint = colorTweenTargets[currentColorIndex]; _tweenClearOverlayColor4(); } } else { specialCell.sprite.tint = 0xFF8800; // Orange tint for others } LK.effects.flashObject(specialCell, 0xFFFFFF, 300); } visibleCellsToDestroy.forEach(function (cell) { LK.setTimeout(function () { if (cell && cell.beingDestroyed && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell) { LK.getSound('Explosion').play(); LK.effects.flashObject(cell, 0xFFFFFF, 200); // --- PARTICLE EFFECT START --- if (particlesEnabled) { // Emit 8-12 particles per meme var numParticles = 8 + Math.floor(Math.random() * 5); for (var p = 0; p < numParticles; p++) { var particle = LK.getAsset('particula', { anchorX: 0.5, anchorY: 0.5 }); // Randomize color (RGB) var r = Math.floor(128 + Math.random() * 127); var g = Math.floor(128 + Math.random() * 127); var b = Math.floor(128 + Math.random() * 127); var rgb = r << 16 | g << 8 | b; particle.tint = rgb; // Start at cell position particle.x = cell.x; particle.y = cell.y; // Random velocity and direction var angle = Math.random() * Math.PI * 2; var speed = 6 + Math.random() * 6; var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; // Animate particle (function (particle, vx, vy) { var lifetime = 200 + Math.random() * 150; var startAlpha = 0.9 + Math.random() * 0.1; particle.alpha = startAlpha; gridContainer.addChild(particle); var elapsed = 0; var updateFn = function updateFn() { particle.x += vx; particle.y += vy; // Fade out particle.alpha -= 0.04 + Math.random() * 0.02; // Shrink particle.scale.x *= 0.93; particle.scale.y *= 0.93; elapsed += 16; if (elapsed > lifetime || particle.alpha <= 0.05) { if (particle.parent) { particle.parent.removeChild(particle); } LK.clearInterval(intervalId); } }; var intervalId = LK.setInterval(updateFn, 16); })(particle, vx, vy); } } // --- PARTICLE EFFECT END --- gridContainer.removeChild(cell); cell.destroy(); gridCells[cell.row][cell.col] = null; } }, delay); delay += delayIncrement; }); // After all cells in this group are destroyed, check if we need to apply gravity LK.setTimeout(function () { // Check if all destructions are complete before applying gravity var allCellsDestroyed = true; for (var r = 0; r < gridSize + extraRows; r++) { for (var c = 0; c < gridSize; c++) { if (gridCells[r] && gridCells[r][c] && gridCells[r][c].beingDestroyed) { allCellsDestroyed = false; } } } // Only the last destruction process should trigger gravity if (allCellsDestroyed && !window.gravityInProgress && !window.fillInProgress) { applyGravity(); } }, totalDestructionTime + 50); } function applyGravity() { isAnimating = true; var cellsToFall = []; if (window.gravityInProgress) { return; } window.gravityInProgress = true; for (var col = 0; col < gridSize; col++) { for (var row = gridSize + extraRows - 1; row >= extraRows; row--) { if (!gridCells[row][col]) { var sourceRow = row - 1; while (sourceRow >= 0 && !gridCells[sourceRow][col]) { sourceRow--; } if (sourceRow >= 0) { var cellToMove = gridCells[sourceRow][col]; if (cellToMove) { cellsToFall.push({ cell: cellToMove, fromRow: sourceRow, toRow: row, col: col }); gridCells[row][col] = cellToMove; gridCells[sourceRow][col] = null; cellToMove.row = row; if (sourceRow < extraRows) { gridContainer.addChild(cellToMove); } } } } } } var longestDelay = 0; var baseDelay = 25; cellsToFall.sort(function (a, b) { if (a.col !== b.col) { return a.col - b.col; } return b.toRow - a.toRow; }); cellsToFall.forEach(function (fallInfo, index) { var delay = index * baseDelay; var newPos = getCellPosition(fallInfo.toRow, fallInfo.col); // Calculate duration based on distance var distance = Math.abs(newPos.y - fallInfo.cell.y); var duration = 100 + distance * 0.35; // Reduced base time + smaller distance multiplier for faster falls var totalTime = delay + duration; if (totalTime > longestDelay) { longestDelay = totalTime; } LK.setTimeout(function () { if (fallInfo.cell && gridCells[fallInfo.cell.row] && gridCells[fallInfo.cell.row][fallInfo.cell.col] === fallInfo.cell) { tween(fallInfo.cell, { y: newPos.y }, { duration: duration, easing: tween.linear }); } }, delay); }); LK.setTimeout(function () { window.gravityInProgress = false; if (!window.destructionInProgress) { fillEmptySpacesWithNewMemes(); } }, longestDelay + 50); } function fillEmptySpacesWithNewMemes() { if (window.fillInProgress || window.gravityInProgress) { return; } window.fillInProgress = true; var anyNewMemeAdded = false; var newCellsToAdd = []; for (var col = 0; col < gridSize; col++) { for (var row = 0; row < gridSize + extraRows; row++) { if (!gridCells[row][col]) { anyNewMemeAdded = true; var newCell = getGridCell(); var pos = getCellPosition(row, col); newCell.x = pos.x; newCell.y = pos.y - 400; var randomValue = Math.floor(Math.random() * 5) + 1; newCell.setValue(randomValue); newCell.row = row; newCell.col = col; gridCells[row][col] = newCell; newCellsToAdd.push({ cell: newCell, destY: pos.y, row: row, col: col }); if (row >= extraRows) { gridContainer.addChild(newCell); } } } } var longestDelay = 0; var baseDelay = 15; var constantDuration = 250; newCellsToAdd.sort(function (a, b) { if (a.col !== b.col) { return a.col - b.col; } return b.row - a.row; }); newCellsToAdd.forEach(function (cellData, index) { var delay = index * baseDelay; var totalTime = delay + constantDuration; if (totalTime > longestDelay) { longestDelay = totalTime; } LK.setTimeout(function () { if (cellData.cell && gridCells[cellData.row] && gridCells[cellData.row][cellData.col] === cellData.cell) { tween(cellData.cell, { y: cellData.destY }, { duration: constantDuration, easing: tween.linear }); } }, delay); }); if (anyNewMemeAdded) { LK.setTimeout(function () { window.fillInProgress = false; if (window.destructionInProgress || window.gravityInProgress) { isAnimating = false; return; } var visibleMatchGroups = getMatches().filter(function (group) { // Check if the match has cells in both visible and invisible areas var visibleCells = group.filter(function (cell) { return cell.row >= extraRows; }); var invisibleCells = group.filter(function (cell) { return cell.row < extraRows; }); // Don't count groups with both visible and invisible cells unless there are at least 3 visible cells if (visibleCells.length > 0 && invisibleCells.length > 0 && visibleCells.length < 3) { return false; } return visibleCells.length >= 3; // At least 3 visible cells make a valid visible match }); var newMatches = visibleMatchGroups; if (newMatches.length > 0) { // Group by meme type var matchesByType = {}; newMatches.forEach(function (group) { if (group.length > 0) { var type = group[0].value; if (!matchesByType[type]) { matchesByType[type] = []; } matchesByType[type].push(group); } }); var newCellsToDestroy = []; var newCellTracker = {}; // Process each type separately for (var type in matchesByType) { var typeGroups = matchesByType[type]; // Find a central cell for this type var centerCell = null; if (typeGroups.length > 0 && typeGroups[0].length > 0) { // Pick the center of the first match group of this type var matchGroup = typeGroups[0]; centerCell = matchGroup[Math.floor(matchGroup.length / 2)]; } // Collect all cells of this type var typeCells = []; typeGroups.forEach(function (group) { group.forEach(function (cell) { var cellKey = cell.row + "_" + cell.col; if (!newCellTracker[cellKey] && cell.row >= extraRows && cell && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell) { typeCells.push(cell); newCellTracker[cellKey] = true; } }); }); // Destroy cells of this type if (typeCells.length) { destroyCells(typeCells, centerCell); } } if (Object.keys(matchesByType).length === 0) { isAnimating = false; } } else { isAnimating = false; } }, longestDelay + 50); } else { window.fillInProgress = false; isAnimating = false; // Reset combo tracking when no more matches are being created comboInProgress = false; comboCounter = 0; } } // Timer management functions function startTimer() { // Clear any existing timer if (timerInterval) { LK.clearInterval(timerInterval); timerInterval = null; } // Calculate initial max timer value based on score // This ensures proper timer initialization when starting a game with existing score var maxTimer = 10; // Use exponential thresholds for slower decrease if (score >= 100) { maxTimer = 9; } if (score >= 500) { maxTimer = 8; } if (score >= 1000) { maxTimer = 7; } if (score >= 3500) { maxTimer = 6; } if (score >= 10000) { maxTimer = 5; } if (score >= 25000) { maxTimer = 4; } if (score >= 50000) { maxTimer = 3; } // Only set timer to max when initializing new game, not when resuming if (!game.timerInitialized) { timerSeconds = maxTimer; game.timerInitialized = true; } // Update timer display initially if (timerText) { timerText.setText("Time: " + timerSeconds.toFixed(1)); } // Start countdown timer with decimals (100ms interval) timerInterval = LK.setInterval(function () { timerSeconds -= 0.1; timerSeconds = Math.max(0, parseFloat(timerSeconds.toFixed(1))); // Fix floating point issues // Update timer display if (timerText) { timerText.setText("Time: " + timerSeconds.toFixed(1)); } // Check for game over if (timerSeconds <= 0) { // Stop timer LK.clearInterval(timerInterval); timerInterval = null; // Instead of showing game over screen, trigger the custom game over game.onGameOver(); } }, 100); } function resetTimer() { // Only reset timer in Fast Mode if (game.gameMode === 'fast') { // Calculate max timer value based on score // Start with 10 seconds, decrease as score increases, minimum of 3 seconds var maxTimer = 10; // Use exponential thresholds for slower decrease if (score >= 100) { maxTimer = 9; } if (score >= 500) { maxTimer = 8; } if (score >= 1000) { maxTimer = 7; } if (score >= 3500) { maxTimer = 6; } if (score >= 10000) { maxTimer = 5; } if (score >= 25000) { maxTimer = 4; } if (score >= 50000) { maxTimer = 3; } // Set timer to calculated max timerSeconds = maxTimer; if (timerText) { timerText.setText("Time: " + timerSeconds.toFixed(1)); } } } // Handle game over events to return to menu game.onGameOver = function () { // Save high score one last time if it's better if (game.gameMode === 'classic' && score > highScoreClassic) { highScoreClassic = score; storage.highScoreClassic = highScoreClassic; } else if (game.gameMode === 'fast' && score > highScoreFast) { highScoreFast = score; storage.highScoreFast = highScoreFast; } else if (game.gameMode === 'seekmode' && score > highScoreSeek) { highScoreSeek = score; storage.highScoreSeek = highScoreSeek; } // Stop timer if running if (timerInterval) { LK.clearInterval(timerInterval); timerInterval = null; } // Clean up seek mode block timer and text seekModeBlockActive = false; if (seekModeBlockTimer) { LK.clearInterval(seekModeBlockTimer); seekModeBlockTimer = null; } if (seekModeBlockText) { game.removeChild(seekModeBlockText); seekModeBlockText.destroy(); seekModeBlockText = null; } // Clean up seek mode combo counter and show-all timer if (seekModeComboText) { game.removeChild(seekModeComboText); seekModeComboText.destroy(); seekModeComboText = null; } if (seekModeShowAllTimer) { LK.clearTimeout(seekModeShowAllTimer); seekModeShowAllTimer = null; } seekModeComboCount = 0; // --- FIX: Reset seekModeForceSeekSprite so memes use correct sprite after game over --- game.seekModeForceSeekSprite = false; // Stop periodic save interval if (saveScoreInterval) { LK.clearInterval(saveScoreInterval); saveScoreInterval = null; } // Clear music interval if (game.musicInterval) { LK.clearInterval(game.musicInterval); game.musicInterval = null; // Stop any currently playing music LK.stopMusic(); } // For Fast Mode, show a custom high score popup instead of the game over screen if (game.gameMode === 'fast') { // Create popup container var popupContainer = new Container(); // Create popup background var popupBg = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 4.5 }); popupBg.tint = 0x0088FF; // Blue tint for Fast Mode // Create popup title var popupTitle = new Text2("GAME OVER", { size: 100, fill: 0xFFFFFF }); popupTitle.anchor.set(0.5, 0); popupTitle.y = -350; // Show current score in Fast Mode game over popup var currentScoreText = new Text2("Score: " + score, { size: 90, fill: 0xffffff }); currentScoreText.anchor.set(0.5, 0); currentScoreText.y = -200; // Show high score var highScoreText = new Text2("High Score: " + highScoreFast, { size: 90, fill: 0xFFFF00 }); highScoreText.anchor.set(0.5, 0); highScoreText.y = -100; // Update high score if needed if (score > highScoreFast) { highScoreFast = score; highScoreText.setText("High Score: " + score + " (New!)"); } // Create button to return to menu var menuButton = new Container(); var menuButtonBg = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 1.5 }); menuButtonBg.tint = 0xFF5500; var menuButtonText = new Text2("BACK TO MENU", { size: 70, fill: 0xFFFFFF }); menuButtonText.anchor.set(0.5, 0.5); menuButton.addChild(menuButtonBg); menuButton.addChild(menuButtonText); menuButton.y = 250; // Add all elements to popup popupContainer.addChild(popupBg); popupContainer.addChild(popupTitle); popupContainer.addChild(highScoreText); popupContainer.addChild(currentScoreText); popupContainer.addChild(menuButton); // Position popup in center of screen popupContainer.x = 2048 / 2; popupContainer.y = 2732 / 2; game.addChild(popupContainer); // Add button interaction menuButton.interactive = true; menuButton.down = function (x, y, obj) { LK.effects.flashObject(menuButtonBg, 0xFFFFFF, 200); // Clear the game board while (gridContainer.children.length > 0) { var child = gridContainer.children[0]; gridContainer.removeChild(child); child.destroy(); } if (scoreText) { game.removeChild(scoreText); scoreText = null; } if (timerText) { game.removeChild(timerText); timerText = null; } // Remove popup game.removeChild(popupContainer); // Clean up any game UI elements if (game.modeText) { game.removeChild(game.modeText); game.modeText = null; } if (game.pauseButton) { game.removeChild(game.pauseButton); game.pauseButton = null; } game.timerInitialized = false; // Show menu initializeMenu(); }; } else { // For Classic Mode, use the standard game over screen LK.setTimeout(function () { // Clear the game board while (gridContainer.children.length > 0) { var child = gridContainer.children[0]; gridContainer.removeChild(child); child.destroy(); } if (scoreText) { game.removeChild(scoreText); scoreText = null; } if (timerText) { game.removeChild(timerText); timerText = null; } // Clean up any game UI elements if (game.modeText) { game.removeChild(game.modeText); game.modeText = null; } if (game.pauseButton) { game.removeChild(game.pauseButton); game.pauseButton = null; } game.timerInitialized = false; // Show menu initializeMenu(); }, 500); } }; function checkForAndDestroyMatches(swappedCellA, swappedCellB) { if (window.gravityInProgress || window.fillInProgress) { return; } var allMatchGroupsOnBoard = getMatches(); if (!allMatchGroupsOnBoard.length) { return; } var relevantMatchGroups = allMatchGroupsOnBoard.filter(function (group) { // Check if the match has cells in both visible and invisible areas var visibleCells = group.filter(function (cell) { return cell.row >= extraRows; }); var invisibleCells = group.filter(function (cell) { return cell.row < extraRows; }); // Don't count groups with both visible and invisible cells unless there are at least 3 visible cells if (visibleCells.length > 0 && invisibleCells.length > 0 && visibleCells.length < 3) { return false; } return group.some(function (cellInGroup) { return (cellInGroup === swappedCellA || cellInGroup === swappedCellB) && cellInGroup.row >= extraRows; }); }); if (!relevantMatchGroups.length) { return; } // Group by meme type var matchesByType = {}; relevantMatchGroups.forEach(function (group) { if (group.length > 0) { var type = group[0].value; if (!matchesByType[type]) { matchesByType[type] = []; } matchesByType[type].push(group); } }); var uniqueCellTracker = {}; var cellsToDestroy = []; // Process each type separately for (var type in matchesByType) { var typeGroups = matchesByType[type]; // Flatten all groups of this type var typeCells = []; typeGroups.forEach(function (group) { group.forEach(function (cell) { if (cell.row >= extraRows) { typeCells.push(cell); } }); }); // Add each cell of this type to cellsToDestroy typeCells.forEach(function (cell) { var cellKey = cell.row + "_" + cell.col; if (!uniqueCellTracker[cellKey] && cell.row >= extraRows && cell && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell) { cellsToDestroy.push(cell); uniqueCellTracker[cellKey] = true; } }); } ; if (cellsToDestroy.length) { // Pass the swapped cell that created the match as the center for the ripple effect var centerCell = swappedCellA && relevantMatchGroups.some(function (group) { return group.includes(swappedCellA); }) ? swappedCellA : swappedCellB; destroyCells(cellsToDestroy, centerCell); LK.setTimeout(function () { if (window.gravityInProgress || window.fillInProgress) { return; } var visibleMatchGroups = getMatches().filter(function (group) { // Check if the match has cells in both visible and invisible areas var visibleCells = group.filter(function (cell) { return cell.row >= extraRows; }); var invisibleCells = group.filter(function (cell) { return cell.row < extraRows; }); // Don't count groups with both visible and invisible cells unless there are at least 3 visible cells if (visibleCells.length > 0 && invisibleCells.length > 0 && visibleCells.length < 3) { return false; } return visibleCells.length >= 3; // At least 3 visible cells make a valid visible match }); var newMatches = visibleMatchGroups; if (newMatches.length > 0) { // Group by meme type var matchesByType = {}; newMatches.forEach(function (group) { if (group.length > 0) { var type = group[0].value; if (!matchesByType[type]) { matchesByType[type] = []; } matchesByType[type].push(group); } }); // Process each type separately for (var type in matchesByType) { var typeGroups = matchesByType[type]; var newCellsToDestroy = []; var newCellTracker = {}; // Collect all cells of this type typeGroups.forEach(function (group) { group.forEach(function (cell) { var cellKey = cell.row + "_" + cell.col; if (!newCellTracker[cellKey] && cell.row >= extraRows && cell && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell) { newCellsToDestroy.push(cell); newCellTracker[cellKey] = true; } }); }); if (newCellsToDestroy.length) { // Find center cell of this type var centerCell = newCellsToDestroy[Math.floor(newCellsToDestroy.length / 2)]; destroyCells(newCellsToDestroy, centerCell); } } } }, 400); } } var gridSize = 7; var extraRows = 9; var cellSpacing = 12; var cellSize = 240; var gridCells = []; var totalGridWidth = gridSize * cellSize + (gridSize - 1) * cellSpacing; var totalVisibleGridHeight = totalGridWidth; var totalGridHeight = totalVisibleGridHeight + extraRows * (cellSize + cellSpacing); var startX = (2048 - totalGridWidth) / 2 + cellSize / 2; var startY = (-1600 - totalVisibleGridHeight) / 2 + cellSize / 2 + extraRows * (cellSize + cellSpacing); function getCellPosition(row, col) { return { x: startX + col * (cellSize + cellSpacing), y: startY + row * (cellSize + cellSpacing) - extraRows * (cellSize + cellSpacing) }; } function initializeGrid() { // Add background image behind grid cells if (gridContainer.gridBackground) { gridContainer.removeChild(gridContainer.gridBackground); gridContainer.gridBackground = null; } var gridBackground = LK.getAsset('Bg3', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 + 100, alpha: 0.5 }); var scaleX = 2.2; var scaleY = 2.2; var scale = Math.max(scaleX, scaleY); gridBackground.scale.set(scale, scale); gridContainer.gridBackground = gridBackground; gridContainer.addChildAt(gridBackground, 0); // --- CUADRICULAS OBJETO PARA LUGARES MOVIBLES --- // Elimina cuadriculas previas si existen if (typeof cuadriculas !== "undefined" && cuadriculas && cuadriculas.parent) { cuadriculas.parent.removeChild(cuadriculas); } cuadriculas = new Container(); // Dibuja cuadricula solo en la zona visible (no en extraRows) for (var row = extraRows; row < gridSize + extraRows; row++) { for (var col = 0; col < gridSize; col++) { var pos = getCellPosition(row, col); var cuadriculaSprite = LK.getAsset('cuadricula', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.05, scaleY: 1.05, alpha: 0.7 }); cuadriculaSprite.x = pos.x; cuadriculaSprite.y = pos.y; cuadriculas.addChild(cuadriculaSprite); } } // Añade cuadriculas detrás de las celdas gridContainer.addChildAt(cuadriculas, 1); gridCells = []; for (var row = 0; row < gridSize + extraRows; row++) { gridCells[row] = []; for (var col = 0; col < gridSize; col++) { var pos = getCellPosition(row, col); var cell = getGridCell ? getGridCell() : new GridCell(); cell.x = pos.x; cell.y = pos.y; cell.row = row; cell.col = col; var randomValue; var attempts = 0; do { randomValue = Math.floor(Math.random() * 5) + 1; attempts++; var leftMatchCount = 0; var aboveMatchCount = 0; if (col >= 2) { if (gridCells[row][col - 1].value === randomValue && gridCells[row][col - 2].value === randomValue) { leftMatchCount = 2; } } if (row >= 2) { if (gridCells[row - 1][col].value === randomValue && gridCells[row - 2][col].value === randomValue) { aboveMatchCount = 2; } } } while ((leftMatchCount >= 2 || aboveMatchCount >= 2) && attempts < 10); cell.setValue(randomValue); if (row >= extraRows) { gridContainer.addChild(cell); } gridCells[row][col] = cell; } } } // --- PUNTERO DRAG --- // Variable global para habilitar/deshabilitar el drag de memes var dragMemeEnabled = true; // Puntero visual que sigue el dedo mientras está presionado var pointerActive = false; var pointerSprite = LK.getAsset('Puntero', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); pointerSprite.visible = false; pointerSprite.zIndex = 9999; // Asegura que esté encima game.addChild(pointerSprite); // --- CHECK CON ASSET PUNTERO ENCIMA DE TODAS LAS COLUMNAS DE MEMES --- var checkPointers = []; var checkRow = extraRows; // Fila visible superior for (var checkCol = 0; checkCol < gridSize; checkCol++) { var checkPos = getCellPosition(checkRow, checkCol); var checkPointer = LK.getAsset('Puntero', { anchorX: 0.5, anchorY: 1, x: checkPos.x, y: checkPos.y - cellSize / 2 - 110, // Encima de la celda, con margen scaleX: 2, scaleY: 2, alpha: 0 }); checkPointer.zIndex = 9998; // Debajo del puntero drag, encima de la grilla game.addChild(checkPointer); checkPointers.push(checkPointer); } // Evento down: activa el puntero y lo posiciona game.down = function (x, y, obj) { pointerActive = true; pointerSprite.visible = true; pointerSprite.x = x; pointerSprite.y = y; if (typeof handleCellTap === "function") { // Si hay una celda debajo, permite la selección normal // (esto mantiene la funcionalidad original) // handleCellTap puede ser llamada aquí si se desea } }; // Evento move: si está activo, sigue el dedo game.move = function (x, y, obj) { if (pointerActive) { pointerSprite.x = x; pointerSprite.y = y; // --- SWAP POR ARRASTRE --- if (dragMemeEnabled && selectedCell && gridCells[selectedCell.row] && gridCells[selectedCell.row][selectedCell.col] === selectedCell) { // Calcula la posición central de la celda seleccionada var cellPos = getCellPosition(selectedCell.row, selectedCell.col); var dx = x - cellPos.x; var dy = y - cellPos.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 100) { // Determina la dirección principal (horizontal o vertical) var dir; if (Math.abs(dx) > Math.abs(dy)) { dir = dx > 0 ? "right" : "left"; } else { dir = dy > 0 ? "down" : "up"; } var targetRow = selectedCell.row; var targetCol = selectedCell.col; if (dir === "right" && selectedCell.col < gridSize - 1) { targetCol++; } else if (dir === "left" && selectedCell.col > 0) { targetCol--; } else if (dir === "down" && selectedCell.row < gridSize + extraRows - 1) { targetRow++; } else if (dir === "up" && selectedCell.row > 0) { targetRow--; } else { // No hay celda adyacente válida return; } var targetCell = gridCells[targetRow][targetCol]; // Solo intercambia si la celda destino existe y no está siendo destruida if (targetCell && !targetCell.beingDestroyed) { // Llama a la lógica de swap como si se hubiera hecho tap en la celda adyacente handleCellTap(targetCell); } } } } }; // Evento up: desactiva el puntero game.up = function (x, y, obj) { pointerActive = false; pointerSprite.visible = false; }; // --- INTRODUCTION SCREEN WITH DARK TRANSITIONS --- // Show a black transition overlay, then introduction, then fade out and show menu var introTransition = null; var introImage = null; var introTimeout = null; // --- INTRO OPTION: persistently store intro enabled/disabled --- // Load dragMemeEnabled from storage if available, otherwise default to true var dragMemeEnabled = typeof storage.dragMemeEnabled === "boolean" ? storage.dragMemeEnabled : true; storage.dragMemeEnabled = dragMemeEnabled; // Load introEnabled from storage if available, otherwise default to true var introEnabled = typeof storage.introEnabled === "boolean" ? storage.introEnabled : true; storage.introEnabled = introEnabled; // Load backgroundColorAnimEnabled from storage if available, otherwise default to true var backgroundColorAnimEnabled = typeof storage.backgroundColorAnimEnabled === "boolean" ? storage.backgroundColorAnimEnabled : true; storage.backgroundColorAnimEnabled = backgroundColorAnimEnabled; // Load particlesEnabled from storage if available, otherwise default to true var particlesEnabled = typeof storage.particlesEnabled === "boolean" ? storage.particlesEnabled : true; storage.particlesEnabled = particlesEnabled; // Function to show the introduction and then the menu function showIntroductionAndMenu() { if (!introEnabled) { // If intro is disabled, go straight to menu initializeMenu(); return; } // 1. Create black overlay (full screen) introTransition = LK.getAsset('transicion', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 1, scaleY: 1, alpha: 1 }); introTransition.zIndex = 99999; game.addChild(introTransition); // 2. Fade in (already black), then show intro image LK.setTimeout(function () { // 3. Show introduction image introImage = LK.getAsset('introduction', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 0.7, scaleY: 0.7, alpha: 0 }); introImage.zIndex = 100000; game.addChild(introImage); // Fade in intro image tween(introImage, { alpha: 1 }, { duration: 800, easing: tween.easeInOutQuad }); // 4. Wait for 2.5 seconds, then fade out intro image, then fade out overlay after a delay introTimeout = LK.setTimeout(function () { // Fade out intro image tween(introImage, { alpha: 0 }, { duration: 700, easing: tween.easeInOutQuad, onFinish: function onFinish() { if (introImage && introImage.parent) { game.removeChild(introImage); introImage = null; } // Fade out black overlay after intro image is gone (extra delay) LK.setTimeout(function () { tween(introTransition, { alpha: 0 }, { duration: 1000, easing: tween.easeInOutQuad, onFinish: function onFinish() { if (introTransition && introTransition.parent) { game.removeChild(introTransition); introTransition = null; } // Now show the menu initializeMenu(); } }); }, 400); // 400ms extra so overlay lasts longer than image } }); }, 2500); }, 400); } // Call introduction instead of menu directly showIntroductionAndMenu(); function ensureNoInitialMatches() { var matches = getMatches(); if (matches.length > 0) { matches.forEach(function (group) { group.forEach(function (cell) { var currentValue = cell.value; var newValue; do { newValue = Math.floor(Math.random() * 5) + 1; } while (newValue === currentValue); cell.setValue(newValue); }); }); ensureNoInitialMatches(); } } ensureNoInitialMatches();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScoreClassic: 0,
highScoreFast: 0,
highScoreSeek: 0
});
/****
* Classes
****/
var CosmeticsScreen = Container.expand(function () {
var self = Container.call(this);
var title = new Text2("Cosmetics", {
size: 150,
fill: 0x000000
});
title.anchor.set(0.5, 0);
title.x = 2048 / 2;
title.y = 300;
self.addChild(title);
title.y = 200;
self.cosmeticsGrid = new Container();
self.addChild(self.cosmeticsGrid);
var cosmeticSets = [{
id: 1,
name: "Default",
memePrefix: 'meme'
}, {
id: 2,
name: "Variant 1",
memePrefix: 'meme1-2'
}, {
id: 3,
name: "Variant 2",
memePrefix: 'meme1-3'
}];
var cardWidth = 550;
var cardHeight = 680;
var cardsPerRow = 3;
var cardSpacing = 50;
var startX = (2048 - (cardsPerRow * cardWidth + (cardsPerRow - 1) * cardSpacing)) / 2 + cardWidth / 2;
var startY = 700;
cosmeticSets.forEach(function (set, index) {
var row = Math.floor(index / cardsPerRow);
var col = index % cardsPerRow;
var card = new Container();
card.x = startX + col * (cardWidth + cardSpacing);
card.y = startY + row * (cardHeight + 80);
var cardShadow = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.6,
scaleY: 3.6
});
cardShadow.tint = 0x000000;
cardShadow.alpha = 0.3;
cardShadow.x = 10;
cardShadow.y = 10;
var cardBg = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 3.5
});
cardBg.tint = 0xFFFFFF;
var nameText = new Text2(set.name, {
size: 70,
fill: 0x000000
});
nameText.anchor.set(0.5, 0);
nameText.y = -cardHeight / 2 + 80;
var setLabel = new Text2("MEME SET", {
size: 40,
fill: 0x555555
});
setLabel.anchor.set(0.5, 1);
setLabel.y = cardHeight / 2 - 40;
function animateCard() {
tween(card, {
y: card.y - 10
}, {
duration: 800,
easing: tween.easeInOutQuad,
onComplete: function onComplete() {
tween(card, {
y: card.y + 10
}, {
duration: 800,
easing: tween.easeInOutQuad,
onComplete: animateCard
});
}
});
}
LK.setTimeout(animateCard, Math.random() * 1000);
var previewContainer = new Container();
var previewSize = 120;
var previewSpacing = 20;
var previewScale = 0.6;
var previewsPerRow = 3;
var previewGridWidth = previewsPerRow * previewSize + (previewsPerRow - 1) * previewSpacing;
var previewStartX = -previewGridWidth / 2 + previewSize / 2;
var previewStartY = -40;
for (var i = 1; i <= 5; i++) {
var row = Math.floor((i - 1) / previewsPerRow);
var col = (i - 1) % previewsPerRow;
var preview;
if (set.id === 1) {
preview = LK.getAsset('meme' + i, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: previewScale,
scaleY: previewScale
});
} else if (set.id === 2) {
preview = LK.getAsset('meme' + i + '-2', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: previewScale,
scaleY: previewScale
});
} else {
preview = LK.getAsset('meme' + i + '-3', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: previewScale,
scaleY: previewScale
});
}
preview.x = previewStartX + col * (previewSize + previewSpacing);
preview.y = previewStartY + row * (previewSize + previewSpacing);
var previewBg = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
alpha: 0.1
});
previewBg.tint = 0x888888;
previewBg.x = preview.x;
previewBg.y = preview.y;
previewContainer.addChild(previewBg);
previewContainer.addChild(preview);
}
var statusOverlay = new Container();
card.statusOverlay = statusOverlay;
var isSelected = set.id === 1; // Default always to set 1
if (isSelected) {
var _pulseBorder = function pulseBorder() {
tween(selectedBorder, {
alpha: 0.2
}, {
duration: 700,
easing: tween.easeInOutQuad,
onComplete: function onComplete() {
tween(selectedBorder, {
alpha: 0.5
}, {
duration: 700,
easing: tween.easeInOutQuad,
onComplete: _pulseBorder
});
}
});
};
var selectedBorder = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.6,
scaleY: 3.6
});
selectedBorder.tint = 0x00FF00;
selectedBorder.alpha = 0.5;
_pulseBorder();
var checkmark = LK.getAsset('check', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
});
checkmark.x = cardWidth / 2 - 70;
checkmark.y = -cardHeight / 2 + 70;
card.addChildAt(selectedBorder, 0);
statusOverlay.addChild(checkmark);
}
card.addChild(cardShadow);
card.addChild(cardBg);
card.addChild(nameText);
card.addChild(setLabel);
card.addChild(previewContainer);
card.addChild(statusOverlay);
card.interactive = true;
card.setId = set.id;
card.isUnlocked = true;
card.price = set.price;
card.down = function (x, y, obj) {
LK.effects.flashObject(cardBg, 0xFFFFFF, 200);
LK.getSound('ButtonSound').play();
// Update selectedSet and persist to storage
selectedSet = card.setId;
storage.selectedSet = selectedSet;
self.cosmeticsGrid.children.forEach(function (otherCard) {
otherCard.children.forEach(function (child) {
if (child.tint === 0x00FF00 && child !== cardBg) {
otherCard.removeChild(child);
}
});
if (otherCard.statusOverlay) {
while (otherCard.statusOverlay.children.length > 0) {
otherCard.statusOverlay.removeChild(otherCard.statusOverlay.children[0]);
}
}
});
var selectedBorder = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.6,
scaleY: 3.6
});
selectedBorder.tint = 0x00FF00;
var checkmark = LK.getAsset('check', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
});
checkmark.x = cardWidth / 2 - 70;
checkmark.y = -cardHeight / 2 + 70;
checkmark.isCheckmark = true;
card.addChildAt(selectedBorder, 0);
statusOverlay.addChild(checkmark);
};
self.cosmeticsGrid.addChild(card);
});
var backButton = createButton("BACK TO MENU", 0xFF5500, 3, 1.5, function () {
game.removeChild(self);
initializeMenu();
}, 2048 / 2, 2732 - 200);
self.addChild(backButton);
return self;
});
var GridCell = Container.expand(function () {
var self = Container.call(this);
self.init = function () {
self.value = 0;
self.sprite = null;
self.row = -1;
self.col = -1;
self.isSpecial = false;
self.specialType = null;
return self;
};
self.setValue = function (newValue) {
if (self.value === newValue) {
return;
}
self.value = newValue;
if (self.sprite) {
self.removeChild(self.sprite);
}
var spriteId;
if (game && game.gameMode === 'seekmode' && game.seekModeForceSeekSprite) {
spriteId = 'seek';
} else {
// Default to meme set 1
// Use global selectedSet for cosmetics
if (typeof selectedSet !== "number") selectedSet = 1;
if (selectedSet === 1) {
spriteId = 'meme' + newValue;
} else if (selectedSet === 2) {
spriteId = 'meme' + newValue + '-2';
} else if (selectedSet === 3) {
spriteId = 'meme' + newValue + '-3';
} else {
spriteId = 'meme' + newValue;
}
}
self.sprite = LK.getAsset(spriteId, {
anchorX: 0.5,
anchorY: 0.5
});
var padding = 20;
var maxWidth = cellSize - padding * 2;
var maxHeight = cellSize - padding * 2;
var scaleX = maxWidth / self.sprite.width;
var scaleY = maxHeight / self.sprite.height;
var scale = Math.min(scaleX, scaleY);
self.sprite.scale.set(scale, scale);
self.addChild(self.sprite);
if (self.isSpecial) {
// Remove any previous overlays
if (self.specialOverlay && self.children.indexOf(self.specialOverlay) !== -1) {
self.removeChild(self.specialOverlay);
self.specialOverlay = null;
}
// --- Special overlays for green+bomb and green+clear/line combos ---
// If this cell was created as a result of a green+bomb or green+clear/line combo, show the overlay on top of the meme asset
if (self.specialType === 'bomb') {
// Overlay habilidadBomba sprite
self.specialOverlay = LK.getAsset('habilidadBomba', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale overlay to fit cell
var padding = 20;
var maxWidth = cellSize - padding * 2;
var maxHeight = cellSize - padding * 2;
var scaleX = maxWidth / self.specialOverlay.width;
var scaleY = maxHeight / self.specialOverlay.height;
var scale = Math.min(scaleX, scaleY);
self.specialOverlay.scale.set(scale, scale);
self.addChild(self.specialOverlay);
// --- Color sync: animate overlay tint with background ---
if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") {
var _tweenBombOverlayColor = function tweenBombOverlayColor() {
if (!self.specialOverlay) {
return;
}
var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length;
var nextColor = colorTweenTargets[nextIndex];
self._specialOverlayTween = tween(self.specialOverlay, {
tint: nextColor
}, {
duration: 14000,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
if (self.specialOverlay) {
_tweenBombOverlayColor();
}
}
});
};
// Remove any previous color tween
if (self._specialOverlayTween) {
self._specialOverlayTween.stop && self._specialOverlayTween.stop();
self._specialOverlayTween = null;
}
self.specialOverlay.tint = colorTweenTargets[currentColorIndex];
_tweenBombOverlayColor();
}
} else if (self.specialType === 'green') {
// Use the 'habilidadVerde' asset for green special ability
if (self.sprite) {
self.removeChild(self.sprite);
}
self.sprite = LK.getAsset('habilidadVerde', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale to fit cell
var padding = 20;
var maxWidth = cellSize - padding * 2;
var maxHeight = cellSize - padding * 2;
var scaleX = maxWidth / self.sprite.width;
var scaleY = maxHeight / self.sprite.height;
var scale = Math.min(scaleX, scaleY);
self.sprite.scale.set(scale, scale);
self.addChild(self.sprite);
} else if (self.specialType === 'clear' || self.specialType === 'vertical' || self.specialType === 'horizontal') {
// Overlay habilidadClear sprite for clear/line specials
self.specialOverlay = LK.getAsset('HabilidadClear', {
anchorX: 0.5,
anchorY: 0.5
});
// Rotate overlay to match direction
if (self.specialType === 'horizontal') {
self.specialOverlay.rotation = 0; // Horizontal: no rotation
} else if (self.specialType === 'vertical') {
self.specialOverlay.rotation = Math.PI / 2; // Vertical: 90 degrees
} else {
self.specialOverlay.rotation = 0; // Default for 'clear'
}
// Scale overlay to fit cell
var padding = 20;
var maxWidth = cellSize - padding * 2;
var maxHeight = cellSize - padding * 2;
var scaleX = maxWidth / self.specialOverlay.width;
var scaleY = maxHeight / self.specialOverlay.height;
var scale = Math.min(scaleX, scaleY);
self.specialOverlay.scale.set(scale, scale);
self.addChild(self.specialOverlay);
// --- Color sync: animate overlay tint with background ---
if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") {
var _tweenClearOverlayColor = function tweenClearOverlayColor() {
if (!self.specialOverlay) {
return;
}
var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length;
var nextColor = colorTweenTargets[nextIndex];
self._specialOverlayTween = tween(self.specialOverlay, {
tint: nextColor
}, {
duration: 14000,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
if (self.specialOverlay) {
_tweenClearOverlayColor();
}
}
});
};
// Remove any previous color tween
if (self._specialOverlayTween) {
self._specialOverlayTween.stop && self._specialOverlayTween.stop();
self._specialOverlayTween = null;
}
self.specialOverlay.tint = colorTweenTargets[currentColorIndex];
_tweenClearOverlayColor();
}
} else {
self.sprite.tint = 0xFF8800;
}
}
};
self.activateSpecialPower = function () {
if (!self.isSpecial) {
return;
}
var cellsToDestroy = [];
var bonusPoints = 0;
if (self.specialType === 'horizontal') {
for (var row = extraRows; row < gridSize + extraRows; row++) {
if (gridCells[row][self.col] && gridCells[row][self.col] !== self) {
cellsToDestroy.push(gridCells[row][self.col]);
}
}
bonusPoints = 100;
} else if (self.specialType === 'vertical') {
for (var col = 0; col < gridSize; col++) {
if (gridCells[self.row][col] && gridCells[self.row][col] !== self) {
cellsToDestroy.push(gridCells[self.row][col]);
}
}
bonusPoints = 100;
} else if (self.specialType === 'bomb') {
for (var row = Math.max(extraRows, self.row - 1); row <= Math.min(gridSize + extraRows - 1, self.row + 1); row++) {
for (var col = Math.max(0, self.col - 1); col <= Math.min(gridSize - 1, self.col + 1); col++) {
if (gridCells[row][col] && gridCells[row][col] !== self) {
cellsToDestroy.push(gridCells[row][col]);
}
}
}
bonusPoints = 150;
} else if (self.specialType === 'green') {
self.value = 99; // Ensure green special always has value 99
bonusPoints = 200;
}
// No score pop-up needed
if (cellsToDestroy.length > 0) {
destroyCells(cellsToDestroy);
self.beingDestroyed = true;
LK.getSound('Explosion').play();
LK.effects.flashObject(self, 0xFFFFFF, 200);
gridContainer.removeChild(self);
self.destroy();
gridCells[self.row][self.col] = null;
}
};
self.showSelection = function () {
if (self.selectionHighlight) {
return;
}
self.selectionHighlight = new Container();
var highlight = LK.getAsset('cuadricula', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.15,
scaleY: 1.15,
alpha: 0.6
});
highlight.tint = 0x00FFFF;
self.selectionHighlight.addChild(highlight);
self.addChildAt(self.selectionHighlight, 0);
function pulseAnimation() {
tween(highlight, {
alpha: 0.3,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeInOutQuad,
onComplete: function onComplete() {
tween(highlight, {
alpha: 0.6,
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 500,
easing: tween.easeInOutQuad,
onComplete: pulseAnimation
});
}
});
}
pulseAnimation();
};
self.hideSelection = function () {
if (self.selectionHighlight) {
self.removeChild(self.selectionHighlight);
self.selectionHighlight = null;
}
};
self.down = function (x, y, obj) {
handleCellTap(self);
};
self.init();
return self;
});
var MemeParticle = Container.expand(function () {
var self = Container.call(this);
self.init = function (textureId) {
self.textureId = textureId;
if (self.sprite) {
self.removeChild(self.sprite);
}
self.sprite = self.attachAsset(self.textureId, {
anchorX: 0.5,
anchorY: 0.5
});
self.alpha = 0.8;
// Aumenta el tamaño un 20%
self.scale.set(0.6);
// Aumenta la velocidad (dobla la velocidad base)
self.speedX = (Math.random() - 0.5) * 2;
self.speedY = 1.0 + Math.random() * 1.0;
self.rotationSpeed = (Math.random() - 0.5) * 0.05;
return self;
};
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
self.rotation += self.rotationSpeed;
if (self.y > 2732 + 100 || self.x < -100 || self.x > 2048 + 100) {
self.destroy();
}
};
return self;
});
var MenuScreen = Container.expand(function () {
var self = Container.call(this);
// Initialize title touch counter
self.titleTouchCount = 0;
var title = self.attachAsset('titulo', {
anchorX: 0.5,
anchorY: 0,
scaleX: 3,
scaleY: 3
});
title.x = 2048 / 2;
title.y = 300;
title.interactive = true;
// Add touch functionality to title
title.down = function (x, y, obj) {
self.titleTouchCount++;
// Flash title when touched
LK.effects.flashObject(title, 0xFFFFFF, 200);
// After 10 touches, change title to easter egg 2
if (self.titleTouchCount >= 10) {
// Remove the current title
self.removeChild(title);
// Create new title with easterEgg2 asset
title = self.attachAsset('easterEgg2', {
anchorX: 0.5,
anchorY: 0,
scaleX: 3,
scaleY: 3
});
title.x = 2048 / 2;
title.y = 300;
// Play a sound effect for the easter egg
LK.getSound('Explosion').play();
}
};
function pulseTitle() {
tween(title, {
scaleX: 3.2,
scaleY: 3.4,
y: 200
}, {
duration: 400,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
tween(title, {
scaleX: 3.5,
scaleY: 2.7,
y: 350
}, {
duration: 300,
easing: tween.easeInQuad,
onFinish: function onFinish() {
tween(title, {
rotation: -0.05,
scaleX: 3.1,
scaleY: 3.2,
y: 320
}, {
duration: 180,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
tween(title, {
rotation: 0.05,
scaleX: 3.2,
scaleY: 3.1
}, {
duration: 180,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
tween(title, {
rotation: 0,
scaleX: 3,
scaleY: 3,
y: 300
}, {
duration: 250,
easing: tween.easeOutElastic,
onFinish: function onFinish() {
LK.setTimeout(pulseTitle, 800);
}
});
}
});
}
});
}
});
}
});
}
pulseTitle();
// First column
var classicButton = createButton("ZEN MODE", 0x00AA00, 3, 1.5, function () {
self.startGame('classic');
}, 2048 / 4, 2732 / 2 + 400);
self.addChild(classicButton);
var seekModeButton = createButton("SEEK MODE", 0xDD5500, 3, 1.5, function () {
self.startGame('seekmode');
}, 2048 / 4, 2732 / 2 + 700);
self.addChild(seekModeButton);
var fastButton = createButton("FAST MODE", 0x0088FF, 3, 1.5, function () {
self.startGame('fast');
}, 2048 / 4, 2732 / 2 + 1000);
self.addChild(fastButton);
// Second column
var cosmeticsButton = createButton("COSMETICS", 0x9932CC, 3, 1.5, function () {
game.removeChild(self);
showCosmeticsScreen();
}, 2048 * 3 / 4, 2732 / 2 + 400);
self.addChild(cosmeticsButton);
var recordsButton = createButton("RECORDS", 0xFFA500, 3, 1.5, function () {
game.removeChild(self);
showRecordsScreen();
}, 2048 * 3 / 4, 2732 / 2 + 700);
self.addChild(recordsButton);
var settingsButton = createButton("SETTINGS", 0x800080, 3, 1.5, function () {
game.removeChild(self);
showSettingsScreen();
}, 2048 * 3 / 4, 2732 / 2 + 1000);
self.addChild(settingsButton);
self.startGame = function (mode) {
game.gameMode = mode || 'classic';
game.removeChild(self);
game.initializeGame();
};
self.particleContainer = new Container();
self.addChildAt(self.particleContainer, 0);
self.memeParticles = ['meme1', 'meme2', 'meme3', 'meme4', 'meme5', 'meme1-2', 'meme2-2', 'meme3-2', 'meme4-2', 'meme5-2', 'meme1-3', 'meme2-3', 'meme3-3', 'meme4-3', 'meme5-3'];
self.particleTimer = LK.setInterval(function () {
self.emitParticle();
}, 500);
self.emitParticle = function () {
if (!particlesEnabled) return;
var randomMemeId = self.memeParticles[Math.floor(Math.random() * self.memeParticles.length)];
var particle = new MemeParticle().init(randomMemeId);
particle.x = Math.random() * 2048;
particle.y = -50;
self.particleContainer.addChild(particle);
};
self.update = function () {
self.particleContainer.children.forEach(function (particle) {
if (particle && particle.update) {
// Check collision with pointerSprite (global)
if (typeof pointerSprite !== "undefined" && pointerSprite.visible && particle.x !== undefined && particle.y !== undefined && pointerSprite.x !== undefined && pointerSprite.y !== undefined) {
// Use simple distance check (circle collision)
var dx = particle.x - pointerSprite.x;
var dy = particle.y - pointerSprite.y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Use average radius (particle is 100x100, pointer is 10x10)
if (dist < 55) {
// Trigger explosion: emit particles at this position
if (particlesEnabled) {
var numParticles = 10 + Math.floor(Math.random() * 5);
for (var p = 0; p < numParticles; p++) {
var part = LK.getAsset('particula', {
anchorX: 0.5,
anchorY: 0.5
});
// Randomize color (RGB)
var r = Math.floor(128 + Math.random() * 127);
var g = Math.floor(128 + Math.random() * 127);
var b = Math.floor(128 + Math.random() * 127);
var rgb = r << 16 | g << 8 | b;
part.tint = rgb;
part.x = particle.x;
part.y = particle.y;
// Random velocity and direction
var angle = Math.random() * Math.PI * 2;
var speed = 7 + Math.random() * 7;
var vx = Math.cos(angle) * speed;
var vy = Math.sin(angle) * speed;
// Animate particle
(function (part, vx, vy) {
var lifetime = 180 + Math.random() * 120;
var startAlpha = 0.9 + Math.random() * 0.1;
part.alpha = startAlpha;
self.particleContainer.addChild(part);
var elapsed = 0;
var updateFn = function updateFn() {
part.x += vx;
part.y += vy;
part.alpha -= 0.05 + Math.random() * 0.03;
part.scale.x *= 0.92;
part.scale.y *= 0.92;
elapsed += 16;
if (elapsed > lifetime || part.alpha <= 0.05) {
if (part.parent) {
part.parent.removeChild(part);
}
LK.clearInterval(intervalId);
}
};
var intervalId = LK.setInterval(updateFn, 16);
})(part, vx, vy);
}
}
// Play explosion sound
LK.getSound('Explosion').play();
// --- Easter Egg: cuenta partículas meme destruidas en menú ---
if (!easterEggActive) {
memeParticlesDestroyedInMenu++;
if (memeParticlesDestroyedInMenu >= 20) {
// Mostrar easter egg solo si no está activo
easterEggActive = true;
memeParticlesDestroyedInMenu = 0;
// Crear contenedor del easter egg
easterEggContainer = new Container();
var egg = LK.getAsset('easterEgg', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1.1,
scaleY: 1.1,
alpha: 0.0
});
easterEggContainer.addChild(egg);
// Fade in
tween(egg, {
alpha: 1
}, {
duration: 600
});
// Añadir al menú
self.addChild(easterEggContainer);
easterEggTimeout = LK.setTimeout(function () {
if (easterEggContainer && easterEggContainer.parent) {
// Fade out
tween(egg, {
alpha: 0
}, {
duration: 500
});
// Removed tween on eggText (no eggText exists)
LK.setTimeout(function () {
if (easterEggContainer.parent) {
self.removeChild(easterEggContainer);
}
easterEggContainer = null;
easterEggActive = false;
}, 600);
} else {
easterEggActive = false;
easterEggContainer = null;
}
}, 5000);
}
}
// Remove the meme particle
if (particle.parent) {
particle.parent.removeChild(particle);
}
if (typeof particle.destroy === "function") {
particle.destroy();
}
return; // Only destroy one per frame
}
}
particle.update();
}
});
};
self.destroy = function () {
LK.clearInterval(self.particleTimer);
self.particleTimer = null;
self.particleContainer.destroy({
children: true
});
self.particleContainer = null;
Container.prototype.destroy.call(self);
};
return self;
});
var PauseButton = Container.expand(function () {
var self = Container.call(this);
var pauseIcon = self.attachAsset('Pause', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
// 0.8 * 1.5 = 1.2
scaleY: 1.2
});
var background = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.05,
// 0.7 * 1.5 = 1.05
scaleY: 1.05
});
background.tint = 0x444444;
background.alpha = 0.7;
self.addChildAt(background, 0);
self.down = function (x, y, obj) {
LK.effects.flashObject(self, 0xFFFFFF, 200);
LK.getSound('ButtonSound').play();
showPausePopup();
};
return self;
});
var PausePopup = Container.expand(function () {
var self = Container.call(this);
var popupBg = self.attachAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.5,
scaleY: 4.5
});
popupBg.tint = 0x222222;
popupBg.alpha = 0.9;
var popupTitle = new Text2("PAUSED", {
size: 100,
fill: 0xFFFFFF
});
popupTitle.anchor.set(0.5, 0);
popupTitle.y = -250;
self.addChild(popupTitle);
var continueButton = createButton("CONTINUE", 0x00AA00, 2.5, 1.2, function () {
game.removeChild(self);
}, 0, -20);
self.addChild(continueButton);
var surrenderButton = createButton("SURRENDER", 0xFF0000, 2.5, 1.2, function () {
game.removeChild(self);
game.onGameOver();
}, 0, 200);
self.addChild(surrenderButton);
return self;
});
var RecordsScreen = Container.expand(function () {
var self = Container.call(this);
// Create title
var title = new Text2("High Scores", {
size: 150,
fill: 0x000000
});
title.anchor.set(0.5, 0);
title.x = 2048 / 2;
title.y = 300;
self.addChild(title);
// Refresh high scores when shown
self.refreshHighScores = function () {
// Clear existing score texts if any
if (self.classicScoreText) {
self.removeChild(self.classicScoreText);
self.classicScoreText = null;
}
if (self.fastScoreText) {
self.removeChild(self.fastScoreText);
self.fastScoreText = null;
}
if (self.seekScoreText) {
self.removeChild(self.seekScoreText);
self.seekScoreText = null;
}
// Create mode buttons with high scores
function createModeButton(modeName, score, color, posY) {
var button = new Container();
var buttonBg = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 1.5
});
buttonBg.tint = color;
var modeText = new Text2(modeName, {
size: 90,
fill: 0xFFFFFF
});
modeText.anchor.set(0.5, 0);
modeText.y = -100;
var scoreText = new Text2(score.toString(), {
size: 70,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
scoreText.y = 10;
button.addChild(buttonBg);
button.addChild(modeText);
button.addChild(scoreText);
button.x = 2048 / 2;
button.y = posY;
// --- Easter Egg 3 logic ---
if (typeof window.easterEgg3Counters === "undefined") {
window.easterEgg3Counters = {
zen: 0,
fast: 0,
seek: 0
};
}
if (typeof window.easterEgg3Active === "undefined") {
window.easterEgg3Active = false;
}
if (typeof window.easterEgg3Container === "undefined") {
window.easterEgg3Container = null;
}
button.interactive = true;
button.down = function (x, y, obj) {
// Identify which button was pressed by modeName
if (modeName === "Zen Mode") {
window.easterEgg3Counters.zen++;
if (window.easterEgg3Counters.zen > 2) window.easterEgg3Counters.zen = 2;
} else if (modeName === "Fast Mode") {
window.easterEgg3Counters.fast++;
if (window.easterEgg3Counters.fast > 3) window.easterEgg3Counters.fast = 3;
} else if (modeName === "Seek Mode") {
window.easterEgg3Counters.seek++;
if (window.easterEgg3Counters.seek > 1) window.easterEgg3Counters.seek = 1;
}
// Check if all conditions are met
if (window.easterEgg3Counters.zen === 2 && window.easterEgg3Counters.fast === 3 && window.easterEgg3Counters.seek === 1 && !window.easterEgg3Active) {
window.easterEgg3Active = true;
// Show easterEgg3 asset centered and above all
if (window.easterEgg3Container && window.easterEgg3Container.parent) {
window.easterEgg3Container.parent.removeChild(window.easterEgg3Container);
}
window.easterEgg3Container = new Container();
var egg3 = LK.getAsset('easterEgg3', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1,
scaleY: 1,
alpha: 0
});
window.easterEgg3Container.addChild(egg3);
// Fade in
tween(egg3, {
alpha: 1
}, {
duration: 600
});
// Add to records screen
if (typeof game !== "undefined" && game.children && game.children.indexOf(self) !== -1) {
self.addChild(window.easterEgg3Container);
} else if (typeof game !== "undefined") {
game.addChild(window.easterEgg3Container);
}
// Fade out and remove after 5 seconds
LK.setTimeout(function () {
tween(egg3, {
alpha: 0
}, {
duration: 500
});
LK.setTimeout(function () {
if (window.easterEgg3Container && window.easterEgg3Container.parent) {
window.easterEgg3Container.parent.removeChild(window.easterEgg3Container);
}
window.easterEgg3Container = null;
window.easterEgg3Active = false;
// Reset counters so it can be triggered again
window.easterEgg3Counters.zen = 0;
window.easterEgg3Counters.fast = 0;
window.easterEgg3Counters.seek = 0;
}, 600);
}, 5000);
}
};
return button;
}
self.addChild(createModeButton("Zen Mode", highScoreClassic, 0x00AA00, 800));
self.addChild(createModeButton("Fast Mode", highScoreFast, 0x0088FF, 1100));
self.addChild(createModeButton("Seek Mode", highScoreSeek, 0xDD5500, 1400));
};
// Refresh scores when created
self.refreshHighScores();
// Back button
var backButton = createButton("BACK TO MENU", 0xFF5500, 3, 1.5, function () {
game.removeChild(self);
initializeMenu();
}, 2048 / 2, 2732 - 200);
self.addChild(backButton);
return self;
});
var SeekMode = Container.expand(function () {
var self = Container.call(this);
// Similar to classic mode logic
var gridContainer = new Container();
self.addChild(gridContainer);
return self;
});
var SettingsScreen = Container.expand(function () {
var self = Container.call(this);
var title = new Text2("Settings", {
size: 150,
fill: 0x000000
});
title.anchor.set(0.5, 0);
title.x = 2048 / 2;
title.y = 300;
self.addChild(title);
// --- Improved vertical separation and position for all settings buttons ---
var settingsButtonSpacing = 320; // Increased spacing
var firstButtonY = 800; // Lowered starting Y
// RESET PROGRESS button (first, red, prominent)
var resetButton = createButton("RESET PROGRESS", 0xFF0000, 3.7, 1.5, function () {
// Show confirmation popup
var confirmPopup = new Container();
// Enlarged popup background
var popupBg = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 5.5,
scaleY: 5.7
});
popupBg.tint = 0x222222;
popupBg.alpha = 0.9;
var popupTitle = new Text2("Reset all progress?", {
size: 80,
fill: 0xFFFFFF
});
popupTitle.anchor.set(0.5, 0);
popupTitle.y = -440;
var popupText = new Text2("This will reset all high scores\nThis cannot be undone.", {
size: 60,
fill: 0xFFFFFF
});
popupText.anchor.set(0.5, 0);
popupText.y = -300;
// Confirm and Cancel buttons, separated vertically
var confirmButton = createButton("CONFIRM", 0xFF0000, 2.5, 1.2, function () {
// Reset high scores
highScoreClassic = 0;
highScoreFast = 0;
highScoreSeek = 0;
storage.highScoreClassic = 0;
storage.highScoreFast = 0;
storage.highScoreSeek = 0;
// Show confirmation message
var messagePopup = new Container();
var messageBg = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 5.5,
scaleY: 1.5
});
messageBg.tint = 0x00AA00;
var messageText = new Text2("Progress reset successfully", {
size: 80,
fill: 0xFFFFFF
});
messageText.anchor.set(0.5, 0.5);
messagePopup.addChild(messageBg);
messagePopup.addChild(messageText);
messagePopup.x = 2048 / 2;
messagePopup.y = 2732 / 2;
game.addChild(messagePopup);
// Remove message after 2 seconds
LK.setTimeout(function () {
game.removeChild(messagePopup);
}, 2000);
// Remove confirmation popup
game.removeChild(confirmPopup);
}, 0, 60);
var cancelButton = createButton("CANCEL", 0x00AA00, 2.5, 1.2, function () {
game.removeChild(confirmPopup);
}, 0, 280); // Increased vertical separation
confirmPopup.addChild(popupBg);
confirmPopup.addChild(popupTitle);
confirmPopup.addChild(popupText);
confirmPopup.addChild(confirmButton);
confirmPopup.addChild(cancelButton);
confirmPopup.x = 2048 / 2;
confirmPopup.y = 2732 / 2;
game.addChild(confirmPopup);
}, 2048 / 2, firstButtonY);
self.addChild(resetButton);
// DRAG MEME button
var dragButtonLabel = function dragButtonLabel() {
return dragMemeEnabled ? "DRAG MEME: ON" : "DRAG MEME: OFF";
};
var dragButton = createButton(dragButtonLabel(), 0x0088FF, 3.7, 1.5, function () {
dragMemeEnabled = !dragMemeEnabled;
storage.dragMemeEnabled = dragMemeEnabled;
dragButton.label.setText(dragButtonLabel());
}, 2048 / 2, firstButtonY + settingsButtonSpacing * 1);
self.addChild(dragButton);
// INTRO ENABLE/DISABLE button
var introButtonLabel = function introButtonLabel() {
return introEnabled ? "SHOW INTRO: ON" : "SHOW INTRO: OFF";
};
var introButton = createButton(introButtonLabel(), 0x222222, 3.7, 1.5, function () {
introEnabled = !introEnabled;
storage.introEnabled = introEnabled;
introButton.label.setText(introButtonLabel());
}, 2048 / 2, firstButtonY + settingsButtonSpacing * 2);
self.addChild(introButton);
// BACKGROUND COLOR ANIMATION button
var bgColorAnimButtonLabel = function bgColorAnimButtonLabel() {
return backgroundColorAnimEnabled ? "BG COLOR: ON" : "BG COLOR: OFF";
};
var bgColorAnimButton = createButton(bgColorAnimButtonLabel(), 0x7ec8e3, 3.7, 1.5, function () {
backgroundColorAnimEnabled = !backgroundColorAnimEnabled;
storage.backgroundColorAnimEnabled = backgroundColorAnimEnabled;
bgColorAnimButton.label.setText(bgColorAnimButtonLabel());
// If disabling, set background and UiBar to static color
if (!backgroundColorAnimEnabled) {
if (typeof backgroundAsset !== "undefined" && backgroundAsset) {
backgroundAsset.tint = colorTweenTargets[0];
}
if (game.uiBarBg) {
game.uiBarBg.tint = colorTweenTargets[0];
}
} else {
// If enabling, restart color tween
if (typeof tweenBackgroundToNextColor === "function") {
tweenBackgroundToNextColor();
}
if (typeof _tweenUiBarToNextColor === "function") {
_tweenUiBarToNextColor();
}
}
}, 2048 / 2, firstButtonY + settingsButtonSpacing * 3);
self.addChild(bgColorAnimButton);
// PARTICLES ENABLE/DISABLE button
var particlesEnabledLabel = function particlesEnabledLabel() {
return particlesEnabled ? "PARTICLES: ON" : "PARTICLES: OFF";
};
var particlesButton = createButton(particlesEnabledLabel(), 0x00bfff, 3.7, 1.5, function () {
particlesEnabled = !particlesEnabled;
storage.particlesEnabled = particlesEnabled;
particlesButton.label.setText(particlesEnabledLabel());
}, 2048 / 2, firstButtonY + settingsButtonSpacing * 4);
self.addChild(particlesButton);
// BACK TO MENU button (bottom, with extra margin)
var backButton = createButton("BACK TO MENU", 0xFF5500, 3, 1.5, function () {
game.removeChild(self);
initializeMenu();
}, 2048 / 2, 2732 - 200);
self.addChild(backButton);
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// --- PARTICULAS AL COLISIONAR checkPointer CON UN MEME ---
game.update = function () {
// Solo si la grilla está inicializada y checkPointers existen
if (typeof gridCells !== "undefined" && Array.isArray(checkPointers) && checkPointers.length > 0) {
// Recorre solo la zona visible
for (var row = extraRows; row < gridSize + extraRows; row++) {
for (var col = 0; col < gridSize; col++) {
if (!gridCells[row]) {
continue;
}
var cell = gridCells[row][col];
if (cell && cell.sprite && !cell.beingDestroyed) {
// Chequea colisión simple (círculo) entre cada checkPointer y el centro de la celda
for (var i = 0; i < checkPointers.length; i++) {
var checkPointer = checkPointers[i];
if (!checkPointer.visible) {
continue;
}
var dx = checkPointer.x - cell.x;
var dy = checkPointer.y - cell.y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Usa radio generoso (meme ~100, checkPointer ~20)
if (dist < 80) {
// Solo una vez por colisión: marca la celda como "siendo destruida por check"
if (!cell._checkCollisionActive) {
cell._checkCollisionActive = true;
// Efecto de partículas: 10-16 partículas
if (particlesEnabled) {
var numParticles = 10 + Math.floor(Math.random() * 7);
for (var p = 0; p < numParticles; p++) {
var part = LK.getAsset('particula', {
anchorX: 0.5,
anchorY: 0.5
});
// Color aleatorio
// Usa el color actual del fondo para las partículas
var bgColor = backgroundAsset.tint;
part.tint = bgColor;
part.x = cell.x;
part.y = cell.y;
// Aumenta el tamaño un 10%
part.scale.x = 1.7;
part.scale.y = 1.7;
// Velocidad y dirección aleatoria, pero nunca hacia arriba (vy >= 0), y velocidad reducida
var angle = Math.random() * Math.PI; // Solo de 0 a PI (derecha a izquierda, abajo)
var speed = 5.5 + Math.random() * 2.5; // Más lento que antes
var vx = Math.cos(angle) * speed;
var vy = Math.abs(Math.sin(angle) * speed); // Siempre hacia abajo o lateral
// Animación de partícula
(function (part, vx, vy) {
var lifetime = 180 + Math.random() * 120;
var startAlpha = 0.9 + Math.random() * 0.1;
part.alpha = startAlpha;
if (gridContainer && typeof gridContainer.addChild === "function") {
gridContainer.addChild(part);
} else {
game.addChild(part);
}
var elapsed = 0;
var updateFn = function updateFn() {
part.x += vx;
part.y += vy;
part.alpha -= 0.05 + Math.random() * 0.03;
part.scale.x *= 0.92;
part.scale.y *= 0.92;
elapsed += 16;
if (elapsed > lifetime || part.alpha <= 0.05) {
if (part.parent) {
part.parent.removeChild(part);
}
LK.clearInterval(intervalId);
}
};
var intervalId = LK.setInterval(updateFn, 16);
})(part, vx, vy);
}
}
// Efecto visual (sin sonido)
LK.effects.flashObject(cell, 0xFFFFFF, 200);
// (Eliminado sonido de explosión)
// Opcional: puedes marcar la celda para que no repita el efecto hasta que se mueva el checkPointer
// Elimina la marca después de un breve tiempo para permitir nuevas colisiones
LK.setTimeout(function () {
cell._checkCollisionActive = false;
}, 400);
}
}
}
}
}
}
}
};
// --- INTRO OPTION: persistently store intro enabled/disabled ---
// Load dragMemeEnabled from storage if available, otherwise default to true
var dragMemeEnabled = typeof storage.dragMemeEnabled === "boolean" ? storage.dragMemeEnabled : true;
storage.dragMemeEnabled = dragMemeEnabled;
// Load introEnabled from storage if available, otherwise default to true
var introEnabled = typeof storage.introEnabled === "boolean" ? storage.introEnabled : true;
storage.introEnabled = introEnabled;
// Load backgroundColorAnimEnabled from storage if available, otherwise default to true
var backgroundColorAnimEnabled = typeof storage.backgroundColorAnimEnabled === "boolean" ? storage.backgroundColorAnimEnabled : true;
storage.backgroundColorAnimEnabled = backgroundColorAnimEnabled;
// Load particlesEnabled from storage if available, otherwise default to true
var particlesEnabled = typeof storage.particlesEnabled === "boolean" ? storage.particlesEnabled : true;
storage.particlesEnabled = particlesEnabled;
var backgroundAsset = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
game.addChildAt(backgroundAsset, 0);
// --- Easter Egg: contador de partículas meme destruidas en menú ---
var memeParticlesDestroyedInMenu = 0;
var easterEggActive = false;
var easterEggTimeout = null;
var easterEggContainer = null;
// --- Smoothly tween background color forever ---
var colorTweenTargets = [0xffb347, 0x7ec8e3, 0x8aff80, 0xff80c0, 0xf9ff80, 0x80ffd9, 0xd1b3ff, 0xffffff, 0xffe066, 0x6ec6ff, 0xff6ec7, 0x6effb3, 0xff6666, 0x66ffea, 0x9d66ff, 0x66ff66, 0xffe6b3, 0xb3e6ff, 0xe6b3ff, 0xb3ffb3];
var currentColorIndex = 0;
function tweenBackgroundToNextColor() {
var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length;
var nextColor = colorTweenTargets[nextIndex];
if (!backgroundColorAnimEnabled) {
// If disabled, set to static color and do not continue tweening
backgroundAsset.tint = colorTweenTargets[0];
return;
}
tween(backgroundAsset, {
tint: nextColor
}, {
duration: 14000,
// much slower and smoother (was 7000)
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
currentColorIndex = nextIndex;
tweenBackgroundToNextColor();
}
});
}
// Start the color tween loop
backgroundAsset.tint = colorTweenTargets[0];
if (backgroundColorAnimEnabled) {
tweenBackgroundToNextColor();
}
// Game state and initialization
var menuScreen;
var cosmeticsScreen;
var recordsScreen;
var settingsScreen;
var pausePopup;
var gameInitialized = false;
var score = 0;
var scoreText;
var gridContainer;
var gameMode = 'classic'; // Track current game mode
var timerText;
var timerSeconds = 10; // 10 seconds timer for Fast Mode
var timerInterval;
var saveScoreInterval; // Interval for saving classic mode high score
var seekMode; // Reference to seek mode instance
// Game tracks scores in local variables, no global persistence
var highScoreClassic = typeof storage.highScoreClassic === "number" ? storage.highScoreClassic : 0; // Zen Mode high score
var highScoreFast = typeof storage.highScoreFast === "number" ? storage.highScoreFast : 0; // Fast Mode high score
var highScoreSeek = typeof storage.highScoreSeek === "number" ? storage.highScoreSeek : 0; // Seek Mode high score
// --- SEEK MODE: Block meme movement for 5 seconds and show countdown ---
var seekModeBlockActive = false;
var seekModeBlockTimer = null;
var seekModeBlockSeconds = 5;
var seekModeBlockText = null;
// --- SEEK MODE: Combo tracking and visualization ---
var seekModeComboCount = 0;
var seekModeComboText = null;
var seekModeShowAllTimer = null;
// Function to show settings screen
function showSettingsScreen() {
if (!settingsScreen) {
settingsScreen = new SettingsScreen();
}
game.addChild(settingsScreen);
}
// Create a reusable button function
function createButton(text, bgColor, width, height, callback, buttonX, buttonY) {
var button = new Container();
var buttonBg = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: width || 3,
scaleY: height || 1.5
});
buttonBg.tint = bgColor || 0x00AA00;
var buttonText = new Text2(text, {
size: 70,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
button.addChild(buttonBg);
button.addChild(buttonText);
// Add button interaction
button.interactive = true;
button.down = function (x, y, obj) {
LK.effects.flashObject(buttonBg, 0xFFFFFF, 200);
LK.getSound('ButtonSound').play();
if (callback) {
LK.setTimeout(function () {
callback();
}, 300);
}
};
// Store references for later access
button.background = buttonBg;
button.label = buttonText;
// Set position if provided
if (buttonX !== undefined) {
button.x = buttonX;
}
if (buttonY !== undefined) {
button.y = buttonY;
}
return button;
}
// --- UiBar initialization: always present, not just in game modes ---
if (!game.uiBarBg) {
var _tweenUiBarToNextColor = function tweenUiBarToNextColor() {
var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length;
var nextColor = colorTweenTargets[nextIndex];
if (!backgroundColorAnimEnabled) {
// If disabled, set to static color and do not continue tweening
uiBar.tint = colorTweenTargets[0];
return;
}
tween(uiBar, {
tint: nextColor
}, {
duration: 14000,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
// No need to update currentColorIndex here, backgroundAsset already does it
_tweenUiBarToNextColor();
}
});
};
// Calculate bar height as 1/6 of screen height
var barHeight = Math.floor(2732 / 6);
var barWidth = 2400;
// Get UiBar asset and scale to fit width and height
var uiBar = LK.getAsset('UiBar', {
anchorX: 0,
anchorY: 0,
x: -30,
y: -30,
scaleX: barWidth / 100,
scaleY: barHeight / 100,
alpha: 1
});
// Place at top of screen, behind score text
game.uiBarBg = uiBar;
// Insert at zIndex 1 (above background, below score text)
game.addChildAt(uiBar, 1);
// --- Smoothly tween UiBar color forever, synchronized with backgroundAsset ---
uiBar.tint = colorTweenTargets[0];
if (backgroundColorAnimEnabled) {
_tweenUiBarToNextColor();
}
}
// Centralized initialization function for adding elements to the game
function initializeGameElements() {
// Create and add grid container if it doesn't exist
if (!gridContainer) {
gridContainer = new Container();
game.addChild(gridContainer);
}
// --- Move UiBar and UI texts above memes ---
// Remove and re-add UiBar to ensure it's above memes
if (game.uiBarBg && game.uiBarBg.parent) {
game.uiBarBg.parent.removeChild(game.uiBarBg);
}
if (game.uiBarBg) {
game.addChild(game.uiBarBg);
}
// Create score text for all modes
if (!scoreText) {
scoreText = new Text2("Score: 0", {
size: 120,
fill: 0x222222
});
scoreText.anchor.set(0.5, 0);
scoreText.x = 2048 / 2;
scoreText.y = 80;
}
game.addChild(scoreText);
// Create timer text for fast mode if needed
if (game.gameMode === 'fast' && !timerText) {
timerText = new Text2("Time: " + timerSeconds.toFixed(1), {
size: 140,
fill: 0xFF0000
});
timerText.anchor.set(0.5, 1);
timerText.x = 2048 / 2;
timerText.y = 2732 - 100;
}
if (timerText) {
game.addChild(timerText);
}
// Add mode indicator text
var modeTextContent = "Zen Mode";
var modeTextColor = 0x00AA00;
if (game.gameMode === 'fast') {
modeTextContent = "Fast Mode";
modeTextColor = 0x0088FF;
} else if (game.gameMode === 'seekmode') {
modeTextContent = "Seek Mode";
modeTextColor = 0xDD5500;
}
if (game.modeText && game.modeText.parent) {
game.modeText.parent.removeChild(game.modeText);
}
game.modeText = new Text2(modeTextContent, {
size: 80,
fill: modeTextColor
});
game.modeText.anchor.set(0.5, 0);
game.modeText.x = 2048 / 2;
game.modeText.y = 180;
game.addChild(game.modeText);
// Add pause button to the top right corner in zen mode and seek mode
if (game.gameMode === 'classic' || game.gameMode === 'seekmode') {
if (game.pauseButton && game.pauseButton.parent) {
game.pauseButton.parent.removeChild(game.pauseButton);
}
game.pauseButton = new PauseButton();
// Add a 20px margin from the top and right edges
game.pauseButton.x = 2048 - 100 - 45;
game.pauseButton.y = 100 + 45;
game.pauseButton.interactive = true;
game.addChild(game.pauseButton);
}
}
// Function to show pause popup
function showPausePopup() {
if (!pausePopup) {
pausePopup = new PausePopup();
pausePopup.x = 2048 / 2;
pausePopup.y = 2732 / 2;
}
game.addChild(pausePopup);
// Fast Mode doesn't have pause functionality, so no need to pause the timer
}
// Show records screen
function showRecordsScreen() {
if (!recordsScreen) {
recordsScreen = new RecordsScreen();
}
// Make sure to refresh high scores every time the screen is shown
if (recordsScreen.refreshHighScores) {
recordsScreen.refreshHighScores();
}
game.addChild(recordsScreen);
}
// --- COSMETICS: Track selected cosmetic set globally and persistently ---
// Load selectedSet from storage if available, otherwise default to 1
var selectedSet = typeof storage.selectedSet === "number" ? storage.selectedSet : 1;
storage.selectedSet = selectedSet; // Ensure it's always persisted on load
// Show cosmetics screen
function showCosmeticsScreen() {
if (!cosmeticsScreen) {
cosmeticsScreen = new CosmeticsScreen();
}
game.addChild(cosmeticsScreen);
}
// Initialize the menu
function initializeMenu() {
if (menuScreen) {
// Clean up meme particles and intervals to prevent duplicates
if (typeof menuScreen.destroy === "function") {
menuScreen.destroy();
}
menuScreen = null;
}
menuScreen = new MenuScreen();
game.addChild(menuScreen);
}
// Function to periodically save high score and manage music
function startPeriodicSave() {
// Clear any existing save interval
if (saveScoreInterval) {
LK.clearInterval(saveScoreInterval);
saveScoreInterval = null;
}
// Save high score every second if it's better
saveScoreInterval = LK.setInterval(function () {
if (game.gameMode === 'classic' && score > highScoreClassic) {
highScoreClassic = score;
storage.highScoreClassic = highScoreClassic;
} else if (game.gameMode === 'seekmode' && score > highScoreSeek) {
highScoreSeek = score;
storage.highScoreSeek = highScoreSeek;
}
}, 1000);
// Start background music
startBackgroundMusic();
}
// Function to manage background music
function startBackgroundMusic() {
// Clear existing music interval if it exists
if (game.musicInterval) {
LK.clearInterval(game.musicInterval);
game.musicInterval = null;
}
// Array of available music tracks
var musicTracks = ['Backgroundmusic', 'backgroundmusic2'];
// Track the last played track to avoid repeats
var lastTrackIndex = -1;
// Function to play the next track (not repeating the same one)
function playNextTrack() {
var nextIndex;
if (musicTracks.length === 1) {
nextIndex = 0;
} else {
// Pick a different track than the last one
do {
nextIndex = Math.floor(Math.random() * musicTracks.length);
} while (nextIndex === lastTrackIndex);
}
var selectedTrack = musicTracks[nextIndex];
lastTrackIndex = nextIndex;
// Play the selected track, and set up onFinish to play the next one
LK.playMusic(selectedTrack, {
loop: false,
onFinish: function onFinish() {
// When the song finishes, play the next one
playNextTrack();
}
});
console.log("Now playing: " + selectedTrack);
}
// Play the first track
playNextTrack();
// Remove the old interval-based music change (not needed anymore)
}
// Initialize the game
game.initializeGame = function () {
// Initialize score system
score = 0;
// Clear existing grid container if it exists
if (gridContainer) {
while (gridContainer.children.length > 0) {
var child = gridContainer.children[0];
gridContainer.removeChild(child);
child.destroy();
}
}
// Initialize timer for Fast Mode
if (game.gameMode === 'fast') {
// Clear any existing timer
if (timerInterval) {
LK.clearInterval(timerInterval);
timerInterval = null;
}
// Calculate initial max timer value based on score
var maxTimer = 10;
// Use exponential thresholds for slower decrease
if (score >= 100) {
maxTimer = 9;
}
if (score >= 500) {
maxTimer = 8;
}
if (score >= 1000) {
maxTimer = 7;
}
if (score >= 3500) {
maxTimer = 6;
}
if (score >= 10000) {
maxTimer = 5;
}
if (score >= 25000) {
maxTimer = 4;
}
if (score >= 50000) {
maxTimer = 3;
}
// Reset timer value
timerSeconds = maxTimer;
if (timerText) {
timerText.setText("Time: " + maxTimer.toFixed(1));
}
// Start timer countdown
startTimer();
}
// Centralize initialization of all game elements
initializeGameElements();
// Initialize score
initializeScore();
// Initialize the game grid
initializeGrid();
ensureNoInitialMatches();
gameInitialized = true;
// Start periodic save of high score for classic mode
startPeriodicSave();
// --- SEEK MODE: Block meme movement for 5 seconds and show countdown ---
if (game.gameMode === 'seekmode') {
// Block meme movement
seekModeBlockActive = true;
seekModeBlockSeconds = 5;
// Reset combo counter
seekModeComboCount = 0;
// Remove previous combo text if any
if (seekModeComboText) {
game.removeChild(seekModeComboText);
seekModeComboText.destroy();
seekModeComboText = null;
}
// Remove previous block text if any
if (seekModeBlockText) {
game.removeChild(seekModeBlockText);
seekModeBlockText.destroy();
seekModeBlockText = null;
}
// Create and show countdown text at bottom center, but a bit higher to leave space for combo text below
seekModeBlockText = new Text2("Wait: " + seekModeBlockSeconds, {
size: 120,
fill: 0xDD5500,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
seekModeBlockText.anchor.set(0.5, 1);
seekModeBlockText.x = 2048 / 2;
seekModeBlockText.y = 2732 - 220; // Move up to leave space for combo text below
game.addChild(seekModeBlockText);
// Start countdown timer
if (seekModeBlockTimer) {
LK.clearInterval(seekModeBlockTimer);
seekModeBlockTimer = null;
}
seekModeBlockTimer = LK.setInterval(function () {
seekModeBlockSeconds--;
if (seekModeBlockText) {
seekModeBlockText.setText("Wait: " + seekModeBlockSeconds);
}
if (seekModeBlockSeconds <= 0) {
seekModeBlockActive = false;
if (seekModeBlockTimer) {
LK.clearInterval(seekModeBlockTimer);
seekModeBlockTimer = null;
}
if (seekModeBlockText) {
game.removeChild(seekModeBlockText);
seekModeBlockText.destroy();
seekModeBlockText = null;
}
// --- Set all current memes to use the 'seek' sprite ---
for (var row = 0; row < gridSize + extraRows; row++) {
for (var col = 0; col < gridSize; col++) {
var cell = gridCells[row][col];
if (cell && cell.sprite) {
// Remove old sprite
cell.removeChild(cell.sprite);
// Attach new 'seek' sprite
cell.sprite = LK.getAsset('seek', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale to fit cell
var padding = 20;
var maxWidth = cellSize - padding * 2;
var maxHeight = cellSize - padding * 2;
var scaleX = maxWidth / cell.sprite.width;
var scaleY = maxHeight / cell.sprite.height;
var scale = Math.min(scaleX, scaleY);
cell.sprite.scale.set(scale, scale);
cell.addChild(cell.sprite);
}
}
}
// --- Mark that new memes should use 'seek' sprite ---
game.seekModeForceSeekSprite = true;
// Create combo counter text
seekModeComboCount = 0;
if (seekModeComboText) {
game.removeChild(seekModeComboText);
seekModeComboText.destroy();
}
seekModeComboText = new Text2("Combos: 0/3", {
size: 120,
fill: 0xDD5500,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
seekModeComboText.anchor.set(0.5, 0);
seekModeComboText.x = 2048 / 2;
seekModeComboText.y = 2732 - 120; // Place just below the wait text
game.addChild(seekModeComboText);
}
}, 1000);
} else {
// Clean up if not in seek mode
seekModeBlockActive = false;
if (seekModeBlockTimer) {
LK.clearInterval(seekModeBlockTimer);
seekModeBlockTimer = null;
}
if (seekModeBlockText) {
game.removeChild(seekModeBlockText);
seekModeBlockText.destroy();
seekModeBlockText = null;
}
// --- FIX: Reset seekModeForceSeekSprite so memes use correct sprite on re-enter ---
game.seekModeForceSeekSprite = false;
}
};
// Create and position score text
function initializeScore() {
score = 0;
if (scoreText) {
scoreText.setText("Score: 0");
}
}
// Update score and score text
function updateScore(points) {
if (typeof points === "number" && points > 0) {
score += points;
if (scoreText) {
scoreText.setText("Score: " + score);
}
}
}
var cellPool = [];
function getGridCell() {
if (cellPool.length > 0) {
return cellPool.pop().init();
}
return new GridCell();
}
function recycleGridCell(cell) {
if (cell) {
cellPool.push(cell);
}
}
var selectedCell = null;
var isAnimating = false;
window.gravityInProgress = false;
window.fillInProgress = false;
// Track combo counts for sound effects
var comboCounter = 0;
var comboInProgress = false;
function handleCellTap(tappedCell) {
// Bloquea romper y mover memes durante el periodo visible en seekmode
if (isAnimating || window.gravityInProgress || window.fillInProgress || pausePopup && game.children.includes(pausePopup) || game.gameMode === 'seekmode' && (seekModeBlockActive || game.seekModeForceSeekSprite && seekModeComboText && seekModeComboText.text && seekModeComboText.text.indexOf('MEMES VISIBLE') === 0)) {
return;
}
if (!tappedCell || !gridCells[tappedCell.row] || gridCells[tappedCell.row][tappedCell.col] !== tappedCell || tappedCell.beingDestroyed) {
return;
}
if (selectedCell !== null) {
// If it's valid
if (selectedCell && gridCells[selectedCell.row] && gridCells[selectedCell.row][selectedCell.col] === selectedCell) {
// If we tap the same cell, deselect it
if (selectedCell === tappedCell) {
selectedCell.hideSelection();
selectedCell = null;
return;
}
// Check if they're adjacent
var isAdjacent = Math.abs(selectedCell.row - tappedCell.row) === 1 && selectedCell.col === tappedCell.col || Math.abs(selectedCell.col - tappedCell.col) === 1 && selectedCell.row === tappedCell.row;
if (!isAdjacent) {
// Not adjacent, switch selection
selectedCell.hideSelection();
selectedCell = tappedCell;
selectedCell.showSelection();
return;
}
// They are adjacent, continue with swap
var cell1 = selectedCell;
var cell2 = tappedCell;
// Hide selection before swap
cell1.hideSelection();
selectedCell = null;
var isAdjacent = Math.abs(cell1.row - cell2.row) === 1 && cell1.col === cell2.col || Math.abs(cell1.col - cell2.col) === 1 && cell1.row === cell2.row;
if (!isAdjacent) {
return;
}
} else {
// Invalid selected cell, select the new one
if (selectedCell) {
selectedCell.hideSelection();
}
selectedCell = tappedCell;
selectedCell.showSelection();
return;
}
} else {
// No selection yet, select this cell
selectedCell = tappedCell;
selectedCell.showSelection();
return;
}
isAnimating = true;
var pos1_x = cell1.x;
var pos1_y = cell1.y;
var pos2_x = cell2.x;
var pos2_y = cell2.y;
var row1 = cell1.row;
var col1 = cell1.col;
var row2 = cell2.row;
var col2 = cell2.col;
// Check if cells are special memes
var greenSpecialCell = null;
var regularCell = null;
var lineSpecialCells = [];
var twoGreenSpecials = false;
// Check for two green special memes
if (cell1.isSpecial && cell1.specialType === 'green' && cell2.isSpecial && cell2.specialType === 'green') {
twoGreenSpecials = true;
}
// Check for green special meme
else if (cell1.isSpecial && cell1.specialType === 'green') {
greenSpecialCell = cell1;
regularCell = cell2;
} else if (cell2.isSpecial && cell2.specialType === 'green') {
greenSpecialCell = cell2;
regularCell = cell1;
}
// Assign a unique value to green special so it doesn't break when combined
if (greenSpecialCell) {
greenSpecialCell.value = 99; // Use a value outside the normal meme range (1-5)
}
if (twoGreenSpecials) {
cell1.value = 99;
cell2.value = 99;
}
// Check for line-clearing special memes (horizontal/vertical)
if (cell1.isSpecial && (cell1.specialType === 'horizontal' || cell1.specialType === 'vertical')) {
lineSpecialCells.push(cell1);
}
if (cell2.isSpecial && (cell2.specialType === 'horizontal' || cell2.specialType === 'vertical')) {
lineSpecialCells.push(cell2);
}
gridCells[row1][col1] = cell2;
gridCells[row2][col2] = cell1;
cell1.row = row2;
cell1.col = col2;
cell2.row = row1;
cell2.col = col1;
// Make sure any selection highlights are removed
if (cell1.selectionHighlight) {
cell1.hideSelection();
}
if (cell2.selectionHighlight) {
cell2.hideSelection();
}
// Check for special combinations: two green special memes, two bombs, line-clearing + bomb, or two line special memes
if (twoGreenSpecials) {
// Two green special memes combination - destroys all visible memes
var cellsToDestroy = [];
// Collect all visible cells on the board
for (var row = extraRows; row < gridSize + extraRows; row++) {
for (var col = 0; col < gridSize; col++) {
if (gridCells[row][col] && gridCells[row][col] !== cell1 && gridCells[row][col] !== cell2) {
cellsToDestroy.push(gridCells[row][col]);
}
}
}
// Award massive bonus for two green special meme combination
updateScore(1000);
// Show mega score popup
var megaScorePopup = new Text2("+1000", {
size: 100,
fill: 0x00FF00
});
megaScorePopup.anchor.set(0.5, 0.5);
megaScorePopup.x = 2048 / 2;
megaScorePopup.y = 2732 / 2;
game.addChild(megaScorePopup);
// Animate mega score popup
tween(megaScorePopup, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 1000,
onComplete: function onComplete() {
game.removeChild(megaScorePopup);
megaScorePopup.destroy();
}
});
// Mark special memes for destruction
cell1.beingDestroyed = true;
cell2.beingDestroyed = true;
// Schedule destruction after the swap animation completes
LK.setTimeout(function () {
LK.getSound('Explosion').play();
// Flash screen effect for this powerful combination
LK.effects.flashScreen(0x00FF00, 500);
// Destroy the collected cells
if (cellsToDestroy.length > 0) {
destroyCells(cellsToDestroy);
}
// Destroy the special memes
LK.effects.flashObject(cell1, 0xFFFFFF, 200);
LK.effects.flashObject(cell2, 0xFFFFFF, 200);
gridContainer.removeChild(cell1);
gridContainer.removeChild(cell2);
cell1.destroy();
cell2.destroy();
gridCells[cell1.row][cell1.col] = null;
gridCells[cell2.row][cell2.col] = null;
}, 250);
} else if (cell1.isSpecial && cell1.specialType === 'bomb' && cell2.isSpecial && cell2.specialType === 'bomb') {
// Two bomb special memes combination - creates a 5x5 explosion
var cellsToDestroy = [];
// Determine which cell was moved by the player
var activatedCell = cell1;
// Create a 5x5 area effect centered on the first bomb
for (var r = Math.max(extraRows, cell1.row - 2); r <= Math.min(gridSize + extraRows - 1, cell1.row + 2); r++) {
for (var c = Math.max(0, cell1.col - 2); c <= Math.min(gridSize - 1, cell1.col + 2); c++) {
if (gridCells[r][c] && gridCells[r][c] !== cell1 && gridCells[r][c] !== cell2) {
cellsToDestroy.push(gridCells[r][c]);
}
}
}
// Mark special memes for destruction
cell1.beingDestroyed = true;
cell2.beingDestroyed = true;
// Schedule destruction after the swap animation completes
LK.setTimeout(function () {
LK.getSound('Explosion').play();
// Destroy the collected cells
if (cellsToDestroy.length > 0) {
destroyCells(cellsToDestroy);
}
// Destroy the special memes
LK.effects.flashObject(cell1, 0xFFFFFF, 200);
LK.effects.flashObject(cell2, 0xFFFFFF, 200);
gridContainer.removeChild(cell1);
gridContainer.removeChild(cell2);
cell1.destroy();
cell2.destroy();
gridCells[cell1.row][cell1.col] = null;
gridCells[cell2.row][cell2.col] = null;
}, 250);
} else if (cell1.isSpecial && cell1.specialType === 'bomb' && cell2.isSpecial && (cell2.specialType === 'horizontal' || cell2.specialType === 'vertical') || cell2.isSpecial && cell2.specialType === 'bomb' && cell1.isSpecial && (cell1.specialType === 'horizontal' || cell1.specialType === 'vertical')) {
// Line-clearing + bomb special combination
var bombCell = cell1.specialType === 'bomb' ? cell1 : cell2;
var lineCell = cell1.specialType === 'horizontal' || cell1.specialType === 'vertical' ? cell1 : cell2;
var cellsToDestroy = [];
var affectedRows = [];
// Determine which cell was moved by the player
var activatedCell = cell1;
// Enhanced effect: destroy 3 rows or columns based on the line special type
if (lineCell.specialType === 'horizontal') {
// Destroy 3 rows centered on the bomb's row
for (var r = Math.max(extraRows, bombCell.row - 1); r <= Math.min(gridSize + extraRows - 1, bombCell.row + 1); r++) {
affectedRows.push(r);
for (var c = 0; c < gridSize; c++) {
if (gridCells[r][c] && gridCells[r][c] !== bombCell && gridCells[r][c] !== lineCell) {
cellsToDestroy.push(gridCells[r][c]);
}
}
}
} else {
// vertical
// Destroy 3 columns centered on the bomb's column
for (var r = extraRows; r < gridSize + extraRows; r++) {
for (var c = Math.max(0, bombCell.col - 1); c <= Math.min(gridSize - 1, bombCell.col + 1); c++) {
if (gridCells[r][c] && gridCells[r][c] !== bombCell && gridCells[r][c] !== lineCell) {
cellsToDestroy.push(gridCells[r][c]);
}
}
}
}
// Mark special memes for destruction
bombCell.beingDestroyed = true;
lineCell.beingDestroyed = true;
// Schedule destruction after the swap animation completes
LK.setTimeout(function () {
LK.getSound('Explosion').play();
// Destroy the collected cells
if (cellsToDestroy.length > 0) {
destroyCells(cellsToDestroy);
}
// Destroy the special memes
LK.effects.flashObject(bombCell, 0xFFFFFF, 200);
LK.effects.flashObject(lineCell, 0xFFFFFF, 200);
gridContainer.removeChild(bombCell);
gridContainer.removeChild(lineCell);
bombCell.destroy();
lineCell.destroy();
gridCells[bombCell.row][bombCell.col] = null;
gridCells[lineCell.row][lineCell.col] = null;
}, 250);
}
// Check if two line special memes were swapped (horizontal and vertical)
else if (lineSpecialCells.length === 2) {
var cellsToDestroy = [];
// Collect cells in both row and column
var row1, col1, row2, col2;
var activatedSpecial;
// Determine which special meme was moved by the player (based on original positions)
if (lineSpecialCells[0] === cell1) {
activatedSpecial = lineSpecialCells[0];
row1 = lineSpecialCells[0].row;
col1 = lineSpecialCells[0].col;
} else {
activatedSpecial = lineSpecialCells[1];
row1 = lineSpecialCells[1].row;
col1 = lineSpecialCells[1].col;
}
// Add cells from the activated special's row or column
for (var row = extraRows; row < gridSize + extraRows; row++) {
// Add cells from column
if (gridCells[row][col1] && gridCells[row][col1] !== lineSpecialCells[0] && gridCells[row][col1] !== lineSpecialCells[1]) {
cellsToDestroy.push(gridCells[row][col1]);
}
}
// Add cells from the activated special's row
for (var col = 0; col < gridSize; col++) {
// Add cells from row
if (col !== col1 && gridCells[row1][col] && gridCells[row1][col] !== lineSpecialCells[0] && gridCells[row1][col] !== lineSpecialCells[1]) {
cellsToDestroy.push(gridCells[row1][col]);
}
}
// Mark special memes for destruction
lineSpecialCells.forEach(function (specialMeme) {
specialMeme.beingDestroyed = true;
});
// Schedule destruction after the swap animation completes
LK.setTimeout(function () {
LK.getSound('Explosion').play();
// Destroy the collected cells
if (cellsToDestroy.length > 0) {
destroyCells(cellsToDestroy);
}
// Destroy the special memes
lineSpecialCells.forEach(function (specialMeme) {
LK.effects.flashObject(specialMeme, 0xFFFFFF, 200);
gridContainer.removeChild(specialMeme);
specialMeme.destroy();
gridCells[specialMeme.row][specialMeme.col] = null;
});
}, 250);
}
// Check if greenSpecialCell was swapped with a line-clearing special meme or bomb
else if (greenSpecialCell && regularCell) {
// Check if regularCell is a line-clearing special meme
if (regularCell.isSpecial && (regularCell.specialType === 'horizontal' || regularCell.specialType === 'vertical')) {
var targetType = regularCell.value;
var cellsToTransform = [];
// Find all cells with the same type as the swapped line-clearing meme
for (var row = extraRows; row < gridSize + extraRows; row++) {
for (var col = 0; col < gridSize; col++) {
var cell = gridCells[row][col];
if (cell && cell.value === targetType && cell !== greenSpecialCell && cell !== regularCell && !cell.isSpecial) {
cellsToTransform.push(cell);
}
}
}
// Mark special memes for destruction
greenSpecialCell.beingDestroyed = true;
// Schedule transformation and activation after the swap animation completes
LK.setTimeout(function () {
LK.getSound('Explosion').play();
LK.effects.flashObject(greenSpecialCell, 0xFFFFFF, 200);
// Transform all cells of the same type into line-clearing memes
cellsToTransform.forEach(function (cell) {
cell.isSpecial = true;
// Randomly assign horizontal or vertical
cell.specialType = Math.random() < 0.5 ? 'horizontal' : 'vertical';
// Apply tint
cell.sprite.tint = 0xFF8800; // Orange tint for line special memes
LK.effects.flashObject(cell, 0xFFFFFF, 200);
// Activate each special meme with a slight delay
LK.setTimeout(function () {
cell.activateSpecialPower();
}, 100 + Math.random() * 300);
});
// Remove the green special meme
gridContainer.removeChild(greenSpecialCell);
greenSpecialCell.destroy();
gridCells[greenSpecialCell.row][greenSpecialCell.col] = null;
// Activate the original line-clearing meme last
LK.setTimeout(function () {
regularCell.activateSpecialPower();
}, 500);
}, 250);
} else if (regularCell.isSpecial && regularCell.specialType === 'bomb') {
// Green special + bomb special interaction
var targetType = regularCell.value;
var cellsToTransform = [];
// Find all cells with the same type as the bomb meme
for (var row = extraRows; row < gridSize + extraRows; row++) {
for (var col = 0; col < gridSize; col++) {
var cell = gridCells[row][col];
if (cell && cell.value === targetType && cell !== greenSpecialCell && cell !== regularCell && !cell.isSpecial) {
cellsToTransform.push(cell);
}
}
}
// Mark green special meme for destruction
greenSpecialCell.beingDestroyed = true;
// Schedule transformation and activation after swap animation
LK.setTimeout(function () {
LK.getSound('Explosion').play();
LK.effects.flashObject(greenSpecialCell, 0xFFFFFF, 200);
// Transform all cells of same type into bomb memes
cellsToTransform.forEach(function (cell) {
cell.isSpecial = true;
cell.specialType = 'bomb';
// Apply blue tint for bomb memes
cell.sprite.tint = 0x0088FF;
LK.effects.flashObject(cell, 0xFFFFFF, 200);
// Activate each bomb with slight delay
LK.setTimeout(function () {
cell.activateSpecialPower();
}, 100 + Math.random() * 300);
});
// Remove the green special meme
gridContainer.removeChild(greenSpecialCell);
greenSpecialCell.destroy();
gridCells[greenSpecialCell.row][greenSpecialCell.col] = null;
// Activate the original bomb last
LK.setTimeout(function () {
regularCell.activateSpecialPower();
}, 500);
}, 250);
} else {
var targetType = regularCell.value;
var cellsToDestroy = [];
// Find all cells with the same type as the swapped meme
for (var row = extraRows; row < gridSize + extraRows; row++) {
for (var col = 0; col < gridSize; col++) {
var cell = gridCells[row][col];
if (cell && cell.value === targetType && cell !== greenSpecialCell) {
cellsToDestroy.push(cell);
}
}
}
// Also destroy the green special meme itself
greenSpecialCell.beingDestroyed = true;
// Schedule destruction after the swap animation completes
LK.setTimeout(function () {
if (cellsToDestroy.length > 0) {
LK.getSound('Explosion').play();
LK.effects.flashObject(greenSpecialCell, 0xFFFFFF, 200);
destroyCells(cellsToDestroy);
gridContainer.removeChild(greenSpecialCell);
greenSpecialCell.destroy();
gridCells[greenSpecialCell.row][greenSpecialCell.col] = null;
}
}, 250);
}
}
tween(cell1, {
x: pos2_x,
y: pos2_y
}, {
duration: 200
});
tween(cell2, {
x: pos1_x,
y: pos1_y
}, {
duration: 200
});
selectedCell = null;
LK.setTimeout(function () {
checkForAndDestroyMatches(cell1, cell2);
if (!window.destructionInProgress && !window.gravityInProgress && !window.fillInProgress) {
isAnimating = false;
}
}, 250);
}
function getMatches() {
var matches = [];
function findDirectionalMatches(isHorizontal) {
var primary, secondary;
var primaryMax = isHorizontal ? gridSize + extraRows : gridSize;
var secondaryMax = isHorizontal ? gridSize : gridSize + extraRows;
for (primary = isHorizontal ? extraRows : 0; primary < primaryMax; primary++) {
if (!gridCells[primary]) {
continue;
}
for (secondary = 0; secondary < secondaryMax;) {
if (secondary > secondaryMax - 3) {
secondary++;
continue;
}
var cell1 = isHorizontal ? gridCells[primary] ? gridCells[primary][secondary] : null : gridCells[secondary] ? gridCells[secondary][primary] : null;
if (!cell1 || !cell1.value || cell1.value === 99) {
secondary++;
continue;
}
var currentMatchValue = cell1.value;
var currentMatchCells = [cell1];
for (var k = secondary + 1; k < secondaryMax; k++) {
var nextCell = isHorizontal ? gridCells[primary] ? gridCells[primary][k] : null : gridCells[k] ? gridCells[k][primary] : null;
if (nextCell && nextCell.value === currentMatchValue && nextCell.value !== 99) {
currentMatchCells.push(nextCell);
} else {
// If no new matches found, reset combo counter
LK.setTimeout(function () {
if (!window.gravityInProgress && !window.fillInProgress) {
comboInProgress = false;
comboCounter = 0;
}
}, 500);
break;
}
}
if (currentMatchCells.length >= 3) {
var validCells = currentMatchCells.filter(function (cell) {
return cell && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell;
});
if (validCells.length >= 3) {
var visibleCells = validCells.filter(function (cell) {
return cell.row >= extraRows;
});
var invisibleCells = validCells.filter(function (cell) {
return cell.row < extraRows;
});
// Either all cells are visible, or all cells are invisible, or there are at least 3 visible cells
if (visibleCells.length === 0 || invisibleCells.length === 0 || visibleCells.length >= 3) {
// Add isHorizontal and matchType to the match data
validCells.isHorizontal = isHorizontal;
validCells.matchType = currentMatchValue;
matches.push(validCells);
}
}
}
secondary += currentMatchCells.length > 0 ? currentMatchCells.length : 1;
}
}
}
findDirectionalMatches(true);
findDirectionalMatches(false);
return matches;
}
function destroyCells(cellsToDestroy) {
isAnimating = true;
// Create a unique ID for this destruction process
var destructionId = Date.now() + Math.random();
// Check if match crosses visible/invisible boundary
var visibleCells = cellsToDestroy.filter(function (cell) {
return cell.row >= extraRows;
});
var invisibleCells = cellsToDestroy.filter(function (cell) {
return cell.row < extraRows;
});
// If mixed visibility and only one cell is visible, don't destroy any
if (visibleCells.length > 0 && invisibleCells.length > 0 && (visibleCells.length === 1 || invisibleCells.length === 1)) {
isAnimating = false;
return;
}
// Reset timer if we're in Fast Mode and destroying visible cells
if (game.gameMode === 'fast' && visibleCells.length > 0) {
resetTimer();
}
// Award points for visible matches
var pointsToAdd = 0;
var visibleCellsToDestroy = cellsToDestroy.filter(function (cell) {
return cell.row >= extraRows && cell && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell;
});
if (visibleCellsToDestroy.length > 0) {
// 10 points per cell, bonus for combos
pointsToAdd = visibleCellsToDestroy.length * 10;
if (visibleCellsToDestroy.length >= 4) {
pointsToAdd += 10;
}
if (visibleCellsToDestroy.length >= 5) {
pointsToAdd += 20;
}
if (visibleCellsToDestroy.length >= 6) {
pointsToAdd += 30;
}
updateScore(pointsToAdd);
}
// Find the center cell (the one that triggered the match)
var centerCell = null;
// If we have the cells from a swap, use one of those as the center
if (arguments.length > 1 && arguments[1]) {
centerCell = arguments[1];
} else if (cellsToDestroy.length > 0) {
// Otherwise use the middle cell of the match
centerCell = cellsToDestroy[Math.floor(cellsToDestroy.length / 2)];
}
// Group cells by meme type
var cellsByType = {};
visibleCellsToDestroy.forEach(function (cell) {
if (!cellsByType[cell.value]) {
cellsByType[cell.value] = [];
}
cellsByType[cell.value].push(cell);
});
// Count unique groups for combo sound
var uniqueGroupCount = Object.keys(cellsByType).length;
if (uniqueGroupCount > 0) {
// Increment combo counter only if this is part of a continuous combo
if (!comboInProgress) {
comboCounter = uniqueGroupCount;
comboInProgress = true;
} else {
comboCounter += uniqueGroupCount;
}
// Update seek mode combo counter if we're in seek mode
if (game && game.gameMode === 'seekmode' && !seekModeBlockActive && game.seekModeForceSeekSprite) {
seekModeComboCount++;
// Create or update combo text
if (!seekModeComboText) {
seekModeComboText = new Text2("Combos: " + seekModeComboCount + "/3", {
size: 120,
fill: 0xDD5500,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
seekModeComboText.anchor.set(0.5, 0);
seekModeComboText.x = 2048 / 2;
seekModeComboText.y = 2732 - 120; // Place just below the wait text
game.addChild(seekModeComboText);
} else {
seekModeComboText.setText("Combos: " + seekModeComboCount + "/3");
}
// Check if we've reached 3 combos
if (seekModeComboCount >= 3) {
// Reset combo counter
seekModeComboCount = 0;
// Show all memes for 3 seconds
for (var row = 0; row < gridSize + extraRows; row++) {
var _loop = function _loop() {
cell = gridCells[row][col];
if (cell && cell.sprite) {
// Remove old seek sprite
cell.removeChild(cell.sprite);
// Attach original meme sprite based on value
// Use global selectedSet for cosmetics
if (typeof selectedSet !== "number") selectedSet = 1;
if (selectedSet === 1) {
spriteId = 'meme' + cell.value;
} else if (selectedSet === 2) {
spriteId = 'meme' + cell.value + '-2';
} else if (selectedSet === 3) {
spriteId = 'meme' + cell.value + '-3';
} else {
spriteId = 'meme' + cell.value;
}
cell.sprite = LK.getAsset(spriteId, {
anchorX: 0.5,
anchorY: 0.5
});
// Scale to fit cell
padding = 20;
maxWidth = cellSize - padding * 2;
maxHeight = cellSize - padding * 2;
scaleX = maxWidth / cell.sprite.width;
scaleY = maxHeight / cell.sprite.height;
scale = Math.min(scaleX, scaleY);
cell.sprite.scale.set(scale, scale);
cell.addChild(cell.sprite);
if (cell.isSpecial) {
// Remove any previous overlays
if (cell.specialOverlay && cell.children.indexOf(cell.specialOverlay) !== -1) {
cell.removeChild(cell.specialOverlay);
cell.specialOverlay = null;
}
if (cell.specialType === 'bomb') {
// Overlay habilidadBomba sprite
cell.specialOverlay = LK.getAsset('habilidadBomba', {
anchorX: 0.5,
anchorY: 0.5
});
padding = 20;
maxWidth = cellSize - padding * 2;
maxHeight = cellSize - padding * 2;
scaleX = maxWidth / cell.specialOverlay.width;
scaleY = maxHeight / cell.specialOverlay.height;
scale = Math.min(scaleX, scaleY);
cell.specialOverlay.scale.set(scale, scale);
cell.addChild(cell.specialOverlay);
// --- Color sync: animate overlay tint with background ---
if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") {
var _tweenBombOverlayColor2 = function tweenBombOverlayColor() {
if (!cell.specialOverlay) {
return;
}
var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length;
var nextColor = colorTweenTargets[nextIndex];
cell._specialOverlayTween = tween(cell.specialOverlay, {
tint: nextColor
}, {
duration: 14000,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
if (cell.specialOverlay) {
_tweenBombOverlayColor2();
}
}
});
};
if (cell._specialOverlayTween) {
cell._specialOverlayTween.stop && cell._specialOverlayTween.stop();
cell._specialOverlayTween = null;
}
cell.specialOverlay.tint = colorTweenTargets[currentColorIndex];
_tweenBombOverlayColor2();
}
} else if (cell.specialType === 'green') {
// Use 'habilidadVerde' asset for green special meme
if (cell.sprite) {
cell.removeChild(cell.sprite);
}
if (game.seekModeForceSeekSprite) {
// Si estamos en modo seek ocultando memes, no mostrar habilidad verde
cell.sprite = null;
} else {
cell.sprite = LK.getAsset('habilidadVerde', {
anchorX: 0.5,
anchorY: 0.5
});
padding = 20;
maxWidth = cellSize - padding * 2;
maxHeight = cellSize - padding * 2;
scaleX = maxWidth / cell.sprite.width;
scaleY = maxHeight / cell.sprite.height;
scale = Math.min(scaleX, scaleY);
cell.sprite.scale.set(scale, scale);
cell.addChild(cell.sprite);
}
} else if (cell.specialType === 'clear' || cell.specialType === 'vertical' || cell.specialType === 'horizontal') {
// Overlay habilidadClear sprite for clear/line specials
cell.specialOverlay = LK.getAsset('HabilidadClear', {
anchorX: 0.5,
anchorY: 0.5
});
// Rotate overlay to match direction
if (cell.specialType === 'horizontal') {
cell.specialOverlay.rotation = 0;
} else if (cell.specialType === 'vertical') {
cell.specialOverlay.rotation = Math.PI / 2;
} else {
cell.specialOverlay.rotation = 0;
}
padding = 20;
maxWidth = cellSize - padding * 2;
maxHeight = cellSize - padding * 2;
scaleX = maxWidth / cell.specialOverlay.width;
scaleY = maxHeight / cell.specialOverlay.height;
scale = Math.min(scaleX, scaleY);
cell.specialOverlay.scale.set(scale, scale);
cell.addChild(cell.specialOverlay);
// --- Color sync: animate overlay tint with background ---
if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") {
var _tweenClearOverlayColor2 = function tweenClearOverlayColor() {
if (!cell.specialOverlay) {
return;
}
var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length;
var nextColor = colorTweenTargets[nextIndex];
cell._specialOverlayTween = tween(cell.specialOverlay, {
tint: nextColor
}, {
duration: 14000,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
if (cell.specialOverlay) {
_tweenClearOverlayColor2();
}
}
});
};
if (cell._specialOverlayTween) {
cell._specialOverlayTween.stop && cell._specialOverlayTween.stop();
cell._specialOverlayTween = null;
}
cell.specialOverlay.tint = colorTweenTargets[currentColorIndex];
_tweenClearOverlayColor2();
}
} else {
cell.sprite.tint = 0xFF8800;
}
}
}
},
cell,
selectedSet,
spriteId,
padding,
maxWidth,
maxHeight,
scaleX,
scaleY,
scale,
padding,
maxWidth,
maxHeight,
scaleX,
scaleY,
scale,
padding,
maxWidth,
maxHeight,
scaleX,
scaleY,
scale,
padding,
maxWidth,
maxHeight,
scaleX,
scaleY,
scale;
for (var col = 0; col < gridSize; col++) {
_loop();
}
}
// Display countdown text
if (seekModeComboText) {
seekModeComboText.setText("MEMES VISIBLE: 3");
if (seekModeComboText.style) {
seekModeComboText.style.size = 120;
seekModeComboText.style.fill = 0xDD5500;
seekModeComboText.style.font = "'GillSans-Bold',Impact,'Arial Black',Tahoma";
}
seekModeComboText.y = 2732 - 120; // Ensure position is below wait text
}
// Start countdown
var showAllSeconds = 3;
var countdownInterval = LK.setInterval(function () {
showAllSeconds--;
if (seekModeComboText) {
seekModeComboText.setText("MEMES VISIBLE: " + showAllSeconds);
if (seekModeComboText.style) {
seekModeComboText.style.size = 120;
seekModeComboText.style.fill = 0xDD5500;
seekModeComboText.style.font = "'GillSans-Bold',Impact,'Arial Black',Tahoma";
}
seekModeComboText.y = 2732 - 120; // Ensure position is below wait text
}
if (showAllSeconds <= 0) {
// Clear interval
LK.clearInterval(countdownInterval);
// Set all memes back to seek sprite
for (var row = 0; row < gridSize + extraRows; row++) {
var _loop2 = function _loop2() {
cell = gridCells[row][col];
if (cell && cell.sprite) {
// Remove old sprite
cell.removeChild(cell.sprite);
// Attach new 'seek' sprite
cell.sprite = LK.getAsset('seek', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale to fit cell
padding = 20;
maxWidth = cellSize - padding * 2;
maxHeight = cellSize - padding * 2;
scaleX = maxWidth / cell.sprite.width;
scaleY = maxHeight / cell.sprite.height;
scale = Math.min(scaleX, scaleY);
cell.sprite.scale.set(scale, scale);
cell.addChild(cell.sprite);
// Preserve special tints if needed
if (cell.isSpecial) {
// Remove any previous overlays
if (cell.specialOverlay && cell.children.indexOf(cell.specialOverlay) !== -1) {
cell.removeChild(cell.specialOverlay);
cell.specialOverlay = null;
}
if (cell.specialType === 'bomb') {
// Overlay habilidadBomba sprite
cell.specialOverlay = LK.getAsset('habilidadBomba', {
anchorX: 0.5,
anchorY: 0.5
});
padding = 20;
maxWidth = cellSize - padding * 2;
maxHeight = cellSize - padding * 2;
scaleX = maxWidth / cell.specialOverlay.width;
scaleY = maxHeight / cell.specialOverlay.height;
scale = Math.min(scaleX, scaleY);
cell.specialOverlay.scale.set(scale, scale);
cell.addChild(cell.specialOverlay);
// --- Color sync: animate overlay tint with background ---
if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") {
var _tweenBombOverlayColor3 = function tweenBombOverlayColor() {
if (!cell.specialOverlay) {
return;
}
var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length;
var nextColor = colorTweenTargets[nextIndex];
cell._specialOverlayTween = tween(cell.specialOverlay, {
tint: nextColor
}, {
duration: 14000,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
if (cell.specialOverlay) {
_tweenBombOverlayColor3();
}
}
});
};
if (cell._specialOverlayTween) {
cell._specialOverlayTween.stop && cell._specialOverlayTween.stop();
cell._specialOverlayTween = null;
}
cell.specialOverlay.tint = colorTweenTargets[currentColorIndex];
_tweenBombOverlayColor3();
}
} else if (cell.specialType === 'green') {
// Use 'habilidadVerde' asset for green special meme
if (cell.sprite) {
cell.removeChild(cell.sprite);
}
if (game.seekModeForceSeekSprite) {
// Si estamos en modo seek ocultando memes, no mostrar habilidad verde
cell.sprite = null;
} else {
cell.sprite = LK.getAsset('habilidadVerde', {
anchorX: 0.5,
anchorY: 0.5
});
padding = 20;
maxWidth = cellSize - padding * 2;
maxHeight = cellSize - padding * 2;
scaleX = maxWidth / cell.sprite.width;
scaleY = maxHeight / cell.sprite.height;
scale = Math.min(scaleX, scaleY);
cell.sprite.scale.set(scale, scale);
cell.addChild(cell.sprite);
}
} else if (cell.specialType === 'clear' || cell.specialType === 'vertical' || cell.specialType === 'horizontal') {
// Overlay habilidadClear sprite for clear/line specials
cell.specialOverlay = LK.getAsset('HabilidadClear', {
anchorX: 0.5,
anchorY: 0.5
});
// Rotate overlay to match direction
if (cell.specialType === 'horizontal') {
cell.specialOverlay.rotation = 0;
} else if (cell.specialType === 'vertical') {
cell.specialOverlay.rotation = Math.PI / 2;
} else {
cell.specialOverlay.rotation = 0;
}
padding = 20;
maxWidth = cellSize - padding * 2;
maxHeight = cellSize - padding * 2;
scaleX = maxWidth / cell.specialOverlay.width;
scaleY = maxHeight / cell.specialOverlay.height;
scale = Math.min(scaleX, scaleY);
cell.specialOverlay.scale.set(scale, scale);
cell.addChild(cell.specialOverlay);
// --- Color sync: animate overlay tint with background ---
if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") {
var _tweenClearOverlayColor3 = function tweenClearOverlayColor() {
if (!cell.specialOverlay) {
return;
}
var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length;
var nextColor = colorTweenTargets[nextIndex];
cell._specialOverlayTween = tween(cell.specialOverlay, {
tint: nextColor
}, {
duration: 14000,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
if (cell.specialOverlay) {
_tweenClearOverlayColor3();
}
}
});
};
if (cell._specialOverlayTween) {
cell._specialOverlayTween.stop && cell._specialOverlayTween.stop();
cell._specialOverlayTween = null;
}
cell.specialOverlay.tint = colorTweenTargets[currentColorIndex];
_tweenClearOverlayColor3();
}
} else {
cell.sprite.tint = 0xFF8800;
}
}
}
},
cell,
padding,
maxWidth,
maxHeight,
scaleX,
scaleY,
scale,
padding,
maxWidth,
maxHeight,
scaleX,
scaleY,
scale,
padding,
maxWidth,
maxHeight,
scaleX,
scaleY,
scale,
padding,
maxWidth,
maxHeight,
scaleX,
scaleY,
scale;
for (var col = 0; col < gridSize; col++) {
_loop2();
}
}
// Reset combo text
if (seekModeComboText) {
seekModeComboText.setText("Combos: 0/3");
if (seekModeComboText.style) {
seekModeComboText.style.size = 120;
seekModeComboText.style.fill = 0xDD5500;
seekModeComboText.style.font = "'GillSans-Bold',Impact,'Arial Black',Tahoma";
}
seekModeComboText.y = 2732 - 120; // Ensure position is below wait text
}
}
}, 1000);
}
}
// Stop any currently playing combo sounds before playing a new one
LK.getSound('Combo1').stop();
LK.getSound('Combo2').stop();
LK.getSound('Combo3').stop();
LK.getSound('Combo4').stop();
LK.getSound('Combo5').stop();
if (comboCounter >= 9) {
LK.getSound('Combo5').play();
} else if (comboCounter >= 6) {
LK.getSound('Combo4').play();
} else if (comboCounter >= 4) {
LK.getSound('Combo3').play();
} else if (comboCounter >= 3) {
LK.getSound('Combo2').play();
} else if (comboCounter >= 2) {
LK.getSound('Combo1').play();
}
}
var specialCell = null;
var isHorizontalMatch = false;
var isVerticalMatch = false;
for (var type in cellsByType) {
var typeCells = cellsByType[type];
// --- Prevent special abilities from creating new special abilities ---
// If any cell in this group is a special, do not create a new special meme
var groupHasSpecial = false;
for (var i = 0; i < typeCells.length; i++) {
if (typeCells[i].isSpecial) {
groupHasSpecial = true;
break;
}
}
if (groupHasSpecial) {
// Do not create a new special meme for this group
continue;
}
if (typeCells.length >= 6) {
// Check if a special ability is currently being used before creating a green special
var specialAbilityInUse = false;
// Loop through all cells to check if any special is activated
for (var r = 0; r < gridSize + extraRows; r++) {
for (var c = 0; c < gridSize; c++) {
if (gridCells[r] && gridCells[r][c] && gridCells[r][c].isSpecial && gridCells[r][c].beingDestroyed) {
specialAbilityInUse = true;
break;
}
}
if (specialAbilityInUse) {
break;
}
}
// Only create a green special meme if no other special ability is in use
if (!specialAbilityInUse) {
// Create green special meme for 6 or more matches
if (centerCell && centerCell.value === parseInt(type)) {
specialCell = centerCell;
} else {
specialCell = typeCells[0];
}
specialCell.isSpecial = true;
specialCell.specialType = 'green';
specialCell.value = 99; // Assign unique value for green special
specialCell.value = 99; // Assign unique value for green special
specialCell.beingDestroyed = false;
// Award bonus points for creating a green special meme
updateScore(100);
// Remove special cell from cells to destroy
visibleCellsToDestroy = visibleCellsToDestroy.filter(function (cell) {
return cell !== specialCell;
});
}
break; // Only create one special meme
} else if (typeCells.length === 5) {
// Create bomb special meme (blue)
if (centerCell && centerCell.value === parseInt(type)) {
specialCell = centerCell;
} else {
specialCell = typeCells[0];
}
specialCell.isSpecial = true;
specialCell.specialType = 'bomb';
specialCell.beingDestroyed = false;
// Award bonus points for creating a bomb special meme
updateScore(75);
// Remove special cell from cells to destroy
visibleCellsToDestroy = visibleCellsToDestroy.filter(function (cell) {
return cell !== specialCell;
});
break; // Only create one special meme
} else if (typeCells.length === 4) {
// Determine if match is horizontal or vertical
var allInSameRow = true;
var allInSameCol = true;
var firstCell = typeCells[0];
for (var i = 1; i < typeCells.length; i++) {
if (typeCells[i].row !== firstCell.row) {
allInSameRow = false;
}
if (typeCells[i].col !== firstCell.col) {
allInSameCol = false;
}
}
isHorizontalMatch = allInSameRow;
isVerticalMatch = allInSameCol;
// Create special meme only if it's a clean horizontal or vertical match
if (isHorizontalMatch || isVerticalMatch) {
// Use center cell if it matches the type, otherwise use the first cell of this type
if (centerCell && centerCell.value === parseInt(type)) {
specialCell = centerCell;
} else {
specialCell = typeCells[0];
}
specialCell.isSpecial = true;
specialCell.specialType = isHorizontalMatch ? 'horizontal' : 'vertical';
specialCell.beingDestroyed = false;
// Award bonus points for creating a line-clearing special meme
updateScore(50);
// Remove special cell from cells to destroy
visibleCellsToDestroy = visibleCellsToDestroy.filter(function (cell) {
return cell !== specialCell;
});
break; // Only create one special meme
}
}
}
// Sort cells by distance from center cell for ripple effect
if (centerCell && visibleCellsToDestroy.length > 0) {
visibleCellsToDestroy.sort(function (a, b) {
var distA = Math.abs(a.row - centerCell.row) + Math.abs(a.col - centerCell.col);
var distB = Math.abs(b.row - centerCell.row) + Math.abs(b.col - centerCell.col);
return distA - distB;
});
}
var delay = 0;
var delayIncrement = 50;
var totalDestructionTime = visibleCellsToDestroy.length * delayIncrement;
// Mark cells as being destroyed to prevent them from being part of new matches
visibleCellsToDestroy.forEach(function (cell) {
if (cell) {
cell.beingDestroyed = true;
// Check if any of these cells are matched with a special meme
if (cell.isSpecial) {
// Special meme is being destroyed, activate its power
LK.setTimeout(function () {
cell.activateSpecialPower();
}, 100);
}
}
});
// If we have a special meme, apply the appropriate overlay or tint
if (specialCell && specialCell.isSpecial) {
// Remove any previous overlays
if (specialCell.specialOverlay && specialCell.children.indexOf(specialCell.specialOverlay) !== -1) {
specialCell.removeChild(specialCell.specialOverlay);
specialCell.specialOverlay = null;
}
if (specialCell.specialType === 'bomb') {
// Overlay habilidadBomba sprite
specialCell.specialOverlay = LK.getAsset('habilidadBomba', {
anchorX: 0.5,
anchorY: 0.5
});
var padding = 20;
var maxWidth = cellSize - padding * 2;
var maxHeight = cellSize - padding * 2;
var scaleX = maxWidth / specialCell.specialOverlay.width;
var scaleY = maxHeight / specialCell.specialOverlay.height;
var scale = Math.min(scaleX, scaleY);
specialCell.specialOverlay.scale.set(scale, scale);
specialCell.addChild(specialCell.specialOverlay);
// --- Color sync: animate overlay tint with background ---
if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") {
var _tweenBombOverlayColor4 = function tweenBombOverlayColor() {
if (!specialCell.specialOverlay) {
return;
}
var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length;
var nextColor = colorTweenTargets[nextIndex];
specialCell._specialOverlayTween = tween(specialCell.specialOverlay, {
tint: nextColor
}, {
duration: 14000,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
if (specialCell.specialOverlay) {
_tweenBombOverlayColor4();
}
}
});
};
if (specialCell._specialOverlayTween) {
specialCell._specialOverlayTween.stop && specialCell._specialOverlayTween.stop();
specialCell._specialOverlayTween = null;
}
specialCell.specialOverlay.tint = colorTweenTargets[currentColorIndex];
_tweenBombOverlayColor4();
}
} else if (specialCell.specialType === 'green') {
// Use 'habilidadVerde' asset for green special meme
if (specialCell.sprite) {
specialCell.removeChild(specialCell.sprite);
}
specialCell.sprite = LK.getAsset('habilidadVerde', {
anchorX: 0.5,
anchorY: 0.5
});
var padding = 20;
var maxWidth = cellSize - padding * 2;
var maxHeight = cellSize - padding * 2;
var scaleX = maxWidth / specialCell.sprite.width;
var scaleY = maxHeight / specialCell.sprite.height;
var scale = Math.min(scaleX, scaleY);
specialCell.sprite.scale.set(scale, scale);
specialCell.addChild(specialCell.sprite);
} else if (specialCell.specialType === 'clear' || specialCell.specialType === 'vertical' || specialCell.specialType === 'horizontal') {
// Overlay habilidadClear sprite for clear/line specials
specialCell.specialOverlay = LK.getAsset('HabilidadClear', {
anchorX: 0.5,
anchorY: 0.5
});
// Rotate overlay to match direction
if (specialCell.specialType === 'horizontal') {
specialCell.specialOverlay.rotation = 0;
} else if (specialCell.specialType === 'vertical') {
specialCell.specialOverlay.rotation = Math.PI / 2;
} else {
specialCell.specialOverlay.rotation = 0;
}
var padding = 20;
var maxWidth = cellSize - padding * 2;
var maxHeight = cellSize - padding * 2;
var scaleX = maxWidth / specialCell.specialOverlay.width;
var scaleY = maxHeight / specialCell.specialOverlay.height;
var scale = Math.min(scaleX, scaleY);
specialCell.specialOverlay.scale.set(scale, scale);
specialCell.addChild(specialCell.specialOverlay);
// --- Color sync: animate overlay tint with background ---
if (typeof colorTweenTargets !== "undefined" && typeof currentColorIndex !== "undefined") {
var _tweenClearOverlayColor4 = function tweenClearOverlayColor() {
if (!specialCell.specialOverlay) {
return;
}
var nextIndex = (currentColorIndex + 1) % colorTweenTargets.length;
var nextColor = colorTweenTargets[nextIndex];
specialCell._specialOverlayTween = tween(specialCell.specialOverlay, {
tint: nextColor
}, {
duration: 14000,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
if (specialCell.specialOverlay) {
_tweenClearOverlayColor4();
}
}
});
};
if (specialCell._specialOverlayTween) {
specialCell._specialOverlayTween.stop && specialCell._specialOverlayTween.stop();
specialCell._specialOverlayTween = null;
}
specialCell.specialOverlay.tint = colorTweenTargets[currentColorIndex];
_tweenClearOverlayColor4();
}
} else {
specialCell.sprite.tint = 0xFF8800; // Orange tint for others
}
LK.effects.flashObject(specialCell, 0xFFFFFF, 300);
}
visibleCellsToDestroy.forEach(function (cell) {
LK.setTimeout(function () {
if (cell && cell.beingDestroyed && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell) {
LK.getSound('Explosion').play();
LK.effects.flashObject(cell, 0xFFFFFF, 200);
// --- PARTICLE EFFECT START ---
if (particlesEnabled) {
// Emit 8-12 particles per meme
var numParticles = 8 + Math.floor(Math.random() * 5);
for (var p = 0; p < numParticles; p++) {
var particle = LK.getAsset('particula', {
anchorX: 0.5,
anchorY: 0.5
});
// Randomize color (RGB)
var r = Math.floor(128 + Math.random() * 127);
var g = Math.floor(128 + Math.random() * 127);
var b = Math.floor(128 + Math.random() * 127);
var rgb = r << 16 | g << 8 | b;
particle.tint = rgb;
// Start at cell position
particle.x = cell.x;
particle.y = cell.y;
// Random velocity and direction
var angle = Math.random() * Math.PI * 2;
var speed = 6 + Math.random() * 6;
var vx = Math.cos(angle) * speed;
var vy = Math.sin(angle) * speed;
// Animate particle
(function (particle, vx, vy) {
var lifetime = 200 + Math.random() * 150;
var startAlpha = 0.9 + Math.random() * 0.1;
particle.alpha = startAlpha;
gridContainer.addChild(particle);
var elapsed = 0;
var updateFn = function updateFn() {
particle.x += vx;
particle.y += vy;
// Fade out
particle.alpha -= 0.04 + Math.random() * 0.02;
// Shrink
particle.scale.x *= 0.93;
particle.scale.y *= 0.93;
elapsed += 16;
if (elapsed > lifetime || particle.alpha <= 0.05) {
if (particle.parent) {
particle.parent.removeChild(particle);
}
LK.clearInterval(intervalId);
}
};
var intervalId = LK.setInterval(updateFn, 16);
})(particle, vx, vy);
}
}
// --- PARTICLE EFFECT END ---
gridContainer.removeChild(cell);
cell.destroy();
gridCells[cell.row][cell.col] = null;
}
}, delay);
delay += delayIncrement;
});
// After all cells in this group are destroyed, check if we need to apply gravity
LK.setTimeout(function () {
// Check if all destructions are complete before applying gravity
var allCellsDestroyed = true;
for (var r = 0; r < gridSize + extraRows; r++) {
for (var c = 0; c < gridSize; c++) {
if (gridCells[r] && gridCells[r][c] && gridCells[r][c].beingDestroyed) {
allCellsDestroyed = false;
}
}
}
// Only the last destruction process should trigger gravity
if (allCellsDestroyed && !window.gravityInProgress && !window.fillInProgress) {
applyGravity();
}
}, totalDestructionTime + 50);
}
function applyGravity() {
isAnimating = true;
var cellsToFall = [];
if (window.gravityInProgress) {
return;
}
window.gravityInProgress = true;
for (var col = 0; col < gridSize; col++) {
for (var row = gridSize + extraRows - 1; row >= extraRows; row--) {
if (!gridCells[row][col]) {
var sourceRow = row - 1;
while (sourceRow >= 0 && !gridCells[sourceRow][col]) {
sourceRow--;
}
if (sourceRow >= 0) {
var cellToMove = gridCells[sourceRow][col];
if (cellToMove) {
cellsToFall.push({
cell: cellToMove,
fromRow: sourceRow,
toRow: row,
col: col
});
gridCells[row][col] = cellToMove;
gridCells[sourceRow][col] = null;
cellToMove.row = row;
if (sourceRow < extraRows) {
gridContainer.addChild(cellToMove);
}
}
}
}
}
}
var longestDelay = 0;
var baseDelay = 25;
cellsToFall.sort(function (a, b) {
if (a.col !== b.col) {
return a.col - b.col;
}
return b.toRow - a.toRow;
});
cellsToFall.forEach(function (fallInfo, index) {
var delay = index * baseDelay;
var newPos = getCellPosition(fallInfo.toRow, fallInfo.col);
// Calculate duration based on distance
var distance = Math.abs(newPos.y - fallInfo.cell.y);
var duration = 100 + distance * 0.35; // Reduced base time + smaller distance multiplier for faster falls
var totalTime = delay + duration;
if (totalTime > longestDelay) {
longestDelay = totalTime;
}
LK.setTimeout(function () {
if (fallInfo.cell && gridCells[fallInfo.cell.row] && gridCells[fallInfo.cell.row][fallInfo.cell.col] === fallInfo.cell) {
tween(fallInfo.cell, {
y: newPos.y
}, {
duration: duration,
easing: tween.linear
});
}
}, delay);
});
LK.setTimeout(function () {
window.gravityInProgress = false;
if (!window.destructionInProgress) {
fillEmptySpacesWithNewMemes();
}
}, longestDelay + 50);
}
function fillEmptySpacesWithNewMemes() {
if (window.fillInProgress || window.gravityInProgress) {
return;
}
window.fillInProgress = true;
var anyNewMemeAdded = false;
var newCellsToAdd = [];
for (var col = 0; col < gridSize; col++) {
for (var row = 0; row < gridSize + extraRows; row++) {
if (!gridCells[row][col]) {
anyNewMemeAdded = true;
var newCell = getGridCell();
var pos = getCellPosition(row, col);
newCell.x = pos.x;
newCell.y = pos.y - 400;
var randomValue = Math.floor(Math.random() * 5) + 1;
newCell.setValue(randomValue);
newCell.row = row;
newCell.col = col;
gridCells[row][col] = newCell;
newCellsToAdd.push({
cell: newCell,
destY: pos.y,
row: row,
col: col
});
if (row >= extraRows) {
gridContainer.addChild(newCell);
}
}
}
}
var longestDelay = 0;
var baseDelay = 15;
var constantDuration = 250;
newCellsToAdd.sort(function (a, b) {
if (a.col !== b.col) {
return a.col - b.col;
}
return b.row - a.row;
});
newCellsToAdd.forEach(function (cellData, index) {
var delay = index * baseDelay;
var totalTime = delay + constantDuration;
if (totalTime > longestDelay) {
longestDelay = totalTime;
}
LK.setTimeout(function () {
if (cellData.cell && gridCells[cellData.row] && gridCells[cellData.row][cellData.col] === cellData.cell) {
tween(cellData.cell, {
y: cellData.destY
}, {
duration: constantDuration,
easing: tween.linear
});
}
}, delay);
});
if (anyNewMemeAdded) {
LK.setTimeout(function () {
window.fillInProgress = false;
if (window.destructionInProgress || window.gravityInProgress) {
isAnimating = false;
return;
}
var visibleMatchGroups = getMatches().filter(function (group) {
// Check if the match has cells in both visible and invisible areas
var visibleCells = group.filter(function (cell) {
return cell.row >= extraRows;
});
var invisibleCells = group.filter(function (cell) {
return cell.row < extraRows;
});
// Don't count groups with both visible and invisible cells unless there are at least 3 visible cells
if (visibleCells.length > 0 && invisibleCells.length > 0 && visibleCells.length < 3) {
return false;
}
return visibleCells.length >= 3; // At least 3 visible cells make a valid visible match
});
var newMatches = visibleMatchGroups;
if (newMatches.length > 0) {
// Group by meme type
var matchesByType = {};
newMatches.forEach(function (group) {
if (group.length > 0) {
var type = group[0].value;
if (!matchesByType[type]) {
matchesByType[type] = [];
}
matchesByType[type].push(group);
}
});
var newCellsToDestroy = [];
var newCellTracker = {};
// Process each type separately
for (var type in matchesByType) {
var typeGroups = matchesByType[type];
// Find a central cell for this type
var centerCell = null;
if (typeGroups.length > 0 && typeGroups[0].length > 0) {
// Pick the center of the first match group of this type
var matchGroup = typeGroups[0];
centerCell = matchGroup[Math.floor(matchGroup.length / 2)];
}
// Collect all cells of this type
var typeCells = [];
typeGroups.forEach(function (group) {
group.forEach(function (cell) {
var cellKey = cell.row + "_" + cell.col;
if (!newCellTracker[cellKey] && cell.row >= extraRows && cell && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell) {
typeCells.push(cell);
newCellTracker[cellKey] = true;
}
});
});
// Destroy cells of this type
if (typeCells.length) {
destroyCells(typeCells, centerCell);
}
}
if (Object.keys(matchesByType).length === 0) {
isAnimating = false;
}
} else {
isAnimating = false;
}
}, longestDelay + 50);
} else {
window.fillInProgress = false;
isAnimating = false;
// Reset combo tracking when no more matches are being created
comboInProgress = false;
comboCounter = 0;
}
}
// Timer management functions
function startTimer() {
// Clear any existing timer
if (timerInterval) {
LK.clearInterval(timerInterval);
timerInterval = null;
}
// Calculate initial max timer value based on score
// This ensures proper timer initialization when starting a game with existing score
var maxTimer = 10;
// Use exponential thresholds for slower decrease
if (score >= 100) {
maxTimer = 9;
}
if (score >= 500) {
maxTimer = 8;
}
if (score >= 1000) {
maxTimer = 7;
}
if (score >= 3500) {
maxTimer = 6;
}
if (score >= 10000) {
maxTimer = 5;
}
if (score >= 25000) {
maxTimer = 4;
}
if (score >= 50000) {
maxTimer = 3;
}
// Only set timer to max when initializing new game, not when resuming
if (!game.timerInitialized) {
timerSeconds = maxTimer;
game.timerInitialized = true;
}
// Update timer display initially
if (timerText) {
timerText.setText("Time: " + timerSeconds.toFixed(1));
}
// Start countdown timer with decimals (100ms interval)
timerInterval = LK.setInterval(function () {
timerSeconds -= 0.1;
timerSeconds = Math.max(0, parseFloat(timerSeconds.toFixed(1))); // Fix floating point issues
// Update timer display
if (timerText) {
timerText.setText("Time: " + timerSeconds.toFixed(1));
}
// Check for game over
if (timerSeconds <= 0) {
// Stop timer
LK.clearInterval(timerInterval);
timerInterval = null;
// Instead of showing game over screen, trigger the custom game over
game.onGameOver();
}
}, 100);
}
function resetTimer() {
// Only reset timer in Fast Mode
if (game.gameMode === 'fast') {
// Calculate max timer value based on score
// Start with 10 seconds, decrease as score increases, minimum of 3 seconds
var maxTimer = 10;
// Use exponential thresholds for slower decrease
if (score >= 100) {
maxTimer = 9;
}
if (score >= 500) {
maxTimer = 8;
}
if (score >= 1000) {
maxTimer = 7;
}
if (score >= 3500) {
maxTimer = 6;
}
if (score >= 10000) {
maxTimer = 5;
}
if (score >= 25000) {
maxTimer = 4;
}
if (score >= 50000) {
maxTimer = 3;
}
// Set timer to calculated max
timerSeconds = maxTimer;
if (timerText) {
timerText.setText("Time: " + timerSeconds.toFixed(1));
}
}
}
// Handle game over events to return to menu
game.onGameOver = function () {
// Save high score one last time if it's better
if (game.gameMode === 'classic' && score > highScoreClassic) {
highScoreClassic = score;
storage.highScoreClassic = highScoreClassic;
} else if (game.gameMode === 'fast' && score > highScoreFast) {
highScoreFast = score;
storage.highScoreFast = highScoreFast;
} else if (game.gameMode === 'seekmode' && score > highScoreSeek) {
highScoreSeek = score;
storage.highScoreSeek = highScoreSeek;
}
// Stop timer if running
if (timerInterval) {
LK.clearInterval(timerInterval);
timerInterval = null;
}
// Clean up seek mode block timer and text
seekModeBlockActive = false;
if (seekModeBlockTimer) {
LK.clearInterval(seekModeBlockTimer);
seekModeBlockTimer = null;
}
if (seekModeBlockText) {
game.removeChild(seekModeBlockText);
seekModeBlockText.destroy();
seekModeBlockText = null;
}
// Clean up seek mode combo counter and show-all timer
if (seekModeComboText) {
game.removeChild(seekModeComboText);
seekModeComboText.destroy();
seekModeComboText = null;
}
if (seekModeShowAllTimer) {
LK.clearTimeout(seekModeShowAllTimer);
seekModeShowAllTimer = null;
}
seekModeComboCount = 0;
// --- FIX: Reset seekModeForceSeekSprite so memes use correct sprite after game over ---
game.seekModeForceSeekSprite = false;
// Stop periodic save interval
if (saveScoreInterval) {
LK.clearInterval(saveScoreInterval);
saveScoreInterval = null;
}
// Clear music interval
if (game.musicInterval) {
LK.clearInterval(game.musicInterval);
game.musicInterval = null;
// Stop any currently playing music
LK.stopMusic();
}
// For Fast Mode, show a custom high score popup instead of the game over screen
if (game.gameMode === 'fast') {
// Create popup container
var popupContainer = new Container();
// Create popup background
var popupBg = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4.5
});
popupBg.tint = 0x0088FF; // Blue tint for Fast Mode
// Create popup title
var popupTitle = new Text2("GAME OVER", {
size: 100,
fill: 0xFFFFFF
});
popupTitle.anchor.set(0.5, 0);
popupTitle.y = -350;
// Show current score in Fast Mode game over popup
var currentScoreText = new Text2("Score: " + score, {
size: 90,
fill: 0xffffff
});
currentScoreText.anchor.set(0.5, 0);
currentScoreText.y = -200;
// Show high score
var highScoreText = new Text2("High Score: " + highScoreFast, {
size: 90,
fill: 0xFFFF00
});
highScoreText.anchor.set(0.5, 0);
highScoreText.y = -100;
// Update high score if needed
if (score > highScoreFast) {
highScoreFast = score;
highScoreText.setText("High Score: " + score + " (New!)");
}
// Create button to return to menu
var menuButton = new Container();
var menuButtonBg = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5
});
menuButtonBg.tint = 0xFF5500;
var menuButtonText = new Text2("BACK TO MENU", {
size: 70,
fill: 0xFFFFFF
});
menuButtonText.anchor.set(0.5, 0.5);
menuButton.addChild(menuButtonBg);
menuButton.addChild(menuButtonText);
menuButton.y = 250;
// Add all elements to popup
popupContainer.addChild(popupBg);
popupContainer.addChild(popupTitle);
popupContainer.addChild(highScoreText);
popupContainer.addChild(currentScoreText);
popupContainer.addChild(menuButton);
// Position popup in center of screen
popupContainer.x = 2048 / 2;
popupContainer.y = 2732 / 2;
game.addChild(popupContainer);
// Add button interaction
menuButton.interactive = true;
menuButton.down = function (x, y, obj) {
LK.effects.flashObject(menuButtonBg, 0xFFFFFF, 200);
// Clear the game board
while (gridContainer.children.length > 0) {
var child = gridContainer.children[0];
gridContainer.removeChild(child);
child.destroy();
}
if (scoreText) {
game.removeChild(scoreText);
scoreText = null;
}
if (timerText) {
game.removeChild(timerText);
timerText = null;
}
// Remove popup
game.removeChild(popupContainer);
// Clean up any game UI elements
if (game.modeText) {
game.removeChild(game.modeText);
game.modeText = null;
}
if (game.pauseButton) {
game.removeChild(game.pauseButton);
game.pauseButton = null;
}
game.timerInitialized = false;
// Show menu
initializeMenu();
};
} else {
// For Classic Mode, use the standard game over screen
LK.setTimeout(function () {
// Clear the game board
while (gridContainer.children.length > 0) {
var child = gridContainer.children[0];
gridContainer.removeChild(child);
child.destroy();
}
if (scoreText) {
game.removeChild(scoreText);
scoreText = null;
}
if (timerText) {
game.removeChild(timerText);
timerText = null;
}
// Clean up any game UI elements
if (game.modeText) {
game.removeChild(game.modeText);
game.modeText = null;
}
if (game.pauseButton) {
game.removeChild(game.pauseButton);
game.pauseButton = null;
}
game.timerInitialized = false;
// Show menu
initializeMenu();
}, 500);
}
};
function checkForAndDestroyMatches(swappedCellA, swappedCellB) {
if (window.gravityInProgress || window.fillInProgress) {
return;
}
var allMatchGroupsOnBoard = getMatches();
if (!allMatchGroupsOnBoard.length) {
return;
}
var relevantMatchGroups = allMatchGroupsOnBoard.filter(function (group) {
// Check if the match has cells in both visible and invisible areas
var visibleCells = group.filter(function (cell) {
return cell.row >= extraRows;
});
var invisibleCells = group.filter(function (cell) {
return cell.row < extraRows;
});
// Don't count groups with both visible and invisible cells unless there are at least 3 visible cells
if (visibleCells.length > 0 && invisibleCells.length > 0 && visibleCells.length < 3) {
return false;
}
return group.some(function (cellInGroup) {
return (cellInGroup === swappedCellA || cellInGroup === swappedCellB) && cellInGroup.row >= extraRows;
});
});
if (!relevantMatchGroups.length) {
return;
}
// Group by meme type
var matchesByType = {};
relevantMatchGroups.forEach(function (group) {
if (group.length > 0) {
var type = group[0].value;
if (!matchesByType[type]) {
matchesByType[type] = [];
}
matchesByType[type].push(group);
}
});
var uniqueCellTracker = {};
var cellsToDestroy = [];
// Process each type separately
for (var type in matchesByType) {
var typeGroups = matchesByType[type];
// Flatten all groups of this type
var typeCells = [];
typeGroups.forEach(function (group) {
group.forEach(function (cell) {
if (cell.row >= extraRows) {
typeCells.push(cell);
}
});
});
// Add each cell of this type to cellsToDestroy
typeCells.forEach(function (cell) {
var cellKey = cell.row + "_" + cell.col;
if (!uniqueCellTracker[cellKey] && cell.row >= extraRows && cell && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell) {
cellsToDestroy.push(cell);
uniqueCellTracker[cellKey] = true;
}
});
}
;
if (cellsToDestroy.length) {
// Pass the swapped cell that created the match as the center for the ripple effect
var centerCell = swappedCellA && relevantMatchGroups.some(function (group) {
return group.includes(swappedCellA);
}) ? swappedCellA : swappedCellB;
destroyCells(cellsToDestroy, centerCell);
LK.setTimeout(function () {
if (window.gravityInProgress || window.fillInProgress) {
return;
}
var visibleMatchGroups = getMatches().filter(function (group) {
// Check if the match has cells in both visible and invisible areas
var visibleCells = group.filter(function (cell) {
return cell.row >= extraRows;
});
var invisibleCells = group.filter(function (cell) {
return cell.row < extraRows;
});
// Don't count groups with both visible and invisible cells unless there are at least 3 visible cells
if (visibleCells.length > 0 && invisibleCells.length > 0 && visibleCells.length < 3) {
return false;
}
return visibleCells.length >= 3; // At least 3 visible cells make a valid visible match
});
var newMatches = visibleMatchGroups;
if (newMatches.length > 0) {
// Group by meme type
var matchesByType = {};
newMatches.forEach(function (group) {
if (group.length > 0) {
var type = group[0].value;
if (!matchesByType[type]) {
matchesByType[type] = [];
}
matchesByType[type].push(group);
}
});
// Process each type separately
for (var type in matchesByType) {
var typeGroups = matchesByType[type];
var newCellsToDestroy = [];
var newCellTracker = {};
// Collect all cells of this type
typeGroups.forEach(function (group) {
group.forEach(function (cell) {
var cellKey = cell.row + "_" + cell.col;
if (!newCellTracker[cellKey] && cell.row >= extraRows && cell && gridCells[cell.row] && gridCells[cell.row][cell.col] === cell) {
newCellsToDestroy.push(cell);
newCellTracker[cellKey] = true;
}
});
});
if (newCellsToDestroy.length) {
// Find center cell of this type
var centerCell = newCellsToDestroy[Math.floor(newCellsToDestroy.length / 2)];
destroyCells(newCellsToDestroy, centerCell);
}
}
}
}, 400);
}
}
var gridSize = 7;
var extraRows = 9;
var cellSpacing = 12;
var cellSize = 240;
var gridCells = [];
var totalGridWidth = gridSize * cellSize + (gridSize - 1) * cellSpacing;
var totalVisibleGridHeight = totalGridWidth;
var totalGridHeight = totalVisibleGridHeight + extraRows * (cellSize + cellSpacing);
var startX = (2048 - totalGridWidth) / 2 + cellSize / 2;
var startY = (-1600 - totalVisibleGridHeight) / 2 + cellSize / 2 + extraRows * (cellSize + cellSpacing);
function getCellPosition(row, col) {
return {
x: startX + col * (cellSize + cellSpacing),
y: startY + row * (cellSize + cellSpacing) - extraRows * (cellSize + cellSpacing)
};
}
function initializeGrid() {
// Add background image behind grid cells
if (gridContainer.gridBackground) {
gridContainer.removeChild(gridContainer.gridBackground);
gridContainer.gridBackground = null;
}
var gridBackground = LK.getAsset('Bg3', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2 + 100,
alpha: 0.5
});
var scaleX = 2.2;
var scaleY = 2.2;
var scale = Math.max(scaleX, scaleY);
gridBackground.scale.set(scale, scale);
gridContainer.gridBackground = gridBackground;
gridContainer.addChildAt(gridBackground, 0);
// --- CUADRICULAS OBJETO PARA LUGARES MOVIBLES ---
// Elimina cuadriculas previas si existen
if (typeof cuadriculas !== "undefined" && cuadriculas && cuadriculas.parent) {
cuadriculas.parent.removeChild(cuadriculas);
}
cuadriculas = new Container();
// Dibuja cuadricula solo en la zona visible (no en extraRows)
for (var row = extraRows; row < gridSize + extraRows; row++) {
for (var col = 0; col < gridSize; col++) {
var pos = getCellPosition(row, col);
var cuadriculaSprite = LK.getAsset('cuadricula', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.05,
scaleY: 1.05,
alpha: 0.7
});
cuadriculaSprite.x = pos.x;
cuadriculaSprite.y = pos.y;
cuadriculas.addChild(cuadriculaSprite);
}
}
// Añade cuadriculas detrás de las celdas
gridContainer.addChildAt(cuadriculas, 1);
gridCells = [];
for (var row = 0; row < gridSize + extraRows; row++) {
gridCells[row] = [];
for (var col = 0; col < gridSize; col++) {
var pos = getCellPosition(row, col);
var cell = getGridCell ? getGridCell() : new GridCell();
cell.x = pos.x;
cell.y = pos.y;
cell.row = row;
cell.col = col;
var randomValue;
var attempts = 0;
do {
randomValue = Math.floor(Math.random() * 5) + 1;
attempts++;
var leftMatchCount = 0;
var aboveMatchCount = 0;
if (col >= 2) {
if (gridCells[row][col - 1].value === randomValue && gridCells[row][col - 2].value === randomValue) {
leftMatchCount = 2;
}
}
if (row >= 2) {
if (gridCells[row - 1][col].value === randomValue && gridCells[row - 2][col].value === randomValue) {
aboveMatchCount = 2;
}
}
} while ((leftMatchCount >= 2 || aboveMatchCount >= 2) && attempts < 10);
cell.setValue(randomValue);
if (row >= extraRows) {
gridContainer.addChild(cell);
}
gridCells[row][col] = cell;
}
}
}
// --- PUNTERO DRAG ---
// Variable global para habilitar/deshabilitar el drag de memes
var dragMemeEnabled = true;
// Puntero visual que sigue el dedo mientras está presionado
var pointerActive = false;
var pointerSprite = LK.getAsset('Puntero', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
pointerSprite.visible = false;
pointerSprite.zIndex = 9999; // Asegura que esté encima
game.addChild(pointerSprite);
// --- CHECK CON ASSET PUNTERO ENCIMA DE TODAS LAS COLUMNAS DE MEMES ---
var checkPointers = [];
var checkRow = extraRows; // Fila visible superior
for (var checkCol = 0; checkCol < gridSize; checkCol++) {
var checkPos = getCellPosition(checkRow, checkCol);
var checkPointer = LK.getAsset('Puntero', {
anchorX: 0.5,
anchorY: 1,
x: checkPos.x,
y: checkPos.y - cellSize / 2 - 110,
// Encima de la celda, con margen
scaleX: 2,
scaleY: 2,
alpha: 0
});
checkPointer.zIndex = 9998; // Debajo del puntero drag, encima de la grilla
game.addChild(checkPointer);
checkPointers.push(checkPointer);
}
// Evento down: activa el puntero y lo posiciona
game.down = function (x, y, obj) {
pointerActive = true;
pointerSprite.visible = true;
pointerSprite.x = x;
pointerSprite.y = y;
if (typeof handleCellTap === "function") {
// Si hay una celda debajo, permite la selección normal
// (esto mantiene la funcionalidad original)
// handleCellTap puede ser llamada aquí si se desea
}
};
// Evento move: si está activo, sigue el dedo
game.move = function (x, y, obj) {
if (pointerActive) {
pointerSprite.x = x;
pointerSprite.y = y;
// --- SWAP POR ARRASTRE ---
if (dragMemeEnabled && selectedCell && gridCells[selectedCell.row] && gridCells[selectedCell.row][selectedCell.col] === selectedCell) {
// Calcula la posición central de la celda seleccionada
var cellPos = getCellPosition(selectedCell.row, selectedCell.col);
var dx = x - cellPos.x;
var dy = y - cellPos.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 100) {
// Determina la dirección principal (horizontal o vertical)
var dir;
if (Math.abs(dx) > Math.abs(dy)) {
dir = dx > 0 ? "right" : "left";
} else {
dir = dy > 0 ? "down" : "up";
}
var targetRow = selectedCell.row;
var targetCol = selectedCell.col;
if (dir === "right" && selectedCell.col < gridSize - 1) {
targetCol++;
} else if (dir === "left" && selectedCell.col > 0) {
targetCol--;
} else if (dir === "down" && selectedCell.row < gridSize + extraRows - 1) {
targetRow++;
} else if (dir === "up" && selectedCell.row > 0) {
targetRow--;
} else {
// No hay celda adyacente válida
return;
}
var targetCell = gridCells[targetRow][targetCol];
// Solo intercambia si la celda destino existe y no está siendo destruida
if (targetCell && !targetCell.beingDestroyed) {
// Llama a la lógica de swap como si se hubiera hecho tap en la celda adyacente
handleCellTap(targetCell);
}
}
}
}
};
// Evento up: desactiva el puntero
game.up = function (x, y, obj) {
pointerActive = false;
pointerSprite.visible = false;
};
// --- INTRODUCTION SCREEN WITH DARK TRANSITIONS ---
// Show a black transition overlay, then introduction, then fade out and show menu
var introTransition = null;
var introImage = null;
var introTimeout = null;
// --- INTRO OPTION: persistently store intro enabled/disabled ---
// Load dragMemeEnabled from storage if available, otherwise default to true
var dragMemeEnabled = typeof storage.dragMemeEnabled === "boolean" ? storage.dragMemeEnabled : true;
storage.dragMemeEnabled = dragMemeEnabled;
// Load introEnabled from storage if available, otherwise default to true
var introEnabled = typeof storage.introEnabled === "boolean" ? storage.introEnabled : true;
storage.introEnabled = introEnabled;
// Load backgroundColorAnimEnabled from storage if available, otherwise default to true
var backgroundColorAnimEnabled = typeof storage.backgroundColorAnimEnabled === "boolean" ? storage.backgroundColorAnimEnabled : true;
storage.backgroundColorAnimEnabled = backgroundColorAnimEnabled;
// Load particlesEnabled from storage if available, otherwise default to true
var particlesEnabled = typeof storage.particlesEnabled === "boolean" ? storage.particlesEnabled : true;
storage.particlesEnabled = particlesEnabled;
// Function to show the introduction and then the menu
function showIntroductionAndMenu() {
if (!introEnabled) {
// If intro is disabled, go straight to menu
initializeMenu();
return;
}
// 1. Create black overlay (full screen)
introTransition = LK.getAsset('transicion', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 1,
scaleY: 1,
alpha: 1
});
introTransition.zIndex = 99999;
game.addChild(introTransition);
// 2. Fade in (already black), then show intro image
LK.setTimeout(function () {
// 3. Show introduction image
introImage = LK.getAsset('introduction', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 0.7,
scaleY: 0.7,
alpha: 0
});
introImage.zIndex = 100000;
game.addChild(introImage);
// Fade in intro image
tween(introImage, {
alpha: 1
}, {
duration: 800,
easing: tween.easeInOutQuad
});
// 4. Wait for 2.5 seconds, then fade out intro image, then fade out overlay after a delay
introTimeout = LK.setTimeout(function () {
// Fade out intro image
tween(introImage, {
alpha: 0
}, {
duration: 700,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
if (introImage && introImage.parent) {
game.removeChild(introImage);
introImage = null;
}
// Fade out black overlay after intro image is gone (extra delay)
LK.setTimeout(function () {
tween(introTransition, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
if (introTransition && introTransition.parent) {
game.removeChild(introTransition);
introTransition = null;
}
// Now show the menu
initializeMenu();
}
});
}, 400); // 400ms extra so overlay lasts longer than image
}
});
}, 2500);
}, 400);
}
// Call introduction instead of menu directly
showIntroductionAndMenu();
function ensureNoInitialMatches() {
var matches = getMatches();
if (matches.length > 0) {
matches.forEach(function (group) {
group.forEach(function (cell) {
var currentValue = cell.value;
var newValue;
do {
newValue = Math.floor(Math.random() * 5) + 1;
} while (newValue === currentValue);
cell.setValue(newValue);
});
});
ensureNoInitialMatches();
}
}
ensureNoInitialMatches();
la figura de una casa color blanca simple para una interfaz. In-Game asset. 2d. High contrast. No shadows
haz el fondo color morado
circular check logo. In-Game asset. 2d. High contrast. No shadows
Cuadrado con los bordes redondeado negro. In-Game asset. 2d. High contrast. No shadows
hazlo un gris claro
Que sea blanco
Que sea blanco