User prompt
agrega una nueva pelotita la de color negro esta solo aparece cuando la pelotita roja toca a otra roja y añade mas dificultad dependiendo del nivel
User prompt
pierdes cuando apenas tiras una pelotita solo debes de perder cuando se detecte una pelotita fuera del cubo por 3 segundos
User prompt
pierdes cuando apenas tiras una pelotita
User prompt
ahora el nivel terminara cuando el cuadrado se llene de pelotitas y no puedan caber mas y añade mas dificultad dependiendo del nivel
User prompt
quita el temporizador y las pelotitas que te tocara ya no deben de ser aleatoreas deben de aparecer dependiendo de las pelotitas que hay en pantalla para hacer que el jugador se le dificulte
User prompt
ponle fisicas ligeramente exageradas para evitar que las pelotitas se apilen
User prompt
el indicador de las pelotitas deben de mostrarlas pequeñas para evitar que tape toda la pantalla
User prompt
ahora cuando tires pelotitas te aparecera una pelotita diferente un poco aleatoreo y arriba ala derecha te indicara la siguiente pelotita que te tocara y en medio de la parte de arriba te indicara la pelotita que tienes
User prompt
la pelotita azul debe de ser mas grande y la pelotita morada debe de ser el doble de grande y agrega una nueva pelotita la roja esta aparece cuando la pelotita morada toca otra morada
User prompt
el selector de nivel no desaparece al seleccionar un nivel
User prompt
te falto el menu principal y la lista de niveles
User prompt
te falto el menu principal
Code edit (1 edits merged)
Please save this source code
User prompt
Color Merge Drop
Initial prompt
creemos un jurgo simple y el juego trata de dejar caer pelotitas de colores cada pelota tendra un color diferente y al tocar una pelotita del mismo color se transformara en otra ligeramente mas grande un ejemplo cuando la pelotita verde toca a otra verde las dos se eliminan y crean una pelotita azul un poco mas grande y la pelotita azul al tocar otra azul se volvera uno morado y sera un poco mas grande y la pelotita morada al tocar a otra morada simplemente desaparecera y marcara puntaje en total hay 3 pelotitas y todas tendran gravedad y colisiones y las pelotias donde se dejaran caer seria desde arriba y para que no se salgan de la camara abra una especie de cuadrado que evitara que se salgan y antes de empezar el juego entraras al menu principal en medio estara un boton sin nombre solo color que te dejara entrar ala lista de niveles cada nivel se desbloqueara por cada nivel jugado y en el gameplay estara un contador de puntaje y el tiempo
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Ball = Container.expand(function (color, size) {
var self = Container.call(this);
self.ballColor = color;
self.ballSize = size;
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 1.2 + gameLevel * 0.2; // Faster gravity per level
self.bounce = Math.max(0.1, 0.3 - gameLevel * 0.02); // Much less bounce per level
self.friction = Math.max(0.85, 0.95 - gameLevel * 0.01); // Less friction per level
self.merged = false;
var ballAsset;
if (color === 'green') {
ballAsset = self.attachAsset('greenBall', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale ball size based on level for increased difficulty
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 70 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier * 1.4;
ballAsset.scale.y = levelSizeMultiplier * 1.4;
} else if (color === 'blue') {
ballAsset = self.attachAsset('blueBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 90 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
} else if (color === 'purple') {
ballAsset = self.attachAsset('purpleBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 160 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
} else if (color === 'red') {
ballAsset = self.attachAsset('redBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 320 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
} else if (color === 'black') {
ballAsset = self.attachAsset('blackBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 640 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
} else if (color === 'white') {
ballAsset = self.attachAsset('whiteBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 45 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier * 1.5;
ballAsset.scale.y = levelSizeMultiplier * 1.5;
} else if (color === 'orange') {
ballAsset = self.attachAsset('orangeBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 600 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
} else if (color === 'golden') {
ballAsset = self.attachAsset('goldenBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 640 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
ballAsset.tint = 0xffd700; // Golden color
}
self.update = function () {
if (self.merged) return;
// Apply gravity
self.velocityY += self.gravity;
// Apply friction
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// Update position
self.x += self.velocityX;
self.y += self.velocityY;
// Floor collision
if (self.y + self.radius > gameFloor) {
self.y = gameFloor - self.radius;
self.velocityY *= -self.bounce;
if (Math.abs(self.velocityY) < 1) {
self.velocityY = 0;
}
}
// Wall collisions
if (self.x - self.radius < gameLeftWall + 20) {
self.x = gameLeftWall + 20 + self.radius;
self.velocityX *= -self.bounce;
}
if (self.x + self.radius > gameRightWall - 20) {
self.x = gameRightWall - 20 - self.radius;
self.velocityX *= -self.bounce;
}
// Check for merges with other balls
for (var i = 0; i < balls.length; i++) {
var otherBall = balls[i];
if (otherBall === self || otherBall.merged) continue;
var dx = self.x - otherBall.x;
var dy = self.y - otherBall.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.radius + otherBall.radius) {
// Collision detected
if (self.ballColor === otherBall.ballColor && !self.merging && !otherBall.merging) {
// Same color - merge
self.merging = true;
otherBall.merging = true;
mergeBalls(self, otherBall);
} else {
// Different colors - bounce with reduced physics
var angle = Math.atan2(dy, dx);
var sin = Math.sin(angle);
var cos = Math.cos(angle);
var vx1 = self.velocityX * cos + self.velocityY * sin;
var vy1 = self.velocityY * cos - self.velocityX * sin;
var vx2 = otherBall.velocityX * cos + otherBall.velocityY * sin;
var vy2 = otherBall.velocityY * cos - otherBall.velocityX * sin;
var finalVx1 = vx2 * 0.6; // Reduced bounce
var finalVx2 = vx1 * 0.6; // Reduced bounce
self.velocityX = finalVx1 * cos - vy1 * sin;
self.velocityY = vy1 * cos + finalVx1 * sin;
otherBall.velocityX = finalVx2 * cos - vy2 * sin;
otherBall.velocityY = vy2 * cos + finalVx2 * sin;
// Separate balls with gentler force
var overlap = self.radius + otherBall.radius - distance;
var separationForce = 0.6; // Gentler separation
var moveX = overlap * separationForce * cos;
var moveY = overlap * separationForce * sin;
self.x += moveX;
self.y += moveY;
otherBall.x -= moveX;
otherBall.y -= moveY;
// Add minimal repulsion to prevent stacking
var repulsionForce = 0.1;
self.velocityX += repulsionForce * cos;
self.velocityY += repulsionForce * sin;
otherBall.velocityX -= repulsionForce * cos;
otherBall.velocityY -= repulsionForce * sin;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu', 'levelSelect', 'playing'
var gamePaused = false;
var balls = [];
var gameLevel = storage.currentLevel || 1;
var maxUnlockedLevel = storage.maxUnlockedLevel || 1;
var gameScore = 0;
var targetScore = gameLevel * 100;
var dropCooldown = 0;
var gameOverThreshold = 300; // Y position where game over occurs
var levelCompleted = false;
var ballsOutsideArea = []; // Track balls outside play area
var gameOverTimer = 0; // Timer for game over condition
var gameOverDelay = Math.max(60, 180 - gameLevel * 10); // Shorter delay per level (minimum 1 second)
var gameWon = false; // Track if game was won
var starRating = 0; // Star rating based on performance
var gameLeftWall = 324;
var gameRightWall = 1724;
var gameFloor = 2500;
var ballColors = ['white', 'green', 'blue', 'purple', 'red'];
var currentBallColor = ballColors[Math.floor(Math.random() * ballColors.length)];
var nextBallColor = ballColors[Math.floor(Math.random() * ballColors.length)];
// UI containers
var menuContainer = new Container();
var levelSelectContainer = new Container();
var gameContainer = new Container();
var uiContainer = new Container();
var scoreScreenContainer = new Container();
// Add containers to game
game.addChild(menuContainer);
game.addChild(levelSelectContainer);
game.addChild(gameContainer);
game.addChild(uiContainer);
game.addChild(scoreScreenContainer);
// Hide level select and game containers initially
levelSelectContainer.visible = false;
gameContainer.visible = false;
scoreScreenContainer.visible = false;
// Create main menu
// Add menu background
var menuBackground = menuContainer.addChild(LK.getAsset('menuBackground', {
anchorX: 0,
anchorY: 0
}));
menuBackground.x = 0;
menuBackground.y = 0;
var menuButton = menuContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
menuButton.x = 1024;
menuButton.y = 1366;
var menuTitle = new Text2('Color Merge Drop', {
size: 120,
fill: 0xFFFFFF
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 1024;
menuTitle.y = 800;
menuContainer.addChild(menuTitle);
var menuInstruction = new Text2('Tap to Start', {
size: 60,
fill: 0xAAAAAA
});
menuInstruction.anchor.set(0.5, 0.5);
menuInstruction.x = 1024;
menuInstruction.y = 1600;
menuContainer.addChild(menuInstruction);
// Create game boundaries (moved to game container)
// Add gameplay background
var gameplayBackground = gameContainer.addChild(LK.getAsset('gameplayBackground', {
anchorX: 0,
anchorY: 0
}));
gameplayBackground.x = 0;
gameplayBackground.y = 0;
var leftWall = gameContainer.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0
}));
leftWall.x = gameLeftWall;
leftWall.y = 200;
var rightWall = gameContainer.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0
}));
rightWall.x = gameRightWall;
rightWall.y = 200;
var floor = gameContainer.addChild(LK.getAsset('floor', {
anchorX: 0,
anchorY: 0
}));
floor.x = gameLeftWall + 20;
floor.y = gameFloor;
// Create drop zone
var dropZone = gameContainer.addChild(LK.getAsset('dropZone', {
anchorX: 0.5,
anchorY: 0
}));
dropZone.x = 1024;
dropZone.y = 100;
// Create level selection screen
// Add level select background
var levelSelectBackground = levelSelectContainer.addChild(LK.getAsset('levelSelectBackground', {
anchorX: 0,
anchorY: 0
}));
levelSelectBackground.x = 0;
levelSelectBackground.y = 0;
var levelSelectTitle = new Text2('Select Level', {
size: 100,
fill: 0xFFFFFF
});
levelSelectTitle.anchor.set(0.5, 0.5);
levelSelectTitle.x = 1024;
levelSelectTitle.y = 400;
levelSelectContainer.addChild(levelSelectTitle);
var levelButtons = [];
var levelsPerRow = 4;
var totalLevels = 9;
var buttonSpacing = 200;
var startX = 1024 - (levelsPerRow - 1) * buttonSpacing / 2;
var startY = 800;
for (var i = 1; i <= totalLevels; i++) {
var row = Math.floor((i - 1) / levelsPerRow);
var col = (i - 1) % levelsPerRow;
var buttonX = startX + col * buttonSpacing;
var buttonY = startY + row * buttonSpacing;
var isUnlocked = i <= maxUnlockedLevel;
var levelButton = levelSelectContainer.addChild(LK.getAsset(isUnlocked ? 'levelButton' : 'lockedButton', {
anchorX: 0.5,
anchorY: 0.5
}));
levelButton.x = buttonX;
levelButton.y = buttonY;
levelButton.levelNumber = i;
levelButton.isUnlocked = isUnlocked;
levelButtons.push(levelButton);
if (isUnlocked) {
var levelLabel = new Text2(i.toString(), {
size: 60,
fill: 0xFFFFFF
});
levelLabel.anchor.set(0.5, 0.5);
levelLabel.x = buttonX;
levelLabel.y = buttonY;
levelSelectContainer.addChild(levelLabel);
}
}
// Back button for level select
var backButton = levelSelectContainer.addChild(LK.getAsset('backButton', {
anchorX: 0.5,
anchorY: 0.5
}));
backButton.x = 200;
backButton.y = 400;
var backLabel = new Text2('Back', {
size: 40,
fill: 0xFFFFFF
});
backLabel.anchor.set(0.5, 0.5);
backLabel.x = 200;
backLabel.y = 400;
levelSelectContainer.addChild(backLabel);
// Create UI (moved to UI container)
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.topLeft.addChild(scoreText);
scoreText.x = 150;
scoreText.y = 50;
var targetText = new Text2('Target: ' + targetScore, {
size: 50,
fill: 0xFFFF00
});
targetText.anchor.set(0, 0);
LK.gui.topLeft.addChild(targetText);
targetText.x = 150;
targetText.y = 120;
var levelText = new Text2('Level ' + gameLevel, {
size: 70,
fill: 0x00FF00
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
levelText.y = 50;
// Current ball display
var currentBallDisplay = LK.getAsset('currentBallDisplay', {
anchorX: 0.5,
anchorY: 0.5
});
currentBallDisplay.x = 0;
currentBallDisplay.y = 150;
LK.gui.top.addChild(currentBallDisplay);
// Next ball display
var nextBallDisplay = LK.getAsset('nextBallDisplay', {
anchorX: 0.5,
anchorY: 0.5
});
nextBallDisplay.x = 0;
nextBallDisplay.y = 150;
LK.gui.topRight.addChild(nextBallDisplay);
nextBallDisplay.x = -100;
// Labels for ball displays
var currentBallLabel = new Text2('Current', {
size: 30,
fill: 0xFFFFFF
});
currentBallLabel.anchor.set(0.5, 0.5);
currentBallLabel.x = 0;
currentBallLabel.y = 240;
LK.gui.top.addChild(currentBallLabel);
var nextBallLabel = new Text2('Next', {
size: 30,
fill: 0xFFFFFF
});
nextBallLabel.anchor.set(0.5, 0.5);
nextBallLabel.x = -100;
nextBallLabel.y = 240;
LK.gui.topRight.addChild(nextBallLabel);
// Create pause/play button
var pauseButton = LK.getAsset('pauseButton', {
anchorX: 0.5,
anchorY: 0.5
});
pauseButton.x = -50;
pauseButton.y = 350;
LK.gui.topRight.addChild(pauseButton);
// Create exit button
var exitButton = LK.getAsset('exitGameButton', {
anchorX: 0.5,
anchorY: 0.5
});
exitButton.x = -50;
exitButton.y = 450;
LK.gui.topRight.addChild(exitButton);
function showScoreScreen() {
gameState = 'scoreScreen';
menuContainer.visible = false;
levelSelectContainer.visible = false;
gameContainer.visible = false;
scoreScreenContainer.visible = true;
// Hide UI elements
scoreText.visible = false;
targetText.visible = false;
levelText.visible = false;
currentBallDisplay.visible = false;
nextBallDisplay.visible = false;
currentBallLabel.visible = false;
nextBallLabel.visible = false;
pauseButton.visible = false;
exitButton.visible = false;
// Clear existing score screen content
scoreScreenContainer.removeChildren();
// Create score screen background
var scoreBackground = scoreScreenContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
scoreBackground.x = 1024;
scoreBackground.y = 1366;
scoreBackground.scale.x = 6;
scoreBackground.scale.y = 8;
scoreBackground.tint = 0x000000;
scoreBackground.alpha = 0.8;
// Title
var titleText = gameWon ? 'Level Complete!' : 'Level Failed!';
var titleColor = gameWon ? 0x00ff00 : 0xff0000;
var title = new Text2(titleText, {
size: 100,
fill: titleColor
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 600;
scoreScreenContainer.addChild(title);
// Score display
var finalScoreText = new Text2('Score: ' + gameScore, {
size: 80,
fill: 0xffffff
});
finalScoreText.anchor.set(0.5, 0.5);
finalScoreText.x = 1024;
finalScoreText.y = 800;
scoreScreenContainer.addChild(finalScoreText);
// Target score display
var targetScoreText = new Text2('Target: ' + targetScore, {
size: 60,
fill: 0xffff00
});
targetScoreText.anchor.set(0.5, 0.5);
targetScoreText.x = 1024;
targetScoreText.y = 900;
scoreScreenContainer.addChild(targetScoreText);
// Star rating display
var currentStarRating = calculateStarRating();
var starY = 1100;
var starSpacing = 120;
var startX = 1024 - starSpacing * 1.5;
// Unlock next level if earned at least one star
if (currentStarRating >= 1 && gameLevel >= maxUnlockedLevel) {
maxUnlockedLevel = gameLevel + 1;
storage.maxUnlockedLevel = maxUnlockedLevel;
}
// Display star rating text
var starRatingText = new Text2('Stars: ' + currentStarRating + '/4', {
size: 70,
fill: 0xffffff
});
starRatingText.anchor.set(0.5, 0.5);
starRatingText.x = 1024;
starRatingText.y = 1000;
scoreScreenContainer.addChild(starRatingText);
// Display the stars
for (var i = 0; i < 4; i++) {
var starAsset;
if (i < currentStarRating) {
if (currentStarRating === 4 && i === 3) {
// Special stellar star for 4th star
starAsset = 'stellarStar';
} else {
// Regular earned star
starAsset = 'earnedStar';
}
} else {
// Unearned star
starAsset = 'unearnedStar';
}
var star = scoreScreenContainer.addChild(LK.getAsset(starAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
star.x = startX + i * starSpacing;
star.y = starY;
star.scale.x = 0.6;
star.scale.y = 0.6;
}
// Add percentage text
var scorePercentage = Math.floor(gameScore / targetScore * 100);
var percentageText = new Text2(scorePercentage + '% of target', {
size: 50,
fill: 0xaaaaaa
});
percentageText.anchor.set(0.5, 0.5);
percentageText.x = 1024;
percentageText.y = 1250;
scoreScreenContainer.addChild(percentageText);
// Continue button
var continueButton = scoreScreenContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
continueButton.x = 1024;
continueButton.y = 1500;
continueButton.scale.x = 1.5;
continueButton.scale.y = 0.8;
continueButton.tint = 0x00aa00;
var continueText = new Text2('Continue', {
size: 60,
fill: 0xffffff
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 1024;
continueText.y = 1500;
scoreScreenContainer.addChild(continueText);
// Store continue button reference for click detection
scoreScreenContainer.continueButton = continueButton;
}
function showMenu() {
gameState = 'menu';
menuContainer.visible = true;
levelSelectContainer.visible = false;
gameContainer.visible = false;
scoreScreenContainer.visible = false;
// Hide UI elements
scoreText.visible = false;
targetText.visible = false;
levelText.visible = false;
currentBallDisplay.visible = false;
nextBallDisplay.visible = false;
currentBallLabel.visible = false;
nextBallLabel.visible = false;
pauseButton.visible = false;
exitButton.visible = false;
// Start background music
LK.playMusic('gameplay');
}
function showLevelSelect() {
gameState = 'levelSelect';
menuContainer.visible = false;
levelSelectContainer.visible = true;
gameContainer.visible = false;
scoreScreenContainer.visible = false;
// Update level buttons based on current unlocked levels
maxUnlockedLevel = storage.maxUnlockedLevel || 1;
for (var i = 0; i < levelButtons.length; i++) {
var button = levelButtons[i];
var isUnlocked = button.levelNumber <= maxUnlockedLevel;
button.isUnlocked = isUnlocked;
// Note: We can't change the asset type dynamically, so we'll handle this in the click handler
}
}
function startGame(level) {
gameState = 'playing';
gameLevel = level;
gameScore = 0;
gameStartTime = Date.now();
targetScore = Math.floor(gameLevel * 100 * Math.pow(1.3, gameLevel - 1)); // Exponential growth
gameOverThreshold = Math.max(150, 300 - gameLevel * 15); // More aggressive threshold
levelCompleted = false;
gameWon = false; // Reset win state
starRating = 0; // Reset star rating
gameOverTimer = 0; // Reset game over timer
ballsOutsideArea = []; // Clear balls outside tracking
// Clear existing balls
for (var i = balls.length - 1; i >= 0; i--) {
balls[i].destroy();
}
balls = [];
// Show game UI
menuContainer.visible = false;
levelSelectContainer.visible = false;
gameContainer.visible = true;
scoreScreenContainer.visible = false;
// Show UI elements
scoreText.visible = true;
targetText.visible = true;
levelText.visible = true;
currentBallDisplay.visible = true;
nextBallDisplay.visible = true;
currentBallLabel.visible = true;
nextBallLabel.visible = true;
pauseButton.visible = true;
exitButton.visible = true;
gamePaused = false;
// Set initial pause button texture
pauseButton.removeChildren();
var pauseAsset = pauseButton.addChild(LK.getAsset('pauseButton', {
anchorX: 0.5,
anchorY: 0.5
}));
// Update UI texts
updateScore();
targetText.setText('Target: ' + targetScore);
levelText.setText('Level ' + gameLevel);
// Initialize ball colors for this level
currentBallColor = getStrategicBallColor();
nextBallColor = getStrategicBallColor();
updateBallDisplays();
}
function createBall(x, y, color) {
var ball = new Ball(color);
ball.x = x;
ball.y = y;
// Add minimal horizontal velocity for less bouncing
ball.velocityX = (Math.random() - 0.5) * 1;
ball.velocityY = Math.random() * 0.5;
balls.push(ball);
gameContainer.addChild(ball);
LK.getSound('drop').play();
return ball;
}
function mergeBalls(ball1, ball2) {
LK.getSound('merge').play();
var mergeX = (ball1.x + ball2.x) / 2;
var mergeY = (ball1.y + ball2.y) / 2;
// Mark balls as merged
ball1.merged = true;
ball2.merged = true;
// Remove from balls array
var index1 = balls.indexOf(ball1);
var index2 = balls.indexOf(ball2);
if (index1 > -1) balls.splice(index1, 1);
if (index2 > -1) balls.splice(index2, 1);
// Remove from display
ball1.destroy();
ball2.destroy();
// Create next color ball or add score
if (ball1.ballColor === 'white') {
var newBall = createBall(mergeX, mergeY, 'green');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// White merge gives 5 points
gameScore += 5;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0xffffff, 300);
} else if (ball1.ballColor === 'green') {
var newBall = createBall(mergeX, mergeY, 'blue');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Green merge gives 10 points
gameScore += 10;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0x00ff00, 300);
} else if (ball1.ballColor === 'blue') {
var newBall = createBall(mergeX, mergeY, 'purple');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Blue merge gives 20 points
gameScore += 20;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0x0066ff, 300);
} else if (ball1.ballColor === 'purple') {
var newBall = createBall(mergeX, mergeY, 'red');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Purple merge gives 40 points
gameScore += 40;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0x9933ff, 300);
} else if (ball1.ballColor === 'red') {
var newBall = createBall(mergeX, mergeY, 'black');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Red merge gives 80 points
gameScore += 80;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0xff0000, 300);
} else if (ball1.ballColor === 'black') {
var newBall = createBall(mergeX, mergeY, 'orange');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Black merge gives 160 points
gameScore += 160;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0x000000, 300);
} else if (ball1.ballColor === 'orange') {
// For level 9, orange balls merge to create golden ball
if (gameLevel === 9) {
var newBall = createBall(mergeX, mergeY, 'golden');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Orange merge in level 9 gives 500 points
gameScore += 500;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0xffd700, 500);
} else {
// Orange balls disappear and give high score
gameScore += 320;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0xff8800, 500);
}
} else if (ball1.ballColor === 'golden') {
// Golden balls disappear and give maximum score
gameScore += 1000;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0xffd700, 800);
}
updateScore();
}
function updateScore() {
scoreText.setText('Score: ' + gameScore);
}
function getStrategicBallColor() {
// Count existing balls by color
var colorCounts = {
white: 0,
green: 0,
blue: 0,
purple: 0,
red: 0,
black: 0,
orange: 0,
golden: 0
};
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (!ball.merged) {
colorCounts[ball.ballColor]++;
}
}
// Determine available colors based on what's on screen
var availableColors = [];
// Good balls (helpful for merging)
if (colorCounts.white > 0) availableColors.push('white');
if (colorCounts.green > 0) availableColors.push('green');
if (colorCounts.blue > 0) availableColors.push('blue');
if (colorCounts.purple > 0) availableColors.push('purple');
// Bad balls (disruptive)
if (colorCounts.white > 0 || colorCounts.green > 0 || colorCounts.blue > 0 || colorCounts.purple > 0) {
// Only add white/green as bad options if there are other colors on screen
if (colorCounts.green > 0 || colorCounts.blue > 0 || colorCounts.purple > 0) {
availableColors.push('white');
}
if (colorCounts.blue > 0 || colorCounts.purple > 0) {
availableColors.push('green');
}
}
// If no balls on screen, start with white
if (availableColors.length === 0) {
availableColors = ['white'];
}
// Weight the selection based on level difficulty
var difficultyFactor = Math.min(0.7, gameLevel * 0.05); // Increase bad ball probability with level
// Separate good and bad options
var goodColors = [];
var badColors = [];
for (var i = 0; i < availableColors.length; i++) {
var color = availableColors[i];
if (colorCounts[color] > 0) {
goodColors.push(color); // Colors that can merge
} else {
badColors.push(color); // Colors that will disrupt
}
}
// Choose based on difficulty and randomness
if (Math.random() < difficultyFactor && badColors.length > 0) {
// Give a bad/disruptive color
return badColors[Math.floor(Math.random() * badColors.length)];
} else if (goodColors.length > 0) {
// Give a good/helpful color
return goodColors[Math.floor(Math.random() * goodColors.length)];
} else {
// Fallback to any available color
return availableColors[Math.floor(Math.random() * availableColors.length)];
}
}
function calculateStarRating() {
var scorePercentage = gameScore / targetScore;
if (scorePercentage >= 1.0) {
return 4; // Stellar rating for 100%+
} else if (scorePercentage >= 0.75) {
return 3; // 3 stars for 75%+
} else if (scorePercentage >= 0.5) {
return 2; // 2 stars for 50%+
} else if (scorePercentage >= 0.25) {
return 1; // 1 star for 25%+
} else {
return 0; // No stars for less than 25%
}
}
function updateBallDisplays() {
// Update current ball display
currentBallDisplay.removeChildren();
var currentAsset;
if (currentBallColor === 'white') {
currentAsset = LK.getAsset('whiteBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'green') {
currentAsset = LK.getAsset('greenBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'blue') {
currentAsset = LK.getAsset('blueBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'purple') {
currentAsset = LK.getAsset('purpleBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'red') {
currentAsset = LK.getAsset('redBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'black') {
currentAsset = LK.getAsset('blackBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'orange') {
currentAsset = LK.getAsset('orangeBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'golden') {
currentAsset = LK.getAsset('goldenBall', {
anchorX: 0.5,
anchorY: 0.5
});
currentAsset.tint = 0xffd700;
}
currentAsset.scale.x = 0.3;
currentAsset.scale.y = 0.3;
currentBallDisplay.addChild(currentAsset);
// Update next ball display
nextBallDisplay.removeChildren();
var nextAsset;
if (nextBallColor === 'white') {
nextAsset = LK.getAsset('whiteBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'green') {
nextAsset = LK.getAsset('greenBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'blue') {
nextAsset = LK.getAsset('blueBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'purple') {
nextAsset = LK.getAsset('purpleBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'red') {
nextAsset = LK.getAsset('redBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'black') {
nextAsset = LK.getAsset('blackBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'orange') {
nextAsset = LK.getAsset('orangeBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'golden') {
nextAsset = LK.getAsset('goldenBall', {
anchorX: 0.5,
anchorY: 0.5
});
nextAsset.tint = 0xffd700;
}
nextAsset.scale.x = 0.25;
nextAsset.scale.y = 0.25;
nextBallDisplay.addChild(nextAsset);
}
// Touch/click handler
game.down = function (x, y, obj) {
if (gameState === 'menu') {
// Check if menu button was clicked
var dx = x - menuButton.x;
var dy = y - menuButton.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
LK.getSound('button').play();
showLevelSelect();
}
} else if (gameState === 'levelSelect') {
// Check if back button was clicked
var dx = x - backButton.x;
var dy = y - backButton.y;
if (Math.abs(dx) < 60 && Math.abs(dy) < 40) {
LK.getSound('button').play();
showMenu();
return;
}
// Check if level button was clicked
for (var i = 0; i < levelButtons.length; i++) {
var button = levelButtons[i];
var dx = x - button.x;
var dy = y - button.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 75 && button.isUnlocked) {
LK.getSound('button').play();
startGame(button.levelNumber);
return;
}
}
} else if (gameState === 'scoreScreen') {
// Check if continue button was clicked
var continueButton = scoreScreenContainer.continueButton;
if (continueButton) {
var dx = x - continueButton.x;
var dy = y - continueButton.y;
if (Math.abs(dx) < 150 && Math.abs(dy) < 80) {
LK.getSound('button').play();
showLevelSelect();
}
}
} else if (gameState === 'playing') {
// Check pause button
var pauseButtonGlobal = LK.gui.topRight.toGlobal({
x: -50,
y: 350
});
var pauseButtonLocal = game.toLocal(pauseButtonGlobal);
var dx = x - pauseButtonLocal.x;
var dy = y - pauseButtonLocal.y;
if (Math.abs(dx) < 60 && Math.abs(dy) < 30) {
LK.getSound('button').play();
gamePaused = !gamePaused;
// Switch button texture based on pause state
pauseButton.removeChildren();
var buttonAsset = pauseButton.addChild(LK.getAsset(gamePaused ? 'playButton' : 'pauseButton', {
anchorX: 0.5,
anchorY: 0.5
}));
return;
}
// Check exit button
var exitButtonGlobal = LK.gui.topRight.toGlobal({
x: -50,
y: 450
});
var exitButtonLocal = game.toLocal(exitButtonGlobal);
var dx = x - exitButtonLocal.x;
var dy = y - exitButtonLocal.y;
if (Math.abs(dx) < 60 && Math.abs(dy) < 30) {
LK.getSound('button').play();
showLevelSelect();
return;
}
if (gamePaused || dropCooldown > 0) return;
// Drop ball at x position within drop zone
var dropX = Math.max(gameLeftWall + 60, Math.min(gameRightWall - 60, x));
createBall(dropX, 200, currentBallColor);
// Advance to next ball
currentBallColor = nextBallColor;
nextBallColor = getStrategicBallColor();
updateBallDisplays();
dropCooldown = Math.max(10, 30 - gameLevel * 2); // Faster required drops per level
}
};
game.update = function () {
// Only run game logic when playing and not paused
if (gameState !== 'playing' || gamePaused) return;
// Update cooldown
if (dropCooldown > 0) {
dropCooldown--;
}
// Add random disruption for higher levels
if (gameLevel >= 3 && LK.ticks % (300 - gameLevel * 20) === 0) {
// Random ball velocity disruption
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (!ball.merged && Math.random() < 0.3) {
ball.velocityX += (Math.random() - 0.5) * (gameLevel * 0.5);
ball.velocityY += (Math.random() - 0.5) * (gameLevel * 0.3);
}
}
}
// Clean up merged balls
for (var i = balls.length - 1; i >= 0; i--) {
if (balls[i].merged) {
balls.splice(i, 1);
}
}
// Check for balls that fell off screen
for (var i = balls.length - 1; i >= 0; i--) {
if (balls[i].y > 2732) {
balls[i].destroy();
balls.splice(i, 1);
}
}
// Check for balls outside play area (above threshold)
if (!levelCompleted && !gameWon) {
var ballsCurrentlyOutside = [];
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (ball.y - ball.radius <= gameOverThreshold) {
ballsCurrentlyOutside.push(ball);
}
}
// If there are balls outside, start/continue timer
if (ballsCurrentlyOutside.length > 0) {
gameOverTimer++;
if (gameOverTimer >= gameOverDelay) {
// 3 seconds have passed with balls outside play area
// Check if player has reached target score - if so, it's a win
if (gameScore >= targetScore) {
gameWon = true;
starRating = calculateStarRating();
// Level complete
storage.currentLevel = gameLevel + 1;
showScoreScreen();
} else {
// Game over - didn't reach target score
levelCompleted = true;
showScoreScreen();
}
return;
}
} else {
// No balls outside, reset timer
gameOverTimer = 0;
}
}
};
// Initialize game in menu state
showMenu(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Ball = Container.expand(function (color, size) {
var self = Container.call(this);
self.ballColor = color;
self.ballSize = size;
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 1.2 + gameLevel * 0.2; // Faster gravity per level
self.bounce = Math.max(0.1, 0.3 - gameLevel * 0.02); // Much less bounce per level
self.friction = Math.max(0.85, 0.95 - gameLevel * 0.01); // Less friction per level
self.merged = false;
var ballAsset;
if (color === 'green') {
ballAsset = self.attachAsset('greenBall', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale ball size based on level for increased difficulty
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 70 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier * 1.4;
ballAsset.scale.y = levelSizeMultiplier * 1.4;
} else if (color === 'blue') {
ballAsset = self.attachAsset('blueBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 90 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
} else if (color === 'purple') {
ballAsset = self.attachAsset('purpleBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 160 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
} else if (color === 'red') {
ballAsset = self.attachAsset('redBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 320 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
} else if (color === 'black') {
ballAsset = self.attachAsset('blackBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 640 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
} else if (color === 'white') {
ballAsset = self.attachAsset('whiteBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 45 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier * 1.5;
ballAsset.scale.y = levelSizeMultiplier * 1.5;
} else if (color === 'orange') {
ballAsset = self.attachAsset('orangeBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 600 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
} else if (color === 'golden') {
ballAsset = self.attachAsset('goldenBall', {
anchorX: 0.5,
anchorY: 0.5
});
var levelSizeMultiplier = Math.max(0.7, 1 - gameLevel * 0.03);
self.radius = 640 * levelSizeMultiplier;
ballAsset.scale.x = levelSizeMultiplier;
ballAsset.scale.y = levelSizeMultiplier;
ballAsset.tint = 0xffd700; // Golden color
}
self.update = function () {
if (self.merged) return;
// Apply gravity
self.velocityY += self.gravity;
// Apply friction
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// Update position
self.x += self.velocityX;
self.y += self.velocityY;
// Floor collision
if (self.y + self.radius > gameFloor) {
self.y = gameFloor - self.radius;
self.velocityY *= -self.bounce;
if (Math.abs(self.velocityY) < 1) {
self.velocityY = 0;
}
}
// Wall collisions
if (self.x - self.radius < gameLeftWall + 20) {
self.x = gameLeftWall + 20 + self.radius;
self.velocityX *= -self.bounce;
}
if (self.x + self.radius > gameRightWall - 20) {
self.x = gameRightWall - 20 - self.radius;
self.velocityX *= -self.bounce;
}
// Check for merges with other balls
for (var i = 0; i < balls.length; i++) {
var otherBall = balls[i];
if (otherBall === self || otherBall.merged) continue;
var dx = self.x - otherBall.x;
var dy = self.y - otherBall.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.radius + otherBall.radius) {
// Collision detected
if (self.ballColor === otherBall.ballColor && !self.merging && !otherBall.merging) {
// Same color - merge
self.merging = true;
otherBall.merging = true;
mergeBalls(self, otherBall);
} else {
// Different colors - bounce with reduced physics
var angle = Math.atan2(dy, dx);
var sin = Math.sin(angle);
var cos = Math.cos(angle);
var vx1 = self.velocityX * cos + self.velocityY * sin;
var vy1 = self.velocityY * cos - self.velocityX * sin;
var vx2 = otherBall.velocityX * cos + otherBall.velocityY * sin;
var vy2 = otherBall.velocityY * cos - otherBall.velocityX * sin;
var finalVx1 = vx2 * 0.6; // Reduced bounce
var finalVx2 = vx1 * 0.6; // Reduced bounce
self.velocityX = finalVx1 * cos - vy1 * sin;
self.velocityY = vy1 * cos + finalVx1 * sin;
otherBall.velocityX = finalVx2 * cos - vy2 * sin;
otherBall.velocityY = vy2 * cos + finalVx2 * sin;
// Separate balls with gentler force
var overlap = self.radius + otherBall.radius - distance;
var separationForce = 0.6; // Gentler separation
var moveX = overlap * separationForce * cos;
var moveY = overlap * separationForce * sin;
self.x += moveX;
self.y += moveY;
otherBall.x -= moveX;
otherBall.y -= moveY;
// Add minimal repulsion to prevent stacking
var repulsionForce = 0.1;
self.velocityX += repulsionForce * cos;
self.velocityY += repulsionForce * sin;
otherBall.velocityX -= repulsionForce * cos;
otherBall.velocityY -= repulsionForce * sin;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Game state management
var gameState = 'menu'; // 'menu', 'levelSelect', 'playing'
var gamePaused = false;
var balls = [];
var gameLevel = storage.currentLevel || 1;
var maxUnlockedLevel = storage.maxUnlockedLevel || 1;
var gameScore = 0;
var targetScore = gameLevel * 100;
var dropCooldown = 0;
var gameOverThreshold = 300; // Y position where game over occurs
var levelCompleted = false;
var ballsOutsideArea = []; // Track balls outside play area
var gameOverTimer = 0; // Timer for game over condition
var gameOverDelay = Math.max(60, 180 - gameLevel * 10); // Shorter delay per level (minimum 1 second)
var gameWon = false; // Track if game was won
var starRating = 0; // Star rating based on performance
var gameLeftWall = 324;
var gameRightWall = 1724;
var gameFloor = 2500;
var ballColors = ['white', 'green', 'blue', 'purple', 'red'];
var currentBallColor = ballColors[Math.floor(Math.random() * ballColors.length)];
var nextBallColor = ballColors[Math.floor(Math.random() * ballColors.length)];
// UI containers
var menuContainer = new Container();
var levelSelectContainer = new Container();
var gameContainer = new Container();
var uiContainer = new Container();
var scoreScreenContainer = new Container();
// Add containers to game
game.addChild(menuContainer);
game.addChild(levelSelectContainer);
game.addChild(gameContainer);
game.addChild(uiContainer);
game.addChild(scoreScreenContainer);
// Hide level select and game containers initially
levelSelectContainer.visible = false;
gameContainer.visible = false;
scoreScreenContainer.visible = false;
// Create main menu
// Add menu background
var menuBackground = menuContainer.addChild(LK.getAsset('menuBackground', {
anchorX: 0,
anchorY: 0
}));
menuBackground.x = 0;
menuBackground.y = 0;
var menuButton = menuContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
menuButton.x = 1024;
menuButton.y = 1366;
var menuTitle = new Text2('Color Merge Drop', {
size: 120,
fill: 0xFFFFFF
});
menuTitle.anchor.set(0.5, 0.5);
menuTitle.x = 1024;
menuTitle.y = 800;
menuContainer.addChild(menuTitle);
var menuInstruction = new Text2('Tap to Start', {
size: 60,
fill: 0xAAAAAA
});
menuInstruction.anchor.set(0.5, 0.5);
menuInstruction.x = 1024;
menuInstruction.y = 1600;
menuContainer.addChild(menuInstruction);
// Create game boundaries (moved to game container)
// Add gameplay background
var gameplayBackground = gameContainer.addChild(LK.getAsset('gameplayBackground', {
anchorX: 0,
anchorY: 0
}));
gameplayBackground.x = 0;
gameplayBackground.y = 0;
var leftWall = gameContainer.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0
}));
leftWall.x = gameLeftWall;
leftWall.y = 200;
var rightWall = gameContainer.addChild(LK.getAsset('wall', {
anchorX: 0,
anchorY: 0
}));
rightWall.x = gameRightWall;
rightWall.y = 200;
var floor = gameContainer.addChild(LK.getAsset('floor', {
anchorX: 0,
anchorY: 0
}));
floor.x = gameLeftWall + 20;
floor.y = gameFloor;
// Create drop zone
var dropZone = gameContainer.addChild(LK.getAsset('dropZone', {
anchorX: 0.5,
anchorY: 0
}));
dropZone.x = 1024;
dropZone.y = 100;
// Create level selection screen
// Add level select background
var levelSelectBackground = levelSelectContainer.addChild(LK.getAsset('levelSelectBackground', {
anchorX: 0,
anchorY: 0
}));
levelSelectBackground.x = 0;
levelSelectBackground.y = 0;
var levelSelectTitle = new Text2('Select Level', {
size: 100,
fill: 0xFFFFFF
});
levelSelectTitle.anchor.set(0.5, 0.5);
levelSelectTitle.x = 1024;
levelSelectTitle.y = 400;
levelSelectContainer.addChild(levelSelectTitle);
var levelButtons = [];
var levelsPerRow = 4;
var totalLevels = 9;
var buttonSpacing = 200;
var startX = 1024 - (levelsPerRow - 1) * buttonSpacing / 2;
var startY = 800;
for (var i = 1; i <= totalLevels; i++) {
var row = Math.floor((i - 1) / levelsPerRow);
var col = (i - 1) % levelsPerRow;
var buttonX = startX + col * buttonSpacing;
var buttonY = startY + row * buttonSpacing;
var isUnlocked = i <= maxUnlockedLevel;
var levelButton = levelSelectContainer.addChild(LK.getAsset(isUnlocked ? 'levelButton' : 'lockedButton', {
anchorX: 0.5,
anchorY: 0.5
}));
levelButton.x = buttonX;
levelButton.y = buttonY;
levelButton.levelNumber = i;
levelButton.isUnlocked = isUnlocked;
levelButtons.push(levelButton);
if (isUnlocked) {
var levelLabel = new Text2(i.toString(), {
size: 60,
fill: 0xFFFFFF
});
levelLabel.anchor.set(0.5, 0.5);
levelLabel.x = buttonX;
levelLabel.y = buttonY;
levelSelectContainer.addChild(levelLabel);
}
}
// Back button for level select
var backButton = levelSelectContainer.addChild(LK.getAsset('backButton', {
anchorX: 0.5,
anchorY: 0.5
}));
backButton.x = 200;
backButton.y = 400;
var backLabel = new Text2('Back', {
size: 40,
fill: 0xFFFFFF
});
backLabel.anchor.set(0.5, 0.5);
backLabel.x = 200;
backLabel.y = 400;
levelSelectContainer.addChild(backLabel);
// Create UI (moved to UI container)
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.topLeft.addChild(scoreText);
scoreText.x = 150;
scoreText.y = 50;
var targetText = new Text2('Target: ' + targetScore, {
size: 50,
fill: 0xFFFF00
});
targetText.anchor.set(0, 0);
LK.gui.topLeft.addChild(targetText);
targetText.x = 150;
targetText.y = 120;
var levelText = new Text2('Level ' + gameLevel, {
size: 70,
fill: 0x00FF00
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
levelText.y = 50;
// Current ball display
var currentBallDisplay = LK.getAsset('currentBallDisplay', {
anchorX: 0.5,
anchorY: 0.5
});
currentBallDisplay.x = 0;
currentBallDisplay.y = 150;
LK.gui.top.addChild(currentBallDisplay);
// Next ball display
var nextBallDisplay = LK.getAsset('nextBallDisplay', {
anchorX: 0.5,
anchorY: 0.5
});
nextBallDisplay.x = 0;
nextBallDisplay.y = 150;
LK.gui.topRight.addChild(nextBallDisplay);
nextBallDisplay.x = -100;
// Labels for ball displays
var currentBallLabel = new Text2('Current', {
size: 30,
fill: 0xFFFFFF
});
currentBallLabel.anchor.set(0.5, 0.5);
currentBallLabel.x = 0;
currentBallLabel.y = 240;
LK.gui.top.addChild(currentBallLabel);
var nextBallLabel = new Text2('Next', {
size: 30,
fill: 0xFFFFFF
});
nextBallLabel.anchor.set(0.5, 0.5);
nextBallLabel.x = -100;
nextBallLabel.y = 240;
LK.gui.topRight.addChild(nextBallLabel);
// Create pause/play button
var pauseButton = LK.getAsset('pauseButton', {
anchorX: 0.5,
anchorY: 0.5
});
pauseButton.x = -50;
pauseButton.y = 350;
LK.gui.topRight.addChild(pauseButton);
// Create exit button
var exitButton = LK.getAsset('exitGameButton', {
anchorX: 0.5,
anchorY: 0.5
});
exitButton.x = -50;
exitButton.y = 450;
LK.gui.topRight.addChild(exitButton);
function showScoreScreen() {
gameState = 'scoreScreen';
menuContainer.visible = false;
levelSelectContainer.visible = false;
gameContainer.visible = false;
scoreScreenContainer.visible = true;
// Hide UI elements
scoreText.visible = false;
targetText.visible = false;
levelText.visible = false;
currentBallDisplay.visible = false;
nextBallDisplay.visible = false;
currentBallLabel.visible = false;
nextBallLabel.visible = false;
pauseButton.visible = false;
exitButton.visible = false;
// Clear existing score screen content
scoreScreenContainer.removeChildren();
// Create score screen background
var scoreBackground = scoreScreenContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
scoreBackground.x = 1024;
scoreBackground.y = 1366;
scoreBackground.scale.x = 6;
scoreBackground.scale.y = 8;
scoreBackground.tint = 0x000000;
scoreBackground.alpha = 0.8;
// Title
var titleText = gameWon ? 'Level Complete!' : 'Level Failed!';
var titleColor = gameWon ? 0x00ff00 : 0xff0000;
var title = new Text2(titleText, {
size: 100,
fill: titleColor
});
title.anchor.set(0.5, 0.5);
title.x = 1024;
title.y = 600;
scoreScreenContainer.addChild(title);
// Score display
var finalScoreText = new Text2('Score: ' + gameScore, {
size: 80,
fill: 0xffffff
});
finalScoreText.anchor.set(0.5, 0.5);
finalScoreText.x = 1024;
finalScoreText.y = 800;
scoreScreenContainer.addChild(finalScoreText);
// Target score display
var targetScoreText = new Text2('Target: ' + targetScore, {
size: 60,
fill: 0xffff00
});
targetScoreText.anchor.set(0.5, 0.5);
targetScoreText.x = 1024;
targetScoreText.y = 900;
scoreScreenContainer.addChild(targetScoreText);
// Star rating display
var currentStarRating = calculateStarRating();
var starY = 1100;
var starSpacing = 120;
var startX = 1024 - starSpacing * 1.5;
// Unlock next level if earned at least one star
if (currentStarRating >= 1 && gameLevel >= maxUnlockedLevel) {
maxUnlockedLevel = gameLevel + 1;
storage.maxUnlockedLevel = maxUnlockedLevel;
}
// Display star rating text
var starRatingText = new Text2('Stars: ' + currentStarRating + '/4', {
size: 70,
fill: 0xffffff
});
starRatingText.anchor.set(0.5, 0.5);
starRatingText.x = 1024;
starRatingText.y = 1000;
scoreScreenContainer.addChild(starRatingText);
// Display the stars
for (var i = 0; i < 4; i++) {
var starAsset;
if (i < currentStarRating) {
if (currentStarRating === 4 && i === 3) {
// Special stellar star for 4th star
starAsset = 'stellarStar';
} else {
// Regular earned star
starAsset = 'earnedStar';
}
} else {
// Unearned star
starAsset = 'unearnedStar';
}
var star = scoreScreenContainer.addChild(LK.getAsset(starAsset, {
anchorX: 0.5,
anchorY: 0.5
}));
star.x = startX + i * starSpacing;
star.y = starY;
star.scale.x = 0.6;
star.scale.y = 0.6;
}
// Add percentage text
var scorePercentage = Math.floor(gameScore / targetScore * 100);
var percentageText = new Text2(scorePercentage + '% of target', {
size: 50,
fill: 0xaaaaaa
});
percentageText.anchor.set(0.5, 0.5);
percentageText.x = 1024;
percentageText.y = 1250;
scoreScreenContainer.addChild(percentageText);
// Continue button
var continueButton = scoreScreenContainer.addChild(LK.getAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
}));
continueButton.x = 1024;
continueButton.y = 1500;
continueButton.scale.x = 1.5;
continueButton.scale.y = 0.8;
continueButton.tint = 0x00aa00;
var continueText = new Text2('Continue', {
size: 60,
fill: 0xffffff
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 1024;
continueText.y = 1500;
scoreScreenContainer.addChild(continueText);
// Store continue button reference for click detection
scoreScreenContainer.continueButton = continueButton;
}
function showMenu() {
gameState = 'menu';
menuContainer.visible = true;
levelSelectContainer.visible = false;
gameContainer.visible = false;
scoreScreenContainer.visible = false;
// Hide UI elements
scoreText.visible = false;
targetText.visible = false;
levelText.visible = false;
currentBallDisplay.visible = false;
nextBallDisplay.visible = false;
currentBallLabel.visible = false;
nextBallLabel.visible = false;
pauseButton.visible = false;
exitButton.visible = false;
// Start background music
LK.playMusic('gameplay');
}
function showLevelSelect() {
gameState = 'levelSelect';
menuContainer.visible = false;
levelSelectContainer.visible = true;
gameContainer.visible = false;
scoreScreenContainer.visible = false;
// Update level buttons based on current unlocked levels
maxUnlockedLevel = storage.maxUnlockedLevel || 1;
for (var i = 0; i < levelButtons.length; i++) {
var button = levelButtons[i];
var isUnlocked = button.levelNumber <= maxUnlockedLevel;
button.isUnlocked = isUnlocked;
// Note: We can't change the asset type dynamically, so we'll handle this in the click handler
}
}
function startGame(level) {
gameState = 'playing';
gameLevel = level;
gameScore = 0;
gameStartTime = Date.now();
targetScore = Math.floor(gameLevel * 100 * Math.pow(1.3, gameLevel - 1)); // Exponential growth
gameOverThreshold = Math.max(150, 300 - gameLevel * 15); // More aggressive threshold
levelCompleted = false;
gameWon = false; // Reset win state
starRating = 0; // Reset star rating
gameOverTimer = 0; // Reset game over timer
ballsOutsideArea = []; // Clear balls outside tracking
// Clear existing balls
for (var i = balls.length - 1; i >= 0; i--) {
balls[i].destroy();
}
balls = [];
// Show game UI
menuContainer.visible = false;
levelSelectContainer.visible = false;
gameContainer.visible = true;
scoreScreenContainer.visible = false;
// Show UI elements
scoreText.visible = true;
targetText.visible = true;
levelText.visible = true;
currentBallDisplay.visible = true;
nextBallDisplay.visible = true;
currentBallLabel.visible = true;
nextBallLabel.visible = true;
pauseButton.visible = true;
exitButton.visible = true;
gamePaused = false;
// Set initial pause button texture
pauseButton.removeChildren();
var pauseAsset = pauseButton.addChild(LK.getAsset('pauseButton', {
anchorX: 0.5,
anchorY: 0.5
}));
// Update UI texts
updateScore();
targetText.setText('Target: ' + targetScore);
levelText.setText('Level ' + gameLevel);
// Initialize ball colors for this level
currentBallColor = getStrategicBallColor();
nextBallColor = getStrategicBallColor();
updateBallDisplays();
}
function createBall(x, y, color) {
var ball = new Ball(color);
ball.x = x;
ball.y = y;
// Add minimal horizontal velocity for less bouncing
ball.velocityX = (Math.random() - 0.5) * 1;
ball.velocityY = Math.random() * 0.5;
balls.push(ball);
gameContainer.addChild(ball);
LK.getSound('drop').play();
return ball;
}
function mergeBalls(ball1, ball2) {
LK.getSound('merge').play();
var mergeX = (ball1.x + ball2.x) / 2;
var mergeY = (ball1.y + ball2.y) / 2;
// Mark balls as merged
ball1.merged = true;
ball2.merged = true;
// Remove from balls array
var index1 = balls.indexOf(ball1);
var index2 = balls.indexOf(ball2);
if (index1 > -1) balls.splice(index1, 1);
if (index2 > -1) balls.splice(index2, 1);
// Remove from display
ball1.destroy();
ball2.destroy();
// Create next color ball or add score
if (ball1.ballColor === 'white') {
var newBall = createBall(mergeX, mergeY, 'green');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// White merge gives 5 points
gameScore += 5;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0xffffff, 300);
} else if (ball1.ballColor === 'green') {
var newBall = createBall(mergeX, mergeY, 'blue');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Green merge gives 10 points
gameScore += 10;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0x00ff00, 300);
} else if (ball1.ballColor === 'blue') {
var newBall = createBall(mergeX, mergeY, 'purple');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Blue merge gives 20 points
gameScore += 20;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0x0066ff, 300);
} else if (ball1.ballColor === 'purple') {
var newBall = createBall(mergeX, mergeY, 'red');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Purple merge gives 40 points
gameScore += 40;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0x9933ff, 300);
} else if (ball1.ballColor === 'red') {
var newBall = createBall(mergeX, mergeY, 'black');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Red merge gives 80 points
gameScore += 80;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0xff0000, 300);
} else if (ball1.ballColor === 'black') {
var newBall = createBall(mergeX, mergeY, 'orange');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Black merge gives 160 points
gameScore += 160;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0x000000, 300);
} else if (ball1.ballColor === 'orange') {
// For level 9, orange balls merge to create golden ball
if (gameLevel === 9) {
var newBall = createBall(mergeX, mergeY, 'golden');
newBall.velocityX = (ball1.velocityX + ball2.velocityX) / 2;
newBall.velocityY = (ball1.velocityY + ball2.velocityY) / 2;
// Orange merge in level 9 gives 500 points
gameScore += 500;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0xffd700, 500);
} else {
// Orange balls disappear and give high score
gameScore += 320;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0xff8800, 500);
}
} else if (ball1.ballColor === 'golden') {
// Golden balls disappear and give maximum score
gameScore += 1000;
LK.getSound('score').play();
LK.effects.flashObject(scoreText, 0xffd700, 800);
}
updateScore();
}
function updateScore() {
scoreText.setText('Score: ' + gameScore);
}
function getStrategicBallColor() {
// Count existing balls by color
var colorCounts = {
white: 0,
green: 0,
blue: 0,
purple: 0,
red: 0,
black: 0,
orange: 0,
golden: 0
};
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (!ball.merged) {
colorCounts[ball.ballColor]++;
}
}
// Determine available colors based on what's on screen
var availableColors = [];
// Good balls (helpful for merging)
if (colorCounts.white > 0) availableColors.push('white');
if (colorCounts.green > 0) availableColors.push('green');
if (colorCounts.blue > 0) availableColors.push('blue');
if (colorCounts.purple > 0) availableColors.push('purple');
// Bad balls (disruptive)
if (colorCounts.white > 0 || colorCounts.green > 0 || colorCounts.blue > 0 || colorCounts.purple > 0) {
// Only add white/green as bad options if there are other colors on screen
if (colorCounts.green > 0 || colorCounts.blue > 0 || colorCounts.purple > 0) {
availableColors.push('white');
}
if (colorCounts.blue > 0 || colorCounts.purple > 0) {
availableColors.push('green');
}
}
// If no balls on screen, start with white
if (availableColors.length === 0) {
availableColors = ['white'];
}
// Weight the selection based on level difficulty
var difficultyFactor = Math.min(0.7, gameLevel * 0.05); // Increase bad ball probability with level
// Separate good and bad options
var goodColors = [];
var badColors = [];
for (var i = 0; i < availableColors.length; i++) {
var color = availableColors[i];
if (colorCounts[color] > 0) {
goodColors.push(color); // Colors that can merge
} else {
badColors.push(color); // Colors that will disrupt
}
}
// Choose based on difficulty and randomness
if (Math.random() < difficultyFactor && badColors.length > 0) {
// Give a bad/disruptive color
return badColors[Math.floor(Math.random() * badColors.length)];
} else if (goodColors.length > 0) {
// Give a good/helpful color
return goodColors[Math.floor(Math.random() * goodColors.length)];
} else {
// Fallback to any available color
return availableColors[Math.floor(Math.random() * availableColors.length)];
}
}
function calculateStarRating() {
var scorePercentage = gameScore / targetScore;
if (scorePercentage >= 1.0) {
return 4; // Stellar rating for 100%+
} else if (scorePercentage >= 0.75) {
return 3; // 3 stars for 75%+
} else if (scorePercentage >= 0.5) {
return 2; // 2 stars for 50%+
} else if (scorePercentage >= 0.25) {
return 1; // 1 star for 25%+
} else {
return 0; // No stars for less than 25%
}
}
function updateBallDisplays() {
// Update current ball display
currentBallDisplay.removeChildren();
var currentAsset;
if (currentBallColor === 'white') {
currentAsset = LK.getAsset('whiteBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'green') {
currentAsset = LK.getAsset('greenBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'blue') {
currentAsset = LK.getAsset('blueBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'purple') {
currentAsset = LK.getAsset('purpleBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'red') {
currentAsset = LK.getAsset('redBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'black') {
currentAsset = LK.getAsset('blackBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'orange') {
currentAsset = LK.getAsset('orangeBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (currentBallColor === 'golden') {
currentAsset = LK.getAsset('goldenBall', {
anchorX: 0.5,
anchorY: 0.5
});
currentAsset.tint = 0xffd700;
}
currentAsset.scale.x = 0.3;
currentAsset.scale.y = 0.3;
currentBallDisplay.addChild(currentAsset);
// Update next ball display
nextBallDisplay.removeChildren();
var nextAsset;
if (nextBallColor === 'white') {
nextAsset = LK.getAsset('whiteBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'green') {
nextAsset = LK.getAsset('greenBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'blue') {
nextAsset = LK.getAsset('blueBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'purple') {
nextAsset = LK.getAsset('purpleBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'red') {
nextAsset = LK.getAsset('redBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'black') {
nextAsset = LK.getAsset('blackBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'orange') {
nextAsset = LK.getAsset('orangeBall', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (nextBallColor === 'golden') {
nextAsset = LK.getAsset('goldenBall', {
anchorX: 0.5,
anchorY: 0.5
});
nextAsset.tint = 0xffd700;
}
nextAsset.scale.x = 0.25;
nextAsset.scale.y = 0.25;
nextBallDisplay.addChild(nextAsset);
}
// Touch/click handler
game.down = function (x, y, obj) {
if (gameState === 'menu') {
// Check if menu button was clicked
var dx = x - menuButton.x;
var dy = y - menuButton.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
LK.getSound('button').play();
showLevelSelect();
}
} else if (gameState === 'levelSelect') {
// Check if back button was clicked
var dx = x - backButton.x;
var dy = y - backButton.y;
if (Math.abs(dx) < 60 && Math.abs(dy) < 40) {
LK.getSound('button').play();
showMenu();
return;
}
// Check if level button was clicked
for (var i = 0; i < levelButtons.length; i++) {
var button = levelButtons[i];
var dx = x - button.x;
var dy = y - button.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 75 && button.isUnlocked) {
LK.getSound('button').play();
startGame(button.levelNumber);
return;
}
}
} else if (gameState === 'scoreScreen') {
// Check if continue button was clicked
var continueButton = scoreScreenContainer.continueButton;
if (continueButton) {
var dx = x - continueButton.x;
var dy = y - continueButton.y;
if (Math.abs(dx) < 150 && Math.abs(dy) < 80) {
LK.getSound('button').play();
showLevelSelect();
}
}
} else if (gameState === 'playing') {
// Check pause button
var pauseButtonGlobal = LK.gui.topRight.toGlobal({
x: -50,
y: 350
});
var pauseButtonLocal = game.toLocal(pauseButtonGlobal);
var dx = x - pauseButtonLocal.x;
var dy = y - pauseButtonLocal.y;
if (Math.abs(dx) < 60 && Math.abs(dy) < 30) {
LK.getSound('button').play();
gamePaused = !gamePaused;
// Switch button texture based on pause state
pauseButton.removeChildren();
var buttonAsset = pauseButton.addChild(LK.getAsset(gamePaused ? 'playButton' : 'pauseButton', {
anchorX: 0.5,
anchorY: 0.5
}));
return;
}
// Check exit button
var exitButtonGlobal = LK.gui.topRight.toGlobal({
x: -50,
y: 450
});
var exitButtonLocal = game.toLocal(exitButtonGlobal);
var dx = x - exitButtonLocal.x;
var dy = y - exitButtonLocal.y;
if (Math.abs(dx) < 60 && Math.abs(dy) < 30) {
LK.getSound('button').play();
showLevelSelect();
return;
}
if (gamePaused || dropCooldown > 0) return;
// Drop ball at x position within drop zone
var dropX = Math.max(gameLeftWall + 60, Math.min(gameRightWall - 60, x));
createBall(dropX, 200, currentBallColor);
// Advance to next ball
currentBallColor = nextBallColor;
nextBallColor = getStrategicBallColor();
updateBallDisplays();
dropCooldown = Math.max(10, 30 - gameLevel * 2); // Faster required drops per level
}
};
game.update = function () {
// Only run game logic when playing and not paused
if (gameState !== 'playing' || gamePaused) return;
// Update cooldown
if (dropCooldown > 0) {
dropCooldown--;
}
// Add random disruption for higher levels
if (gameLevel >= 3 && LK.ticks % (300 - gameLevel * 20) === 0) {
// Random ball velocity disruption
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (!ball.merged && Math.random() < 0.3) {
ball.velocityX += (Math.random() - 0.5) * (gameLevel * 0.5);
ball.velocityY += (Math.random() - 0.5) * (gameLevel * 0.3);
}
}
}
// Clean up merged balls
for (var i = balls.length - 1; i >= 0; i--) {
if (balls[i].merged) {
balls.splice(i, 1);
}
}
// Check for balls that fell off screen
for (var i = balls.length - 1; i >= 0; i--) {
if (balls[i].y > 2732) {
balls[i].destroy();
balls.splice(i, 1);
}
}
// Check for balls outside play area (above threshold)
if (!levelCompleted && !gameWon) {
var ballsCurrentlyOutside = [];
for (var i = 0; i < balls.length; i++) {
var ball = balls[i];
if (ball.y - ball.radius <= gameOverThreshold) {
ballsCurrentlyOutside.push(ball);
}
}
// If there are balls outside, start/continue timer
if (ballsCurrentlyOutside.length > 0) {
gameOverTimer++;
if (gameOverTimer >= gameOverDelay) {
// 3 seconds have passed with balls outside play area
// Check if player has reached target score - if so, it's a win
if (gameScore >= targetScore) {
gameWon = true;
starRating = calculateStarRating();
// Level complete
storage.currentLevel = gameLevel + 1;
showScoreScreen();
} else {
// Game over - didn't reach target score
levelCompleted = true;
showScoreScreen();
}
return;
}
} else {
// No balls outside, reset timer
gameOverTimer = 0;
}
}
};
// Initialize game in menu state
showMenu();
crea la imagen de un circulo azul simple y contorno azul fuerte. In-Game asset. 2d. High contrast. No shadows
crea la imagen de un circulo azul grisaseo simple y contorno azul fuerte.
crea la imagen de un rectangulo verde con borde verde oscuro y en medio que tenga la palabra play. In-Game asset. 2d. High contrast. No shadows
crea un fondo bonito que tenga pelotitas de colores. In-Game asset. 2d. High contrast. No shadows
crea la imagen de una pelotita simple con una carita kawai verde. In-Game asset. 2d. High contrast. No shadows
crea la imagen de una pelotita blanca simple kawai. In-Game asset. 2d. High contrast. No shadows
crea la imagen de una pelotita negra simple kawai y nerviosa. In-Game asset. 2d. High contrast. No shadows
crea la imagen de una pelotita azul kawai con lentes y simple. In-Game asset. 2d. High contrast. No shadows
crea una estrella dorada kawai y simple. In-Game asset. 2d. High contrast. No shadows
crea una estrella kawai deprimida y de color gris simple. In-Game asset. 2d. High contrast. No shadows
crea la imagen de una pelotita morada algo preocupada kawai y simple. In-Game asset. 2d. High contrast. No shadows
crea la imagen de una pelotita roja enojada y kawai simple. In-Game asset. 2d. High contrast. No shadows
crea un paisaje kawai. In-Game asset. 2d. High contrast. No shadows
crea la imagen de un rectangulo azul simple. In-Game asset. 2d. High contrast. No shadows
crea la imagen de un circulo naranja kawai enojada y simple. In-Game asset. 2d. High contrast. No shadows
crea ua pelotita kawai dorada simple y con lentes oscuros. In-Game asset. 2d. High contrast. No shadows
crea un rectangulo rojo que diga salir, simple. In-Game asset. 2d. High contrast. No shadows
crea un rectangulo verde que diga pausar. In-Game asset. 2d. High contrast. No shadows