User prompt
Aumenta el tamaño de la cuadricula un 60%
User prompt
aumenta su tamaño un 60% ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
crea un grid 9*9 en el centro de la pantalla con un espaciado de 10 pixeles entre cada uno
Code edit (1 edits merged)
Please save this source code
User prompt
Bubble Popper: Tap 'n' Blast
Initial prompt
hi
/****
* 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