/****
* Classes
****/
// Ball class to represent the game balls
var Ball = Container.expand(function (type) {
var self = Container.call(this);
var assetId = 'ball' + type;
var ballGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type;
// Initialize velocity property for gravity effect
self.velocity = {
x: 0,
y: 0
};
self.upgrade = function () {
var nextType;
switch (self.type) {
case '1':
nextType = '2';
break;
case '2':
nextType = '3';
break;
case '3':
nextType = '4';
break;
case '4':
nextType = '5';
break;
case '5':
nextType = '6';
break;
case '6':
nextType = '7';
break;
case '7':
nextType = '8';
break;
case '8':
nextType = '9';
break;
case '9':
// Already at largest size, no upgrade
return;
}
// Replace current asset with the next upgrade
self.removeChild(ballGraphics);
assetId = 'ball' + nextType;
ballGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.type = nextType;
// Increase the score when balls merge
LK.setScore(LK.getScore() + 1);
};
});
// Class to display the next ball type in the top left corner
var NextBallDisplay = Container.expand(function (type) {
var self = Container.call(this);
self.nextBallGraphic = self.attachAsset('ball' + type, {
anchorX: 0.5,
anchorY: 0.5,
x: 100,
// Position x-coordinate
y: 100,
// Position y-coordinate
width: 100,
// Set width to 100
height: 100 // Set height to 100
});
self.updateNextBallType = function (type) {
self.removeChild(self.nextBallGraphic);
self.nextBallGraphic = self.attachAsset('ball' + type, {
anchorX: 0.5,
anchorY: 0.5,
x: 100,
// Position x-coordinate
y: 100,
// Position y-coordinate
width: 100,
// Set width to 100
height: 100 // Set height to 100
});
};
});
var Score = Container.expand(function () {
var self = Container.call(this);
var scoreText = new Text2('0', {
size: 150,
fill: "#ffffff"
});
self.addChild(scoreText);
self.value = 0;
self.updateScore = function () {
self.value = balls.reduce(function (score, ball) {
return score + Math.pow(2, parseBallType(ball.type) - 1);
}, 0);
scoreText.setText(self.value.toString());
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000 // Init game with black background
});
/****
* Game Code
****/
// Initialize an array to keep track of all balls
// Define ball assets with increasing sizes and colors for each upgrade level
function calculateNextBallType(ballCount) {
var ballType = '1';
if (ballCount > 50) {
var rand = Math.random();
if (rand < 0.1) {
ballType = '4';
} else if (rand < 0.4) {
ballType = '3';
} else if (rand < 0.9) {
ballType = '2';
} else {
ballType = '1';
}
} else if (ballCount > 25) {
var rand = Math.random();
if (rand < 0.3) {
ballType = '2';
} else if (rand < 0.6) {
ballType = '3';
}
} else if (ballCount > 5 && Math.random() < 0.3) {
ballType = '2';
}
return ballType;
}
var nextBallDisplay = new NextBallDisplay(nextBallType);
LK.gui.topLeft.addChild(nextBallDisplay);
function parseBallType(type) {
return Number(type);
}
var score = new Score();
LK.gui.top.addChild(score);
var balls = [];
// Function to check for collisions and upgrade balls
function checkCollisions() {
for (var i = 0; i < balls.length; i++) {
for (var j = i + 1; j < balls.length; j++) {
if (balls[i].intersects(balls[j]) && balls[i].type === balls[j].type) {
balls[i].upgrade();
balls[j].destroy();
balls.splice(j, 1);
// Adjust index to continue checking without skipping a ball
j--;
}
}
}
}
// Event listener for spawning balls on touch
var ballCount = 0;
var canThrowBall = true;
var delayEnabled = true; // Clause to enable or disable the delay on spawning balls
var nextBallType = '1'; // Pre-calculate the next ball type
game.on('down', function (x, y, obj) {
if (canThrowBall) {
var ballTypesToString = function ballTypesToString(ballsArray) {
var counts = {};
ballsArray.forEach(function (ball) {
counts[ball.type] = (counts[ball.type] || 0) + 1;
});
return Object.keys(counts).map(function (type) {
return 'Type ' + type + ': ' + counts[type];
}).join(', ');
};
canThrowBall = false;
var event = obj;
var pos = game.toLocal(event.global);
var ballType = nextBallType; // Use the precalculated ball type
var newBall = new Ball(ballType);
newBall.x = pos.x + (Math.random() * 20 - 10);
newBall.y = 300 + (Math.random() * 20 - 10);
// Update the next ball type display
nextBallDisplay.updateNextBallType(ballType);
balls.push(newBall);
game.addChild(newBall);
ballCount++;
console.log('Ball count: ' + ballCount + ', Ball types: ' + ballTypesToString(balls));
if (delayEnabled) {
LK.setTimeout(function () {
canThrowBall = true;
// Calculate the next ball type for the upcoming throw
nextBallType = calculateNextBallType(ballCount);
nextBallDisplay.updateNextBallType(nextBallType); // Update the display with the new next ball type
}, 1000);
} else {
canThrowBall = true;
// Calculate the next ball type for the upcoming throw
nextBallType = calculateNextBallType(ballCount);
nextBallDisplay.updateNextBallType(nextBallType); // Update the display with the new next ball type
}
}
});
// Main game update loop
var gameOverTimer = null;
var flashInterval = null;
function startGameOverTimer() {
if (gameOverTimer) {
return;
} // Timer already running
gameOverTimer = LK.setTimeout(function () {
var objectAboveZero = balls.some(function (ball) {
return ball.y < 0;
});
if (objectAboveZero) {
LK.showGameOver();
} else {
// Continue the game
LK.clearTimeout(gameOverTimer);
LK.clearInterval(flashInterval);
gameOverTimer = null;
flashInterval = null;
}
}, 3000);
flashInterval = LK.setInterval(function () {
LK.effects.flashScreen(0xff0000, 500);
}, 1000);
}
LK.on('tick', function () {
// Check if any object is above y=0
var objectAboveZero = balls.some(function (ball) {
return ball.y < 0;
});
if (objectAboveZero) {
startGameOverTimer();
}
score.updateScore();
// Check if any object is above y=0
var objectAboveZero = balls.some(function (ball) {
return ball.y < 0;
});
if (objectAboveZero) {
startGameOverTimer();
}
// Apply gravity to each ball
for (var i = 0; i < balls.length; i++) {
balls[i].velocity.y += 0.5; // Increased gravity acceleration
if (balls[i].y + balls[i].velocity.y > 2732 - balls[i].height / 2) {
balls[i].velocity.y *= -0.5; // Reverse direction with damping
balls[i].y = 2732 - balls[i].height / 2; // Position at the bottom
} else {
balls[i].y += balls[i].velocity.y;
}
// Check if part of the ball is offscreen and roll it back on screen
if (balls[i].x - balls[i].width / 2 < 0) {
balls[i].x = balls[i].width / 2;
balls[i].velocity.x = Math.abs(balls[i].velocity.x);
} else if (balls[i].x + balls[i].width / 2 > 2048) {
balls[i].x = 2048 - balls[i].width / 2;
balls[i].velocity.x = -Math.abs(balls[i].velocity.x);
}
balls[i].velocity.x *= 0.95; // Apply roll resistance
balls[i].x += balls[i].velocity.x;
// Adjusted collision response to prevent constant movement and clipping
for (var j = 0; j < balls.length; j++) {
if (i != j && balls[i].intersects(balls[j])) {
var dx = balls[j].x - balls[i].x;
var dy = balls[j].y - balls[i].y;
var distance = Math.sqrt(dx * dx + dy * dy);
var overlap = balls[i].width / 2 + balls[j].width / 2 - distance;
if (overlap > 0) {
var angle = Math.atan2(dy, dx);
// Apply a small force to separate the balls
var separationForce = 0.03 * overlap;
balls[i].velocity.x -= separationForce * Math.cos(angle);
balls[i].velocity.y -= separationForce * Math.sin(angle);
balls[j].velocity.x += separationForce * Math.cos(angle);
balls[j].velocity.y += separationForce * Math.sin(angle);
// Adjust positions to ensure balls are no longer intersecting
var correctionFactor = 0.5 * overlap / distance;
balls[i].x -= correctionFactor * dx;
balls[i].y -= correctionFactor * dy;
balls[j].x += correctionFactor * dx;
balls[j].y += correctionFactor * dy;
}
}
}
}
checkCollisions();
});