User prompt
increase more little speed
User prompt
make speed a little fast of launcher
User prompt
make changes because the ball is not launching
Code edit (1 edits merged)
Please save this source code
User prompt
Bubble Blast: Cosmic Bubble Shooter
Initial prompt
Develop Bubble Blast, a professional 2D bubble shooter game using Unity or Godot, targeting PC and mobile platforms. Create addictive match-3 gameplay with smooth aiming, shooting, and popping mechanics, incorporating standard and special bubbles (bomb, rainbow, lightning). Design a vibrant cosmic-themed art style with modern 2D visuals, parallax backgrounds, and dynamic animations. Implement 100+ campaign levels, an endless mode, daily challenges, and a shop system for power-ups and cosmetics. Add global leaderboards, cloud saves, and monetization via in-app purchases and opt-in ads. Optimize for 60 FPS performance on low-to-mid-end devices, ensuring intuitive controls and engaging audio. Deliver a polished, addictive puzzle game with high replayability and regular content updates
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bubble = Container.expand(function (color, type) {
var self = Container.call(this);
self.color = color || 'red';
self.type = type || 'normal';
self.gridX = -1;
self.gridY = -1;
self.isMoving = false;
self.velocityX = 0;
self.velocityY = 0;
self.isMarkedForRemoval = false;
var assetName = 'bubble_' + self.color;
if (self.type === 'bomb') assetName = 'bubble_bomb';
if (self.type === 'rainbow') assetName = 'bubble_rainbow';
var bubbleGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (self.isMoving) {
self.x += self.velocityX;
self.y += self.velocityY;
// Bounce off walls
if (self.x <= 40 && self.velocityX < 0) {
self.velocityX = -self.velocityX;
}
if (self.x >= 2008 && self.velocityX > 0) {
self.velocityX = -self.velocityX;
}
}
};
return self;
});
var BubbleGrid = Container.expand(function () {
var self = Container.call(this);
self.grid = [];
self.gridWidth = 13;
self.gridHeight = 20;
self.bubbleSize = 80;
self.offsetX = 200;
self.offsetY = 200;
self.initializeGrid = function () {
for (var y = 0; y < self.gridHeight; y++) {
self.grid[y] = [];
for (var x = 0; x < self.gridWidth; x++) {
self.grid[y][x] = null;
}
}
// Create initial bubble pattern
var colors = ['red', 'blue', 'green', 'yellow', 'purple', 'orange'];
for (var y = 0; y < 8; y++) {
for (var x = 0; x < self.gridWidth; x++) {
if (y % 2 === 1 && x === self.gridWidth - 1) continue; // Offset rows
var randomColor = colors[Math.floor(Math.random() * colors.length)];
var bubble = new Bubble(randomColor);
self.addBubbleToGrid(bubble, x, y);
}
}
};
self.addBubbleToGrid = function (bubble, gridX, gridY) {
if (gridY < 0 || gridY >= self.gridHeight || gridX < 0 || gridX >= self.gridWidth) return false;
if (self.grid[gridY][gridX] !== null) return false;
bubble.gridX = gridX;
bubble.gridY = gridY;
self.grid[gridY][gridX] = bubble;
var offsetForRow = gridY % 2 * (self.bubbleSize / 2);
bubble.x = self.offsetX + gridX * self.bubbleSize + offsetForRow;
bubble.y = self.offsetY + gridY * self.bubbleSize * 0.85;
self.addChild(bubble);
return true;
};
self.getGridPosition = function (worldX, worldY) {
var localX = worldX - self.offsetX;
var localY = worldY - self.offsetY;
var gridY = Math.round(localY / (self.bubbleSize * 0.85));
var offsetForRow = gridY % 2 * (self.bubbleSize / 2);
var gridX = Math.round((localX - offsetForRow) / self.bubbleSize);
return {
x: gridX,
y: gridY
};
};
self.findMatchingBubbles = function (startX, startY, targetColor) {
var visited = [];
var matches = [];
function floodFill(x, y) {
if (y < 0 || y >= self.gridHeight || x < 0 || x >= self.gridWidth) return;
if (visited[y * self.gridWidth + x]) return;
if (!self.grid[y][x]) return;
if (self.grid[y][x].color !== targetColor && self.grid[y][x].type !== 'rainbow') return;
visited[y * self.gridWidth + x] = true;
matches.push({
x: x,
y: y,
bubble: self.grid[y][x]
});
// Check neighbors (hexagonal grid)
var neighbors = self.getNeighbors(x, y);
for (var i = 0; i < neighbors.length; i++) {
floodFill(neighbors[i].x, neighbors[i].y);
}
}
floodFill(startX, startY);
return matches;
};
self.getNeighbors = function (x, y) {
var neighbors = [];
var isOddRow = y % 2 === 1;
// Standard neighbors
if (x > 0) neighbors.push({
x: x - 1,
y: y
});
if (x < self.gridWidth - 1) neighbors.push({
x: x + 1,
y: y
});
// Upper neighbors
if (y > 0) {
neighbors.push({
x: x,
y: y - 1
});
if (isOddRow) {
if (x < self.gridWidth - 1) neighbors.push({
x: x + 1,
y: y - 1
});
} else {
if (x > 0) neighbors.push({
x: x - 1,
y: y - 1
});
}
}
// Lower neighbors
if (y < self.gridHeight - 1) {
neighbors.push({
x: x,
y: y + 1
});
if (isOddRow) {
if (x < self.gridWidth - 1) neighbors.push({
x: x + 1,
y: y + 1
});
} else {
if (x > 0) neighbors.push({
x: x - 1,
y: y + 1
});
}
}
return neighbors;
};
self.removeBubbles = function (bubblesToRemove) {
for (var i = 0; i < bubblesToRemove.length; i++) {
var bubble = bubblesToRemove[i].bubble;
self.grid[bubble.gridY][bubble.gridX] = null;
tween(bubble, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 200,
onFinish: function onFinish() {
if (bubble.parent) {
bubble.parent.removeChild(bubble);
}
}
});
}
LK.getSound('bubble_pop').play();
};
self.findFloatingBubbles = function () {
var connected = [];
var floating = [];
// Mark all bubbles connected to top row
function markConnected(x, y) {
if (y < 0 || y >= self.gridHeight || x < 0 || x >= self.gridWidth) return;
if (connected[y * self.gridWidth + x]) return;
if (!self.grid[y][x]) return;
connected[y * self.gridWidth + x] = true;
var neighbors = self.getNeighbors(x, y);
for (var i = 0; i < neighbors.length; i++) {
markConnected(neighbors[i].x, neighbors[i].y);
}
}
// Start from top row
for (var x = 0; x < self.gridWidth; x++) {
if (self.grid[0][x]) {
markConnected(x, 0);
}
}
// Find floating bubbles
for (var y = 0; y < self.gridHeight; y++) {
for (var x = 0; x < self.gridWidth; x++) {
if (self.grid[y][x] && !connected[y * self.gridWidth + x]) {
floating.push({
x: x,
y: y,
bubble: self.grid[y][x]
});
}
}
}
return floating;
};
self.checkLevelComplete = function () {
for (var y = 0; y < self.gridHeight; y++) {
for (var x = 0; x < self.gridWidth; x++) {
if (self.grid[y][x]) return false;
}
}
return true;
};
return self;
});
var Launcher = Container.expand(function () {
var self = Container.call(this);
self.currentBubble = null;
self.nextBubble = null;
self.aimAngle = -Math.PI / 2;
self.trajectoryDots = [];
self.isAiming = false;
var launcherGraphics = self.attachAsset('launcher', {
anchorX: 0.5,
anchorY: 0.5
});
self.generateNextBubble = function () {
var colors = ['red', 'blue', 'green', 'yellow', 'purple', 'orange'];
var randomColor = colors[Math.floor(Math.random() * colors.length)];
return new Bubble(randomColor);
};
self.loadNextBubble = function () {
if (self.currentBubble) {
self.removeChild(self.currentBubble);
}
self.currentBubble = self.nextBubble || self.generateNextBubble();
self.nextBubble = self.generateNextBubble();
if (self.currentBubble) {
self.addChild(self.currentBubble);
self.currentBubble.x = 0;
self.currentBubble.y = -40;
}
};
self.createTrajectoryDots = function () {
for (var i = 0; i < 20; i++) {
var dot = LK.getAsset('trajectory_dot', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.trajectoryDots.push(dot);
self.addChild(dot);
}
};
self.updateTrajectory = function () {
if (!self.isAiming || !self.currentBubble) {
for (var i = 0; i < self.trajectoryDots.length; i++) {
self.trajectoryDots[i].alpha = 0;
}
return;
}
var startX = self.x;
var startY = self.y - 40;
var velocityX = Math.cos(self.aimAngle) * 15;
var velocityY = Math.sin(self.aimAngle) * 15;
var simX = startX;
var simY = startY;
var simVelX = velocityX;
var simVelY = velocityY;
for (var i = 0; i < self.trajectoryDots.length; i++) {
simX += simVelX;
simY += simVelY;
// Bounce off walls
if (simX <= 40 && simVelX < 0) {
simVelX = -simVelX;
}
if (simX >= 2008 && simVelX > 0) {
simVelX = -simVelX;
}
if (simY < 200) break;
self.trajectoryDots[i].x = simX - self.x;
self.trajectoryDots[i].y = simY - self.y;
self.trajectoryDots[i].alpha = 1 - i / self.trajectoryDots.length;
}
};
self.shoot = function () {
if (!self.currentBubble) return null;
var shootingBubble = self.currentBubble;
self.removeChild(shootingBubble);
shootingBubble.isMoving = true;
shootingBubble.velocityX = Math.cos(self.aimAngle) * 15;
shootingBubble.velocityY = Math.sin(self.aimAngle) * 15;
self.currentBubble = null;
self.isAiming = false;
LK.getSound('bubble_shoot').play();
return shootingBubble;
};
return self;
});
var StarField = Container.expand(function () {
var self = Container.call(this);
self.stars = [];
self.numStars = 100;
self.createStars = function () {
for (var i = 0; i < self.numStars; i++) {
var star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
alpha: Math.random() * 0.8 + 0.2
});
star.x = Math.random() * 2048;
star.y = Math.random() * 2732;
star.speed = Math.random() * 2 + 0.5;
self.addChild(star);
self.stars.push(star);
}
};
self.update = function () {
for (var i = 0; i < self.stars.length; i++) {
var star = self.stars[i];
star.y += star.speed;
if (star.y > 2732) {
star.y = -10;
star.x = Math.random() * 2048;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a0d2e
});
/****
* Game Code
****/
var starField = game.addChild(new StarField());
starField.createStars();
var bubbleGrid = game.addChild(new BubbleGrid());
bubbleGrid.initializeGrid();
var launcher = game.addChild(new Launcher());
launcher.x = 1024;
launcher.y = 2600;
launcher.createTrajectoryDots();
launcher.loadNextBubble();
var currentShot = null;
var gameState = 'playing'; // 'playing', 'gameOver', 'levelComplete'
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var levelTxt = new Text2('Level 1', {
size: 50,
fill: 0xFFFFFF
});
levelTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(levelTxt);
function handleAiming(x, y) {
if (gameState !== 'playing' || currentShot) return;
var dx = x - launcher.x;
var dy = y - launcher.y;
if (dy < 0) {
launcher.aimAngle = Math.atan2(dy, dx);
launcher.isAiming = true;
launcher.updateTrajectory();
}
}
function handleShoot() {
if (gameState !== 'playing' || currentShot || !launcher.currentBubble) return;
if (launcher.isAiming) {
currentShot = launcher.shoot();
if (currentShot) {
game.addChild(currentShot);
}
}
}
game.move = function (x, y, obj) {
handleAiming(x, y);
};
game.down = function (x, y, obj) {
handleAiming(x, y);
};
game.up = function (x, y, obj) {
handleShoot();
};
game.update = function () {
starField.update();
if (currentShot) {
// Check collision with existing bubbles
var shotPos = bubbleGrid.getGridPosition(currentShot.x, currentShot.y);
// Check if shot reaches top boundary
if (currentShot.y <= bubbleGrid.offsetY) {
var validPos = false;
// Find nearest valid grid position
for (var dy = 0; dy < 3; dy++) {
for (var dx = -1; dx <= 1; dx++) {
var testX = shotPos.x + dx;
var testY = shotPos.y + dy;
if (bubbleGrid.addBubbleToGrid(currentShot, testX, testY)) {
validPos = true;
break;
}
}
if (validPos) break;
}
if (validPos) {
currentShot.isMoving = false;
// Check for matches
var matches = bubbleGrid.findMatchingBubbles(currentShot.gridX, currentShot.gridY, currentShot.color);
if (matches.length >= 3) {
bubbleGrid.removeBubbles(matches);
LK.setScore(LK.getScore() + matches.length * 10);
// Check for floating bubbles
var floating = bubbleGrid.findFloatingBubbles();
if (floating.length > 0) {
bubbleGrid.removeBubbles(floating);
LK.setScore(LK.getScore() + floating.length * 5);
}
}
// Check level complete
if (bubbleGrid.checkLevelComplete()) {
gameState = 'levelComplete';
LK.getSound('level_complete').play();
LK.showYouWin();
}
scoreTxt.setText('Score: ' + LK.getScore());
} else {
// Game over - couldn't place bubble
LK.showGameOver();
}
currentShot = null;
launcher.loadNextBubble();
} else {
// Check collision with grid bubbles
for (var y = 0; y < bubbleGrid.gridHeight; y++) {
for (var x = 0; x < bubbleGrid.gridWidth; x++) {
var gridBubble = bubbleGrid.grid[y][x];
if (gridBubble) {
var dx = currentShot.x - gridBubble.x;
var dy = currentShot.y - gridBubble.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 70) {
// Find nearest empty spot
var neighbors = bubbleGrid.getNeighbors(x, y);
var placed = false;
for (var i = 0; i < neighbors.length; i++) {
var neighbor = neighbors[i];
if (bubbleGrid.grid[neighbor.y] && bubbleGrid.grid[neighbor.y][neighbor.x] === null) {
if (bubbleGrid.addBubbleToGrid(currentShot, neighbor.x, neighbor.y)) {
placed = true;
break;
}
}
}
if (placed) {
currentShot.isMoving = false;
// Check for matches
var matches = bubbleGrid.findMatchingBubbles(currentShot.gridX, currentShot.gridY, currentShot.color);
if (matches.length >= 3) {
bubbleGrid.removeBubbles(matches);
LK.setScore(LK.getScore() + matches.length * 10);
// Check for floating bubbles
var floating = bubbleGrid.findFloatingBubbles();
if (floating.length > 0) {
bubbleGrid.removeBubbles(floating);
LK.setScore(LK.getScore() + floating.length * 5);
}
}
// Check level complete
if (bubbleGrid.checkLevelComplete()) {
gameState = 'levelComplete';
LK.getSound('level_complete').play();
LK.showYouWin();
}
scoreTxt.setText('Score: ' + LK.getScore());
currentShot = null;
launcher.loadNextBubble();
return;
}
}
}
}
}
}
}
launcher.updateTrajectory();
}; ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,503 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+var Bubble = Container.expand(function (color, type) {
+ var self = Container.call(this);
+ self.color = color || 'red';
+ self.type = type || 'normal';
+ self.gridX = -1;
+ self.gridY = -1;
+ self.isMoving = false;
+ self.velocityX = 0;
+ self.velocityY = 0;
+ self.isMarkedForRemoval = false;
+ var assetName = 'bubble_' + self.color;
+ if (self.type === 'bomb') assetName = 'bubble_bomb';
+ if (self.type === 'rainbow') assetName = 'bubble_rainbow';
+ var bubbleGraphics = self.attachAsset(assetName, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.update = function () {
+ if (self.isMoving) {
+ self.x += self.velocityX;
+ self.y += self.velocityY;
+ // Bounce off walls
+ if (self.x <= 40 && self.velocityX < 0) {
+ self.velocityX = -self.velocityX;
+ }
+ if (self.x >= 2008 && self.velocityX > 0) {
+ self.velocityX = -self.velocityX;
+ }
+ }
+ };
+ return self;
+});
+var BubbleGrid = Container.expand(function () {
+ var self = Container.call(this);
+ self.grid = [];
+ self.gridWidth = 13;
+ self.gridHeight = 20;
+ self.bubbleSize = 80;
+ self.offsetX = 200;
+ self.offsetY = 200;
+ self.initializeGrid = function () {
+ for (var y = 0; y < self.gridHeight; y++) {
+ self.grid[y] = [];
+ for (var x = 0; x < self.gridWidth; x++) {
+ self.grid[y][x] = null;
+ }
+ }
+ // Create initial bubble pattern
+ var colors = ['red', 'blue', 'green', 'yellow', 'purple', 'orange'];
+ for (var y = 0; y < 8; y++) {
+ for (var x = 0; x < self.gridWidth; x++) {
+ if (y % 2 === 1 && x === self.gridWidth - 1) continue; // Offset rows
+ var randomColor = colors[Math.floor(Math.random() * colors.length)];
+ var bubble = new Bubble(randomColor);
+ self.addBubbleToGrid(bubble, x, y);
+ }
+ }
+ };
+ self.addBubbleToGrid = function (bubble, gridX, gridY) {
+ if (gridY < 0 || gridY >= self.gridHeight || gridX < 0 || gridX >= self.gridWidth) return false;
+ if (self.grid[gridY][gridX] !== null) return false;
+ bubble.gridX = gridX;
+ bubble.gridY = gridY;
+ self.grid[gridY][gridX] = bubble;
+ var offsetForRow = gridY % 2 * (self.bubbleSize / 2);
+ bubble.x = self.offsetX + gridX * self.bubbleSize + offsetForRow;
+ bubble.y = self.offsetY + gridY * self.bubbleSize * 0.85;
+ self.addChild(bubble);
+ return true;
+ };
+ self.getGridPosition = function (worldX, worldY) {
+ var localX = worldX - self.offsetX;
+ var localY = worldY - self.offsetY;
+ var gridY = Math.round(localY / (self.bubbleSize * 0.85));
+ var offsetForRow = gridY % 2 * (self.bubbleSize / 2);
+ var gridX = Math.round((localX - offsetForRow) / self.bubbleSize);
+ return {
+ x: gridX,
+ y: gridY
+ };
+ };
+ self.findMatchingBubbles = function (startX, startY, targetColor) {
+ var visited = [];
+ var matches = [];
+ function floodFill(x, y) {
+ if (y < 0 || y >= self.gridHeight || x < 0 || x >= self.gridWidth) return;
+ if (visited[y * self.gridWidth + x]) return;
+ if (!self.grid[y][x]) return;
+ if (self.grid[y][x].color !== targetColor && self.grid[y][x].type !== 'rainbow') return;
+ visited[y * self.gridWidth + x] = true;
+ matches.push({
+ x: x,
+ y: y,
+ bubble: self.grid[y][x]
+ });
+ // Check neighbors (hexagonal grid)
+ var neighbors = self.getNeighbors(x, y);
+ for (var i = 0; i < neighbors.length; i++) {
+ floodFill(neighbors[i].x, neighbors[i].y);
+ }
+ }
+ floodFill(startX, startY);
+ return matches;
+ };
+ self.getNeighbors = function (x, y) {
+ var neighbors = [];
+ var isOddRow = y % 2 === 1;
+ // Standard neighbors
+ if (x > 0) neighbors.push({
+ x: x - 1,
+ y: y
+ });
+ if (x < self.gridWidth - 1) neighbors.push({
+ x: x + 1,
+ y: y
+ });
+ // Upper neighbors
+ if (y > 0) {
+ neighbors.push({
+ x: x,
+ y: y - 1
+ });
+ if (isOddRow) {
+ if (x < self.gridWidth - 1) neighbors.push({
+ x: x + 1,
+ y: y - 1
+ });
+ } else {
+ if (x > 0) neighbors.push({
+ x: x - 1,
+ y: y - 1
+ });
+ }
+ }
+ // Lower neighbors
+ if (y < self.gridHeight - 1) {
+ neighbors.push({
+ x: x,
+ y: y + 1
+ });
+ if (isOddRow) {
+ if (x < self.gridWidth - 1) neighbors.push({
+ x: x + 1,
+ y: y + 1
+ });
+ } else {
+ if (x > 0) neighbors.push({
+ x: x - 1,
+ y: y + 1
+ });
+ }
+ }
+ return neighbors;
+ };
+ self.removeBubbles = function (bubblesToRemove) {
+ for (var i = 0; i < bubblesToRemove.length; i++) {
+ var bubble = bubblesToRemove[i].bubble;
+ self.grid[bubble.gridY][bubble.gridX] = null;
+ tween(bubble, {
+ alpha: 0,
+ scaleX: 0.1,
+ scaleY: 0.1
+ }, {
+ duration: 200,
+ onFinish: function onFinish() {
+ if (bubble.parent) {
+ bubble.parent.removeChild(bubble);
+ }
+ }
+ });
+ }
+ LK.getSound('bubble_pop').play();
+ };
+ self.findFloatingBubbles = function () {
+ var connected = [];
+ var floating = [];
+ // Mark all bubbles connected to top row
+ function markConnected(x, y) {
+ if (y < 0 || y >= self.gridHeight || x < 0 || x >= self.gridWidth) return;
+ if (connected[y * self.gridWidth + x]) return;
+ if (!self.grid[y][x]) return;
+ connected[y * self.gridWidth + x] = true;
+ var neighbors = self.getNeighbors(x, y);
+ for (var i = 0; i < neighbors.length; i++) {
+ markConnected(neighbors[i].x, neighbors[i].y);
+ }
+ }
+ // Start from top row
+ for (var x = 0; x < self.gridWidth; x++) {
+ if (self.grid[0][x]) {
+ markConnected(x, 0);
+ }
+ }
+ // Find floating bubbles
+ for (var y = 0; y < self.gridHeight; y++) {
+ for (var x = 0; x < self.gridWidth; x++) {
+ if (self.grid[y][x] && !connected[y * self.gridWidth + x]) {
+ floating.push({
+ x: x,
+ y: y,
+ bubble: self.grid[y][x]
+ });
+ }
+ }
+ }
+ return floating;
+ };
+ self.checkLevelComplete = function () {
+ for (var y = 0; y < self.gridHeight; y++) {
+ for (var x = 0; x < self.gridWidth; x++) {
+ if (self.grid[y][x]) return false;
+ }
+ }
+ return true;
+ };
+ return self;
+});
+var Launcher = Container.expand(function () {
+ var self = Container.call(this);
+ self.currentBubble = null;
+ self.nextBubble = null;
+ self.aimAngle = -Math.PI / 2;
+ self.trajectoryDots = [];
+ self.isAiming = false;
+ var launcherGraphics = self.attachAsset('launcher', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.generateNextBubble = function () {
+ var colors = ['red', 'blue', 'green', 'yellow', 'purple', 'orange'];
+ var randomColor = colors[Math.floor(Math.random() * colors.length)];
+ return new Bubble(randomColor);
+ };
+ self.loadNextBubble = function () {
+ if (self.currentBubble) {
+ self.removeChild(self.currentBubble);
+ }
+ self.currentBubble = self.nextBubble || self.generateNextBubble();
+ self.nextBubble = self.generateNextBubble();
+ if (self.currentBubble) {
+ self.addChild(self.currentBubble);
+ self.currentBubble.x = 0;
+ self.currentBubble.y = -40;
+ }
+ };
+ self.createTrajectoryDots = function () {
+ for (var i = 0; i < 20; i++) {
+ var dot = LK.getAsset('trajectory_dot', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0
+ });
+ self.trajectoryDots.push(dot);
+ self.addChild(dot);
+ }
+ };
+ self.updateTrajectory = function () {
+ if (!self.isAiming || !self.currentBubble) {
+ for (var i = 0; i < self.trajectoryDots.length; i++) {
+ self.trajectoryDots[i].alpha = 0;
+ }
+ return;
+ }
+ var startX = self.x;
+ var startY = self.y - 40;
+ var velocityX = Math.cos(self.aimAngle) * 15;
+ var velocityY = Math.sin(self.aimAngle) * 15;
+ var simX = startX;
+ var simY = startY;
+ var simVelX = velocityX;
+ var simVelY = velocityY;
+ for (var i = 0; i < self.trajectoryDots.length; i++) {
+ simX += simVelX;
+ simY += simVelY;
+ // Bounce off walls
+ if (simX <= 40 && simVelX < 0) {
+ simVelX = -simVelX;
+ }
+ if (simX >= 2008 && simVelX > 0) {
+ simVelX = -simVelX;
+ }
+ if (simY < 200) break;
+ self.trajectoryDots[i].x = simX - self.x;
+ self.trajectoryDots[i].y = simY - self.y;
+ self.trajectoryDots[i].alpha = 1 - i / self.trajectoryDots.length;
+ }
+ };
+ self.shoot = function () {
+ if (!self.currentBubble) return null;
+ var shootingBubble = self.currentBubble;
+ self.removeChild(shootingBubble);
+ shootingBubble.isMoving = true;
+ shootingBubble.velocityX = Math.cos(self.aimAngle) * 15;
+ shootingBubble.velocityY = Math.sin(self.aimAngle) * 15;
+ self.currentBubble = null;
+ self.isAiming = false;
+ LK.getSound('bubble_shoot').play();
+ return shootingBubble;
+ };
+ return self;
+});
+var StarField = Container.expand(function () {
+ var self = Container.call(this);
+ self.stars = [];
+ self.numStars = 100;
+ self.createStars = function () {
+ for (var i = 0; i < self.numStars; i++) {
+ var star = LK.getAsset('star', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: Math.random() * 0.8 + 0.2
+ });
+ star.x = Math.random() * 2048;
+ star.y = Math.random() * 2732;
+ star.speed = Math.random() * 2 + 0.5;
+ self.addChild(star);
+ self.stars.push(star);
+ }
+ };
+ self.update = function () {
+ for (var i = 0; i < self.stars.length; i++) {
+ var star = self.stars[i];
+ star.y += star.speed;
+ if (star.y > 2732) {
+ star.y = -10;
+ star.x = Math.random() * 2048;
+ }
+ }
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x1a0d2e
+});
+
+/****
+* Game Code
+****/
+var starField = game.addChild(new StarField());
+starField.createStars();
+var bubbleGrid = game.addChild(new BubbleGrid());
+bubbleGrid.initializeGrid();
+var launcher = game.addChild(new Launcher());
+launcher.x = 1024;
+launcher.y = 2600;
+launcher.createTrajectoryDots();
+launcher.loadNextBubble();
+var currentShot = null;
+var gameState = 'playing'; // 'playing', 'gameOver', 'levelComplete'
+var scoreTxt = new Text2('Score: 0', {
+ size: 60,
+ fill: 0xFFFFFF
+});
+scoreTxt.anchor.set(0.5, 0);
+LK.gui.top.addChild(scoreTxt);
+var levelTxt = new Text2('Level 1', {
+ size: 50,
+ fill: 0xFFFFFF
+});
+levelTxt.anchor.set(1, 0);
+LK.gui.topRight.addChild(levelTxt);
+function handleAiming(x, y) {
+ if (gameState !== 'playing' || currentShot) return;
+ var dx = x - launcher.x;
+ var dy = y - launcher.y;
+ if (dy < 0) {
+ launcher.aimAngle = Math.atan2(dy, dx);
+ launcher.isAiming = true;
+ launcher.updateTrajectory();
+ }
+}
+function handleShoot() {
+ if (gameState !== 'playing' || currentShot || !launcher.currentBubble) return;
+ if (launcher.isAiming) {
+ currentShot = launcher.shoot();
+ if (currentShot) {
+ game.addChild(currentShot);
+ }
+ }
+}
+game.move = function (x, y, obj) {
+ handleAiming(x, y);
+};
+game.down = function (x, y, obj) {
+ handleAiming(x, y);
+};
+game.up = function (x, y, obj) {
+ handleShoot();
+};
+game.update = function () {
+ starField.update();
+ if (currentShot) {
+ // Check collision with existing bubbles
+ var shotPos = bubbleGrid.getGridPosition(currentShot.x, currentShot.y);
+ // Check if shot reaches top boundary
+ if (currentShot.y <= bubbleGrid.offsetY) {
+ var validPos = false;
+ // Find nearest valid grid position
+ for (var dy = 0; dy < 3; dy++) {
+ for (var dx = -1; dx <= 1; dx++) {
+ var testX = shotPos.x + dx;
+ var testY = shotPos.y + dy;
+ if (bubbleGrid.addBubbleToGrid(currentShot, testX, testY)) {
+ validPos = true;
+ break;
+ }
+ }
+ if (validPos) break;
+ }
+ if (validPos) {
+ currentShot.isMoving = false;
+ // Check for matches
+ var matches = bubbleGrid.findMatchingBubbles(currentShot.gridX, currentShot.gridY, currentShot.color);
+ if (matches.length >= 3) {
+ bubbleGrid.removeBubbles(matches);
+ LK.setScore(LK.getScore() + matches.length * 10);
+ // Check for floating bubbles
+ var floating = bubbleGrid.findFloatingBubbles();
+ if (floating.length > 0) {
+ bubbleGrid.removeBubbles(floating);
+ LK.setScore(LK.getScore() + floating.length * 5);
+ }
+ }
+ // Check level complete
+ if (bubbleGrid.checkLevelComplete()) {
+ gameState = 'levelComplete';
+ LK.getSound('level_complete').play();
+ LK.showYouWin();
+ }
+ scoreTxt.setText('Score: ' + LK.getScore());
+ } else {
+ // Game over - couldn't place bubble
+ LK.showGameOver();
+ }
+ currentShot = null;
+ launcher.loadNextBubble();
+ } else {
+ // Check collision with grid bubbles
+ for (var y = 0; y < bubbleGrid.gridHeight; y++) {
+ for (var x = 0; x < bubbleGrid.gridWidth; x++) {
+ var gridBubble = bubbleGrid.grid[y][x];
+ if (gridBubble) {
+ var dx = currentShot.x - gridBubble.x;
+ var dy = currentShot.y - gridBubble.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < 70) {
+ // Find nearest empty spot
+ var neighbors = bubbleGrid.getNeighbors(x, y);
+ var placed = false;
+ for (var i = 0; i < neighbors.length; i++) {
+ var neighbor = neighbors[i];
+ if (bubbleGrid.grid[neighbor.y] && bubbleGrid.grid[neighbor.y][neighbor.x] === null) {
+ if (bubbleGrid.addBubbleToGrid(currentShot, neighbor.x, neighbor.y)) {
+ placed = true;
+ break;
+ }
+ }
+ }
+ if (placed) {
+ currentShot.isMoving = false;
+ // Check for matches
+ var matches = bubbleGrid.findMatchingBubbles(currentShot.gridX, currentShot.gridY, currentShot.color);
+ if (matches.length >= 3) {
+ bubbleGrid.removeBubbles(matches);
+ LK.setScore(LK.getScore() + matches.length * 10);
+ // Check for floating bubbles
+ var floating = bubbleGrid.findFloatingBubbles();
+ if (floating.length > 0) {
+ bubbleGrid.removeBubbles(floating);
+ LK.setScore(LK.getScore() + floating.length * 5);
+ }
+ }
+ // Check level complete
+ if (bubbleGrid.checkLevelComplete()) {
+ gameState = 'levelComplete';
+ LK.getSound('level_complete').play();
+ LK.showYouWin();
+ }
+ scoreTxt.setText('Score: ' + LK.getScore());
+ currentShot = null;
+ launcher.loadNextBubble();
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ launcher.updateTrajectory();
+};
\ No newline at end of file