/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BlankScreen = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 9,
scaleY: 12
});
background.x = 1024;
background.y = 1366;
background.alpha = 0.95;
self.addChild(background);
var titleText = new Text2('JUST JOKING', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
self.addChild(titleText);
// Add some content to the blank screen
var contentText = new Text2('Click the green button', {
size: 80,
fill: 0x00FF00
});
contentText.anchor.set(0.5, 0.5);
contentText.x = 1024;
contentText.y = 800;
self.addChild(contentText);
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backButton.x = 1024;
backButton.y = 2300;
self.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2300;
self.addChild(backText);
backButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideBlankScreen();
};
return self;
});
var FeedbackScreen = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 9,
scaleY: 12
});
background.x = 1024;
background.y = 1366;
background.alpha = 0.95;
self.addChild(background);
var titleText = new Text2('FEEDBACK', {
size: 120,
fill: 0xFFAA00
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
self.addChild(titleText);
// Feedback form instructions
var instructions = ['We would love to hear your thoughts!', '', 'What did you think of the game?', '• Was it fun and engaging?', '• Were the controls easy to use?', '• How was the difficulty level?', '• Any suggestions for improvement?', '', 'Thank you for playing MAVUS BABA!', 'Your feedback helps us make better games.', '', 'Contact us at:', 'INSTAGRAM: abdurrahim_scu'];
var yOffset = 650;
for (var i = 0; i < instructions.length; i++) {
var instructionText = new Text2(instructions[i], {
size: instructions[i] === '' ? 30 : instructions[i].startsWith('Contact') || instructions[i].includes('@') ? 50 : instructions[i].startsWith('•') ? 45 : instructions[i] === 'We would love to hear your thoughts!' ? 70 : instructions[i] === 'Thank you for playing MAVUS BABA!' ? 65 : 55,
fill: instructions[i].includes('@') ? 0x44FF44 : instructions[i].startsWith('•') ? 0xFFFF44 : instructions[i] === 'Thank you for playing MAVUS BABA!' ? 0xFFAA00 : 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = yOffset + i * 80;
self.addChild(instructionText);
}
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backButton.x = 1024;
backButton.y = 2300;
self.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2300;
self.addChild(backText);
backButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideFeedbackScreen();
};
return self;
});
var Gem = Container.expand(function () {
var self = Container.call(this);
self.gemType = 0;
self.gridX = 0;
self.gridY = 0;
self.graphics = null;
self.isSelected = false;
self.isMatched = false;
self.isFalling = false;
self.isBomb = false;
self.init = function (type, gx, gy) {
self.gemType = type;
self.gridX = gx;
self.gridY = gy;
var gemTypes = ['gemRed', 'gemBlue', 'gemGreen', 'gemYellow', 'gemPurple', 'gemOrange', 'gemBomb'];
if (type === 6) {
self.isBomb = true;
}
self.graphics = self.attachAsset(gemTypes[type], {
anchorX: 0.5,
anchorY: 0.5
});
self.updatePosition();
};
self.updatePosition = function () {
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var startY = 400;
// Force exact grid alignment to prevent overlapping
var exactX = startX + self.gridX * CELL_SIZE + CELL_SIZE / 2;
var exactY = startY + self.gridY * CELL_SIZE + CELL_SIZE / 2;
self.x = exactX;
self.y = exactY;
};
self.setSelected = function (selected) {
self.isSelected = selected;
if (selected) {
self.graphics.alpha = 0.7;
tween(self.graphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200
});
} else {
self.graphics.alpha = 1.0;
tween(self.graphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
};
self.fallTo = function (newY, callback) {
self.isFalling = true;
self.gridY = newY;
var newPosY = 400 + newY * CELL_SIZE + CELL_SIZE / 2;
// Ensure gem position is properly aligned to grid
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var correctX = startX + self.gridX * CELL_SIZE + CELL_SIZE / 2;
self.x = correctX; // Force correct X position to prevent overlap
tween(self, {
y: newPosY
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.isFalling = false;
// Double-check final position alignment
self.x = correctX;
self.y = newPosY;
if (callback) callback();
}
});
};
self.destroy = function () {
self.isMatched = true;
// Play explosion sound effect
LK.getSound('explosion').play();
// Create explosion effect with scaling and rotation
tween(self.graphics, {
scaleX: 1.8,
scaleY: 1.8,
rotation: Math.PI * 2,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
};
self.explodeBomb = function () {
if (!self.isBomb) return;
// Create dramatic bomb explosion effect
tween(self.graphics, {
scaleX: 3,
scaleY: 3,
rotation: Math.PI * 4,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
// Add pulsing effect before explosion
tween(self.graphics, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self.graphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeInOut
});
}
});
// Mark gems in 3x3 area for destruction
var gemsToDestroy = [];
for (var dx = -1; dx <= 1; dx++) {
for (var dy = -1; dy <= 1; dy++) {
var targetX = self.gridX + dx;
var targetY = self.gridY + dy;
if (targetX >= 0 && targetX < GRID_SIZE && targetY >= 0 && targetY < GRID_SIZE) {
if (grid[targetX][targetY] && !grid[targetX][targetY].isMatched) {
gemsToDestroy.push({
x: targetX,
y: targetY
});
}
}
}
}
// Destroy gems with delay and explosion effects
for (var i = 0; i < gemsToDestroy.length; i++) {
(function (destroyData, index) {
LK.setTimeout(function () {
if (grid[destroyData.x][destroyData.y]) {
var gemToExplode = grid[destroyData.x][destroyData.y];
// Play explosion sound for each destroyed gem
LK.getSound('explosion').play();
// Create explosion effect for each gem
tween(gemToExplode.graphics, {
scaleX: 2.0,
scaleY: 2.0,
rotation: Math.PI * (1 + Math.random()),
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (gemToExplode.parent) {
gemToExplode.parent.removeChild(gemToExplode);
}
}
});
grid[destroyData.x][destroyData.y] = null;
}
}, i * 80);
})(gemsToDestroy[i], i);
}
// Add explosion score - in extreme mode, only count gems of the selected type
var scoringGems = gemsToDestroy.length;
if (gameMode === 'extreme') {
scoringGems = 0;
for (var j = 0; j < gemsToDestroy.length; j++) {
var destroyData = gemsToDestroy[j];
if (grid[destroyData.x] && grid[destroyData.x][destroyData.y] && grid[destroyData.x][destroyData.y].gemType === extremeGemType) {
scoringGems++;
}
}
}
var explosionScore = scoringGems * 50 * multiplier;
score += explosionScore;
updateScore();
// Play explosion sound
LK.getSound('explosion').play();
// Wait for all destruction then cascade and fill empty spaces
LK.setTimeout(function () {
cascadeGems();
// Ensure empty spaces are filled after bomb explosion
LK.setTimeout(function () {
fillEmptySpaces();
}, 100);
}, 500);
};
self.down = function (x, y, obj) {
if (!self.isFalling && !isProcessing) {
if (self.isBomb) {
isProcessing = true;
self.explodeBomb();
} else {
selectGem(self);
}
}
};
return self;
});
var HowToPlayScreen = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 9,
scaleY: 12
});
background.x = 1024;
background.y = 1366;
background.alpha = 0.95;
self.addChild(background);
var titleText = new Text2('HOW TO PLAY', {
size: 120,
fill: 0x44FF44
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
self.addChild(titleText);
// Game instructions text
var instructions = ['Match 3 or more gems of the same color', 'to clear them and earn points!', '', '• Tap gems to select them', '• Select adjacent gems to swap', '• Create cascades for bonus points', '• Match 4+ gems to create bombs', '• Bombs destroy 3x3 areas', '• Match exactly 5 gems to create rockets', '• Tap rockets to fire and clear columns', '• Score multiplier increases with points', '• You have 60 seconds to score big!', '', 'EXTREME MODE:', 'Only one gem type scores points!'];
var yOffset = 650;
for (var i = 0; i < instructions.length; i++) {
var instructionText = new Text2(instructions[i], {
size: instructions[i] === '' ? 30 : instructions[i].startsWith('EXTREME') ? 80 : 55,
fill: instructions[i].startsWith('EXTREME') ? 0xFF4444 : instructions[i].startsWith('•') ? 0xFFFF44 : 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = yOffset + i * 90;
self.addChild(instructionText);
}
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backButton.x = 1024;
backButton.y = 2300;
self.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2300;
self.addChild(backText);
backButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideHowToPlayScreen();
};
return self;
});
var LevelsScreen = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 9,
scaleY: 12
});
background.x = 1024;
background.y = 1366;
background.alpha = 0.95;
self.addChild(background);
var titleText = new Text2('SELECT LEVEL', {
size: 120,
fill: 0x00AAFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
self.addChild(titleText);
// Create level buttons 1-10
for (var levelNum = 1; levelNum <= 10; levelNum++) {
(function (level) {
var levelButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.8
});
// Position buttons in a 2x5 grid
var col = (level - 1) % 2;
var row = Math.floor((level - 1) / 2);
levelButton.x = 800 + col * 448;
levelButton.y = 700 + row * 200;
self.addChild(levelButton);
var levelText = new Text2(level.toString(), {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = levelButton.x;
levelText.y = levelButton.y;
self.addChild(levelText);
levelButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
gameMode = 'level';
currentLevel = level;
hideLevelsScreen();
startGame();
};
})(levelNum);
}
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backButton.x = 1024;
backButton.y = 2300;
self.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2300;
self.addChild(backText);
backButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideLevelsScreen();
};
return self;
});
var OptionsMenu = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 6
});
background.x = 1024;
background.y = 1366;
background.alpha = 0.9;
self.addChild(background);
var titleText = new Text2('Options', {
size: 150,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
self.addChild(titleText);
// Music Volume Toggle Button
var musicVolumeButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.6
});
musicVolumeButton.x = 1024;
musicVolumeButton.y = 1000;
self.addChild(musicVolumeButton);
// Get current music volume state
var isMusicOn = storage.musicVolume === undefined ? true : storage.musicVolume > 0;
var musicVolumeText = new Text2(isMusicOn ? 'Music: ON' : 'Music: OFF', {
size: 80,
fill: 0xFFFFFF
});
musicVolumeText.anchor.set(0.5, 0.5);
musicVolumeText.x = 1024;
musicVolumeText.y = 1000;
self.addChild(musicVolumeText);
musicVolumeButton.down = function (x, y, obj) {
// Toggle music volume
var currentVolume = storage.musicVolume === undefined ? 1.0 : storage.musicVolume;
if (currentVolume > 0) {
// Turn music off
storage.musicVolume = 0;
musicVolumeText.setText('Music: OFF');
LK.stopMusic();
} else {
// Turn music on
storage.musicVolume = 1.0;
musicVolumeText.setText('Music: ON');
// Play appropriate music based on game state
if (gameState === 'playing') {
LK.playMusic('background', {
fade: {
start: 0,
end: 0.9,
duration: 500
}
});
} else if (gameState === 'menu' || gameState === 'options') {
LK.playMusic('background', {
fade: {
start: 0,
end: 0.8,
duration: 500
}
});
}
}
};
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backButton.x = 1024;
backButton.y = 1200;
self.addChild(backButton);
var backText = new Text2('Back', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 1200;
self.addChild(backText);
backButton.down = function (x, y, obj) {
hideOptionsMenu();
};
return self;
});
var StartScreen = Container.expand(function () {
var self = Container.call(this);
// Create animated star background
var stars = [];
for (var i = 0; i < 50; i++) {
var star = self.attachAsset('starBg', {
anchorX: 0.5,
anchorY: 0.5
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.alpha = Math.random() * 0.8 + 0.2;
stars.push(star);
// Animate each star with twinkling effect
tween(star, {
alpha: Math.random() * 0.3 + 0.1
}, {
duration: Math.random() * 2000 + 1000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
// Add gentle floating movement
tween(star, {
y: star.y + (Math.random() * 100 - 50)
}, {
duration: Math.random() * 4000 + 2000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
// Create 3D effect with shadow layers
var titleShadow3 = new Text2('MAVUS BABA', {
size: 200,
fill: 0x000000
});
titleShadow3.anchor.set(0.5, 0.5);
titleShadow3.x = 1032;
titleShadow3.y = 808;
self.addChild(titleShadow3);
var titleShadow2 = new Text2('MAVUS BABA', {
size: 200,
fill: 0x333333
});
titleShadow2.anchor.set(0.5, 0.5);
titleShadow2.x = 1028;
titleShadow2.y = 804;
self.addChild(titleShadow2);
var titleShadow1 = new Text2('MAVUS BABA', {
size: 200,
fill: 0x666666
});
titleShadow1.anchor.set(0.5, 0.5);
titleShadow1.x = 1026;
titleShadow1.y = 802;
self.addChild(titleShadow1);
var titleText = new Text2('MAVUS BABA', {
size: 200,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
self.addChild(titleText);
// Add 3D glow animation
tween(titleText, {
alpha: 0.8
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
alpha: 1.0
}, {
duration: 1500,
easing: tween.easeInOut
});
}
});
var startButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
startButton.x = 1024;
startButton.y = 1200;
self.addChild(startButton);
// Create 3D effect for Start text
var startShadow2 = new Text2('START', {
size: 100,
fill: 0x000000
});
startShadow2.anchor.set(0.5, 0.5);
startShadow2.x = 1028;
startShadow2.y = 1204;
self.addChild(startShadow2);
var startShadow1 = new Text2('START', {
size: 100,
fill: 0x444444
});
startShadow1.anchor.set(0.5, 0.5);
startShadow1.x = 1026;
startShadow1.y = 1202;
self.addChild(startShadow1);
var startText = new Text2('START', {
size: 100,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = 1024;
startText.y = 1200;
self.addChild(startText);
var optionsButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
optionsButton.x = 1024;
optionsButton.y = 1500;
self.addChild(optionsButton);
// Create 3D effect for Options text
var optionsShadow2 = new Text2('OPTIONS', {
size: 100,
fill: 0x000000
});
optionsShadow2.anchor.set(0.5, 0.5);
optionsShadow2.x = 1028;
optionsShadow2.y = 1504;
self.addChild(optionsShadow2);
var optionsShadow1 = new Text2('OPTIONS', {
size: 100,
fill: 0x444444
});
optionsShadow1.anchor.set(0.5, 0.5);
optionsShadow1.x = 1026;
optionsShadow1.y = 1502;
self.addChild(optionsShadow1);
var optionsText = new Text2('OPTIONS', {
size: 100,
fill: 0xFFFFFF
});
optionsText.anchor.set(0.5, 0.5);
optionsText.x = 1024;
optionsText.y = 1500;
self.addChild(optionsText);
// Add "MADE BY MAVUS DONDURMA" text to bottom left corner
var madeByText = new Text2('MADE BY MAVUS DONDURMA', {
size: 60,
fill: 0xCCCCCC
});
madeByText.anchor.set(0, 1);
madeByText.x = 150;
madeByText.y = 2580;
self.addChild(madeByText);
// Add version text under "Made by Mavus Dondurma"
var versionText = new Text2('Version 1.0', {
size: 40,
fill: 0x888888
});
versionText.anchor.set(0, 1);
versionText.x = 150;
versionText.y = 2630;
self.addChild(versionText);
// Add play time display in top right corner
var currentTotalTime = storage.totalPlayTime || 0;
var hours = Math.floor(currentTotalTime / 3600);
var minutes = Math.floor(currentTotalTime % 3600 / 60);
var seconds = currentTotalTime % 60;
var timeString = hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
playTimeText = new Text2('Play Time: ' + timeString, {
size: 60,
fill: 0xAAFFAA
});
playTimeText.anchor.set(1, 0);
playTimeText.x = 1900;
playTimeText.y = 150;
self.addChild(playTimeText);
startButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
startGame();
};
optionsButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
showOptionsMenu();
};
var extremeButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
extremeButton.x = 1024;
extremeButton.y = 1800;
self.addChild(extremeButton);
// Create 3D effect for Extreme Mode text
var extremeShadow2 = new Text2('EXTREME MODE', {
size: 80,
fill: 0x000000
});
extremeShadow2.anchor.set(0.5, 0.5);
extremeShadow2.x = 1028;
extremeShadow2.y = 1804;
self.addChild(extremeShadow2);
var extremeShadow1 = new Text2('EXTREME MODE', {
size: 80,
fill: 0x444444
});
extremeShadow1.anchor.set(0.5, 0.5);
extremeShadow1.x = 1026;
extremeShadow1.y = 1802;
self.addChild(extremeShadow1);
var extremeText = new Text2('EXTREME MODE', {
size: 80,
fill: 0xFF4444
});
extremeText.anchor.set(0.5, 0.5);
extremeText.x = 1024;
extremeText.y = 1800;
self.addChild(extremeText);
extremeButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
gameMode = 'extreme';
startGame();
};
var storyButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
storyButton.x = 1024;
storyButton.y = 2100;
self.addChild(storyButton);
// Create 3D effect for Story Mode text
var storyShadow2 = new Text2('STORY MODE', {
size: 80,
fill: 0x000000
});
storyShadow2.anchor.set(0.5, 0.5);
storyShadow2.x = 1028;
storyShadow2.y = 2104;
self.addChild(storyShadow2);
var storyShadow1 = new Text2('STORY MODE', {
size: 80,
fill: 0x444444
});
storyShadow1.anchor.set(0.5, 0.5);
storyShadow1.x = 1026;
storyShadow1.y = 2102;
self.addChild(storyShadow1);
var storyText = new Text2('STORY MODE', {
size: 80,
fill: 0x44FF44
});
storyText.anchor.set(0.5, 0.5);
storyText.x = 1024;
storyText.y = 2100;
self.addChild(storyText);
storyButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
// Hide all start screen elements
self.visible = false;
showStoryMode();
};
var levelsButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
levelsButton.x = 1024;
levelsButton.y = 2300;
self.addChild(levelsButton);
// Create 3D effect for Levels text
var levelsShadow2 = new Text2('LEVELS', {
size: 80,
fill: 0x000000
});
levelsShadow2.anchor.set(0.5, 0.5);
levelsShadow2.x = 1028;
levelsShadow2.y = 2304;
self.addChild(levelsShadow2);
var levelsShadow1 = new Text2('LEVELS', {
size: 80,
fill: 0x444444
});
levelsShadow1.anchor.set(0.5, 0.5);
levelsShadow1.x = 1026;
levelsShadow1.y = 2302;
self.addChild(levelsShadow1);
var levelsText = new Text2('LEVELS', {
size: 80,
fill: 0x00AAFF
});
levelsText.anchor.set(0.5, 0.5);
levelsText.x = 1024;
levelsText.y = 2300;
self.addChild(levelsText);
levelsButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
showLevelsScreen();
};
// How to Play button at bottom right
var howToPlayButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 0.7
});
howToPlayButton.x = 1700;
howToPlayButton.y = 2400;
self.addChild(howToPlayButton);
// Create 3D effect for How to Play text
var howToPlayShadow2 = new Text2('HOW TO PLAY', {
size: 60,
fill: 0x000000
});
howToPlayShadow2.anchor.set(0.5, 0.5);
howToPlayShadow2.x = 1704;
howToPlayShadow2.y = 2404;
self.addChild(howToPlayShadow2);
var howToPlayShadow1 = new Text2('HOW TO PLAY', {
size: 60,
fill: 0x444444
});
howToPlayShadow1.anchor.set(0.5, 0.5);
howToPlayShadow1.x = 1702;
howToPlayShadow1.y = 2402;
self.addChild(howToPlayShadow1);
var howToPlayText = new Text2('HOW TO PLAY', {
size: 60,
fill: 0x44FF44
});
howToPlayText.anchor.set(0.5, 0.5);
howToPlayText.x = 1700;
howToPlayText.y = 2400;
self.addChild(howToPlayText);
howToPlayButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
showHowToPlayScreen();
};
// Feedback button at bottom right under How to Play
var feedbackButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 0.7
});
feedbackButton.x = 1700;
feedbackButton.y = 2550;
self.addChild(feedbackButton);
// Create 3D effect for Feedback text
var feedbackShadow2 = new Text2('FEEDBACK', {
size: 60,
fill: 0x000000
});
feedbackShadow2.anchor.set(0.5, 0.5);
feedbackShadow2.x = 1704;
feedbackShadow2.y = 2554;
self.addChild(feedbackShadow2);
var feedbackShadow1 = new Text2('FEEDBACK', {
size: 60,
fill: 0x444444
});
feedbackShadow1.anchor.set(0.5, 0.5);
feedbackShadow1.x = 1702;
feedbackShadow1.y = 2552;
self.addChild(feedbackShadow1);
var feedbackText = new Text2('FEEDBACK', {
size: 60,
fill: 0xFFAA00
});
feedbackText.anchor.set(0.5, 0.5);
feedbackText.x = 1700;
feedbackText.y = 2550;
self.addChild(feedbackText);
feedbackButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
showFeedbackScreen();
};
// Green smiley ball button above how to play button
var greenButton = LK.getAsset('greenSmileyBall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
greenButton.x = 1600;
greenButton.y = 2200;
self.addChild(greenButton);
greenButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
// Stop the continuous shake effect
stopContinuousShake();
// Flash the screen green to indicate the button was clicked
LK.effects.flashScreen(0x00FF00, 1000);
// Create a visual feedback effect
tween(greenButton, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(greenButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut
});
}
});
};
// Blank button with red smiley ball
var blankButton = LK.getAsset('redSmileyBall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
blankButton.x = 1800;
blankButton.y = 2200;
self.addChild(blankButton);
// Face features removed - now just a plain red ball
blankButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
// Start continuous screen shake effect
startContinuousShake();
showBlankScreen();
};
return self;
});
var StoryModeScreen = Container.expand(function () {
var self = Container.call(this);
// Create animated star background (same as start screen but dimmer)
var stars = [];
for (var i = 0; i < 30; i++) {
var star = self.attachAsset('starBg', {
anchorX: 0.5,
anchorY: 0.5
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.alpha = Math.random() * 0.3 + 0.1; // Dimmer stars
stars.push(star);
// Animate each star with gentle twinkling
tween(star, {
alpha: Math.random() * 0.2 + 0.05
}, {
duration: Math.random() * 3000 + 2000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
// Add the alien character at bottom of screen
var alien = self.attachAsset('alien', {
anchorX: 0.5,
anchorY: 1,
scaleX: 3,
scaleY: 3
});
alien.x = 1024;
alien.y = 2400;
// Add gentle floating animation to alien
tween(alien, {
y: alien.y - 20
}, {
duration: 2000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
// Add larger speech bubble background
var speechBubble = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 1,
scaleX: 6,
scaleY: 2.5,
alpha: 0.9
});
speechBubble.x = 1024;
speechBubble.y = 1200;
self.addChild(speechBubble);
// Add speech text - split into multiple lines for better readability
var speechLines = ["Hello, I've been stuck in this", "universe for a long time.", "My chance of escape depends on you.", "", "If you win this game, I can escape,", "but remember, you only have", "one chance."];
var speechTexts = [];
for (var i = 0; i < speechLines.length; i++) {
if (speechLines[i] === '') {
continue; // Skip empty lines for spacing
}
var speechText = new Text2(speechLines[i], {
size: 60,
fill: 0xFFFFFF
});
speechText.anchor.set(0.5, 0.5);
speechText.x = 1024;
speechText.y = 680 + i * 70; // Position each line with 70px spacing
self.addChild(speechText);
speechTexts.push(speechText);
}
// Add back button (invisible initially, appears after a delay) - positioned at bottom left
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6,
alpha: 0
});
backButton.x = 700;
backButton.y = 2600;
self.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 700;
backText.y = 2600;
backText.alpha = 0;
self.addChild(backText);
// Add Go! button next to back button - positioned at bottom right
var goButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6,
alpha: 0
});
goButton.x = 1348;
goButton.y = 2600;
self.addChild(goButton);
var goText = new Text2('GO!', {
size: 80,
fill: 0x44FF44
});
goText.anchor.set(0.5, 0.5);
goText.x = 1348;
goText.y = 2600;
goText.alpha = 0;
self.addChild(goText);
// Fade in both buttons after 3 seconds
LK.setTimeout(function () {
tween(backButton, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
tween(backText, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
tween(goButton, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
tween(goText, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
}, 3000);
backButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideStoryMode();
};
goButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
// Hide story mode screen completely
self.visible = false;
gameMode = 'story';
startGame();
};
return self;
});
var StorySuccessScreen = Container.expand(function () {
var self = Container.call(this);
// Create animated star background (same as story mode but brighter)
var stars = [];
for (var i = 0; i < 40; i++) {
var star = self.attachAsset('starBg', {
anchorX: 0.5,
anchorY: 0.5
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.alpha = Math.random() * 0.5 + 0.3; // Brighter stars
stars.push(star);
// Animate each star with twinkling effect
tween(star, {
alpha: Math.random() * 0.3 + 0.2
}, {
duration: Math.random() * 2000 + 1000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
// Add the alien character at bottom of screen
var alien = self.attachAsset('alien', {
anchorX: 0.5,
anchorY: 1,
scaleX: 3,
scaleY: 3
});
alien.x = 1024;
alien.y = 2400;
// Add celebratory floating animation to alien
tween(alien, {
y: alien.y - 30
}, {
duration: 1500,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
// Add larger speech bubble background
var speechBubble = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 1,
scaleX: 6,
scaleY: 2.5,
alpha: 0.9
});
speechBubble.x = 1024;
speechBubble.y = 1200;
self.addChild(speechBubble);
// Add speech text in the speech bubble
var speechText = new Text2('Thank you very much, I promise that this help will not go unrewarded.', {
size: 60,
fill: 0xFFFFFF
});
speechText.anchor.set(0.5, 0.5);
speechText.x = 1024;
speechText.y = 850;
self.addChild(speechText);
var speechTexts = [];
speechTexts.push(speechText);
// Add back to menu button at bottom center
var menuButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.7,
alpha: 0
});
menuButton.x = 1024;
menuButton.y = 2600;
self.addChild(menuButton);
var menuText = new Text2('BACK TO MENU', {
size: 80,
fill: 0xFFFFFF
});
menuText.anchor.set(0.5, 0.5);
menuText.x = 1024;
menuText.y = 2600;
menuText.alpha = 0;
self.addChild(menuText);
// Fade in button after 4 seconds
LK.setTimeout(function () {
tween(menuButton, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
tween(menuText, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
}, 4000);
menuButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideStorySuccessScreen();
};
return self;
});
var VerticalRocket = Container.expand(function () {
var self = Container.call(this);
self.gridX = 0;
self.gridY = 0;
self.graphics = null;
self.init = function (gx, gy) {
self.gridX = gx;
self.gridY = gy;
self.graphics = self.attachAsset('verticalRocket', {
anchorX: 0.5,
anchorY: 0.5
});
self.updatePosition();
// Add pulsing glow effect
tween(self.graphics, {
alpha: 0.7
}, {
duration: 800,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
};
self.updatePosition = function () {
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var startY = 400;
var exactX = startX + self.gridX * CELL_SIZE + CELL_SIZE / 2;
var exactY = startY + self.gridY * CELL_SIZE + CELL_SIZE / 2;
self.x = exactX;
self.y = exactY;
};
self.fire = function () {
if (isProcessing) return;
isProcessing = true;
// Play rocket fire sound
LK.getSound('rocketFire').play();
// Create dramatic explosion effect
tween(self.graphics, {
scaleX: 2.5,
scaleY: 2.5,
rotation: Math.PI * 4,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut
});
// Add initial flash effect
tween(self.graphics, {
tint: 0xFF4444
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.graphics, {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeOut
});
}
});
// Destroy all gems in the same column
var gemsDestroyed = 0;
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[self.gridX][y] && !grid[self.gridX][y].isMatched) {
var gem = grid[self.gridX][y];
if (gem !== self) {
// Add score for each destroyed gem
if (gameMode === 'extreme' && gem.gemType === extremeGemType) {
score += 25 * multiplier;
gemsDestroyed++;
} else if (gameMode !== 'extreme') {
score += 25 * multiplier;
gemsDestroyed++;
}
// Create explosion effect for each gem
tween(gem.graphics, {
scaleX: 2.0,
scaleY: 2.0,
rotation: Math.PI * (1 + Math.random()),
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (gem.parent) {
gem.parent.removeChild(gem);
}
}
});
grid[self.gridX][y] = null;
}
}
}
updateScore();
// Remove rocket after firing
LK.setTimeout(function () {
if (self.parent) {
self.parent.removeChild(self);
}
grid[self.gridX][self.gridY] = null;
// Start cascade after destruction
cascadeGems();
}, 500);
};
self.down = function (x, y, obj) {
if (!isProcessing) {
self.fire();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var GRID_SIZE = 8;
var CELL_SIZE = 200;
var GEM_TYPES = 6;
var BOMB_TYPE = 6;
var grid = [];
var selectedGem = null;
var score = 0;
var multiplier = 1;
var isProcessing = false;
var cascadeCount = 0;
var gameTimeLeft = 60;
var gameTimer = null;
var timerTxt = null;
var gameMode = 'normal'; // 'normal', 'extreme', 'story', or 'level'
var currentLevel = 1;
var levelsScreen = null;
var extremeGemType = 0; // Only this gem type scores points in extreme mode
var extremeGemTxt = null;
// Game state management
var gameState = 'menu'; // 'menu', 'playing', 'options'
var startScreen = null;
var optionsMenu = null;
var gameElements = [];
// Time tracking variables
var totalPlayTime = 0; // Total time in seconds
var sessionStartTime = null; // When current session started
var playTimeText = null; // Text display for play time
var isGameActive = false; // Track if player is actively playing
var playTimeUpdateTimer = null; // Timer for updating play time display
// Spaceship for background animation
var spaceship = null;
// UFO for background animation
var ufo = null;
// Score display will be created when game starts
var scoreTxt = null;
var multiplierTxt = null;
// Initialize grid
function initializeGrid() {
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var startY = 400;
// Create background cells
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
var cell = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0,
alpha: 0.95
});
cell.x = startX + x * CELL_SIZE;
cell.y = startY + y * CELL_SIZE;
game.addChild(cell);
}
}
// Initialize grid array
for (var x = 0; x < GRID_SIZE; x++) {
grid[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
grid[x][y] = null;
}
}
// Fill grid with gems
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
createGem(x, y);
}
}
// Add initial rocket at game start
// Place vertical rocket at position (4, 1)
if (grid[4][1]) {
if (grid[4][1].parent) {
grid[4][1].parent.removeChild(grid[4][1]);
}
var verticalRocket = new VerticalRocket();
verticalRocket.init(4, 1);
grid[4][1] = verticalRocket;
game.addChild(verticalRocket);
}
// Remove initial matches
removeInitialMatches();
}
function createGem(x, y) {
var gem = new Gem();
var gemType = Math.floor(Math.random() * GEM_TYPES);
gem.init(gemType, x, y);
grid[x][y] = gem;
game.addChild(gem);
return gem;
}
function removeInitialMatches() {
var hasMatches = true;
var attempts = 0;
while (hasMatches && attempts < 50) {
hasMatches = false;
attempts++;
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x][y] && hasMatchAt(x, y)) {
// Regenerate this gem
var oldGem = grid[x][y];
if (oldGem.parent) {
oldGem.parent.removeChild(oldGem);
}
createGem(x, y);
hasMatches = true;
}
}
}
}
}
function selectGem(gem) {
if (selectedGem === gem) {
// Deselect
selectedGem.setSelected(false);
selectedGem = null;
return;
}
if (selectedGem === null) {
// First selection
selectedGem = gem;
gem.setSelected(true);
} else {
// Second selection - check if adjacent
if (areAdjacent(selectedGem, gem)) {
swapGems(selectedGem, gem);
}
selectedGem.setSelected(false);
selectedGem = null;
}
}
function areAdjacent(gem1, gem2) {
var dx = Math.abs(gem1.gridX - gem2.gridX);
var dy = Math.abs(gem1.gridY - gem2.gridY);
return dx === 1 && dy === 0 || dx === 0 && dy === 1;
}
function swapGems(gem1, gem2) {
if (isProcessing) return;
isProcessing = true;
// Store positions
var tempX = gem1.gridX;
var tempY = gem1.gridY;
// Update grid positions
gem1.gridX = gem2.gridX;
gem1.gridY = gem2.gridY;
gem2.gridX = tempX;
gem2.gridY = tempY;
// Update grid array
grid[gem1.gridX][gem1.gridY] = gem1;
grid[gem2.gridX][gem2.gridY] = gem2;
// Animate swap
var gem1NewX = gem1.x;
var gem1NewY = gem1.y;
gem1.updatePosition();
var gem2NewX = gem2.x;
var gem2NewY = gem2.y;
gem2.updatePosition();
gem1.x = gem2NewX;
gem1.y = gem2NewY;
gem2.x = gem1NewX;
gem2.y = gem1NewY;
tween(gem1, {
x: gem1NewX,
y: gem1NewY
}, {
duration: 300
});
tween(gem2, {
x: gem2NewX,
y: gem2NewY
}, {
duration: 300,
onFinish: function onFinish() {
checkForMatches();
}
});
}
function hasMatchAt(x, y) {
if (!grid[x] || !grid[x][y]) return false;
var gemType = grid[x][y].gemType;
// Check horizontal
var hCount = 1;
// Check left
for (var i = x - 1; i >= 0 && grid[i][y] && grid[i][y].gemType === gemType; i--) {
hCount++;
}
// Check right
for (var i = x + 1; i < GRID_SIZE && grid[i][y] && grid[i][y].gemType === gemType; i++) {
hCount++;
}
if (hCount >= 3) return true;
// Check vertical
var vCount = 1;
// Check up
for (var i = y - 1; i >= 0 && grid[x][i] && grid[x][i].gemType === gemType; i--) {
vCount++;
}
// Check down
for (var i = y + 1; i < GRID_SIZE && grid[x][i] && grid[x][i].gemType === gemType; i++) {
vCount++;
}
return vCount >= 3;
}
function checkForMatches() {
var matches = [];
// Find all matches
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x][y] && hasMatchAt(x, y)) {
matches.push({
x: x,
y: y
});
}
}
}
if (matches.length > 0) {
cascadeCount++;
LK.getSound('match').play();
// Calculate score - in extreme mode, only count matches of the selected gem type
var scoringMatches = matches;
if (gameMode === 'extreme') {
scoringMatches = [];
for (var i = 0; i < matches.length; i++) {
var match = matches[i];
if (grid[match.x] && grid[match.x][match.y] && grid[match.x][match.y].gemType === extremeGemType) {
scoringMatches.push(match);
}
}
}
var points = scoringMatches.length * 10 * multiplier * cascadeCount;
score += points;
updateScore();
// Create bomb if match is 4 or more gems
var shouldCreateBomb = matches.length >= 4;
var bombPosition = null;
if (shouldCreateBomb && matches.length > 0) {
bombPosition = matches[Math.floor(Math.random() * matches.length)];
}
// Create rockets when exactly 5 gems are matched
var shouldCreateRocket = matches.length === 5;
var rocketPosition = null;
var rocketType = null;
if (shouldCreateRocket && matches.length > 0) {
rocketPosition = matches[Math.floor(Math.random() * matches.length)];
rocketType = 'vertical'; // Only create vertical rockets
}
// Mark matched gems for destruction
for (var i = 0; i < matches.length; i++) {
var match = matches[i];
if (grid[match.x][match.y]) {
grid[match.x][match.y].destroy();
grid[match.x][match.y] = null;
}
}
// Create bomb after destruction
if (shouldCreateBomb && bombPosition) {
LK.setTimeout(function () {
if (grid[bombPosition.x][bombPosition.y] === null) {
var bomb = new Gem();
bomb.init(BOMB_TYPE, bombPosition.x, bombPosition.y);
grid[bombPosition.x][bombPosition.y] = bomb;
game.addChild(bomb);
// Animate bomb creation
bomb.graphics.scaleX = 0;
bomb.graphics.scaleY = 0;
tween(bomb.graphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.bounceOut
});
}
}, 200);
}
// Create rocket after destruction
if (shouldCreateRocket && rocketPosition) {
LK.setTimeout(function () {
if (grid[rocketPosition.x][rocketPosition.y] === null) {
var rocket = new VerticalRocket();
rocket.init(rocketPosition.x, rocketPosition.y);
grid[rocketPosition.x][rocketPosition.y] = rocket;
game.addChild(rocket);
// Animate rocket creation
rocket.graphics.scaleX = 0;
rocket.graphics.scaleY = 0;
tween(rocket.graphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 400,
easing: tween.bounceOut
});
}
}, 250);
}
// Wait for destruction animation then cascade
LK.setTimeout(function () {
cascadeGems();
}, 400);
} else {
cascadeCount = 0;
isProcessing = false;
}
}
function cascadeGems() {
var needsCascade = false;
var fallPromises = 0;
// Process each column from left to right
for (var x = 0; x < GRID_SIZE; x++) {
// Compact the column - move all existing gems down to fill gaps
var compactedGems = [];
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x][y] !== null) {
compactedGems.push(grid[x][y]);
grid[x][y] = null;
}
}
// Place compacted gems at the bottom of the column
var startY = GRID_SIZE - compactedGems.length;
for (var i = 0; i < compactedGems.length; i++) {
var gem = compactedGems[i];
var newY = startY + i;
var oldY = gem.gridY;
if (oldY !== newY) {
needsCascade = true;
grid[x][newY] = gem;
// Ensure gem X position is correct before falling to prevent overlap
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var exactX = startX + x * CELL_SIZE + CELL_SIZE / 2;
gem.x = exactX; // Force correct X position immediately
fallPromises++;
gem.fallTo(newY, function () {
fallPromises--;
if (fallPromises === 0) {
// Always fill empty spaces after cascading
fillEmptySpaces();
}
});
} else {
// Gem doesn't need to move - ensure correct position
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var exactX = startX + x * CELL_SIZE + CELL_SIZE / 2;
var exactY = 400 + newY * CELL_SIZE + CELL_SIZE / 2;
gem.x = exactX;
gem.y = exactY;
grid[x][newY] = gem;
}
}
}
// Always ensure empty spaces are filled, regardless of cascade
fillEmptySpaces();
if (needsCascade) {
LK.getSound('cascade').play();
}
}
function fillEmptySpaces() {
var newGemsCreated = 0;
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x][y] === null) {
createGem(x, y);
newGemsCreated++;
// Calculate exact positions to prevent overlapping
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var exactX = startX + x * CELL_SIZE + CELL_SIZE / 2;
var exactY = 400 + y * CELL_SIZE + CELL_SIZE / 2;
// Start gem above the screen and animate it falling
var startY = 400 - newGemsCreated * CELL_SIZE;
grid[x][y].x = exactX; // Force correct X position
grid[x][y].y = startY;
tween(grid[x][y], {
y: exactY
}, {
duration: 500 + newGemsCreated * 50,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Ensure final position is exactly aligned
if (grid[x] && grid[x][y]) {
grid[x][y].x = exactX;
grid[x][y].y = exactY;
}
}
});
}
}
}
// Check for new matches after a delay
LK.setTimeout(function () {
checkForMatches();
}, 600);
}
function getLevelTargetScore(level) {
return level * 1000;
}
function updateScore() {
if (scoreTxt) {
scoreTxt.setText('Score: ' + score);
}
// Update multiplier based on score
var newMultiplier = 1;
if (score >= 2500) newMultiplier = 5;else if (score >= 1000) newMultiplier = 4;else if (score >= 500) newMultiplier = 3;else if (score >= 100) newMultiplier = 2;
if (newMultiplier !== multiplier) {
multiplier = newMultiplier;
if (multiplierTxt) {
multiplierTxt.setText('Multiplier: x' + multiplier);
}
LK.effects.flashScreen(0x00ff00, 500);
}
// Win condition - check if we're in story mode or levels mode
var targetScore = 5000; // Default target score
if (gameMode === 'level') {
targetScore = getLevelTargetScore(currentLevel);
}
if (score >= targetScore) {
if (gameMode === 'story') {
// Clear game timer
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
// Stop all gem processing to prevent further combos
isProcessing = true;
// Explode all gems before showing success screen
explodeAllGems();
// Show story success screen after gem explosions
LK.setTimeout(function () {
showStorySuccessScreen();
}, 2000); // Wait 2 seconds for all explosions to finish
} else if (gameMode === 'level') {
// Clear game timer
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
// Stop all gem processing to prevent further combos
isProcessing = true;
// Explode all gems when level is completed
explodeAllGems();
if (currentLevel < 10) {
// Levels 1-9: Continue to next level after explosions
currentLevel++;
// Wait for explosions then start next level
LK.setTimeout(function () {
startGame();
}, 2500);
} else {
// Level 10: Return to main menu after explosions
LK.setTimeout(function () {
showStartScreen();
}, 2500);
}
} else {
// Normal/extreme mode: Explode all gems then return to main menu
explodeAllGems();
LK.setTimeout(function () {
showStartScreen();
}, 2000);
}
}
}
// Start continuous shake when game opens
startContinuousShake();
// Initialize start screen instead of game
showStartScreen();
game.update = function () {
// Game loop - handle any continuous updates here
};
function showStartScreen() {
gameState = 'menu';
gameMode = 'normal'; // Reset to normal mode when returning to menu
if (startScreen) {
game.removeChild(startScreen);
}
// Clear game timer if it exists
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
// Clear all game elements when returning to menu
for (var i = 0; i < gameElements.length; i++) {
if (gameElements[i].parent) {
gameElements[i].parent.removeChild(gameElements[i]);
}
}
gameElements = [];
// Clear grid elements
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x] && grid[x][y] && grid[x][y].parent) {
grid[x][y].parent.removeChild(grid[x][y]);
}
}
}
// Clear all children from game to ensure clean slate
while (game.children.length > 0) {
game.removeChild(game.children[0]);
}
startScreen = new StartScreen();
game.addChild(startScreen);
updatePlayTimeDisplay();
// Continue play time tracking in main menu
startPlayTimeTracking();
// Start spaceship animation
createSpaceship();
// Start UFO animation
createUfo();
// Start background music for start screen
var musicVolume = storage.musicVolume === undefined ? 1.0 : storage.musicVolume;
if (musicVolume > 0) {
LK.playMusic('background', {
fade: {
start: 0,
end: musicVolume * 0.8,
duration: 2000
}
});
}
}
function startGame() {
gameState = 'playing';
startPlayTimeTracking();
if (startScreen) {
game.removeChild(startScreen);
startScreen = null;
}
if (optionsMenu) {
game.removeChild(optionsMenu);
optionsMenu = null;
}
// Clear any existing game elements
for (var i = 0; i < gameElements.length; i++) {
if (gameElements[i].parent) {
gameElements[i].parent.removeChild(gameElements[i]);
}
}
gameElements = [];
// Reset game state
grid = [];
selectedGem = null;
score = 0;
multiplier = 1;
isProcessing = false;
cascadeCount = 0;
gameTimeLeft = 60;
// Set up extreme mode if selected
if (gameMode === 'extreme') {
extremeGemType = Math.floor(Math.random() * GEM_TYPES);
// Start continuous shake for extreme mode
startContinuousShake();
}
// Clear existing timer
if (gameTimer) {
LK.clearInterval(gameTimer);
}
// Start countdown timer
gameTimer = LK.setInterval(function () {
gameTimeLeft--;
if (timerTxt) {
timerTxt.setText('Time: ' + gameTimeLeft);
}
// Change color when time is running low
if (gameTimeLeft <= 10 && timerTxt) {
timerTxt.fill = 0xFF0000;
} else if (gameTimeLeft <= 30 && timerTxt) {
timerTxt.fill = 0xFF8800;
}
// Game over when time runs out
if (gameTimeLeft <= 0) {
LK.clearInterval(gameTimer);
gameTimer = null;
// Explode all gems when time runs out
explodeAllGems();
// Return to menu after explosions
LK.setTimeout(function () {
showStartScreen();
}, 2000);
}
}, 1000);
// Create animated star background for gameplay
for (var i = 0; i < 50; i++) {
var star = LK.getAsset('starBg', {
anchorX: 0.5,
anchorY: 0.5
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.alpha = Math.random() * 0.8 + 0.2;
game.addChild(star);
gameElements.push(star);
// Animate each star with twinkling effect
tween(star, {
alpha: Math.random() * 0.3 + 0.1
}, {
duration: Math.random() * 2000 + 1000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
// Add gentle floating movement
tween(star, {
y: star.y + (Math.random() * 100 - 50)
}, {
duration: Math.random() * 4000 + 2000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
// Initialize score display
scoreTxt = new Text2('Score: 0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 1);
LK.gui.bottom.addChild(scoreTxt);
gameElements.push(scoreTxt);
multiplierTxt = new Text2('Multiplier: x1', {
size: 70,
fill: 0xFFFF00
});
multiplierTxt.anchor.set(0.5, 1);
multiplierTxt.y = -120;
LK.gui.bottom.addChild(multiplierTxt);
gameElements.push(multiplierTxt);
timerTxt = new Text2('Time: 60', {
size: 100,
fill: 0xFF4444
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
gameElements.push(timerTxt);
// Add extreme mode indicator if in extreme mode
if (gameMode === 'extreme') {
var gemTypeNames = ['Red', 'Blue', 'Green', 'Yellow', 'Purple', 'Orange'];
extremeGemTxt = new Text2('EXTREME MODE - Only ' + gemTypeNames[extremeGemType] + ' gems score!', {
size: 60,
fill: 0xFF4444
});
extremeGemTxt.anchor.set(0.5, 0);
extremeGemTxt.y = 120;
LK.gui.top.addChild(extremeGemTxt);
gameElements.push(extremeGemTxt);
}
// Add level indicator if in levels mode
if (gameMode === 'level') {
var targetScore = getLevelTargetScore(currentLevel);
var levelIndicator = new Text2('LEVEL ' + currentLevel + ' - Target: ' + targetScore, {
size: 80,
fill: 0x00AAFF
});
levelIndicator.anchor.set(0.5, 0);
levelIndicator.y = 120;
LK.gui.top.addChild(levelIndicator);
gameElements.push(levelIndicator);
}
// Create menu button
var menuButton = LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
menuButton.x = 1800;
menuButton.y = 200;
menuButton.alpha = 0.8;
game.addChild(menuButton);
gameElements.push(menuButton);
// Create menu button text
var menuButtonText = new Text2('MENU', {
size: 60,
fill: 0xFFFFFF
});
menuButtonText.anchor.set(0.5, 0.5);
menuButtonText.x = 1800;
menuButtonText.y = 200;
game.addChild(menuButtonText);
gameElements.push(menuButtonText);
// Add click handler for menu button
menuButton.down = function (x, y, obj) {
// Clear game timer
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
// Return to main menu
showStartScreen();
};
// Initialize the game
initializeGrid();
updateScore();
// Apply sound volume settings
updateAllSoundVolumes();
// Start background music with current volume setting
var musicVolume = storage.musicVolume === undefined ? 1.0 : storage.musicVolume;
if (musicVolume > 0) {
LK.playMusic('background', {
fade: {
start: 0,
end: musicVolume * 0.9,
duration: 1000
}
});
}
// Continue spaceship animation during gameplay
if (!spaceship || !spaceship.parent) {
createSpaceship();
}
// Continue UFO animation during gameplay
if (!ufo || !ufo.parent) {
createUfo();
}
}
function showOptionsMenu() {
gameState = 'options';
if (optionsMenu) {
game.removeChild(optionsMenu);
}
optionsMenu = new OptionsMenu();
game.addChild(optionsMenu);
}
function hideOptionsMenu() {
gameState = 'menu';
if (optionsMenu) {
game.removeChild(optionsMenu);
optionsMenu = null;
}
}
function createSpaceship() {
if (spaceship) {
game.removeChild(spaceship);
}
spaceship = LK.getAsset('spaceship', {
anchorX: 0.5,
anchorY: 0.5
});
// Start from left side, random Y position
spaceship.x = -100;
spaceship.y = Math.random() * 1800 + 400; // Random position between 400 and 2200
spaceship.alpha = 0.3;
game.addChild(spaceship);
// Animate spaceship moving across screen
tween(spaceship, {
x: 2148 // Move to right edge
}, {
duration: Math.random() * 8000 + 12000,
// 12-20 seconds to cross screen
easing: tween.linear,
onFinish: function onFinish() {
// When spaceship reaches right edge, create a new one
LK.setTimeout(function () {
createSpaceship();
}, Math.random() * 5000 + 2000); // Wait 2-7 seconds before next spaceship
}
});
}
function createUfo() {
if (ufo) {
game.removeChild(ufo);
}
ufo = LK.getAsset('ufo', {
anchorX: 0.5,
anchorY: 0.5
});
// Start from right side, random Y position
ufo.x = 2148;
ufo.y = Math.random() * 1200 + 300; // Random position between 300 and 1500
ufo.alpha = 0.4;
game.addChild(ufo);
// Animate UFO moving across screen from right to left
tween(ufo, {
x: -150 // Move to left edge
}, {
duration: Math.random() * 10000 + 15000,
// 15-25 seconds to cross screen
easing: tween.linear,
onFinish: function onFinish() {
// When UFO reaches left edge, create a new one
LK.setTimeout(function () {
createUfo();
}, Math.random() * 8000 + 3000); // Wait 3-11 seconds before next UFO
}
});
// Add subtle bobbing motion to UFO
tween(ufo, {
y: ufo.y + 30
}, {
duration: 2000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
var howToPlayScreen = null;
function showHowToPlayScreen() {
gameState = 'howtoplay';
if (howToPlayScreen) {
game.removeChild(howToPlayScreen);
}
howToPlayScreen = new HowToPlayScreen();
game.addChild(howToPlayScreen);
}
function hideHowToPlayScreen() {
gameState = 'menu';
if (howToPlayScreen) {
game.removeChild(howToPlayScreen);
howToPlayScreen = null;
}
}
var feedbackScreen = null;
function showFeedbackScreen() {
gameState = 'feedback';
if (feedbackScreen) {
game.removeChild(feedbackScreen);
}
feedbackScreen = new FeedbackScreen();
game.addChild(feedbackScreen);
}
function hideFeedbackScreen() {
gameState = 'menu';
if (feedbackScreen) {
game.removeChild(feedbackScreen);
feedbackScreen = null;
}
}
var blankScreen = null;
var isShaking = false;
var shakeTimer = null;
function startContinuousShake() {
if (isShaking) return; // Already shaking
isShaking = true;
function performShake() {
if (!isShaking) return;
var shakeIntensity = 15;
var shakeX = (Math.random() - 0.5) * shakeIntensity;
var shakeY = (Math.random() - 0.5) * shakeIntensity;
tween(game, {
x: shakeX,
y: shakeY
}, {
duration: 80,
easing: tween.easeOut,
onFinish: function onFinish() {
// Continue shaking if still enabled
if (isShaking) {
shakeTimer = LK.setTimeout(performShake, 20);
} else {
// Reset position when shaking stops
tween(game, {
x: 0,
y: 0
}, {
duration: 100,
easing: tween.easeOut
});
}
}
});
}
performShake();
}
function stopContinuousShake() {
isShaking = false;
if (shakeTimer) {
LK.clearTimeout(shakeTimer);
shakeTimer = null;
}
}
function showBlankScreen() {
gameState = 'blank';
if (blankScreen) {
game.removeChild(blankScreen);
}
blankScreen = new BlankScreen();
game.addChild(blankScreen);
}
function hideBlankScreen() {
gameState = 'menu';
if (blankScreen) {
game.removeChild(blankScreen);
blankScreen = null;
}
}
var storyModeScreen = null;
function showStoryMode() {
gameState = 'story';
if (storyModeScreen) {
game.removeChild(storyModeScreen);
}
storyModeScreen = new StoryModeScreen();
game.addChild(storyModeScreen);
}
function hideStoryMode() {
gameState = 'menu';
if (storyModeScreen) {
game.removeChild(storyModeScreen);
storyModeScreen = null;
}
// Show start screen again when returning from story mode
if (startScreen) {
startScreen.visible = true;
}
}
var storySuccessScreen = null;
function showStorySuccessScreen() {
gameState = 'story_success';
// Hide all game elements first
for (var i = 0; i < gameElements.length; i++) {
if (gameElements[i].parent) {
gameElements[i].parent.removeChild(gameElements[i]);
}
}
gameElements = [];
// Clear grid elements
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x] && grid[x][y] && grid[x][y].parent) {
grid[x][y].parent.removeChild(grid[x][y]);
}
}
}
// Clear spaceship and UFO if they exist
if (spaceship && spaceship.parent) {
spaceship.parent.removeChild(spaceship);
spaceship = null;
}
if (ufo && ufo.parent) {
ufo.parent.removeChild(ufo);
ufo = null;
}
// Clear all game children to ensure clean slate
while (game.children.length > 0) {
game.removeChild(game.children[0]);
}
if (storySuccessScreen) {
game.removeChild(storySuccessScreen);
}
storySuccessScreen = new StorySuccessScreen();
game.addChild(storySuccessScreen);
}
function hideStorySuccessScreen() {
gameState = 'menu';
if (storySuccessScreen) {
game.removeChild(storySuccessScreen);
storySuccessScreen = null;
}
// Return to main menu
showStartScreen();
}
function showLevelsScreen() {
gameState = 'levels';
if (levelsScreen) {
game.removeChild(levelsScreen);
}
levelsScreen = new LevelsScreen();
game.addChild(levelsScreen);
}
function hideLevelsScreen() {
gameState = 'menu';
if (levelsScreen) {
game.removeChild(levelsScreen);
levelsScreen = null;
}
}
function updatePlayTimeDisplay() {
if (!playTimeText) return;
var currentTotalTime;
if (isGameActive && sessionStartTime) {
// Show live time including current session
var currentSessionTime = Math.floor((Date.now() - sessionStartTime) / 1000);
currentTotalTime = (storage.totalPlayTime || 0) + currentSessionTime;
} else {
// Show stored total time
currentTotalTime = storage.totalPlayTime || 0;
}
var hours = Math.floor(currentTotalTime / 3600);
var minutes = Math.floor(currentTotalTime % 3600 / 60);
var seconds = currentTotalTime % 60;
var timeString = hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
playTimeText.setText('Play Time: ' + timeString);
}
function startPlayTimeTracking() {
if (!isGameActive) {
isGameActive = true;
sessionStartTime = Date.now();
// Start timer to update play time display every second
if (playTimeUpdateTimer) {
LK.clearInterval(playTimeUpdateTimer);
}
playTimeUpdateTimer = LK.setInterval(function () {
if (isGameActive && sessionStartTime) {
var currentSessionTime = Math.floor((Date.now() - sessionStartTime) / 1000);
var totalCurrentTime = (storage.totalPlayTime || 0) + currentSessionTime;
if (playTimeText) {
var hours = Math.floor(totalCurrentTime / 3600);
var minutes = Math.floor(totalCurrentTime % 3600 / 60);
var seconds = totalCurrentTime % 60;
var timeString = hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
playTimeText.setText('Play Time: ' + timeString);
}
}
}, 1000);
}
}
function stopPlayTimeTracking() {
if (isGameActive && sessionStartTime) {
var sessionDuration = Math.floor((Date.now() - sessionStartTime) / 1000);
var currentTotal = storage.totalPlayTime || 0;
storage.totalPlayTime = currentTotal + sessionDuration;
isGameActive = false;
sessionStartTime = null;
// Stop the update timer
if (playTimeUpdateTimer) {
LK.clearInterval(playTimeUpdateTimer);
playTimeUpdateTimer = null;
}
updatePlayTimeDisplay();
}
}
function explodeAllGems() {
// Stop all gem processing to prevent cascading and score changes
isProcessing = true;
// Explode all gems on the grid with random delays for visual effect
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x] && grid[x][y] && grid[x][y].graphics) {
(function (gem, delay) {
LK.setTimeout(function () {
// Create explosion effect without destroying the gem object
if (gem && gem.graphics) {
// Play explosion sound effect
LK.getSound('explosion').play();
// Create explosion effect with scaling and rotation
tween(gem.graphics, {
scaleX: 2.0,
scaleY: 2.0,
rotation: Math.PI * 2,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut
});
}
}, delay);
})(grid[x][y], Math.random() * 1500); // Random delay up to 1.5 seconds
}
}
}
}
function updateAllSoundVolumes() {
var musicVolume = storage.musicVolume === undefined ? 1.0 : storage.musicVolume;
var soundEffectsVolume = storage.soundEffectsVolume === undefined ? 1.0 : storage.soundEffectsVolume;
// Re-initialize sound assets with new volumes
// Update background music volume if it's playing
if (musicVolume > 0) {
if (gameState === 'playing') {
LK.stopMusic();
LK.playMusic('background', {
fade: {
start: 0,
end: musicVolume * 0.9,
duration: 500
}
});
} else if (gameState === 'menu') {
LK.stopMusic();
LK.playMusic('background', {
fade: {
start: 0,
end: musicVolume * 0.8,
duration: 500
}
});
}
} else {
LK.stopMusic();
}
} /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BlankScreen = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 9,
scaleY: 12
});
background.x = 1024;
background.y = 1366;
background.alpha = 0.95;
self.addChild(background);
var titleText = new Text2('JUST JOKING', {
size: 120,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
self.addChild(titleText);
// Add some content to the blank screen
var contentText = new Text2('Click the green button', {
size: 80,
fill: 0x00FF00
});
contentText.anchor.set(0.5, 0.5);
contentText.x = 1024;
contentText.y = 800;
self.addChild(contentText);
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backButton.x = 1024;
backButton.y = 2300;
self.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2300;
self.addChild(backText);
backButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideBlankScreen();
};
return self;
});
var FeedbackScreen = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 9,
scaleY: 12
});
background.x = 1024;
background.y = 1366;
background.alpha = 0.95;
self.addChild(background);
var titleText = new Text2('FEEDBACK', {
size: 120,
fill: 0xFFAA00
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
self.addChild(titleText);
// Feedback form instructions
var instructions = ['We would love to hear your thoughts!', '', 'What did you think of the game?', '• Was it fun and engaging?', '• Were the controls easy to use?', '• How was the difficulty level?', '• Any suggestions for improvement?', '', 'Thank you for playing MAVUS BABA!', 'Your feedback helps us make better games.', '', 'Contact us at:', 'INSTAGRAM: abdurrahim_scu'];
var yOffset = 650;
for (var i = 0; i < instructions.length; i++) {
var instructionText = new Text2(instructions[i], {
size: instructions[i] === '' ? 30 : instructions[i].startsWith('Contact') || instructions[i].includes('@') ? 50 : instructions[i].startsWith('•') ? 45 : instructions[i] === 'We would love to hear your thoughts!' ? 70 : instructions[i] === 'Thank you for playing MAVUS BABA!' ? 65 : 55,
fill: instructions[i].includes('@') ? 0x44FF44 : instructions[i].startsWith('•') ? 0xFFFF44 : instructions[i] === 'Thank you for playing MAVUS BABA!' ? 0xFFAA00 : 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = yOffset + i * 80;
self.addChild(instructionText);
}
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backButton.x = 1024;
backButton.y = 2300;
self.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2300;
self.addChild(backText);
backButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideFeedbackScreen();
};
return self;
});
var Gem = Container.expand(function () {
var self = Container.call(this);
self.gemType = 0;
self.gridX = 0;
self.gridY = 0;
self.graphics = null;
self.isSelected = false;
self.isMatched = false;
self.isFalling = false;
self.isBomb = false;
self.init = function (type, gx, gy) {
self.gemType = type;
self.gridX = gx;
self.gridY = gy;
var gemTypes = ['gemRed', 'gemBlue', 'gemGreen', 'gemYellow', 'gemPurple', 'gemOrange', 'gemBomb'];
if (type === 6) {
self.isBomb = true;
}
self.graphics = self.attachAsset(gemTypes[type], {
anchorX: 0.5,
anchorY: 0.5
});
self.updatePosition();
};
self.updatePosition = function () {
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var startY = 400;
// Force exact grid alignment to prevent overlapping
var exactX = startX + self.gridX * CELL_SIZE + CELL_SIZE / 2;
var exactY = startY + self.gridY * CELL_SIZE + CELL_SIZE / 2;
self.x = exactX;
self.y = exactY;
};
self.setSelected = function (selected) {
self.isSelected = selected;
if (selected) {
self.graphics.alpha = 0.7;
tween(self.graphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200
});
} else {
self.graphics.alpha = 1.0;
tween(self.graphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
};
self.fallTo = function (newY, callback) {
self.isFalling = true;
self.gridY = newY;
var newPosY = 400 + newY * CELL_SIZE + CELL_SIZE / 2;
// Ensure gem position is properly aligned to grid
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var correctX = startX + self.gridX * CELL_SIZE + CELL_SIZE / 2;
self.x = correctX; // Force correct X position to prevent overlap
tween(self, {
y: newPosY
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
self.isFalling = false;
// Double-check final position alignment
self.x = correctX;
self.y = newPosY;
if (callback) callback();
}
});
};
self.destroy = function () {
self.isMatched = true;
// Play explosion sound effect
LK.getSound('explosion').play();
// Create explosion effect with scaling and rotation
tween(self.graphics, {
scaleX: 1.8,
scaleY: 1.8,
rotation: Math.PI * 2,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
};
self.explodeBomb = function () {
if (!self.isBomb) return;
// Create dramatic bomb explosion effect
tween(self.graphics, {
scaleX: 3,
scaleY: 3,
rotation: Math.PI * 4,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
// Add pulsing effect before explosion
tween(self.graphics, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self.graphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeInOut
});
}
});
// Mark gems in 3x3 area for destruction
var gemsToDestroy = [];
for (var dx = -1; dx <= 1; dx++) {
for (var dy = -1; dy <= 1; dy++) {
var targetX = self.gridX + dx;
var targetY = self.gridY + dy;
if (targetX >= 0 && targetX < GRID_SIZE && targetY >= 0 && targetY < GRID_SIZE) {
if (grid[targetX][targetY] && !grid[targetX][targetY].isMatched) {
gemsToDestroy.push({
x: targetX,
y: targetY
});
}
}
}
}
// Destroy gems with delay and explosion effects
for (var i = 0; i < gemsToDestroy.length; i++) {
(function (destroyData, index) {
LK.setTimeout(function () {
if (grid[destroyData.x][destroyData.y]) {
var gemToExplode = grid[destroyData.x][destroyData.y];
// Play explosion sound for each destroyed gem
LK.getSound('explosion').play();
// Create explosion effect for each gem
tween(gemToExplode.graphics, {
scaleX: 2.0,
scaleY: 2.0,
rotation: Math.PI * (1 + Math.random()),
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (gemToExplode.parent) {
gemToExplode.parent.removeChild(gemToExplode);
}
}
});
grid[destroyData.x][destroyData.y] = null;
}
}, i * 80);
})(gemsToDestroy[i], i);
}
// Add explosion score - in extreme mode, only count gems of the selected type
var scoringGems = gemsToDestroy.length;
if (gameMode === 'extreme') {
scoringGems = 0;
for (var j = 0; j < gemsToDestroy.length; j++) {
var destroyData = gemsToDestroy[j];
if (grid[destroyData.x] && grid[destroyData.x][destroyData.y] && grid[destroyData.x][destroyData.y].gemType === extremeGemType) {
scoringGems++;
}
}
}
var explosionScore = scoringGems * 50 * multiplier;
score += explosionScore;
updateScore();
// Play explosion sound
LK.getSound('explosion').play();
// Wait for all destruction then cascade and fill empty spaces
LK.setTimeout(function () {
cascadeGems();
// Ensure empty spaces are filled after bomb explosion
LK.setTimeout(function () {
fillEmptySpaces();
}, 100);
}, 500);
};
self.down = function (x, y, obj) {
if (!self.isFalling && !isProcessing) {
if (self.isBomb) {
isProcessing = true;
self.explodeBomb();
} else {
selectGem(self);
}
}
};
return self;
});
var HowToPlayScreen = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 9,
scaleY: 12
});
background.x = 1024;
background.y = 1366;
background.alpha = 0.95;
self.addChild(background);
var titleText = new Text2('HOW TO PLAY', {
size: 120,
fill: 0x44FF44
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
self.addChild(titleText);
// Game instructions text
var instructions = ['Match 3 or more gems of the same color', 'to clear them and earn points!', '', '• Tap gems to select them', '• Select adjacent gems to swap', '• Create cascades for bonus points', '• Match 4+ gems to create bombs', '• Bombs destroy 3x3 areas', '• Match exactly 5 gems to create rockets', '• Tap rockets to fire and clear columns', '• Score multiplier increases with points', '• You have 60 seconds to score big!', '', 'EXTREME MODE:', 'Only one gem type scores points!'];
var yOffset = 650;
for (var i = 0; i < instructions.length; i++) {
var instructionText = new Text2(instructions[i], {
size: instructions[i] === '' ? 30 : instructions[i].startsWith('EXTREME') ? 80 : 55,
fill: instructions[i].startsWith('EXTREME') ? 0xFF4444 : instructions[i].startsWith('•') ? 0xFFFF44 : 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = yOffset + i * 90;
self.addChild(instructionText);
}
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backButton.x = 1024;
backButton.y = 2300;
self.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2300;
self.addChild(backText);
backButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideHowToPlayScreen();
};
return self;
});
var LevelsScreen = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 9,
scaleY: 12
});
background.x = 1024;
background.y = 1366;
background.alpha = 0.95;
self.addChild(background);
var titleText = new Text2('SELECT LEVEL', {
size: 120,
fill: 0x00AAFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
self.addChild(titleText);
// Create level buttons 1-10
for (var levelNum = 1; levelNum <= 10; levelNum++) {
(function (level) {
var levelButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.8
});
// Position buttons in a 2x5 grid
var col = (level - 1) % 2;
var row = Math.floor((level - 1) / 2);
levelButton.x = 800 + col * 448;
levelButton.y = 700 + row * 200;
self.addChild(levelButton);
var levelText = new Text2(level.toString(), {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0.5);
levelText.x = levelButton.x;
levelText.y = levelButton.y;
self.addChild(levelText);
levelButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
gameMode = 'level';
currentLevel = level;
hideLevelsScreen();
startGame();
};
})(levelNum);
}
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backButton.x = 1024;
backButton.y = 2300;
self.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2300;
self.addChild(backText);
backButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideLevelsScreen();
};
return self;
});
var OptionsMenu = Container.expand(function () {
var self = Container.call(this);
var background = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 6
});
background.x = 1024;
background.y = 1366;
background.alpha = 0.9;
self.addChild(background);
var titleText = new Text2('Options', {
size: 150,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
self.addChild(titleText);
// Music Volume Toggle Button
var musicVolumeButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 0.6
});
musicVolumeButton.x = 1024;
musicVolumeButton.y = 1000;
self.addChild(musicVolumeButton);
// Get current music volume state
var isMusicOn = storage.musicVolume === undefined ? true : storage.musicVolume > 0;
var musicVolumeText = new Text2(isMusicOn ? 'Music: ON' : 'Music: OFF', {
size: 80,
fill: 0xFFFFFF
});
musicVolumeText.anchor.set(0.5, 0.5);
musicVolumeText.x = 1024;
musicVolumeText.y = 1000;
self.addChild(musicVolumeText);
musicVolumeButton.down = function (x, y, obj) {
// Toggle music volume
var currentVolume = storage.musicVolume === undefined ? 1.0 : storage.musicVolume;
if (currentVolume > 0) {
// Turn music off
storage.musicVolume = 0;
musicVolumeText.setText('Music: OFF');
LK.stopMusic();
} else {
// Turn music on
storage.musicVolume = 1.0;
musicVolumeText.setText('Music: ON');
// Play appropriate music based on game state
if (gameState === 'playing') {
LK.playMusic('background', {
fade: {
start: 0,
end: 0.9,
duration: 500
}
});
} else if (gameState === 'menu' || gameState === 'options') {
LK.playMusic('background', {
fade: {
start: 0,
end: 0.8,
duration: 500
}
});
}
}
};
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6
});
backButton.x = 1024;
backButton.y = 1200;
self.addChild(backButton);
var backText = new Text2('Back', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 1200;
self.addChild(backText);
backButton.down = function (x, y, obj) {
hideOptionsMenu();
};
return self;
});
var StartScreen = Container.expand(function () {
var self = Container.call(this);
// Create animated star background
var stars = [];
for (var i = 0; i < 50; i++) {
var star = self.attachAsset('starBg', {
anchorX: 0.5,
anchorY: 0.5
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.alpha = Math.random() * 0.8 + 0.2;
stars.push(star);
// Animate each star with twinkling effect
tween(star, {
alpha: Math.random() * 0.3 + 0.1
}, {
duration: Math.random() * 2000 + 1000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
// Add gentle floating movement
tween(star, {
y: star.y + (Math.random() * 100 - 50)
}, {
duration: Math.random() * 4000 + 2000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
// Create 3D effect with shadow layers
var titleShadow3 = new Text2('MAVUS BABA', {
size: 200,
fill: 0x000000
});
titleShadow3.anchor.set(0.5, 0.5);
titleShadow3.x = 1032;
titleShadow3.y = 808;
self.addChild(titleShadow3);
var titleShadow2 = new Text2('MAVUS BABA', {
size: 200,
fill: 0x333333
});
titleShadow2.anchor.set(0.5, 0.5);
titleShadow2.x = 1028;
titleShadow2.y = 804;
self.addChild(titleShadow2);
var titleShadow1 = new Text2('MAVUS BABA', {
size: 200,
fill: 0x666666
});
titleShadow1.anchor.set(0.5, 0.5);
titleShadow1.x = 1026;
titleShadow1.y = 802;
self.addChild(titleShadow1);
var titleText = new Text2('MAVUS BABA', {
size: 200,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
self.addChild(titleText);
// Add 3D glow animation
tween(titleText, {
alpha: 0.8
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
alpha: 1.0
}, {
duration: 1500,
easing: tween.easeInOut
});
}
});
var startButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
startButton.x = 1024;
startButton.y = 1200;
self.addChild(startButton);
// Create 3D effect for Start text
var startShadow2 = new Text2('START', {
size: 100,
fill: 0x000000
});
startShadow2.anchor.set(0.5, 0.5);
startShadow2.x = 1028;
startShadow2.y = 1204;
self.addChild(startShadow2);
var startShadow1 = new Text2('START', {
size: 100,
fill: 0x444444
});
startShadow1.anchor.set(0.5, 0.5);
startShadow1.x = 1026;
startShadow1.y = 1202;
self.addChild(startShadow1);
var startText = new Text2('START', {
size: 100,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = 1024;
startText.y = 1200;
self.addChild(startText);
var optionsButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
optionsButton.x = 1024;
optionsButton.y = 1500;
self.addChild(optionsButton);
// Create 3D effect for Options text
var optionsShadow2 = new Text2('OPTIONS', {
size: 100,
fill: 0x000000
});
optionsShadow2.anchor.set(0.5, 0.5);
optionsShadow2.x = 1028;
optionsShadow2.y = 1504;
self.addChild(optionsShadow2);
var optionsShadow1 = new Text2('OPTIONS', {
size: 100,
fill: 0x444444
});
optionsShadow1.anchor.set(0.5, 0.5);
optionsShadow1.x = 1026;
optionsShadow1.y = 1502;
self.addChild(optionsShadow1);
var optionsText = new Text2('OPTIONS', {
size: 100,
fill: 0xFFFFFF
});
optionsText.anchor.set(0.5, 0.5);
optionsText.x = 1024;
optionsText.y = 1500;
self.addChild(optionsText);
// Add "MADE BY MAVUS DONDURMA" text to bottom left corner
var madeByText = new Text2('MADE BY MAVUS DONDURMA', {
size: 60,
fill: 0xCCCCCC
});
madeByText.anchor.set(0, 1);
madeByText.x = 150;
madeByText.y = 2580;
self.addChild(madeByText);
// Add version text under "Made by Mavus Dondurma"
var versionText = new Text2('Version 1.0', {
size: 40,
fill: 0x888888
});
versionText.anchor.set(0, 1);
versionText.x = 150;
versionText.y = 2630;
self.addChild(versionText);
// Add play time display in top right corner
var currentTotalTime = storage.totalPlayTime || 0;
var hours = Math.floor(currentTotalTime / 3600);
var minutes = Math.floor(currentTotalTime % 3600 / 60);
var seconds = currentTotalTime % 60;
var timeString = hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
playTimeText = new Text2('Play Time: ' + timeString, {
size: 60,
fill: 0xAAFFAA
});
playTimeText.anchor.set(1, 0);
playTimeText.x = 1900;
playTimeText.y = 150;
self.addChild(playTimeText);
startButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
startGame();
};
optionsButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
showOptionsMenu();
};
var extremeButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
extremeButton.x = 1024;
extremeButton.y = 1800;
self.addChild(extremeButton);
// Create 3D effect for Extreme Mode text
var extremeShadow2 = new Text2('EXTREME MODE', {
size: 80,
fill: 0x000000
});
extremeShadow2.anchor.set(0.5, 0.5);
extremeShadow2.x = 1028;
extremeShadow2.y = 1804;
self.addChild(extremeShadow2);
var extremeShadow1 = new Text2('EXTREME MODE', {
size: 80,
fill: 0x444444
});
extremeShadow1.anchor.set(0.5, 0.5);
extremeShadow1.x = 1026;
extremeShadow1.y = 1802;
self.addChild(extremeShadow1);
var extremeText = new Text2('EXTREME MODE', {
size: 80,
fill: 0xFF4444
});
extremeText.anchor.set(0.5, 0.5);
extremeText.x = 1024;
extremeText.y = 1800;
self.addChild(extremeText);
extremeButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
gameMode = 'extreme';
startGame();
};
var storyButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
storyButton.x = 1024;
storyButton.y = 2100;
self.addChild(storyButton);
// Create 3D effect for Story Mode text
var storyShadow2 = new Text2('STORY MODE', {
size: 80,
fill: 0x000000
});
storyShadow2.anchor.set(0.5, 0.5);
storyShadow2.x = 1028;
storyShadow2.y = 2104;
self.addChild(storyShadow2);
var storyShadow1 = new Text2('STORY MODE', {
size: 80,
fill: 0x444444
});
storyShadow1.anchor.set(0.5, 0.5);
storyShadow1.x = 1026;
storyShadow1.y = 2102;
self.addChild(storyShadow1);
var storyText = new Text2('STORY MODE', {
size: 80,
fill: 0x44FF44
});
storyText.anchor.set(0.5, 0.5);
storyText.x = 1024;
storyText.y = 2100;
self.addChild(storyText);
storyButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
// Hide all start screen elements
self.visible = false;
showStoryMode();
};
var levelsButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8
});
levelsButton.x = 1024;
levelsButton.y = 2300;
self.addChild(levelsButton);
// Create 3D effect for Levels text
var levelsShadow2 = new Text2('LEVELS', {
size: 80,
fill: 0x000000
});
levelsShadow2.anchor.set(0.5, 0.5);
levelsShadow2.x = 1028;
levelsShadow2.y = 2304;
self.addChild(levelsShadow2);
var levelsShadow1 = new Text2('LEVELS', {
size: 80,
fill: 0x444444
});
levelsShadow1.anchor.set(0.5, 0.5);
levelsShadow1.x = 1026;
levelsShadow1.y = 2302;
self.addChild(levelsShadow1);
var levelsText = new Text2('LEVELS', {
size: 80,
fill: 0x00AAFF
});
levelsText.anchor.set(0.5, 0.5);
levelsText.x = 1024;
levelsText.y = 2300;
self.addChild(levelsText);
levelsButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
showLevelsScreen();
};
// How to Play button at bottom right
var howToPlayButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 0.7
});
howToPlayButton.x = 1700;
howToPlayButton.y = 2400;
self.addChild(howToPlayButton);
// Create 3D effect for How to Play text
var howToPlayShadow2 = new Text2('HOW TO PLAY', {
size: 60,
fill: 0x000000
});
howToPlayShadow2.anchor.set(0.5, 0.5);
howToPlayShadow2.x = 1704;
howToPlayShadow2.y = 2404;
self.addChild(howToPlayShadow2);
var howToPlayShadow1 = new Text2('HOW TO PLAY', {
size: 60,
fill: 0x444444
});
howToPlayShadow1.anchor.set(0.5, 0.5);
howToPlayShadow1.x = 1702;
howToPlayShadow1.y = 2402;
self.addChild(howToPlayShadow1);
var howToPlayText = new Text2('HOW TO PLAY', {
size: 60,
fill: 0x44FF44
});
howToPlayText.anchor.set(0.5, 0.5);
howToPlayText.x = 1700;
howToPlayText.y = 2400;
self.addChild(howToPlayText);
howToPlayButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
showHowToPlayScreen();
};
// Feedback button at bottom right under How to Play
var feedbackButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 0.7
});
feedbackButton.x = 1700;
feedbackButton.y = 2550;
self.addChild(feedbackButton);
// Create 3D effect for Feedback text
var feedbackShadow2 = new Text2('FEEDBACK', {
size: 60,
fill: 0x000000
});
feedbackShadow2.anchor.set(0.5, 0.5);
feedbackShadow2.x = 1704;
feedbackShadow2.y = 2554;
self.addChild(feedbackShadow2);
var feedbackShadow1 = new Text2('FEEDBACK', {
size: 60,
fill: 0x444444
});
feedbackShadow1.anchor.set(0.5, 0.5);
feedbackShadow1.x = 1702;
feedbackShadow1.y = 2552;
self.addChild(feedbackShadow1);
var feedbackText = new Text2('FEEDBACK', {
size: 60,
fill: 0xFFAA00
});
feedbackText.anchor.set(0.5, 0.5);
feedbackText.x = 1700;
feedbackText.y = 2550;
self.addChild(feedbackText);
feedbackButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
showFeedbackScreen();
};
// Green smiley ball button above how to play button
var greenButton = LK.getAsset('greenSmileyBall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
greenButton.x = 1600;
greenButton.y = 2200;
self.addChild(greenButton);
greenButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
// Stop the continuous shake effect
stopContinuousShake();
// Flash the screen green to indicate the button was clicked
LK.effects.flashScreen(0x00FF00, 1000);
// Create a visual feedback effect
tween(greenButton, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(greenButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut
});
}
});
};
// Blank button with red smiley ball
var blankButton = LK.getAsset('redSmileyBall', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
blankButton.x = 1800;
blankButton.y = 2200;
self.addChild(blankButton);
// Face features removed - now just a plain red ball
blankButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
// Start continuous screen shake effect
startContinuousShake();
showBlankScreen();
};
return self;
});
var StoryModeScreen = Container.expand(function () {
var self = Container.call(this);
// Create animated star background (same as start screen but dimmer)
var stars = [];
for (var i = 0; i < 30; i++) {
var star = self.attachAsset('starBg', {
anchorX: 0.5,
anchorY: 0.5
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.alpha = Math.random() * 0.3 + 0.1; // Dimmer stars
stars.push(star);
// Animate each star with gentle twinkling
tween(star, {
alpha: Math.random() * 0.2 + 0.05
}, {
duration: Math.random() * 3000 + 2000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
// Add the alien character at bottom of screen
var alien = self.attachAsset('alien', {
anchorX: 0.5,
anchorY: 1,
scaleX: 3,
scaleY: 3
});
alien.x = 1024;
alien.y = 2400;
// Add gentle floating animation to alien
tween(alien, {
y: alien.y - 20
}, {
duration: 2000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
// Add larger speech bubble background
var speechBubble = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 1,
scaleX: 6,
scaleY: 2.5,
alpha: 0.9
});
speechBubble.x = 1024;
speechBubble.y = 1200;
self.addChild(speechBubble);
// Add speech text - split into multiple lines for better readability
var speechLines = ["Hello, I've been stuck in this", "universe for a long time.", "My chance of escape depends on you.", "", "If you win this game, I can escape,", "but remember, you only have", "one chance."];
var speechTexts = [];
for (var i = 0; i < speechLines.length; i++) {
if (speechLines[i] === '') {
continue; // Skip empty lines for spacing
}
var speechText = new Text2(speechLines[i], {
size: 60,
fill: 0xFFFFFF
});
speechText.anchor.set(0.5, 0.5);
speechText.x = 1024;
speechText.y = 680 + i * 70; // Position each line with 70px spacing
self.addChild(speechText);
speechTexts.push(speechText);
}
// Add back button (invisible initially, appears after a delay) - positioned at bottom left
var backButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6,
alpha: 0
});
backButton.x = 700;
backButton.y = 2600;
self.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 700;
backText.y = 2600;
backText.alpha = 0;
self.addChild(backText);
// Add Go! button next to back button - positioned at bottom right
var goButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 0.6,
alpha: 0
});
goButton.x = 1348;
goButton.y = 2600;
self.addChild(goButton);
var goText = new Text2('GO!', {
size: 80,
fill: 0x44FF44
});
goText.anchor.set(0.5, 0.5);
goText.x = 1348;
goText.y = 2600;
goText.alpha = 0;
self.addChild(goText);
// Fade in both buttons after 3 seconds
LK.setTimeout(function () {
tween(backButton, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
tween(backText, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
tween(goButton, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
tween(goText, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
}, 3000);
backButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideStoryMode();
};
goButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
// Hide story mode screen completely
self.visible = false;
gameMode = 'story';
startGame();
};
return self;
});
var StorySuccessScreen = Container.expand(function () {
var self = Container.call(this);
// Create animated star background (same as story mode but brighter)
var stars = [];
for (var i = 0; i < 40; i++) {
var star = self.attachAsset('starBg', {
anchorX: 0.5,
anchorY: 0.5
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.alpha = Math.random() * 0.5 + 0.3; // Brighter stars
stars.push(star);
// Animate each star with twinkling effect
tween(star, {
alpha: Math.random() * 0.3 + 0.2
}, {
duration: Math.random() * 2000 + 1000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
// Add the alien character at bottom of screen
var alien = self.attachAsset('alien', {
anchorX: 0.5,
anchorY: 1,
scaleX: 3,
scaleY: 3
});
alien.x = 1024;
alien.y = 2400;
// Add celebratory floating animation to alien
tween(alien, {
y: alien.y - 30
}, {
duration: 1500,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
// Add larger speech bubble background
var speechBubble = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 1,
scaleX: 6,
scaleY: 2.5,
alpha: 0.9
});
speechBubble.x = 1024;
speechBubble.y = 1200;
self.addChild(speechBubble);
// Add speech text in the speech bubble
var speechText = new Text2('Thank you very much, I promise that this help will not go unrewarded.', {
size: 60,
fill: 0xFFFFFF
});
speechText.anchor.set(0.5, 0.5);
speechText.x = 1024;
speechText.y = 850;
self.addChild(speechText);
var speechTexts = [];
speechTexts.push(speechText);
// Add back to menu button at bottom center
var menuButton = LK.getAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.7,
alpha: 0
});
menuButton.x = 1024;
menuButton.y = 2600;
self.addChild(menuButton);
var menuText = new Text2('BACK TO MENU', {
size: 80,
fill: 0xFFFFFF
});
menuText.anchor.set(0.5, 0.5);
menuText.x = 1024;
menuText.y = 2600;
menuText.alpha = 0;
self.addChild(menuText);
// Fade in button after 4 seconds
LK.setTimeout(function () {
tween(menuButton, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
tween(menuText, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeOut
});
}, 4000);
menuButton.down = function (x, y, obj) {
LK.getSound('buttonClick').play();
hideStorySuccessScreen();
};
return self;
});
var VerticalRocket = Container.expand(function () {
var self = Container.call(this);
self.gridX = 0;
self.gridY = 0;
self.graphics = null;
self.init = function (gx, gy) {
self.gridX = gx;
self.gridY = gy;
self.graphics = self.attachAsset('verticalRocket', {
anchorX: 0.5,
anchorY: 0.5
});
self.updatePosition();
// Add pulsing glow effect
tween(self.graphics, {
alpha: 0.7
}, {
duration: 800,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
};
self.updatePosition = function () {
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var startY = 400;
var exactX = startX + self.gridX * CELL_SIZE + CELL_SIZE / 2;
var exactY = startY + self.gridY * CELL_SIZE + CELL_SIZE / 2;
self.x = exactX;
self.y = exactY;
};
self.fire = function () {
if (isProcessing) return;
isProcessing = true;
// Play rocket fire sound
LK.getSound('rocketFire').play();
// Create dramatic explosion effect
tween(self.graphics, {
scaleX: 2.5,
scaleY: 2.5,
rotation: Math.PI * 4,
alpha: 0
}, {
duration: 600,
easing: tween.easeOut
});
// Add initial flash effect
tween(self.graphics, {
tint: 0xFF4444
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.graphics, {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeOut
});
}
});
// Destroy all gems in the same column
var gemsDestroyed = 0;
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[self.gridX][y] && !grid[self.gridX][y].isMatched) {
var gem = grid[self.gridX][y];
if (gem !== self) {
// Add score for each destroyed gem
if (gameMode === 'extreme' && gem.gemType === extremeGemType) {
score += 25 * multiplier;
gemsDestroyed++;
} else if (gameMode !== 'extreme') {
score += 25 * multiplier;
gemsDestroyed++;
}
// Create explosion effect for each gem
tween(gem.graphics, {
scaleX: 2.0,
scaleY: 2.0,
rotation: Math.PI * (1 + Math.random()),
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
if (gem.parent) {
gem.parent.removeChild(gem);
}
}
});
grid[self.gridX][y] = null;
}
}
}
updateScore();
// Remove rocket after firing
LK.setTimeout(function () {
if (self.parent) {
self.parent.removeChild(self);
}
grid[self.gridX][self.gridY] = null;
// Start cascade after destruction
cascadeGems();
}, 500);
};
self.down = function (x, y, obj) {
if (!isProcessing) {
self.fire();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
var GRID_SIZE = 8;
var CELL_SIZE = 200;
var GEM_TYPES = 6;
var BOMB_TYPE = 6;
var grid = [];
var selectedGem = null;
var score = 0;
var multiplier = 1;
var isProcessing = false;
var cascadeCount = 0;
var gameTimeLeft = 60;
var gameTimer = null;
var timerTxt = null;
var gameMode = 'normal'; // 'normal', 'extreme', 'story', or 'level'
var currentLevel = 1;
var levelsScreen = null;
var extremeGemType = 0; // Only this gem type scores points in extreme mode
var extremeGemTxt = null;
// Game state management
var gameState = 'menu'; // 'menu', 'playing', 'options'
var startScreen = null;
var optionsMenu = null;
var gameElements = [];
// Time tracking variables
var totalPlayTime = 0; // Total time in seconds
var sessionStartTime = null; // When current session started
var playTimeText = null; // Text display for play time
var isGameActive = false; // Track if player is actively playing
var playTimeUpdateTimer = null; // Timer for updating play time display
// Spaceship for background animation
var spaceship = null;
// UFO for background animation
var ufo = null;
// Score display will be created when game starts
var scoreTxt = null;
var multiplierTxt = null;
// Initialize grid
function initializeGrid() {
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var startY = 400;
// Create background cells
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
var cell = LK.getAsset('gridCell', {
anchorX: 0,
anchorY: 0,
alpha: 0.95
});
cell.x = startX + x * CELL_SIZE;
cell.y = startY + y * CELL_SIZE;
game.addChild(cell);
}
}
// Initialize grid array
for (var x = 0; x < GRID_SIZE; x++) {
grid[x] = [];
for (var y = 0; y < GRID_SIZE; y++) {
grid[x][y] = null;
}
}
// Fill grid with gems
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
createGem(x, y);
}
}
// Add initial rocket at game start
// Place vertical rocket at position (4, 1)
if (grid[4][1]) {
if (grid[4][1].parent) {
grid[4][1].parent.removeChild(grid[4][1]);
}
var verticalRocket = new VerticalRocket();
verticalRocket.init(4, 1);
grid[4][1] = verticalRocket;
game.addChild(verticalRocket);
}
// Remove initial matches
removeInitialMatches();
}
function createGem(x, y) {
var gem = new Gem();
var gemType = Math.floor(Math.random() * GEM_TYPES);
gem.init(gemType, x, y);
grid[x][y] = gem;
game.addChild(gem);
return gem;
}
function removeInitialMatches() {
var hasMatches = true;
var attempts = 0;
while (hasMatches && attempts < 50) {
hasMatches = false;
attempts++;
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x][y] && hasMatchAt(x, y)) {
// Regenerate this gem
var oldGem = grid[x][y];
if (oldGem.parent) {
oldGem.parent.removeChild(oldGem);
}
createGem(x, y);
hasMatches = true;
}
}
}
}
}
function selectGem(gem) {
if (selectedGem === gem) {
// Deselect
selectedGem.setSelected(false);
selectedGem = null;
return;
}
if (selectedGem === null) {
// First selection
selectedGem = gem;
gem.setSelected(true);
} else {
// Second selection - check if adjacent
if (areAdjacent(selectedGem, gem)) {
swapGems(selectedGem, gem);
}
selectedGem.setSelected(false);
selectedGem = null;
}
}
function areAdjacent(gem1, gem2) {
var dx = Math.abs(gem1.gridX - gem2.gridX);
var dy = Math.abs(gem1.gridY - gem2.gridY);
return dx === 1 && dy === 0 || dx === 0 && dy === 1;
}
function swapGems(gem1, gem2) {
if (isProcessing) return;
isProcessing = true;
// Store positions
var tempX = gem1.gridX;
var tempY = gem1.gridY;
// Update grid positions
gem1.gridX = gem2.gridX;
gem1.gridY = gem2.gridY;
gem2.gridX = tempX;
gem2.gridY = tempY;
// Update grid array
grid[gem1.gridX][gem1.gridY] = gem1;
grid[gem2.gridX][gem2.gridY] = gem2;
// Animate swap
var gem1NewX = gem1.x;
var gem1NewY = gem1.y;
gem1.updatePosition();
var gem2NewX = gem2.x;
var gem2NewY = gem2.y;
gem2.updatePosition();
gem1.x = gem2NewX;
gem1.y = gem2NewY;
gem2.x = gem1NewX;
gem2.y = gem1NewY;
tween(gem1, {
x: gem1NewX,
y: gem1NewY
}, {
duration: 300
});
tween(gem2, {
x: gem2NewX,
y: gem2NewY
}, {
duration: 300,
onFinish: function onFinish() {
checkForMatches();
}
});
}
function hasMatchAt(x, y) {
if (!grid[x] || !grid[x][y]) return false;
var gemType = grid[x][y].gemType;
// Check horizontal
var hCount = 1;
// Check left
for (var i = x - 1; i >= 0 && grid[i][y] && grid[i][y].gemType === gemType; i--) {
hCount++;
}
// Check right
for (var i = x + 1; i < GRID_SIZE && grid[i][y] && grid[i][y].gemType === gemType; i++) {
hCount++;
}
if (hCount >= 3) return true;
// Check vertical
var vCount = 1;
// Check up
for (var i = y - 1; i >= 0 && grid[x][i] && grid[x][i].gemType === gemType; i--) {
vCount++;
}
// Check down
for (var i = y + 1; i < GRID_SIZE && grid[x][i] && grid[x][i].gemType === gemType; i++) {
vCount++;
}
return vCount >= 3;
}
function checkForMatches() {
var matches = [];
// Find all matches
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x][y] && hasMatchAt(x, y)) {
matches.push({
x: x,
y: y
});
}
}
}
if (matches.length > 0) {
cascadeCount++;
LK.getSound('match').play();
// Calculate score - in extreme mode, only count matches of the selected gem type
var scoringMatches = matches;
if (gameMode === 'extreme') {
scoringMatches = [];
for (var i = 0; i < matches.length; i++) {
var match = matches[i];
if (grid[match.x] && grid[match.x][match.y] && grid[match.x][match.y].gemType === extremeGemType) {
scoringMatches.push(match);
}
}
}
var points = scoringMatches.length * 10 * multiplier * cascadeCount;
score += points;
updateScore();
// Create bomb if match is 4 or more gems
var shouldCreateBomb = matches.length >= 4;
var bombPosition = null;
if (shouldCreateBomb && matches.length > 0) {
bombPosition = matches[Math.floor(Math.random() * matches.length)];
}
// Create rockets when exactly 5 gems are matched
var shouldCreateRocket = matches.length === 5;
var rocketPosition = null;
var rocketType = null;
if (shouldCreateRocket && matches.length > 0) {
rocketPosition = matches[Math.floor(Math.random() * matches.length)];
rocketType = 'vertical'; // Only create vertical rockets
}
// Mark matched gems for destruction
for (var i = 0; i < matches.length; i++) {
var match = matches[i];
if (grid[match.x][match.y]) {
grid[match.x][match.y].destroy();
grid[match.x][match.y] = null;
}
}
// Create bomb after destruction
if (shouldCreateBomb && bombPosition) {
LK.setTimeout(function () {
if (grid[bombPosition.x][bombPosition.y] === null) {
var bomb = new Gem();
bomb.init(BOMB_TYPE, bombPosition.x, bombPosition.y);
grid[bombPosition.x][bombPosition.y] = bomb;
game.addChild(bomb);
// Animate bomb creation
bomb.graphics.scaleX = 0;
bomb.graphics.scaleY = 0;
tween(bomb.graphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.bounceOut
});
}
}, 200);
}
// Create rocket after destruction
if (shouldCreateRocket && rocketPosition) {
LK.setTimeout(function () {
if (grid[rocketPosition.x][rocketPosition.y] === null) {
var rocket = new VerticalRocket();
rocket.init(rocketPosition.x, rocketPosition.y);
grid[rocketPosition.x][rocketPosition.y] = rocket;
game.addChild(rocket);
// Animate rocket creation
rocket.graphics.scaleX = 0;
rocket.graphics.scaleY = 0;
tween(rocket.graphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 400,
easing: tween.bounceOut
});
}
}, 250);
}
// Wait for destruction animation then cascade
LK.setTimeout(function () {
cascadeGems();
}, 400);
} else {
cascadeCount = 0;
isProcessing = false;
}
}
function cascadeGems() {
var needsCascade = false;
var fallPromises = 0;
// Process each column from left to right
for (var x = 0; x < GRID_SIZE; x++) {
// Compact the column - move all existing gems down to fill gaps
var compactedGems = [];
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x][y] !== null) {
compactedGems.push(grid[x][y]);
grid[x][y] = null;
}
}
// Place compacted gems at the bottom of the column
var startY = GRID_SIZE - compactedGems.length;
for (var i = 0; i < compactedGems.length; i++) {
var gem = compactedGems[i];
var newY = startY + i;
var oldY = gem.gridY;
if (oldY !== newY) {
needsCascade = true;
grid[x][newY] = gem;
// Ensure gem X position is correct before falling to prevent overlap
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var exactX = startX + x * CELL_SIZE + CELL_SIZE / 2;
gem.x = exactX; // Force correct X position immediately
fallPromises++;
gem.fallTo(newY, function () {
fallPromises--;
if (fallPromises === 0) {
// Always fill empty spaces after cascading
fillEmptySpaces();
}
});
} else {
// Gem doesn't need to move - ensure correct position
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var exactX = startX + x * CELL_SIZE + CELL_SIZE / 2;
var exactY = 400 + newY * CELL_SIZE + CELL_SIZE / 2;
gem.x = exactX;
gem.y = exactY;
grid[x][newY] = gem;
}
}
}
// Always ensure empty spaces are filled, regardless of cascade
fillEmptySpaces();
if (needsCascade) {
LK.getSound('cascade').play();
}
}
function fillEmptySpaces() {
var newGemsCreated = 0;
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x][y] === null) {
createGem(x, y);
newGemsCreated++;
// Calculate exact positions to prevent overlapping
var startX = (2048 - GRID_SIZE * CELL_SIZE) / 2;
var exactX = startX + x * CELL_SIZE + CELL_SIZE / 2;
var exactY = 400 + y * CELL_SIZE + CELL_SIZE / 2;
// Start gem above the screen and animate it falling
var startY = 400 - newGemsCreated * CELL_SIZE;
grid[x][y].x = exactX; // Force correct X position
grid[x][y].y = startY;
tween(grid[x][y], {
y: exactY
}, {
duration: 500 + newGemsCreated * 50,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Ensure final position is exactly aligned
if (grid[x] && grid[x][y]) {
grid[x][y].x = exactX;
grid[x][y].y = exactY;
}
}
});
}
}
}
// Check for new matches after a delay
LK.setTimeout(function () {
checkForMatches();
}, 600);
}
function getLevelTargetScore(level) {
return level * 1000;
}
function updateScore() {
if (scoreTxt) {
scoreTxt.setText('Score: ' + score);
}
// Update multiplier based on score
var newMultiplier = 1;
if (score >= 2500) newMultiplier = 5;else if (score >= 1000) newMultiplier = 4;else if (score >= 500) newMultiplier = 3;else if (score >= 100) newMultiplier = 2;
if (newMultiplier !== multiplier) {
multiplier = newMultiplier;
if (multiplierTxt) {
multiplierTxt.setText('Multiplier: x' + multiplier);
}
LK.effects.flashScreen(0x00ff00, 500);
}
// Win condition - check if we're in story mode or levels mode
var targetScore = 5000; // Default target score
if (gameMode === 'level') {
targetScore = getLevelTargetScore(currentLevel);
}
if (score >= targetScore) {
if (gameMode === 'story') {
// Clear game timer
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
// Stop all gem processing to prevent further combos
isProcessing = true;
// Explode all gems before showing success screen
explodeAllGems();
// Show story success screen after gem explosions
LK.setTimeout(function () {
showStorySuccessScreen();
}, 2000); // Wait 2 seconds for all explosions to finish
} else if (gameMode === 'level') {
// Clear game timer
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
// Stop all gem processing to prevent further combos
isProcessing = true;
// Explode all gems when level is completed
explodeAllGems();
if (currentLevel < 10) {
// Levels 1-9: Continue to next level after explosions
currentLevel++;
// Wait for explosions then start next level
LK.setTimeout(function () {
startGame();
}, 2500);
} else {
// Level 10: Return to main menu after explosions
LK.setTimeout(function () {
showStartScreen();
}, 2500);
}
} else {
// Normal/extreme mode: Explode all gems then return to main menu
explodeAllGems();
LK.setTimeout(function () {
showStartScreen();
}, 2000);
}
}
}
// Start continuous shake when game opens
startContinuousShake();
// Initialize start screen instead of game
showStartScreen();
game.update = function () {
// Game loop - handle any continuous updates here
};
function showStartScreen() {
gameState = 'menu';
gameMode = 'normal'; // Reset to normal mode when returning to menu
if (startScreen) {
game.removeChild(startScreen);
}
// Clear game timer if it exists
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
// Clear all game elements when returning to menu
for (var i = 0; i < gameElements.length; i++) {
if (gameElements[i].parent) {
gameElements[i].parent.removeChild(gameElements[i]);
}
}
gameElements = [];
// Clear grid elements
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x] && grid[x][y] && grid[x][y].parent) {
grid[x][y].parent.removeChild(grid[x][y]);
}
}
}
// Clear all children from game to ensure clean slate
while (game.children.length > 0) {
game.removeChild(game.children[0]);
}
startScreen = new StartScreen();
game.addChild(startScreen);
updatePlayTimeDisplay();
// Continue play time tracking in main menu
startPlayTimeTracking();
// Start spaceship animation
createSpaceship();
// Start UFO animation
createUfo();
// Start background music for start screen
var musicVolume = storage.musicVolume === undefined ? 1.0 : storage.musicVolume;
if (musicVolume > 0) {
LK.playMusic('background', {
fade: {
start: 0,
end: musicVolume * 0.8,
duration: 2000
}
});
}
}
function startGame() {
gameState = 'playing';
startPlayTimeTracking();
if (startScreen) {
game.removeChild(startScreen);
startScreen = null;
}
if (optionsMenu) {
game.removeChild(optionsMenu);
optionsMenu = null;
}
// Clear any existing game elements
for (var i = 0; i < gameElements.length; i++) {
if (gameElements[i].parent) {
gameElements[i].parent.removeChild(gameElements[i]);
}
}
gameElements = [];
// Reset game state
grid = [];
selectedGem = null;
score = 0;
multiplier = 1;
isProcessing = false;
cascadeCount = 0;
gameTimeLeft = 60;
// Set up extreme mode if selected
if (gameMode === 'extreme') {
extremeGemType = Math.floor(Math.random() * GEM_TYPES);
// Start continuous shake for extreme mode
startContinuousShake();
}
// Clear existing timer
if (gameTimer) {
LK.clearInterval(gameTimer);
}
// Start countdown timer
gameTimer = LK.setInterval(function () {
gameTimeLeft--;
if (timerTxt) {
timerTxt.setText('Time: ' + gameTimeLeft);
}
// Change color when time is running low
if (gameTimeLeft <= 10 && timerTxt) {
timerTxt.fill = 0xFF0000;
} else if (gameTimeLeft <= 30 && timerTxt) {
timerTxt.fill = 0xFF8800;
}
// Game over when time runs out
if (gameTimeLeft <= 0) {
LK.clearInterval(gameTimer);
gameTimer = null;
// Explode all gems when time runs out
explodeAllGems();
// Return to menu after explosions
LK.setTimeout(function () {
showStartScreen();
}, 2000);
}
}, 1000);
// Create animated star background for gameplay
for (var i = 0; i < 50; i++) {
var star = LK.getAsset('starBg', {
anchorX: 0.5,
anchorY: 0.5
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.alpha = Math.random() * 0.8 + 0.2;
game.addChild(star);
gameElements.push(star);
// Animate each star with twinkling effect
tween(star, {
alpha: Math.random() * 0.3 + 0.1
}, {
duration: Math.random() * 2000 + 1000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
// Add gentle floating movement
tween(star, {
y: star.y + (Math.random() * 100 - 50)
}, {
duration: Math.random() * 4000 + 2000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
// Initialize score display
scoreTxt = new Text2('Score: 0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 1);
LK.gui.bottom.addChild(scoreTxt);
gameElements.push(scoreTxt);
multiplierTxt = new Text2('Multiplier: x1', {
size: 70,
fill: 0xFFFF00
});
multiplierTxt.anchor.set(0.5, 1);
multiplierTxt.y = -120;
LK.gui.bottom.addChild(multiplierTxt);
gameElements.push(multiplierTxt);
timerTxt = new Text2('Time: 60', {
size: 100,
fill: 0xFF4444
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
gameElements.push(timerTxt);
// Add extreme mode indicator if in extreme mode
if (gameMode === 'extreme') {
var gemTypeNames = ['Red', 'Blue', 'Green', 'Yellow', 'Purple', 'Orange'];
extremeGemTxt = new Text2('EXTREME MODE - Only ' + gemTypeNames[extremeGemType] + ' gems score!', {
size: 60,
fill: 0xFF4444
});
extremeGemTxt.anchor.set(0.5, 0);
extremeGemTxt.y = 120;
LK.gui.top.addChild(extremeGemTxt);
gameElements.push(extremeGemTxt);
}
// Add level indicator if in levels mode
if (gameMode === 'level') {
var targetScore = getLevelTargetScore(currentLevel);
var levelIndicator = new Text2('LEVEL ' + currentLevel + ' - Target: ' + targetScore, {
size: 80,
fill: 0x00AAFF
});
levelIndicator.anchor.set(0.5, 0);
levelIndicator.y = 120;
LK.gui.top.addChild(levelIndicator);
gameElements.push(levelIndicator);
}
// Create menu button
var menuButton = LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
menuButton.x = 1800;
menuButton.y = 200;
menuButton.alpha = 0.8;
game.addChild(menuButton);
gameElements.push(menuButton);
// Create menu button text
var menuButtonText = new Text2('MENU', {
size: 60,
fill: 0xFFFFFF
});
menuButtonText.anchor.set(0.5, 0.5);
menuButtonText.x = 1800;
menuButtonText.y = 200;
game.addChild(menuButtonText);
gameElements.push(menuButtonText);
// Add click handler for menu button
menuButton.down = function (x, y, obj) {
// Clear game timer
if (gameTimer) {
LK.clearInterval(gameTimer);
gameTimer = null;
}
// Return to main menu
showStartScreen();
};
// Initialize the game
initializeGrid();
updateScore();
// Apply sound volume settings
updateAllSoundVolumes();
// Start background music with current volume setting
var musicVolume = storage.musicVolume === undefined ? 1.0 : storage.musicVolume;
if (musicVolume > 0) {
LK.playMusic('background', {
fade: {
start: 0,
end: musicVolume * 0.9,
duration: 1000
}
});
}
// Continue spaceship animation during gameplay
if (!spaceship || !spaceship.parent) {
createSpaceship();
}
// Continue UFO animation during gameplay
if (!ufo || !ufo.parent) {
createUfo();
}
}
function showOptionsMenu() {
gameState = 'options';
if (optionsMenu) {
game.removeChild(optionsMenu);
}
optionsMenu = new OptionsMenu();
game.addChild(optionsMenu);
}
function hideOptionsMenu() {
gameState = 'menu';
if (optionsMenu) {
game.removeChild(optionsMenu);
optionsMenu = null;
}
}
function createSpaceship() {
if (spaceship) {
game.removeChild(spaceship);
}
spaceship = LK.getAsset('spaceship', {
anchorX: 0.5,
anchorY: 0.5
});
// Start from left side, random Y position
spaceship.x = -100;
spaceship.y = Math.random() * 1800 + 400; // Random position between 400 and 2200
spaceship.alpha = 0.3;
game.addChild(spaceship);
// Animate spaceship moving across screen
tween(spaceship, {
x: 2148 // Move to right edge
}, {
duration: Math.random() * 8000 + 12000,
// 12-20 seconds to cross screen
easing: tween.linear,
onFinish: function onFinish() {
// When spaceship reaches right edge, create a new one
LK.setTimeout(function () {
createSpaceship();
}, Math.random() * 5000 + 2000); // Wait 2-7 seconds before next spaceship
}
});
}
function createUfo() {
if (ufo) {
game.removeChild(ufo);
}
ufo = LK.getAsset('ufo', {
anchorX: 0.5,
anchorY: 0.5
});
// Start from right side, random Y position
ufo.x = 2148;
ufo.y = Math.random() * 1200 + 300; // Random position between 300 and 1500
ufo.alpha = 0.4;
game.addChild(ufo);
// Animate UFO moving across screen from right to left
tween(ufo, {
x: -150 // Move to left edge
}, {
duration: Math.random() * 10000 + 15000,
// 15-25 seconds to cross screen
easing: tween.linear,
onFinish: function onFinish() {
// When UFO reaches left edge, create a new one
LK.setTimeout(function () {
createUfo();
}, Math.random() * 8000 + 3000); // Wait 3-11 seconds before next UFO
}
});
// Add subtle bobbing motion to UFO
tween(ufo, {
y: ufo.y + 30
}, {
duration: 2000,
easing: tween.easeInOut,
loop: true,
yoyo: true
});
}
var howToPlayScreen = null;
function showHowToPlayScreen() {
gameState = 'howtoplay';
if (howToPlayScreen) {
game.removeChild(howToPlayScreen);
}
howToPlayScreen = new HowToPlayScreen();
game.addChild(howToPlayScreen);
}
function hideHowToPlayScreen() {
gameState = 'menu';
if (howToPlayScreen) {
game.removeChild(howToPlayScreen);
howToPlayScreen = null;
}
}
var feedbackScreen = null;
function showFeedbackScreen() {
gameState = 'feedback';
if (feedbackScreen) {
game.removeChild(feedbackScreen);
}
feedbackScreen = new FeedbackScreen();
game.addChild(feedbackScreen);
}
function hideFeedbackScreen() {
gameState = 'menu';
if (feedbackScreen) {
game.removeChild(feedbackScreen);
feedbackScreen = null;
}
}
var blankScreen = null;
var isShaking = false;
var shakeTimer = null;
function startContinuousShake() {
if (isShaking) return; // Already shaking
isShaking = true;
function performShake() {
if (!isShaking) return;
var shakeIntensity = 15;
var shakeX = (Math.random() - 0.5) * shakeIntensity;
var shakeY = (Math.random() - 0.5) * shakeIntensity;
tween(game, {
x: shakeX,
y: shakeY
}, {
duration: 80,
easing: tween.easeOut,
onFinish: function onFinish() {
// Continue shaking if still enabled
if (isShaking) {
shakeTimer = LK.setTimeout(performShake, 20);
} else {
// Reset position when shaking stops
tween(game, {
x: 0,
y: 0
}, {
duration: 100,
easing: tween.easeOut
});
}
}
});
}
performShake();
}
function stopContinuousShake() {
isShaking = false;
if (shakeTimer) {
LK.clearTimeout(shakeTimer);
shakeTimer = null;
}
}
function showBlankScreen() {
gameState = 'blank';
if (blankScreen) {
game.removeChild(blankScreen);
}
blankScreen = new BlankScreen();
game.addChild(blankScreen);
}
function hideBlankScreen() {
gameState = 'menu';
if (blankScreen) {
game.removeChild(blankScreen);
blankScreen = null;
}
}
var storyModeScreen = null;
function showStoryMode() {
gameState = 'story';
if (storyModeScreen) {
game.removeChild(storyModeScreen);
}
storyModeScreen = new StoryModeScreen();
game.addChild(storyModeScreen);
}
function hideStoryMode() {
gameState = 'menu';
if (storyModeScreen) {
game.removeChild(storyModeScreen);
storyModeScreen = null;
}
// Show start screen again when returning from story mode
if (startScreen) {
startScreen.visible = true;
}
}
var storySuccessScreen = null;
function showStorySuccessScreen() {
gameState = 'story_success';
// Hide all game elements first
for (var i = 0; i < gameElements.length; i++) {
if (gameElements[i].parent) {
gameElements[i].parent.removeChild(gameElements[i]);
}
}
gameElements = [];
// Clear grid elements
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x] && grid[x][y] && grid[x][y].parent) {
grid[x][y].parent.removeChild(grid[x][y]);
}
}
}
// Clear spaceship and UFO if they exist
if (spaceship && spaceship.parent) {
spaceship.parent.removeChild(spaceship);
spaceship = null;
}
if (ufo && ufo.parent) {
ufo.parent.removeChild(ufo);
ufo = null;
}
// Clear all game children to ensure clean slate
while (game.children.length > 0) {
game.removeChild(game.children[0]);
}
if (storySuccessScreen) {
game.removeChild(storySuccessScreen);
}
storySuccessScreen = new StorySuccessScreen();
game.addChild(storySuccessScreen);
}
function hideStorySuccessScreen() {
gameState = 'menu';
if (storySuccessScreen) {
game.removeChild(storySuccessScreen);
storySuccessScreen = null;
}
// Return to main menu
showStartScreen();
}
function showLevelsScreen() {
gameState = 'levels';
if (levelsScreen) {
game.removeChild(levelsScreen);
}
levelsScreen = new LevelsScreen();
game.addChild(levelsScreen);
}
function hideLevelsScreen() {
gameState = 'menu';
if (levelsScreen) {
game.removeChild(levelsScreen);
levelsScreen = null;
}
}
function updatePlayTimeDisplay() {
if (!playTimeText) return;
var currentTotalTime;
if (isGameActive && sessionStartTime) {
// Show live time including current session
var currentSessionTime = Math.floor((Date.now() - sessionStartTime) / 1000);
currentTotalTime = (storage.totalPlayTime || 0) + currentSessionTime;
} else {
// Show stored total time
currentTotalTime = storage.totalPlayTime || 0;
}
var hours = Math.floor(currentTotalTime / 3600);
var minutes = Math.floor(currentTotalTime % 3600 / 60);
var seconds = currentTotalTime % 60;
var timeString = hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
playTimeText.setText('Play Time: ' + timeString);
}
function startPlayTimeTracking() {
if (!isGameActive) {
isGameActive = true;
sessionStartTime = Date.now();
// Start timer to update play time display every second
if (playTimeUpdateTimer) {
LK.clearInterval(playTimeUpdateTimer);
}
playTimeUpdateTimer = LK.setInterval(function () {
if (isGameActive && sessionStartTime) {
var currentSessionTime = Math.floor((Date.now() - sessionStartTime) / 1000);
var totalCurrentTime = (storage.totalPlayTime || 0) + currentSessionTime;
if (playTimeText) {
var hours = Math.floor(totalCurrentTime / 3600);
var minutes = Math.floor(totalCurrentTime % 3600 / 60);
var seconds = totalCurrentTime % 60;
var timeString = hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0') + ':' + seconds.toString().padStart(2, '0');
playTimeText.setText('Play Time: ' + timeString);
}
}
}, 1000);
}
}
function stopPlayTimeTracking() {
if (isGameActive && sessionStartTime) {
var sessionDuration = Math.floor((Date.now() - sessionStartTime) / 1000);
var currentTotal = storage.totalPlayTime || 0;
storage.totalPlayTime = currentTotal + sessionDuration;
isGameActive = false;
sessionStartTime = null;
// Stop the update timer
if (playTimeUpdateTimer) {
LK.clearInterval(playTimeUpdateTimer);
playTimeUpdateTimer = null;
}
updatePlayTimeDisplay();
}
}
function explodeAllGems() {
// Stop all gem processing to prevent cascading and score changes
isProcessing = true;
// Explode all gems on the grid with random delays for visual effect
for (var x = 0; x < GRID_SIZE; x++) {
for (var y = 0; y < GRID_SIZE; y++) {
if (grid[x] && grid[x][y] && grid[x][y].graphics) {
(function (gem, delay) {
LK.setTimeout(function () {
// Create explosion effect without destroying the gem object
if (gem && gem.graphics) {
// Play explosion sound effect
LK.getSound('explosion').play();
// Create explosion effect with scaling and rotation
tween(gem.graphics, {
scaleX: 2.0,
scaleY: 2.0,
rotation: Math.PI * 2,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut
});
}
}, delay);
})(grid[x][y], Math.random() * 1500); // Random delay up to 1.5 seconds
}
}
}
}
function updateAllSoundVolumes() {
var musicVolume = storage.musicVolume === undefined ? 1.0 : storage.musicVolume;
var soundEffectsVolume = storage.soundEffectsVolume === undefined ? 1.0 : storage.soundEffectsVolume;
// Re-initialize sound assets with new volumes
// Update background music volume if it's playing
if (musicVolume > 0) {
if (gameState === 'playing') {
LK.stopMusic();
LK.playMusic('background', {
fade: {
start: 0,
end: musicVolume * 0.9,
duration: 500
}
});
} else if (gameState === 'menu') {
LK.stopMusic();
LK.playMusic('background', {
fade: {
start: 0,
end: musicVolume * 0.8,
duration: 500
}
});
}
} else {
LK.stopMusic();
}
}
red smiley ball. In-Game asset. 2d. High contrast. No shadows
Blue smiley ball. In-Game asset. 2d. High contrast. No shadows
Green smiley face. In-Game asset. 2d. High contrast. No shadows
Orange smiley ball. In-Game asset. 2d. High contrast. No shadows
Purple smiley ball. In-Game asset. 2d. High contrast. No shadows
Yellow smiley ball. In-Game asset. 2d. High contrast. No shadows
Bomba. In-Game asset. 2d. High contrast. No shadows
Star. In-Game asset. 2d. High contrast. No shadows
Spaceship. In-Game asset. 2d. High contrast. No shadows
A UFO with aliens inside. In-Game asset. 2d. High contrast. No shadows
Red smiley face. In-Game asset. 2d. High contrast. No shadows
Green smiley ball. In-Game asset. 2d. High contrast. No shadows
Male Cyber Character. In-Game asset. 2d. High contrast. No shadows
Vertical rocket. In-Game asset. 2d. High contrast. No shadows