User prompt
move click x2 a little up
User prompt
actually click should say +1 not x1
User prompt
click should be +1 and the updated to +2 and so on
User prompt
on game start there has to be one ball already
User prompt
on game start load current points, and if it is not empty, use it to replace the $0 placehodler
Code edit (3 edits merged)
Please save this source code
User prompt
in new levelballs will randomly spawn from the top or the bottom of the screen but not in a position where there is a brick
User prompt
can we have the grid centered in the y axis when loaded
User prompt
when balls are spawned either from the top or the bottom, make sure it ese even, so if there are 4 balls, two top and 2 bottom.
User prompt
close upgrades when player click on a brick
User prompt
upgrade button should be greyed out if player don't have enough money to buy upgrade
User prompt
Rename upgrade button for UPGRADES!
Code edit (1 edits merged)
Please save this source code
User prompt
move welcome message 50 pixels down
Code edit (1 edits merged)
Please save this source code
User prompt
upgrade button shoudl be greyed out if none of the powerups can be purachesd
User prompt
first notmal extra ball should cost 25
User prompt
update price of first ball to 25 in button
Code edit (2 edits merged)
Please save this source code
User prompt
normal ball should always double its prize
User prompt
when upgrades button is disabled also close powerups
User prompt
can we crate a frame arroudn the whole screen? it will be used to give the feel ing that balls are bouncing there
User prompt
frame shoudl use its own asset frame
User prompt
Frame is only covering the top left of the screen
User prompt
can we move thhe left and bottom frames closer to the center of the screen
===================================================================
--- original.js
+++ change.js
@@ -34,188 +34,150 @@
self.direction.y /= magnitude;
};
self.update = function () {
var gridSize = levelConfig[level] ? levelConfig[level].gridSize : 200;
- var velocityX = self.direction.x * self.speed;
- var velocityY = self.direction.y * self.speed;
- // Store previous position for continuous collision detection
- var oldX = self.x;
- var oldY = self.y;
- var newX = self.x + velocityX;
- var newY = self.y + velocityY;
- // Wall collisions
- if (newX <= BALL_RADIUS || newX >= GAME_WIDTH - BALL_RADIUS) {
- velocityX *= -1;
- newX = Math.max(BALL_RADIUS, Math.min(GAME_WIDTH - BALL_RADIUS, newX));
- velocityY += (Math.random() - 0.5) * 0.1 * self.speed;
- LK.getSound('bounce').play();
- }
- if (newY <= BALL_RADIUS || newY >= GAME_HEIGHT - BALL_RADIUS) {
- velocityY *= -1;
- newY = Math.max(BALL_RADIUS, Math.min(GAME_HEIGHT - BALL_RADIUS, newY));
- velocityX += (Math.random() - 0.5) * 0.1 * self.speed;
- LK.getSound('bounce').play();
- }
- // Sniper logic
+ var stepSize = self.speed;
if (self.type === 'sniper') {
self.sniperCooldown--;
if (self.sniperCooldown <= 0) {
var nearestBrick = findNearestBrick(self.x, self.y);
if (nearestBrick) {
var dx = nearestBrick.x - self.x;
var dy = nearestBrick.y - self.y;
var magnitude = Math.sqrt(dx * dx + dy * dy);
- velocityX = dx / magnitude * self.speed;
- velocityY = dy / magnitude * self.speed;
+ self.direction.x = dx / magnitude;
+ self.direction.y = dy / magnitude;
self.sniperCooldown = self.sniperCooldownMax;
}
}
}
- // Continuous collision detection with bricks
- if (isNearBricks(newX, newY)) {
- var gridXMin = Math.floor((Math.min(oldX, newX) - BALL_RADIUS) / gridSize);
- var gridXMax = Math.floor((Math.max(oldX, newX) + BALL_RADIUS) / gridSize);
- var gridYMin = Math.floor((Math.min(oldY, newY) - BALL_RADIUS) / gridSize);
- var gridYMax = Math.floor((Math.max(oldY, newY) + BALL_RADIUS) / gridSize);
- var closestCollision = null;
- var closestTime = 1; // Time of collision (0 = start, 1 = end of frame)
- for (var gx = gridXMin; gx <= gridXMax; gx++) {
- for (var gy = gridYMin; gy <= gridYMax; gy++) {
+ // Move in smaller steps to catch collisions accurately
+ var steps = Math.ceil(stepSize / BALL_RADIUS); // Break movement into smaller chunks
+ var dx = self.direction.x * stepSize / steps;
+ var dy = self.direction.y * stepSize / steps;
+ for (var step = 0; step < steps; step++) {
+ self.x += dx;
+ self.y += dy;
+ // Wall collisions
+ if (self.x <= BALL_RADIUS || self.x >= GAME_WIDTH - BALL_RADIUS) {
+ self.direction.x *= -1;
+ self.x = Math.max(BALL_RADIUS, Math.min(GAME_WIDTH - BALL_RADIUS, self.x));
+ self.direction.y += (Math.random() - 0.5) * 0.1;
+ LK.getSound('bounce').play();
+ }
+ if (self.y <= BALL_RADIUS || self.y >= GAME_HEIGHT - BALL_RADIUS) {
+ self.direction.y *= -1;
+ self.y = Math.max(BALL_RADIUS, Math.min(GAME_HEIGHT - BALL_RADIUS, self.y));
+ self.direction.x += (Math.random() - 0.5) * 0.1;
+ LK.getSound('bounce').play();
+ }
+ // Skip brick collision check if not near bricks
+ if (!isNearBricks(self.x, self.y)) {
+ continue;
+ }
+ // Check collisions with bricks in nearby grid cells
+ var gridXMin = Math.floor((self.x - BALL_RADIUS) / gridSize);
+ var gridXMax = Math.floor((self.x + BALL_RADIUS) / gridSize);
+ var gridYMin = Math.floor((self.y - BALL_RADIUS) / gridSize);
+ var gridYMax = Math.floor((self.y + BALL_RADIUS) / gridSize);
+ var hasCollided = false;
+ for (var gx = gridXMin; gx <= gridXMax && !hasCollided; gx++) {
+ for (var gy = gridYMin; gy <= gridYMax && !hasCollided; gy++) {
var gridKey = "".concat(gx, ",").concat(gy);
var cellBricks = brickGrid[gridKey];
if (!cellBricks || cellBricks.length === 0) {
continue;
}
- for (var j = 0; j < cellBricks.length; j++) {
+ for (var j = cellBricks.length - 1; j >= 0; j--) {
var brick = cellBricks[j];
if (!brick || brick.health <= 0) {
continue;
}
- var collision = checkCollision(oldX, oldY, newX, newY, brick);
- if (collision && collision.time < closestTime) {
- closestCollision = collision;
- closestTime = collision.time;
- closestCollision.brick = brick;
- closestCollision.brickIndex = j;
- closestCollision.gridKey = gridKey;
+ // Precise circle vs. rectangle collision
+ var collision = checkCollision(self, brick);
+ if (!collision) {
+ continue;
}
+ // Resolve collision
+ resolveCollision(self, brick, collision);
+ brick.hit(self.power);
+ if (self.type === 'splash' && brick.health > 0) {
+ applySplashDamage(brick, gridSize);
+ } else if (self.type === 'scatter') {
+ scatterOnImpact(self);
+ self.destroy();
+ balls.splice(balls.indexOf(self), 1);
+ hasCollided = true;
+ break;
+ }
+ if (brick.health <= 0) {
+ cellBricks.splice(j, 1);
+ }
+ hasCollided = true;
+ break;
}
}
}
- if (closestCollision) {
- // Move to collision point
- self.x = oldX + velocityX * closestTime;
- self.y = oldY + velocityY * closestTime;
- // Resolve collision
- resolveCollision(self, closestCollision, velocityX, velocityY);
- // Handle brick interaction
- var brick = closestCollision.brick;
- brick.hit(self.power);
- if (self.type === 'splash' && brick.health > 0) {
- applySplashDamage(brick, gridSize);
- } else if (self.type === 'scatter') {
- scatterOnImpact(self);
- self.destroy();
- balls.splice(balls.indexOf(self), 1);
- return; // Exit update after scatter
- }
- if (brick.health <= 0) {
- brickGrid[closestCollision.gridKey].splice(closestCollision.brickIndex, 1);
- }
- // Update velocity to direction for next frame
- velocityX = self.direction.x * self.speed;
- velocityY = self.direction.y * self.speed;
- } else {
- // No collision, move to new position
- self.x = newX;
- self.y = newY;
- }
- } else {
- // No bricks nearby, move to new position
- self.x = newX;
- self.y = newY;
+ if (hasCollided) {
+ break;
+ } // Stop stepping after a collision
}
- // Update direction based on final velocity
- var magnitude = Math.sqrt(velocityX * velocityX + velocityY * velocityY);
+ // Normalize direction to maintain consistent speed
+ var magnitude = Math.sqrt(self.direction.x * self.direction.x + self.direction.y * self.direction.y);
if (magnitude > 0) {
- self.direction.x = velocityX / magnitude;
- self.direction.y = velocityY / magnitude;
+ self.direction.x /= magnitude;
+ self.direction.y /= magnitude;
}
};
- // Continuous collision detection: Circle vs. Rectangle over a frame
- function checkCollision(oldX, oldY, newX, newY, brick) {
- var vx = newX - oldX;
- var vy = newY - oldY;
- // Expand brick bounds by ball radius
- var left = brick.x - BRICK_WIDTH / 2 - BALL_RADIUS;
- var right = brick.x + BRICK_WIDTH / 2 + BALL_RADIUS;
- var top = brick.y - BRICK_HEIGHT / 2 - BALL_RADIUS;
- var bottom = brick.y + BRICK_HEIGHT / 2 + BALL_RADIUS;
- // Early exit if no movement or already outside bounds
- if (vx === 0 && vy === 0) {
- return null;
+ // Precise collision check: Circle (ball) vs. Rectangle (brick)
+ function checkCollision(ball, brick) {
+ var closestX = Math.max(brick.x - BRICK_WIDTH / 2, Math.min(ball.x, brick.x + BRICK_WIDTH / 2));
+ var closestY = Math.max(brick.y - BRICK_HEIGHT / 2, Math.min(ball.y, brick.y + BRICK_HEIGHT / 2));
+ var dx = ball.x - closestX;
+ var dy = ball.y - closestY;
+ var distanceSquared = dx * dx + dy * dy;
+ if (distanceSquared <= BALL_RADIUS * BALL_RADIUS) {
+ // Determine collision side
+ var relX = ball.x - brick.x;
+ var relY = ball.y - brick.y;
+ var absX = Math.abs(relX);
+ var absY = Math.abs(relY);
+ var halfW = BRICK_WIDTH / 2 + BALL_RADIUS;
+ var halfH = BRICK_HEIGHT / 2 + BALL_RADIUS;
+ if (absX > halfW || absY > halfH) {
+ return null;
+ } // Outside bounding box
+ if (absX / halfW > absY / halfH) {
+ return {
+ side: relX > 0 ? 'right' : 'left',
+ normalX: relX > 0 ? 1 : -1,
+ normalY: 0
+ };
+ } else {
+ return {
+ side: relY > 0 ? 'bottom' : 'top',
+ normalX: 0,
+ normalY: relY > 0 ? 1 : -1
+ };
+ }
}
- if (oldX < left && newX < left) {
- return null;
- }
- if (oldX > right && newX > right) {
- return null;
- }
- if (oldY < top && newY < top) {
- return null;
- }
- if (oldY > bottom && newY > bottom) {
- return null;
- }
- // Calculate time of collision for each axis
- var timeX = vx > 0 ? (left - oldX) / vx : (right - oldX) / vx;
- var timeY = vy > 0 ? (top - oldY) / vy : (bottom - oldY) / vy;
- timeX = isNaN(timeX) || timeX < 0 || timeX > 1 ? Infinity : timeX;
- timeY = isNaN(timeY) || timeY < 0 || timeY > 1 ? Infinity : timeY;
- var entryTime = Math.max(timeX, timeY);
- if (entryTime >= 1 || entryTime < 0) {
- return null;
- }
- // Check if the ball is actually inside at the collision time
- var hitX = oldX + vx * entryTime;
- var hitY = oldY + vy * entryTime;
- if (hitX < left || hitX > right || hitY < top || hitY > bottom) {
- return null;
- }
- // Determine collision normal
- var normalX = 0;
- var normalY = 0;
- if (entryTime === timeX) {
- normalX = vx > 0 ? -1 : 1;
- } else {
- normalY = vy > 0 ? -1 : 1;
- }
- return {
- time: entryTime,
- normalX: normalX,
- normalY: normalY
- };
+ return null;
}
- // Resolve collision with velocity reflection
- function resolveCollision(ball, collision, velocityX, velocityY) {
- // Reflect velocity based on normal
+ // Resolve collision: Adjust position and reflect direction
+ function resolveCollision(ball, brick, collision) {
+ // Move ball out of brick based on collision normal
+ var overlapX = BALL_RADIUS - Math.abs(ball.x - (brick.x + collision.normalX * BRICK_WIDTH / 2));
+ var overlapY = BALL_RADIUS - Math.abs(ball.y - (brick.y + collision.normalY * BRICK_HEIGHT / 2));
if (collision.normalX !== 0) {
- velocityX = -velocityX;
- ball.x += collision.normalX * 0.1; // Nudge slightly to prevent sticking
+ ball.x += collision.normalX * overlapX;
+ ball.direction.x = -ball.direction.x;
+ } else if (collision.normalY !== 0) {
+ ball.y += collision.normalY * overlapY;
+ ball.direction.y = -ball.direction.y;
}
- if (collision.normalY !== 0) {
- velocityY = -velocityY;
- ball.y += collision.normalY * 0.1; // Nudge slightly to prevent sticking
- }
- // Add randomness to avoid straight bounces
- velocityX += (Math.random() - 0.5) * 0.2 * ball.speed;
- velocityY += (Math.random() - 0.5) * 0.2 * ball.speed;
- // Update direction based on new velocity
- var magnitude = Math.sqrt(velocityX * velocityX + velocityY * velocityY);
- if (magnitude > 0) {
- ball.direction.x = velocityX / magnitude;
- ball.direction.y = velocityY / magnitude;
- }
+ // Add slight randomness to prevent sticking or straight bounces
+ ball.direction.x += (Math.random() - 0.5) * 0.2;
+ ball.direction.y += (Math.random() - 0.5) * 0.2;
LK.getSound('bounce').play();
}
});
var Brick = Container.expand(function () {
@@ -1186,10 +1148,17 @@
function createBall() {
var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'normal';
var ball = new Ball(type);
var gridBottom = brickGridBounds ? brickGridBounds.maxY + 150 : GAME_HEIGHT * 0.7;
+ var spawnFromTop = Math.random() < 0.5;
ball.x = GAME_WIDTH / 2 + (Math.random() * 200 - 100);
- ball.y = Math.min(gridBottom + 100, GAME_HEIGHT - BALL_RADIUS * 2);
+ ball.y = spawnFromTop ? BALL_RADIUS : GAME_HEIGHT - BALL_RADIUS;
+ for (var i = 0; i < bricks.length; i++) {
+ if (ball.intersects(bricks[i])) {
+ ball.y = spawnFromTop ? BALL_RADIUS : GAME_HEIGHT - BALL_RADIUS;
+ break;
+ }
+ }
var angle = (Math.random() * 0.5 + 0.25) * Math.PI;
ball.direction.x = Math.cos(angle);
ball.direction.y = -Math.sin(angle);
var magnitude = Math.sqrt(ball.direction.x * ball.direction.x + ball.direction.y * ball.direction.y);