/****
* 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