User prompt
quita Add storage import and initialize game start logic with name input requirement
User prompt
no me solicitó el nombre, si esta funcional debe superponerlo sobre la burbujas y no empezar hasta que no ponga su nombre ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
agrega una ventana emergente para que el usuario ingrese su nombre y de esa forma crear el top 5 jugadores ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
superpon sobre las burbujas la top bar
User prompt
colaca la barra sobre las burbujas
User prompt
finish: Add a decorative top bar that overlaps bubbles with earth square edge styling
User prompt
agrega una barra superior que se superponga a las burbujas y que temga estilo del corte lateral de un cuadrado de tierra
User prompt
crea alguna forma mas elegante de mostrar ese dato ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
cambia el color 0x003366 del label por blanco
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught TypeError: LK.togglePause is not a function' in or related to this line: 'LK.togglePause();' Line Number: 1397
User prompt
cambia el boton de pausar el juego y colocalo al lado izquierdo del disparador de burbujas
User prompt
remueve el leaderboard
User prompt
cambia el color del laber del score a un azul profundo
Code edit (2 edits merged)
Please save this source code
User prompt
evita que al hacer click en el boton top player inicie el juego
Code edit (1 edits merged)
Please save this source code
User prompt
ha el contador de score mas pequeño y que se centre con relacion a la altura del topribbon
User prompt
Has el boton Top Player 2 veces mas grande
User prompt
coloca sobre el topribbon el boton Top Player
User prompt
coloca el topribbon sobre las burbujas
User prompt
ahora coloca una cinta superior de forma que pareza que la burbujas salen de ella
User prompt
Coloca el boton Top Player en la esquina superior derecha
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Barrier = Container.expand(function () {
var self = Container.call(this);
var barrierGraphics = self.attachAsset('barrier', {
anchorX: .5,
anchorY: .5
});
});
var BonusUX = Container.expand(function () {
var self = Container.call(this);
//Insert label here
var barHeight = 50;
// Dropshadow for bonusLabel
var bonusLabelShadow = self.addChild(new Text2('Bunny Bonus', {
size: 90,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
}));
bonusLabelShadow.anchor.set(1, 1);
bonusLabelShadow.x = -10 + 4;
bonusLabelShadow.y = barHeight / 2 + 4;
// Main bonusLabel
var bonusLabel = self.addChild(new Text2('Bunny Bonus', {
size: 90,
fill: 0xF4F5FF,
font: "Impact"
}));
bonusLabel.anchor.set(1, 1);
bonusLabel.y = barHeight / 2;
var rightMargin = -10;
bonusLabel.x = rightMargin;
// Dropshadow for bonusAmountLabel
var bonusAmountLabelShadow = self.addChild(new Text2('1x', {
size: 170,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
}));
bonusAmountLabelShadow.anchor.set(.5, .5);
bonusAmountLabelShadow.x = 100 + 4;
bonusAmountLabelShadow.y = 4;
// Main bonusAmountLabel
var bonusAmountLabel = self.addChild(new Text2('1x', {
size: 170,
fill: 0xF4F5FF,
font: "Impact"
}));
bonusAmountLabel.anchor.set(.5, .5);
bonusAmountLabel.x = 100;
var bonusBarWidth = bonusLabel.width;
var bonusBarStart = self.attachAsset('bonusend', {
y: 30,
x: -bonusBarWidth + rightMargin
});
var bonuseBarEnd = self.attachAsset('bonusend', {
y: 30,
x: -bonusBarWidth + rightMargin
});
var bonusBarMiddle = self.attachAsset('bonusbarmiddle', {
y: 30,
x: -bonusBarWidth + rightMargin + barHeight / 2,
width: 0
});
self.x = game.width - 270;
self.y = game.height - 145;
var bonusBarStepSize = (bonusBarWidth - barHeight) / 5;
var targetWidth = 0;
var currentWidth = 0;
var jumpToAtEnd = 0;
self.bonusAmount = 1;
self.streakCount = 0;
var maxLevel = 40;
self.setStreakCount = function (level) {
self.streakCount = Math.min(level, maxLevel);
var newBonus = Math.floor(self.streakCount / 5) + 1;
if (newBonus != self.bonusAmount) {
for (var a = 0; a < scoreMultipliers.length; a++) {
scoreMultipliers[a].setMultiplier(newBonus);
}
}
self.bonusAmount = newBonus;
bonusAmountLabel.setText(self.bonusAmount + 'x');
var newbarpos = level >= maxLevel ? 5 : level % 5;
targetWidth = newbarpos * bonusBarStepSize;
jumpToAtEnd = targetWidth;
if (newbarpos == 0 && level > 0) {
targetWidth = 5 * bonusBarStepSize;
jumpToAtEnd = 0;
}
};
self.update = function () {
var delta = targetWidth - currentWidth;
if (delta < 1) {
targetWidth = currentWidth = jumpToAtEnd;
} else {
currentWidth += delta / 8;
}
bonuseBarEnd.x = -bonusBarWidth + currentWidth + rightMargin;
bonusBarMiddle.width = currentWidth;
};
// bonuseBarEnd.x = -bonusLabel.width;
});
var Bubble = Container.expand(function (max_types, isFireBall, type) {
var self = Container.call(this);
self.isFireBall = isFireBall;
var state = 0;
self.isAttached = true;
self.isFreeBubble = false;
var speedX = 0;
var speedY = 0;
self.targetX = 0;
self.targetY = 0;
self.setPos = function (x, y) {
self.x = self.targetX = x;
self.y = self.targetY = y;
};
if (type !== undefined) {
this.type = type;
} else {
max_types = max_types || 3;
if (max_types > 4) {
self.type = Math.floor(Math.random() * (.8 + Math.random() * .2) * max_types);
} else {
self.type = Math.floor(Math.random() * max_types);
}
}
if (isFireBall) {
var bubbleGraphics = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
bubbleGraphics.width = 150;
bubbleGraphics.height = 150;
} else {
var bubbleGraphics = self.attachAsset('bubble' + self.type, {
anchorX: 0.5,
anchorY: 0.5
});
}
/*if (!isFireBall && self.type > 1) {
bubbleGraphics.tint = bubbleColors[self.type];
}*/
self.detach = function () {
freeBubbleLayer.addChild(self);
LK.getSound('detachCircle').play();
self.y += grid.y;
self.isAttached = false;
speedX = Math.random() * 40 - 20;
speedY = -Math.random() * 30;
self.down = undefined;
};
var spawnMod = 0;
self.update = function () {
if (self.isFreeBubble) {
if (isFireBall) {
if (++spawnMod % 2 == 0 && self.parent) {
// Spawn fire particles every 5 ticks
var angle = Math.random() * Math.PI * 2;
var fireParticle = self.parent.addChild(new FireParticle(angle));
fireParticle.x = self.x + Math.cos(angle) * self.width / 4;
fireParticle.y = self.y + Math.sin(angle) * self.width / 4;
}
}
return;
}
if (self.isAttached) {
if (self.x != self.targetX) {
self.x += (self.targetX - self.x) / 10;
}
if (self.y != self.targetY) {
self.y += (self.targetY - self.y) / 10;
}
} else {
self.x += speedX;
self.y += speedY;
speedY += 1.5;
if (self.x < bubbleSize / 2 && speedX < 0 || self.x > game.width - bubbleSize / 2 && speedX > 0) {
speedX = -speedX;
LK.getSound('circleBounce').play();
}
// Check for collision with barriers
for (var i = 0; i < barriers.length; i++) {
var barrier = barriers[i];
var dx = self.x - barrier.x;
var dy = self.y - barrier.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var minDist = bubbleSize / 2 + barrier.width / 2;
if (distance < minDist) {
// Calculate the angle of the collision
var angle = Math.atan2(dy, dx);
// Calculate the new speed based on the angle of collision, treating the barrier as a static billiard ball
var newSpeed = Math.sqrt(speedX * speedX + speedY * speedY);
speedX = Math.cos(angle) * newSpeed * .7;
speedY = Math.sin(angle) * newSpeed * .7;
// Move the bubble back to the point where it just touches the barrier
var overlap = minDist - distance;
self.x += overlap * Math.cos(angle);
self.y += overlap * Math.sin(angle);
LK.getSound('circleBounce').play();
}
}
// Remove unattached bubbles that fall below 2732 - 500
if (self.y > 2732 - 400) {
self.destroy();
scoreMultipliers[Math.floor(self.x / (2048 / 5))].applyBubble(self);
LK.getSound('scoreCollected').play();
}
}
};
});
var BubbleRemoveParticle = Container.expand(function () {
var self = Container.call(this);
var particle = self.attachAsset('removebubbleeffect', {
anchorX: 0.5,
anchorY: 0.5
});
particle.blendMode = 1;
self.scale.set(.33, .33);
var cscale = .5;
self.update = function () {
cscale += .02;
self.scale.set(cscale, cscale);
self.alpha = 1 - (cscale - .5) * 1.5;
if (self.alpha < 0) {
self.destroy();
}
};
});
var FireBallPowerupOverlay = Container.expand(function () {
var self = Container.call(this);
var bubbleGraphics = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
self.y = game.height - 140;
self.x = 200;
var countBG = self.attachAsset('countbg', {
anchorX: 0.5,
anchorY: 0.5
});
countBG.x = 90;
countBG.y = 50;
self.fireballsLeft = 0; // Start with 0 fireballs
// Dropshadow for label
var labelShadow = self.addChild(new Text2(self.fireballsLeft, {
size: 70,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
}));
labelShadow.anchor.set(.5, .5);
labelShadow.x = 90 + 3;
labelShadow.y = 50 + 3;
// Main label
var label = self.addChild(new Text2(self.fireballsLeft, {
size: 70,
fill: 0xFFFFFF,
font: "Impact"
}));
label.anchor.set(.5, .5);
label.x = 90;
label.y = 50;
self.alpha = 0.5; // Start with greyed out overlay
self.increaseFireballCount = function () {
self.fireballsLeft++;
label.setText(self.fireballsLeft);
labelShadow.setText(self.fireballsLeft);
self.alpha = 1;
tween(self.scale, {
x: 1.3,
y: 1.3
}, {
duration: 120,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self.scale, {
x: 1,
y: 1
}, {
duration: 220,
easing: tween.bounceOut
});
}
});
// Spawn powerup particles
for (var i = 0; i < 12; i++) {
var angle = Math.random() * Math.PI * 2;
var speed = 8 + Math.random() * 6;
var particle = game.addChild(new PowerupParticle(angle, speed));
particle.x = self.x + 90; // center of indicator
particle.y = self.y + 50;
}
};
self.down = function () {
if (self.fireballsLeft > 0 && !launcher.isFireBall()) {
self.fireballsLeft--;
label.setText(self.fireballsLeft);
labelShadow.setText(self.fireballsLeft);
launcher.triggerFireBall();
if (self.fireballsLeft == 0) {
self.alpha = .5;
}
}
};
// State for wiggle animation
self.isWiggling = false;
// Update method to handle wiggle animation
self.update = function () {
// Check if bubbles are getting close to bottom and we have powerups
if (self.fireballsLeft > 0 && !self.isWiggling) {
// Get warning scores from grid
var warningScores = grid.calculateWarningScoreList();
var maxWarning = 0;
for (var i = 0; i < warningScores.length; i++) {
if (warningScores[i] > maxWarning) {
maxWarning = warningScores[i];
}
}
// If any column has high warning score (bubbles close to bottom), trigger wiggle
if (maxWarning > 1.5) {
// Threshold for "getting close"
self.isWiggling = true;
// First wiggle to the right
tween(self, {
rotation: 0.15
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Then wiggle to the left
tween(self, {
rotation: -0.15
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Then wiggle to the right again
tween(self, {
rotation: 0.15
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Return to normal position
tween(self, {
rotation: 0
}, {
duration: 100,
easing: tween.easeIn,
onFinish: function onFinish() {
// Reset wiggle state after a cooldown
LK.setTimeout(function () {
self.isWiggling = false;
}, 2000); // 2 second cooldown before next wiggle
}
});
}
});
}
});
}
});
}
}
};
});
var FireParticle = Container.expand(function (angle) {
var self = Container.call(this);
var particleGraphics = self.attachAsset('fireparticle', {
anchorX: 0.5,
anchorY: 0.5
});
particleGraphics.blendMode = 1;
var speedX = Math.cos(angle) * 1;
var speedY = Math.sin(angle) * 1;
var rotationSpeed = Math.random() * 0.1 - 0.05;
self.update = function () {
self.x += speedX * self.alpha;
self.y += speedY * self.alpha;
particleGraphics.rotation += rotationSpeed;
self.alpha -= 0.01;
if (self.alpha <= 0) {
self.destroy();
}
};
});
var Grid = Container.expand(function () {
var self = Container.call(this);
var rows = [];
self.container = self.addChild(new Container());
var rowCount = 0;
function insertRow() {
var row = [];
var rowWidth = rowCount % 2 == 0 ? 13 : 12;
// Determine if this row should have a powerup
var shouldSpawnPowerup = false;
var powerupCol = -1;
var POWERUP_ROW_INTERVAL = 20; // Every 20th row has a powerup (was 10)
// Move first powerup spawn to row 6, and then every POWERUP_ROW_INTERVAL after that
if (rowCount === 6) {
shouldSpawnPowerup = true;
powerupCol = Math.floor(Math.random() * rowWidth);
} else if (rowCount - 6 > 0 && (rowCount - 6) % POWERUP_ROW_INTERVAL === 0) {
shouldSpawnPowerup = true;
powerupCol = Math.floor(Math.random() * rowWidth);
}
for (var a = 0; a < rowWidth; a++) {
var bubble;
if (shouldSpawnPowerup && a === powerupCol) {
bubble = new PowerupBubble();
} else {
bubble = new Bubble(getMaxTypes());
}
bubble.setPos((2048 - bubbleSize * rowWidth) / 2 + bubbleSize * a + bubbleSize / 2, -rowCount * (1.7320508076 * bubbleSize) / 2);
self.container.addChild(bubble);
row.push(bubble);
/*bubble.down = function () {
var bubbles = self.getConnectedBubbles(this);
self.removeBubbles(bubbles);
var disconnected = self.getDetachedBubbles();
self.removeBubbles(disconnected);
};*/
}
rows.push(row);
rowCount++;
}
//Method that removes an array of bubbles from the rows array.
self.removeBubbles = function (bubbles) {
for (var i = 0; i < bubbles.length; i++) {
var bubble = bubbles[i];
if (bubble) {
var bubbleIndex = this.findBubbleIndex(bubble);
if (bubbleIndex) {
rows[bubbleIndex.row][bubbleIndex.col] = null;
bubble.detach();
}
}
}
};
self.getConnectedBubbles = function (bubble, ignoreType) {
var connectedBubbles = [];
var queue = [bubble];
var visited = [];
while (queue.length > 0) {
var currentBubble = queue.shift();
if (visited.indexOf(currentBubble) === -1) {
visited.push(currentBubble);
connectedBubbles.push(currentBubble);
var neighbors = self.getNeighbors(currentBubble);
for (var i = 0; i < neighbors.length; i++) {
var neighbor = neighbors[i];
if (neighbor) {
if (neighbor.isPowerup) {
// Powerup bubbles connect with everything
queue.push(neighbor);
} else if (neighbor.type === bubble.type || ignoreType) {
queue.push(neighbor);
}
}
}
}
}
return connectedBubbles;
};
//Get a list of bubbles that are not connected to the top row, or to a chain of bubbles connected to the top row.
self.getDetachedBubbles = function () {
var detachedBubbles = [];
var connectedToTop = [];
// Mark all bubbles connected to the bottom row
var lastRowIndex = rows.length - 1;
for (var i = 0; i < rows[lastRowIndex].length; i++) {
if (rows[lastRowIndex][i] !== null) {
var bottomConnected = self.getConnectedBubbles(rows[lastRowIndex][i], true);
connectedToTop = connectedToTop.concat(bottomConnected);
}
}
// Mark all bubbles as visited or not
var visited = connectedToTop.filter(function (bubble) {
return bubble != null;
});
// Find all bubbles that are not visited and not connected to the top
for (var row = 0; row < rows.length - 1; row++) {
for (var col = 0; col < rows[row].length; col++) {
var bubble = rows[row][col];
if (bubble !== null && visited.indexOf(bubble) == -1) {
detachedBubbles.push(bubble);
}
}
}
return detachedBubbles;
};
self.getNeighbors = function (bubble) {
var neighbors = [];
var bubbleIndex = this.findBubbleIndex(bubble);
if (!bubbleIndex) {
return [];
}
var directions = [[-1, 0], [1, 0],
// left and right
[0, -1], [0, 1],
// above and below
[-1, -1], [1, -1] // diagonals for even rows
];
if (bubbleIndex && rows[bubbleIndex.row] && rows[bubbleIndex.row].length == 12) {
// Adjust diagonals for odd rows
directions[4] = [-1, 1];
directions[5] = [1, 1];
}
for (var i = 0; i < directions.length; i++) {
var dir = directions[i];
if (bubbleIndex && rows[bubbleIndex.row]) {
var newRow = bubbleIndex.row + dir[0];
}
var newCol = bubbleIndex.col + dir[1];
if (newRow >= 0 && newRow < rows.length && newCol >= 0 && newCol < rows[newRow].length) {
neighbors.push(rows[newRow][newCol]);
}
}
return neighbors;
};
self.findBubbleIndex = function (bubble) {
for (var row = 0; row < rows.length; row++) {
var col = rows[row].indexOf(bubble);
if (col !== -1) {
return {
row: row,
col: col
};
}
}
return null;
};
self.printRowsToConsole = function () {
var gridString = '';
for (var i = rows.length - 1; i >= 0; i--) {
var rowString = ': ' + (rows[i].length == 13 ? '' : ' ');
for (var j = 0; j < rows[i].length; j++) {
var bubble = rows[i][j];
rowString += bubble ? '[' + bubble.type + ']' : '[_]';
}
gridString += rowString + '\n';
}
console.log(gridString);
};
// Method to calculate path of movement based on angle and starting point
//TODO: MAKE THIS MUCH FASTER!
self.bubbleIntersectsGrid = function (nextX, nextY) {
outer: for (var row = 0; row < rows.length; row++) {
for (var col = 0; col < rows[row].length; col++) {
var bubble = rows[row][col];
if (bubble) {
var dist = nextY - bubble.y - self.y;
//Quick exit if we are nowhere near the row
if (dist > 145 || dist < -145) {
continue outer;
}
var dx = nextX - bubble.x - self.x;
var dy = nextY - bubble.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < (bubbleSize - 70) / 2 + bubbleSize / 2) {
return bubble;
}
}
}
}
return false;
};
self.calculatePath = function (startPoint, angle) {
var path = [];
var currentPoint = {
x: startPoint.x,
y: startPoint.y
};
var radians = angle;
var stepSize = 4;
var hitBubble = false;
while (currentPoint.y > 0 && !hitBubble) {
// Calculate next point
var nextX = currentPoint.x + stepSize * Math.cos(radians);
var nextY = currentPoint.y + stepSize * Math.sin(radians);
// Check for wall collisions
if (nextX < 150 / 2 || nextX > 2048 - 150 / 2) {
radians = Math.PI - radians; // Reflect angle
nextX = currentPoint.x + stepSize * Math.cos(radians); // Recalculate nextX after reflection
}
hitBubble = self.bubbleIntersectsGrid(nextX, nextY);
// Add point to path and update currentPoint
path.push({
x: nextX,
y: nextY
});
currentPoint.x = nextX;
currentPoint.y = nextY;
}
if (hitBubble) {
//Only increase avilable bubble type when we have actually pointed as such a bubble
if (hitBubble.type >= 0 && hitBubble.type + 1 > maxSelectableBubble) {
maxSelectableBubble = hitBubble.type + 1;
}
;
}
return path;
};
var bubblesInFlight = [];
self.fireBubble = function (bubble, angle) {
self.addChild(bubble);
bubble.x = launcher.x;
bubble.y += launcher.y - self.y;
bubblesInFlight.push({
bubble: bubble,
angle: angle
});
};
self.calculateWarningScoreList = function () {
var warningScores = [];
for (var i = 0; i < 13; i++) {
warningScores.push(0); // Initialize all scores to 0
}
// Calculate the distance from the bottom for each bubble and increment the warning score based on proximity
for (var row = 0; row < rows.length; row++) {
for (var col = 0; col < rows[row].length; col++) {
var bubble = rows[row][col];
if (bubble) {
var distanceFromBottom = 2732 - (bubble.y + self.y);
if (distanceFromBottom < 2000) {
// If a bubble is within 500px from the bottom
var columnIndex = Math.floor(bubble.x / (2048 / 13));
warningScores[columnIndex] += (2000 - distanceFromBottom) / 2000; // Increment the warning score for the column
}
}
}
}
return warningScores;
};
self.update = function () {
outer: for (var a = 0; a < bubblesInFlight.length; a++) {
var current = bubblesInFlight[a];
var bubble = current.bubble;
var nextX = bubble.x;
var nextY = bubble.y + gridSpeed;
var prevX = bubble.x;
var prevY = bubble.y;
for (var rep = 0; rep < 25; rep++) {
prevX = nextX;
prevY = nextY;
nextX += Math.cos(current.angle) * 4;
nextY += Math.sin(current.angle) * 4;
if (nextX < 150 / 2 || nextX > 2048 - 150 / 2) {
current.angle = Math.PI - current.angle; // Reflect angle
nextX = Math.min(Math.max(nextX, 150 / 2), 2048 - 150 / 2);
LK.getSound('circleBounce').play();
}
var intersectedBubble = self.bubbleIntersectsGrid(nextX + self.x, nextY + self.y);
if (intersectedBubble) {
gameIsStarted = true;
if (bubble.isFireBall) {
self.removeBubbles([intersectedBubble]);
var disconnected = self.getDetachedBubbles();
self.removeBubbles(disconnected);
} else {
var intersectedBubblePos = self.findBubbleIndex(intersectedBubble);
var colOffset = rows[intersectedBubblePos.row].length == 13 ? 0 : 1;
var offsetPositions = [{
x: intersectedBubble.targetX - bubbleSize / 2,
y: intersectedBubble.targetY - 1.7320508076 * bubbleSize / 2,
ro: intersectedBubblePos.row + 1,
co: intersectedBubblePos.col - 1 + colOffset
}, {
x: intersectedBubble.targetX + bubbleSize / 2,
y: intersectedBubble.targetY - 1.7320508076 * bubbleSize / 2,
ro: intersectedBubblePos.row + 1,
co: intersectedBubblePos.col + colOffset
}, {
x: intersectedBubble.targetX + bubbleSize,
y: intersectedBubble.targetY,
ro: intersectedBubblePos.row,
co: intersectedBubblePos.col + 1
}, {
x: intersectedBubble.targetX + bubbleSize / 2,
y: intersectedBubble.targetY + 1.7320508076 * bubbleSize / 2,
ro: intersectedBubblePos.row - 1,
co: intersectedBubblePos.col + colOffset
}, {
x: intersectedBubble.targetX - bubbleSize / 2,
y: intersectedBubble.targetY + 1.7320508076 * bubbleSize / 2,
ro: intersectedBubblePos.row - 1,
co: intersectedBubblePos.col - 1 + colOffset
}, {
x: intersectedBubble.targetX - bubbleSize,
y: intersectedBubble.targetY,
ro: intersectedBubblePos.row,
co: intersectedBubblePos.col - 1
}];
var closestPosition = 0;
var closestDistance = Math.sqrt(Math.pow(offsetPositions[0].x - bubble.x, 2) + Math.pow(offsetPositions[0].y - bubble.y, 2));
for (var i = 1; i < offsetPositions.length; i++) {
var currentPosition = offsetPositions[i];
var currentDistance = Math.sqrt(Math.pow(currentPosition.x - bubble.x, 2) + Math.pow(currentPosition.y - bubble.y, 2));
if (currentDistance < closestDistance) {
var row = rows[currentPosition.ro];
if (currentPosition.co < 0) {
continue;
}
if (row) {
if (row[currentPosition.co]) {
continue;
}
if (currentPosition.co >= row.length) {
continue;
}
} else {
var newRowLength = rows[intersectedBubblePos.row].length == 13 ? 12 : 13;
if (currentPosition.co >= newRowLength) {
continue;
}
}
closestDistance = currentDistance;
closestPosition = i;
}
}
// Attach bubble to the closest position
var currentMatch = offsetPositions[closestPosition];
bubble.x = prevX;
bubble.y = prevY;
bubble.targetX = currentMatch.x;
bubble.targetY = currentMatch.y;
bubble.isFreeBubble = false;
var row = rows[offsetPositions[closestPosition].ro];
if (!row) {
if (rows[intersectedBubblePos.row].length == 13) {
row = [null, null, null, null, null, null, null, null, null, null, null, null];
} else {
row = [null, null, null, null, null, null, null, null, null, null, null, null, null];
}
rows.unshift(row);
}
row[offsetPositions[closestPosition].co] = bubble;
bubblesInFlight.splice(a--, 1);
refreshHintLine();
var bubbles = self.getConnectedBubbles(bubble);
if (bubbles.length > 2) {
self.removeBubbles(bubbles);
var disconnected = self.getDetachedBubbles();
self.removeBubbles(disconnected);
bonusUX.setStreakCount(bonusUX.streakCount + 1);
} else {
bonusUX.setStreakCount(0);
LK.getSound('attachCircle').play();
}
//Add a grid movement effect when you don't do a match
var neighbors = self.getNeighbors(bubble);
var touched = [];
var neighbors2 = [];
for (var i = 0; i < neighbors.length; i++) {
var neighbor = neighbors[i];
if (neighbor) {
touched.push(neighbor);
neighbors2 = neighbors2.concat(self.getNeighbors(neighbor));
var ox = neighbor.x - bubble.x;
var oy = neighbor.y - bubble.y;
var angle = Math.atan2(oy, ox);
neighbor.x += Math.cos(angle) * 20;
neighbor.y += Math.sin(angle) * 20;
}
}
//One more layer
for (var i = 0; i < neighbors2.length; i++) {
var neighbor = neighbors2[i];
if (neighbor && touched.indexOf(neighbor) == -1) {
touched.push(neighbor);
var ox = neighbor.x - bubble.x;
var oy = neighbor.y - bubble.y;
var angle = Math.atan2(oy, ox);
neighbor.x += Math.cos(angle) * 10;
neighbor.y += Math.sin(angle) * 10;
}
}
//self.printRowsToConsole();
continue outer;
}
}
}
bubble.x = nextX;
bubble.y = nextY;
if (bubble.y + self.y < -1000) {
//Destory bubbles that somehow manages to escape at the top
bubblesInFlight.splice(a--, 1);
bubble.destroy();
}
}
if (gameIsStarted) {
self.y += gridSpeed;
}
var zeroRow = rows[rows.length - 1];
if (zeroRow) {
for (var a = 0; a < zeroRow.length; a++) {
var bubble = zeroRow[a];
if (bubble) {
if (bubble.y + self.y > 0) {
insertRow();
}
break;
}
}
} else {
insertRow();
}
for (var row = rows.length - 1; row >= 0; row--) {
if (rows[row].every(function (bubble) {
return !bubble;
})) {
rows.splice(row, 1);
}
}
var lastRow = rows[0];
/*if(LK.ticks % 10 == 0){
self.printRowsToConsole()
}*/
if (lastRow) {
for (var a = 0; a < zeroRow.length; a++) {
var bubble = lastRow[a];
if (bubble) {
if (bubble.y + self.y > 2200) {
LK.effects.flashScreen(0xff0000, 3000);
LK.getSound('gameOverJingle').play();
// Show regular game over
LK.showGameOver();
}
if (gameIsStarted) {
var targetSpeed = Math.pow(Math.pow((2200 - (bubble.y + self.y)) / 2200, 2), 2) * 4 + 0.5;
if (bubble.y + self.y > 2000) {
targetSpeed = .2;
}
gridSpeed += (targetSpeed - gridSpeed) / 20;
if (LK.ticks % 10 == 0) {
//console.log(gridSpeed)
}
}
break;
}
}
}
};
for (var a = 0; a < 8; a++) {
insertRow();
}
});
var HintBubble = Container.expand(function () {
var self = Container.call(this);
var bubble = self.attachAsset('hintbubble', {
anchorX: 0.5,
anchorY: 0.5
});
self.currentType = -1;
self.setType = function (type, isFireBall) {
if (self.currentType !== type || self.isFireBall !== isFireBall) {
self.currentType = type;
self.isFireBall = isFireBall;
if (bubble.parent) {
bubble.parent.removeChild(bubble);
}
if (isFireBall) {
bubble = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
bubble.width = 100;
bubble.height = 100;
} else {
bubble = self.attachAsset('bubble' + type, {
anchorX: 0.5,
anchorY: 0.5
});
bubble.width = 100;
bubble.height = 100;
}
bubble.alpha = 0.6; // Make hint bubbles semi-transparent
}
};
self.setTint = function (tint) {
// Keep for backwards compatibility but not used anymore
};
self.getTint = function (tint) {
return 0xffffff; // Return default since we don't use tinting anymore
};
});
var Launcher = Container.expand(function () {
var self = Container.call(this);
var bubble = self.addChild(new Bubble(getMaxTypes(), false));
bubble.isFreeBubble = true;
var previewBubble;
var lastTypes = [undefined, bubble.type];
function createPreviewBubble() {
var nextType;
do {
nextType = Math.floor(Math.random() * maxSelectableBubble);
} while (nextType == lastTypes[0] && nextType == lastTypes[1]);
lastTypes.shift();
lastTypes.push(nextType);
previewBubble = self.addChildAt(new Bubble(maxSelectableBubble, false, nextType), 0);
previewBubble.scale.set(.7, .7);
previewBubble.x = -90;
previewBubble.y = 20;
previewBubble.isFreeBubble = true;
}
createPreviewBubble();
self.fire = function () {
bulletsFired++;
LK.getSound('fireBubble').play(); // Play sound when the ball is fired
grid.fireBubble(bubble, self.angle);
bubble = previewBubble;
previewBubble.x = previewBubble.y = 0;
previewBubble.scale.set(1, 1);
createPreviewBubble();
};
self.angle = -Math.PI / 2;
self.getBubble = function () {
return bubble;
};
self.isFireBall = function () {
return bubble.isFireBall;
};
self.triggerFireBall = function () {
bubble.destroy();
bubble = self.addChild(new Bubble(getMaxTypes(), true));
bubble.isFreeBubble = true;
};
});
var LeaderboardPopup = Container.expand(function (leaderboard) {
var self = Container.call(this);
// Semi-transparent background overlay
var overlay = LK.getAsset('uxoverlay', {
width: 2048,
height: 2732,
anchorX: 0,
anchorY: 0
});
overlay.tint = 0x000000;
overlay.alpha = 0.7;
self.addChild(overlay);
// Main popup container
var popup = new Container();
popup.x = game.width / 2;
popup.y = game.height / 2 - 100;
self.addChild(popup);
// Popup background
var popupBg = LK.getAsset('uxoverlay', {
width: 900,
height: 700,
anchorX: 0.5,
anchorY: 0.5
});
popupBg.tint = 0x2fbe2d;
popup.addChild(popupBg);
// Title text shadow
var titleShadow = new Text2('TOP 5 PLAYERS', {
size: 80,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 4;
titleShadow.y = -280 + 4;
popup.addChild(titleShadow);
// Title text
var titleText = new Text2('TOP 5 PLAYERS', {
size: 80,
fill: 0xFFFFFF,
font: "Impact"
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -280;
popup.addChild(titleText);
// Display leaderboard entries
for (var i = 0; i < Math.min(5, leaderboard.length); i++) {
var entry = leaderboard[i];
var yPos = -180 + i * 80;
// Rank and name shadow
var entryShadow = new Text2(i + 1 + '. ' + entry.name, {
size: 50,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
entryShadow.anchor.set(0, 0.5);
entryShadow.x = -350 + 4;
entryShadow.y = yPos + 4;
popup.addChild(entryShadow);
// Rank and name
var entryText = new Text2(i + 1 + '. ' + entry.name, {
size: 50,
fill: 0xFFFFFF,
font: "Impact"
});
entryText.anchor.set(0, 0.5);
entryText.x = -350;
entryText.y = yPos;
popup.addChild(entryText);
// Score shadow
var scoreShadow = new Text2(entry.score.toString(), {
size: 50,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
scoreShadow.anchor.set(1, 0.5);
scoreShadow.x = 350 + 4;
scoreShadow.y = yPos + 4;
popup.addChild(scoreShadow);
// Score
var scoreText = new Text2(entry.score.toString(), {
size: 50,
fill: 0xFFD700,
font: "Impact"
});
scoreText.anchor.set(1, 0.5);
scoreText.x = 350;
scoreText.y = yPos;
popup.addChild(scoreText);
}
// Continue button background
var buttonBg = LK.getAsset('uxoverlay', {
width: 250,
height: 70,
anchorX: 0.5,
anchorY: 0.5
});
buttonBg.tint = 0x43d11f;
buttonBg.y = 250;
popup.addChild(buttonBg);
// Continue button text shadow
var buttonShadow = new Text2('CONTINUE', {
size: 45,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
buttonShadow.anchor.set(0.5, 0.5);
buttonShadow.x = 2;
buttonShadow.y = 252;
popup.addChild(buttonShadow);
// Continue button text
var buttonText = new Text2('CONTINUE', {
size: 45,
fill: 0xFFFFFF,
font: "Impact"
});
buttonText.anchor.set(0.5, 0.5);
buttonText.y = 250;
popup.addChild(buttonText);
// Handle continue button click
buttonBg.down = function () {
self.destroy();
// The game will automatically restart after game over
};
return self;
});
var NameInputPopup = Container.expand(function (finalScore) {
var self = Container.call(this);
// Semi-transparent background overlay
var overlay = LK.getAsset('uxoverlay', {
width: 2048,
height: 2732,
anchorX: 0,
anchorY: 0
});
overlay.tint = 0x000000;
overlay.alpha = 0.7;
self.addChild(overlay);
// Main popup container
var popup = new Container();
popup.x = game.width / 2;
popup.y = game.height / 2 - 200;
self.addChild(popup);
// Popup background
var popupBg = LK.getAsset('uxoverlay', {
width: 800,
height: 500,
anchorX: 0.5,
anchorY: 0.5
});
popupBg.tint = 0x2fbe2d;
popup.addChild(popupBg);
// Title text shadow
var titleShadow = new Text2('NEW HIGH SCORE!', {
size: 80,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 4;
titleShadow.y = -150 + 4;
popup.addChild(titleShadow);
// Title text
var titleText = new Text2('NEW HIGH SCORE!', {
size: 80,
fill: 0xFFFFFF,
font: "Impact"
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -150;
popup.addChild(titleText);
// Score display shadow
var scoreShadow = new Text2('Score: ' + finalScore, {
size: 60,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
scoreShadow.anchor.set(0.5, 0.5);
scoreShadow.x = 4;
scoreShadow.y = -80 + 4;
popup.addChild(scoreShadow);
// Score display
var scoreText = new Text2('Score: ' + finalScore, {
size: 60,
fill: 0xFFD700,
font: "Impact"
});
scoreText.anchor.set(0.5, 0.5);
scoreText.y = -80;
popup.addChild(scoreText);
// Input prompt shadow
var promptShadow = new Text2('Enter your name:', {
size: 50,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
promptShadow.anchor.set(0.5, 0.5);
promptShadow.x = 4;
promptShadow.y = -20 + 4;
popup.addChild(promptShadow);
// Input prompt
var promptText = new Text2('Enter your name:', {
size: 50,
fill: 0xFFFFFF,
font: "Impact"
});
promptText.anchor.set(0.5, 0.5);
promptText.y = -20;
popup.addChild(promptText);
// Name input background
var inputBg = LK.getAsset('uxoverlay', {
width: 400,
height: 80,
anchorX: 0.5,
anchorY: 0.5
});
inputBg.tint = 0xFFFFFF;
inputBg.y = 40;
popup.addChild(inputBg);
// Current name text
var nameText = new Text2('', {
size: 45,
fill: 0x000000,
font: "Impact"
});
nameText.anchor.set(0.5, 0.5);
nameText.y = 40;
popup.addChild(nameText);
// Submit button background
var buttonBg = LK.getAsset('uxoverlay', {
width: 200,
height: 60,
anchorX: 0.5,
anchorY: 0.5
});
buttonBg.tint = 0x43d11f;
buttonBg.y = 140;
popup.addChild(buttonBg);
// Submit button text shadow
var buttonShadow = new Text2('SUBMIT', {
size: 40,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
buttonShadow.anchor.set(0.5, 0.5);
buttonShadow.x = 2;
buttonShadow.y = 142;
popup.addChild(buttonShadow);
// Submit button text
var buttonText = new Text2('SUBMIT', {
size: 40,
fill: 0xFFFFFF,
font: "Impact"
});
buttonText.anchor.set(0.5, 0.5);
buttonText.y = 140;
popup.addChild(buttonText);
// Name input handling
self.playerName = '';
var cursor = '_';
var cursorVisible = true;
var cursorTimer = 0;
self.addLetter = function (letter) {
if (self.playerName.length < 12) {
self.playerName += letter;
self.updateDisplay();
}
};
self.removeLetter = function () {
if (self.playerName.length > 0) {
self.playerName = self.playerName.slice(0, -1);
self.updateDisplay();
}
};
self.updateDisplay = function () {
var displayText = self.playerName;
if (cursorVisible) displayText += cursor;
nameText.setText(displayText);
};
self.submitScore = function () {
if (self.playerName.length > 0) {
// Just show game over instead of leaderboard
LK.showGameOver();
// Remove this popup
self.destroy();
}
};
// Handle submit button click
buttonBg.down = function () {
self.submitScore();
};
// Update cursor blinking
self.update = function () {
cursorTimer++;
if (cursorTimer % 30 === 0) {
cursorVisible = !cursorVisible;
self.updateDisplay();
}
};
// Initial display
self.updateDisplay();
return self;
});
var PowerupBubble = Container.expand(function () {
var self = Container.call(this);
var bubbleGraphics = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
bubbleGraphics.width = 150;
bubbleGraphics.height = 150;
self.type = -1; // Special type for powerup
self.isPowerup = true;
self.isAttached = true;
self.isFreeBubble = false;
var speedX = 0;
var speedY = 0;
self.targetX = 0;
self.targetY = 0;
self.setPos = function (x, y) {
self.x = self.targetX = x;
self.y = self.targetY = y;
};
self.detach = function () {
freeBubbleLayer.addChild(self);
LK.getSound('detachCircle').play();
self.y += grid.y;
self.isAttached = false;
speedX = Math.random() * 40 - 20;
speedY = -Math.random() * 30;
self.down = undefined;
};
var spawnMod = 0;
self.update = function () {
if (self.isFreeBubble) {
return;
}
if (self.isAttached) {
if (self.x != self.targetX) {
self.x += (self.targetX - self.x) / 10;
}
if (self.y != self.targetY) {
self.y += (self.targetY - self.y) / 10;
}
} else {
self.x += speedX;
self.y += speedY;
speedY += 1.5;
if (self.x < bubbleSize / 2 && speedX < 0 || self.x > game.width - bubbleSize / 2 && speedX > 0) {
speedX = -speedX;
LK.getSound('circleBounce').play();
}
// Check for collision with barriers
for (var i = 0; i < barriers.length; i++) {
var barrier = barriers[i];
var dx = self.x - barrier.x;
var dy = self.y - barrier.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var minDist = bubbleSize / 2 + barrier.width / 2;
if (distance < minDist) {
var angle = Math.atan2(dy, dx);
var newSpeed = Math.sqrt(speedX * speedX + speedY * speedY);
speedX = Math.cos(angle) * newSpeed * .7;
speedY = Math.sin(angle) * newSpeed * .7;
var overlap = minDist - distance;
self.x += overlap * Math.cos(angle);
self.y += overlap * Math.sin(angle);
LK.getSound('circleBounce').play();
}
}
// When powerup reaches the bottom of the screen, trigger powerup earned animation
if (self.y > 2732 - 400) {
// Play sound
LK.getSound('scoreCollected').play();
// Create and start powerup earned animation
var animation = game.addChild(new PowerupEarnedAnimation(self.x, self.y));
animation.start();
// Destroy the original bubble
self.destroy();
}
}
};
});
var PowerupEarnedAnimation = Container.expand(function (startX, startY) {
var self = Container.call(this);
// Create a 2x size powerup graphic
var powerupGraphics = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
// Create dropshadow for earnedText (not a child of self, but a global overlay)
var earnedTextShadow = new Text2('POWERUP EARNED!', {
size: 150,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
earnedTextShadow.anchor.set(0.5, 0.5);
earnedTextShadow.y = game.height / 2 - 250 + 20 + 100 + 8;
earnedTextShadow.x = game.width / 2 + 8;
earnedTextShadow.alpha = 0;
// Create main earnedText (no stroke)
var earnedText = new Text2('POWERUP EARNED!', {
size: 150,
fill: 0xFFFFFF,
font: "Impact"
});
earnedText.anchor.set(0.5, 0.5);
powerupGraphics.width = 150;
powerupGraphics.height = 150;
earnedText.y = game.height / 2 - 250 + 20 + 100;
earnedText.x = game.width / 2;
earnedText.alpha = 0;
// Set initial position
self.x = startX;
self.y = startY;
// Animation phases
var phase = 0;
self.start = function () {
// Add text and dropshadow to overlay (so it doesn't move with self)
LK.getSound('powerupSwoosh').play();
if (!earnedTextShadow.parent) {
game.addChild(earnedTextShadow);
}
if (!earnedText.parent) {
game.addChild(earnedText);
}
// Phase 1: Move to center of screen
tween(powerupGraphics.scale, {
x: 2,
y: 2
}, {
duration: 300,
easing: tween.easeIn
});
tween(self, {
x: game.width / 2,
y: game.height / 2
}, {
duration: 300,
easing: tween.easeIn
});
// Show text and dropshadow
tween(earnedTextShadow, {
alpha: 0.35,
y: game.height / 2 - 250 + 8
}, {
duration: 300,
delay: 100,
easing: tween.easeOut
});
tween(earnedText, {
alpha: 1,
y: game.height / 2 - 250
}, {
duration: 300,
delay: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
// Wait a moment before moving to powerup counter
LK.setTimeout(function () {
LK.getSound('powerupSwoosh').play();
// Get target position (powerup counter)
var targetX = fireBallPowerupOverlay.x;
var targetY = fireBallPowerupOverlay.y;
// Phase 2: Move to powerup counter
tween(self, {
x: targetX,
y: targetY
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
LK.getSound('powerupThump').play();
// Increment powerup counter
fireBallPowerupOverlay.increaseFireballCount();
// Destroy animation
if (earnedTextShadow.parent) {
earnedTextShadow.parent.removeChild(earnedTextShadow);
}
if (earnedText.parent) {
earnedText.parent.removeChild(earnedText);
}
self.destroy();
}
});
// Fade out text and dropshadow as we move
tween(earnedTextShadow, {
alpha: 0,
y: game.height / 2 - 250 - 20 + 8
}, {
duration: 300,
easing: tween.easeOut
});
tween(earnedText, {
alpha: 0,
y: game.height / 2 - 250 - 20
}, {
duration: 300,
easing: tween.easeOut
});
// Scale down as we move to counter
tween(powerupGraphics, {
width: 210,
height: 210
}, {
duration: 300,
easing: tween.easeIn
});
tween(powerupGraphics.scale, {
x: 1,
y: 1
}, {
duration: 300,
easing: tween.easeIn
});
}, 1000);
}
});
};
return self;
});
// Make active when we have fireballs
var PowerupParticle = Container.expand(function (angle, speed) {
var self = Container.call(this);
var particle = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
particle.width = 60;
particle.height = 60;
self.scale.set(0.7 + Math.random() * 0.5, 0.7 + Math.random() * 0.5);
self.alpha = 1;
var vx = Math.cos(angle) * speed;
var vy = Math.sin(angle) * speed;
var gravity = 0.5 + Math.random() * 0.3;
var life = 24 + Math.floor(Math.random() * 10);
var tick = 0;
self.update = function () {
self.x += vx;
self.y += vy;
vy += gravity;
self.alpha -= 0.04 + Math.random() * 0.01;
tick++;
if (tick > life || self.alpha <= 0) {
self.destroy();
}
};
return self;
});
var ScoreIndicatorLabel = Container.expand(function (score, type) {
var self = Container.call(this);
var label = new Text2(score, {
size: 100,
fill: "#" + bubbleColors[type].toString(16).padStart(6, '0'),
font: "Impact"
});
label.anchor.set(0.5, 0);
self.addChild(label);
self.update = function () {
self.y -= 7;
self.alpha -= .05;
if (self.alpha <= 0) {
self.destroy();
increaseScore(score);
}
};
});
var ScoreMultipliers = Container.expand(function (baseValue) {
var self = Container.call(this);
// Dropshadow for scoreMultiplierLabel
var scoreMultiplierLabelShadow = new Text2(baseValue, {
size: 100,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
scoreMultiplierLabelShadow.anchor.set(0.5, 0);
self.addChild(scoreMultiplierLabelShadow);
// Create a score label text string for ScoreMultipliers
var scoreMultiplierLabel = new Text2(baseValue, {
size: 100,
fill: 0x3954FF,
font: "Impact"
});
scoreMultiplierLabel.anchor.set(0.5, 0);
self.addChild(scoreMultiplierLabel);
var currentMultiplier = 1;
self.applyBubble = function (bubble) {
var scoreIndicator = game.addChild(new ScoreIndicatorLabel(baseValue * currentMultiplier, bubble.type));
scoreIndicator.x = self.x;
scoreIndicator.y = self.y;
var particle = particlesLayer.addChild(new BubbleRemoveParticle());
particle.x = bubble.x;
particle.y = self.y + 150;
};
self.setMultiplier = function (multiplier) {
currentMultiplier = multiplier;
scoreMultiplierLabel.setText(baseValue * currentMultiplier);
scoreMultiplierLabelShadow.setText(baseValue * currentMultiplier);
};
});
var SimpleKeyboard = Container.expand(function (namePopup) {
var self = Container.call(this);
// Position keyboard at bottom of screen
self.x = game.width / 2;
self.y = game.height - 300;
var letters = ['ABCDEFGHIJ', 'KLMNOPQRST', 'UVWXYZ'];
var keyWidth = 60;
var keyHeight = 60;
var keySpacing = 70;
var rowSpacing = 70;
// Create letter keys
for (var row = 0; row < letters.length; row++) {
var rowLetters = letters[row];
var startX = -(rowLetters.length * keySpacing) / 2 + keySpacing / 2;
for (var col = 0; col < rowLetters.length; col++) {
var letter = rowLetters[col];
var keyContainer = new Container();
keyContainer.x = startX + col * keySpacing;
keyContainer.y = row * rowSpacing;
self.addChild(keyContainer);
// Key background
var keyBg = LK.getAsset('uxoverlay', {
width: keyWidth,
height: keyHeight,
anchorX: 0.5,
anchorY: 0.5
});
keyBg.tint = 0x43d11f;
keyContainer.addChild(keyBg);
// Key text
var keyText = new Text2(letter, {
size: 35,
fill: 0xFFFFFF,
font: "Impact"
});
keyText.anchor.set(0.5, 0.5);
keyContainer.addChild(keyText);
// Make key clickable
keyBg.letter = letter;
keyBg.down = function () {
namePopup.addLetter(this.letter);
};
}
}
// Backspace key
var backspaceContainer = new Container();
backspaceContainer.x = keySpacing * 4;
backspaceContainer.y = rowSpacing * 3;
self.addChild(backspaceContainer);
var backspaceBg = LK.getAsset('uxoverlay', {
width: keyWidth * 2,
height: keyHeight,
anchorX: 0.5,
anchorY: 0.5
});
backspaceBg.tint = 0xff2853;
backspaceContainer.addChild(backspaceBg);
var backspaceText = new Text2('DELETE', {
size: 30,
fill: 0xFFFFFF,
font: "Impact"
});
backspaceText.anchor.set(0.5, 0.5);
backspaceContainer.addChild(backspaceText);
backspaceBg.down = function () {
namePopup.removeLetter();
};
return self;
});
var StartNameInputPopup = Container.expand(function () {
var self = Container.call(this);
// Semi-transparent background overlay
var overlay = LK.getAsset('uxoverlay', {
width: 2048,
height: 2732,
anchorX: 0,
anchorY: 0
});
overlay.tint = 0x000000;
overlay.alpha = 0.8;
self.addChild(overlay);
// Main popup container
var popup = new Container();
popup.x = game.width / 2;
popup.y = game.height / 2 - 200;
self.addChild(popup);
// Popup background
var popupBg = LK.getAsset('uxoverlay', {
width: 800,
height: 500,
anchorX: 0.5,
anchorY: 0.5
});
popupBg.tint = 0x2fbe2d;
popup.addChild(popupBg);
// Title text shadow
var titleShadow = new Text2('ENTER YOUR NAME', {
size: 80,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 4;
titleShadow.y = -150 + 4;
popup.addChild(titleShadow);
// Title text
var titleText = new Text2('ENTER YOUR NAME', {
size: 80,
fill: 0xFFFFFF,
font: "Impact"
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -150;
popup.addChild(titleText);
// Input prompt shadow
var promptShadow = new Text2('Name required to play:', {
size: 50,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
promptShadow.anchor.set(0.5, 0.5);
promptShadow.x = 4;
promptShadow.y = -80 + 4;
popup.addChild(promptShadow);
// Input prompt
var promptText = new Text2('Name required to play:', {
size: 50,
fill: 0xFFFFFF,
font: "Impact"
});
promptText.anchor.set(0.5, 0.5);
promptText.y = -80;
popup.addChild(promptText);
// Name input background
var inputBg = LK.getAsset('uxoverlay', {
width: 400,
height: 80,
anchorX: 0.5,
anchorY: 0.5
});
inputBg.tint = 0xFFFFFF;
inputBg.y = 0;
popup.addChild(inputBg);
// Current name text
var nameText = new Text2('', {
size: 45,
fill: 0x000000,
font: "Impact"
});
nameText.anchor.set(0.5, 0.5);
nameText.y = 0;
popup.addChild(nameText);
// Start button background
var buttonBg = LK.getAsset('uxoverlay', {
width: 200,
height: 60,
anchorX: 0.5,
anchorY: 0.5
});
buttonBg.tint = 0x43d11f;
buttonBg.y = 100;
buttonBg.alpha = 0.5; // Start disabled
popup.addChild(buttonBg);
// Start button text shadow
var buttonShadow = new Text2('START GAME', {
size: 35,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
buttonShadow.anchor.set(0.5, 0.5);
buttonShadow.x = 2;
buttonShadow.y = 102;
popup.addChild(buttonShadow);
// Start button text
var buttonText = new Text2('START GAME', {
size: 35,
fill: 0xFFFFFF,
font: "Impact"
});
buttonText.anchor.set(0.5, 0.5);
buttonText.y = 100;
popup.addChild(buttonText);
// Name input handling
self.playerName = '';
var cursor = '_';
var cursorVisible = true;
var cursorTimer = 0;
self.addLetter = function (letter) {
if (self.playerName.length < 12) {
self.playerName += letter;
self.updateDisplay();
}
};
self.removeLetter = function () {
if (self.playerName.length > 0) {
self.playerName = self.playerName.slice(0, -1);
self.updateDisplay();
}
};
self.updateDisplay = function () {
var displayText = self.playerName;
if (cursorVisible) displayText += cursor;
nameText.setText(displayText);
// Enable/disable start button based on name length
if (self.playerName.length > 0) {
buttonBg.alpha = 1;
} else {
buttonBg.alpha = 0.5;
}
};
self.startGame = function () {
if (self.playerName.length > 0) {
playerName = self.playerName;
gameCanStart = true;
// Remove this popup
self.destroy();
}
};
// Handle start button click
buttonBg.down = function () {
self.startGame();
};
// Update cursor blinking
self.update = function () {
cursorTimer++;
if (cursorTimer % 30 === 0) {
cursorVisible = !cursorVisible;
self.updateDisplay();
}
};
// Initial display
self.updateDisplay();
return self;
});
var WarningLine = Container.expand(function () {
var self = Container.call(this);
var warning = self.attachAsset('warningstripe', {
anchorX: .5,
anchorY: .5
});
var warningOffset = Math.random() * 100;
var speed = Math.random() * 1 + 1;
self.update = function () {
warningOffset += speed;
warning.alpha = (Math.cos(warningOffset / 50) + 1) / 2 * 0.3 + .7;
};
warning.blendMode = 1;
warning.rotation = .79;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x0c0d25
});
/****
* Game Code
****/
var gridSpeed = .5;
function increaseScore(amount) {
var currentScore = LK.getScore();
var newScore = currentScore + amount;
LK.setScore(newScore); // Update the game score using LK method
// Only animate if score actually increased and we're not already animating
if (newScore > lastScore && !isAnimatingScore) {
isAnimatingScore = true;
lastScore = newScore;
// Update all text elements
scoreLabel.setText(newScore.toString());
scoreLabelShadow.setText(newScore.toString());
scoreGlow.setText(newScore.toString());
// Stop any existing tweens
tween.stop(scoreContainer.scale);
tween.stop(scoreGlow);
// Scale up animation with bounce
tween(scoreContainer.scale, {
x: 1.2,
y: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Scale back down with bounce
tween(scoreContainer.scale, {
x: 1.0,
y: 1.0
}, {
duration: 200,
easing: tween.bounceOut,
onFinish: function onFinish() {
isAnimatingScore = false;
}
});
}
});
// Glow effect - fade in and out
tween(scoreGlow, {
alpha: 0.8
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(scoreGlow, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
// Subtle color pulse on main label
var originalTint = scoreLabel.tint;
scoreLabel.tint = 0xFFD700; // Gold color
tween(scoreLabel, {
tint: originalTint
}, {
duration: 400,
easing: tween.easeOut
});
} else {
// Just update text without animation
scoreLabel.setText(newScore.toString());
scoreLabelShadow.setText(newScore.toString());
scoreGlow.setText(newScore.toString());
lastScore = newScore;
}
}
//Game size 2048x2732
/*
Todo:
[X] Make sure we GC nodes that drop of screen
[ ] Make preview line fade out at the end
*/
var bulletsFired = 0; //3*30+1
var bubbleSize = 150;
var gameIsStarted = false;
var gameCanStart = true;
var playerName = '';
var bubbleColors = [0xff2853, 0x44d31f, 0x5252ff, 0xcb2bff, 0x28f2f0, 0xffc411];
var barriers = [];
var maxSelectableBubble = 3;
var warningLines = [];
for (var a = 0; a < 13; a++) {
var wl = game.addChild(new WarningLine());
wl.x = 2048 / 13 * (a + .5);
wl.y = 2200;
wl.alpha = 0;
warningLines.push(wl);
wl.scale.set(16, 40);
}
var warningOverlay = game.attachAsset('dangeroverlay', {});
warningOverlay.y = 2280;
// Create top earth bar that overlaps bubbles
var topEarthBar = new Container();
game.addChild(topEarthBar);
// Main earth bar background
var earthBarBg = topEarthBar.attachAsset('uxoverlay', {
width: 2048,
height: 120
});
earthBarBg.y = 0;
// Add diagonal cut effect on bottom edge using multiple small rectangles
for (var i = 0; i < 25; i++) {
var cutPiece = LK.getAsset('uxoverlay', {
width: 82,
height: 40
});
topEarthBar.addChild(cutPiece);
cutPiece.x = i * 82;
cutPiece.y = 120 - (i % 2 === 0 ? 15 : 25); // Alternating heights for jagged edge
cutPiece.tint = 0x268f26; // Slightly different green for variation
}
// Add some darker accent lines for depth
for (var i = 0; i < 3; i++) {
var accentLine = LK.getAsset('uxoverlay', {
width: 2048,
height: 8
});
topEarthBar.addChild(accentLine);
accentLine.tint = 0x1a5c1a; // Darker green
accentLine.y = 30 + i * 25;
}
// Add rocky texture details
for (var i = 0; i < 15; i++) {
var rockDetail = LK.getAsset('uxoverlay', {
width: 40 + Math.random() * 30,
height: 6 + Math.random() * 8
});
topEarthBar.addChild(rockDetail);
rockDetail.x = Math.random() * 2008 + 20;
rockDetail.y = 15 + Math.random() * 80;
rockDetail.tint = 0x1f7d1f; // Medium green
rockDetail.alpha = 0.7;
}
// Position the earth bar to overlap with bubbles (higher z-index)
topEarthBar.y = 0;
var uxoverlay = game.attachAsset('uxoverlay', {});
uxoverlay.y = 2440;
var uxoverlay2 = game.attachAsset('uxoverlay2', {});
uxoverlay2.y = 2460;
for (var a = 0; a < 4; a++) {
for (var b = 0; b < 3; b++) {
var barrier = game.addChild(new Barrier());
barrier.y = 2732 - 450 + b * 70;
barrier.x = 2048 / 5 * a + 2048 / 5;
barriers.push(barrier);
}
var barrierBlock = game.attachAsset('barrierblock', {});
barrierBlock.x = 2048 / 5 * a + 2048 / 5;
barrierBlock.y = 2732 - 450;
barrierBlock.anchor.x = .5;
}
// Create animated score display container
var scoreContainer = new Container();
scoreContainer.x = 0;
scoreContainer.y = 0;
LK.gui.top.addChild(scoreContainer);
// Create a score label dropshadow (offset, semi-transparent)
var scoreLabelShadow = new Text2('0', {
size: 120,
fill: 0x000000,
alpha: 0.35,
font: "Impact"
});
scoreLabelShadow.anchor.set(0.5, 0);
scoreLabelShadow.x = 4;
scoreLabelShadow.y = 6;
scoreContainer.addChild(scoreLabelShadow);
// Create a score label (main)
var scoreLabel = new Text2('0', {
size: 120,
fill: 0xFFFFFF,
font: "Impact"
});
scoreLabel.anchor.set(0.5, 0);
scoreLabel.x = 0;
scoreLabel.y = 0;
scoreContainer.addChild(scoreLabel);
// Create glow effect for score (initially invisible)
var scoreGlow = new Text2('0', {
size: 130,
fill: 0xFFD700,
font: "Impact"
});
scoreGlow.anchor.set(0.5, 0);
scoreGlow.x = 0;
scoreGlow.y = -5;
scoreGlow.alpha = 0;
scoreContainer.addChild(scoreGlow);
// Variables to track score animation state
var lastScore = 0;
var isAnimatingScore = false;
var scoreMultipliers = [];
var baseScores = [100, 250, 500, 250, 100];
for (var a = 0; a < 5; a++) {
var sm = new ScoreMultipliers(baseScores[a]);
sm.x = 2048 / 5 * a + 2048 / 10;
sm.y = 2300;
scoreMultipliers.push(sm);
game.addChild(sm);
}
var bonusUX = game.addChild(new BonusUX());
var fireBallPowerupOverlay = game.addChild(new FireBallPowerupOverlay());
var particlesLayer = game.addChild(new Container());
var grid = game.addChild(new Grid());
grid.y = 1000;
var freeBubbleLayer = game.addChild(new Container());
var hintBubblePlayer = game.addChild(new Container());
var launcher = game.addChild(new Launcher());
launcher.x = game.width / 2;
launcher.y = game.height - 138;
// Pause functionality is handled automatically by the LK engine
var hintBubbleCache = [];
var hintBubbles = [];
var isValid = false;
var path = [];
var bubbleAlpha = 1;
var hintTargetX = game.width / 2;
var hintTargetY = 0;
game.move = function (x, y, obj) {
hintTargetX = x;
hintTargetY = y;
refreshHintLine();
// }
};
game.down = game.move;
function getMaxTypes() {
if (bulletsFired > 30 * 3 * 3) {
return 6;
} else if (bulletsFired > 30 * 3) {
return 5;
} else if (bulletsFired > 30) {
return 4;
}
return 3;
}
function refreshHintLine() {
var ox = hintTargetX - launcher.x;
var oy = hintTargetY - launcher.y;
var angle = Math.atan2(oy, ox);
launcher.angle = angle;
isValid = angle < -.2 && angle > -Math.PI + .2;
if (isValid) {
path = grid.calculatePath(launcher, angle);
//This allows updated faster than 60fps, making everyting feel better.
}
renderHintBubbels();
}
var hintOffset = 0;
var distanceBetweenHintbubbles = 100;
function renderHintBubbels() {
if (isValid) {
hintOffset = hintOffset % distanceBetweenHintbubbles;
var distanceSinceLastDot = -hintOffset + 100;
var hintBubbleOffset = 0;
var lastPoint = path[0];
var bubble = launcher.getBubble();
var tint = bubble.isFireBall ? 0xff9c00 : bubbleColors[bubble.type];
var updateTint = true;
for (var a = 1; a < path.length; a++) {
var p2 = path[a];
var ox = p2.x - lastPoint.x;
var oy = p2.y - lastPoint.y;
var dist = Math.sqrt(ox * ox + oy * oy);
distanceSinceLastDot += dist;
if (distanceSinceLastDot >= distanceBetweenHintbubbles) {
var amountOver = distanceSinceLastDot - distanceBetweenHintbubbles;
var angle = Math.atan2(oy, ox);
var currentBubble = hintBubbles[hintBubbleOffset];
if (!currentBubble) {
currentBubble = hintBubbles[hintBubbleOffset] = new HintBubble();
hintBubblePlayer.addChild(currentBubble);
}
currentBubble.alpha = bubbleAlpha;
currentBubble.visible = true;
// Set the correct bubble type based on the launcher bubble
if (hintBubbleOffset == 0) {
currentBubble.setType(bubble.type, bubble.isFireBall);
} else {
currentBubble.setType(bubble.type, bubble.isFireBall);
}
currentBubble.x = lastPoint.x - Math.cos(angle) * amountOver;
currentBubble.y = lastPoint.y - Math.sin(angle) * amountOver;
hintBubbleOffset++;
distanceSinceLastDot = 0;
lastPoint = currentBubble;
} else {
lastPoint = p2;
}
}
for (var a = hintBubbleOffset; a < hintBubbles.length; a++) {
hintBubbles[a].visible = false;
}
} else {
for (var a = 0; a < hintBubbles.length; a++) {
hintBubbles[a].alpha = bubbleAlpha;
}
}
}
game.update = function () {
hintOffset += 5;
if (isValid) {
bubbleAlpha = Math.min(bubbleAlpha + .05, 1);
} else {
bubbleAlpha = Math.max(bubbleAlpha - .05, 0);
}
refreshHintLine();
var alphaList = grid.calculateWarningScoreList();
for (var a = 0; a < warningLines.length; a++) {
var value = alphaList[a] / 3;
warningLines[a].alpha += (Math.min(value, .6) - warningLines[a].alpha) / 100;
warningLines[a].scale.y += (value * 60 - warningLines[a].scale.y) / 100;
}
};
game.up = function () {
if (isValid) {
launcher.fire();
}
};
// Move top earth bar to front to overlay all bubbles
game.removeChild(topEarthBar);
game.addChild(topEarthBar);
// Game can start immediately
gameCanStart = true; ===================================================================
--- original.js
+++ change.js
@@ -1,9 +1,8 @@
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
-var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
@@ -831,31 +830,10 @@
if (bubble) {
if (bubble.y + self.y > 2200) {
LK.effects.flashScreen(0xff0000, 3000);
LK.getSound('gameOverJingle').play();
- // Check if current score qualifies for top 5
- var currentScore = LK.getScore();
- var leaderboard = storage.leaderboard || [];
- var isHighScore = false;
- if (leaderboard.length < 5) {
- isHighScore = true;
- } else {
- // Sort leaderboard to find lowest high score
- leaderboard.sort(function (a, b) {
- return b.score - a.score;
- });
- if (currentScore > leaderboard[4].score) {
- isHighScore = true;
- }
- }
- if (isHighScore && currentScore > 0) {
- // Show name input popup
- var namePopup = game.addChild(new NameInputPopup(currentScore));
- var keyboard = namePopup.addChild(new SimpleKeyboard(namePopup));
- } else {
- // Show regular game over
- LK.showGameOver();
- }
+ // Show regular game over
+ LK.showGameOver();
}
if (gameIsStarted) {
var targetSpeed = Math.pow(Math.pow((2200 - (bubble.y + self.y)) / 2200, 2), 2) * 4 + 0.5;
if (bubble.y + self.y > 2000) {
@@ -934,9 +912,8 @@
previewBubble.isFreeBubble = true;
}
createPreviewBubble();
self.fire = function () {
- if (!gameCanStart) return; // Don't allow firing until name is entered
bulletsFired++;
LK.getSound('fireBubble').play(); // Play sound when the ball is fired
grid.fireBubble(bubble, self.angle);
bubble = previewBubble;
@@ -1245,27 +1222,10 @@
nameText.setText(displayText);
};
self.submitScore = function () {
if (self.playerName.length > 0) {
- // Get existing leaderboard or create new one
- var leaderboard = storage.leaderboard || [];
- // Add new score
- leaderboard.push({
- name: self.playerName,
- score: finalScore
- });
- // Sort by score (highest first)
- leaderboard.sort(function (a, b) {
- return b.score - a.score;
- });
- // Keep only top 5
- if (leaderboard.length > 5) {
- leaderboard = leaderboard.slice(0, 5);
- }
- // Save to storage
- storage.leaderboard = leaderboard;
- // Show leaderboard
- var leaderboardPopup = game.addChild(new LeaderboardPopup(leaderboard));
+ // Just show game over instead of leaderboard
+ LK.showGameOver();
// Remove this popup
self.destroy();
}
};
@@ -1798,10 +1758,8 @@
}
};
self.startGame = function () {
if (self.playerName.length > 0) {
- // Save player name to storage
- storage.playerName = self.playerName;
playerName = self.playerName;
gameCanStart = true;
// Remove this popup
self.destroy();
@@ -1926,10 +1884,10 @@
*/
var bulletsFired = 0; //3*30+1
var bubbleSize = 150;
var gameIsStarted = false;
-var gameCanStart = false;
-var playerName = storage.playerName || '';
+var gameCanStart = true;
+var playerName = '';
var bubbleColors = [0xff2853, 0x44d31f, 0x5252ff, 0xcb2bff, 0x28f2f0, 0xffc411];
var barriers = [];
var maxSelectableBubble = 3;
var warningLines = [];
@@ -2070,9 +2028,8 @@
var bubbleAlpha = 1;
var hintTargetX = game.width / 2;
var hintTargetY = 0;
game.move = function (x, y, obj) {
- if (!gameCanStart) return; // Don't allow aiming until name is entered
hintTargetX = x;
hintTargetY = y;
refreshHintLine();
// }
@@ -2166,19 +2123,13 @@
warningLines[a].scale.y += (value * 60 - warningLines[a].scale.y) / 100;
}
};
game.up = function () {
- if (!gameCanStart) return; // Don't allow firing until name is entered
if (isValid) {
launcher.fire();
}
};
// Move top earth bar to front to overlay all bubbles
game.removeChild(topEarthBar);
game.addChild(topEarthBar);
-// Show name input popup at start if no name is stored
-if (!playerName) {
- var startNamePopup = game.addChild(new StartNameInputPopup());
- var startKeyboard = startNamePopup.addChild(new SimpleKeyboard(startNamePopup));
-} else {
- gameCanStart = true;
-}
\ No newline at end of file
+// Game can start immediately
+gameCanStart = true;
\ No newline at end of file
Soft straight Long red paint on black background. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
green notification bubble. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
bubble with a bunny on it. In-Game asset. 2d. High contrast. No shadows
a Carrot in a bubble. In-Game asset. 2d. High contrast. No shadows
an easten egg in a bubble. In-Game asset. 2d. High contrast. No shadows
the coin inside the bubble have a cute rabbit
a bubble with a cute rabbit holding a diamond stone like a troffy. In-Game asset. 2d. High contrast. No shadows
a bubble with a golden king rabbit inside sitting in a throne. In-Game asset. 2d. High contrast. No shadows
vertical and realistic carrot. In-Game asset. 2d. High contrast. No shadows
add a green color
a fire bubble ball with a rabbit on flame. In-Game asset. 2d. High contrast. No shadows
change the color to #2fbe2d
crear un rectangulo de color azu profundo. In-Game asset. 2d. High contrast. No shadows