Initial commit

This commit is contained in:
Marcel 2019-04-06 19:19:26 +02:00
commit 5c8296fbe3
15 changed files with 1078 additions and 0 deletions

BIN
assets/game_over.wav Normal file

Binary file not shown.

BIN
assets/hit_ball.wav Normal file

Binary file not shown.

BIN
assets/hit_block.wav Normal file

Binary file not shown.

BIN
assets/hit_coin.wav Normal file

Binary file not shown.

BIN
assets/round_success.wav Normal file

Binary file not shown.

107
ball.js Normal file
View File

@ -0,0 +1,107 @@
class Ball {
constructor(game, cx, cy, v, dir) {
this.game = game;
this.id = this.game.balls.length;
this.cx = cx;
this.cy = cy;
this.vx = v * Math.cos(dir);
this.vy = v * Math.sin(dir);
this.collide = true;
}
update(stepCount = 10) {
if (!this.game.isMoving)
return;
for (let i = 0; i < stepCount; i++) {
this.cx += this.vx / stepCount;
this.cy -= this.vy / stepCount;
if (this.cx - this.game.ballRadius <= 0 || this.cx + this.game.ballRadius >= this.game.canvas.width) {
this.vx *= -1;
}
if (this.cy - this.game.ballRadius <= 0) {
this.vy *= -1;
}
if (this.cy >= this.game.originY && this.vy < 0) {
this.vy = 0;
this.cy = this.game.originY;
if (this.game.newOriginX === null || this.cx === this.game.newOriginX) {
this.game.newOriginX = this.cx;
this.vy = 0;
this.remove();
} else {
this.vx = (this.game.newOriginX - this.cx) / 60 * 2;
setTimeout(() => {
this.vx = 0;
this.remove();
}, 500);
}
}
if (this.collide) {
this.game.rects.forEach(rect => {
this.checkHitRect(rect);
});
this.game.powerUps.forEach(powerUp => {
this.checkHitPowerUp(powerUp);
});
}
}
}
remove() {
this.game.balls.splice(this.game.balls.findIndex(ball => ball === this), 1);
if (this.game.isMoving && this.game.balls.length === 0) {
this.game.roundOver();
}
}
checkHitRect(rect) {
const closestX = Math.clamp(this.cx, rect.x, rect.x + this.game.rectSize);
const closestY = Math.clamp(this.cy, rect.y, rect.y + this.game.rectSize);
const distX = this.cx - closestX;
const distY = this.cy - closestY;
const distSquared = (distX ** 2) + (distY ** 2);
if (distSquared < this.game.ballRadius ** 2) {
const collBottom = rect.y + this.game.rectSize - (this.cy - this.game.ballRadius);
const collTop = (this.cy + this.game.ballRadius) - rect.y;
const collLeft = (this.cx + this.game.ballRadius) - rect.x;
const collRight = rect.x + this.game.rectSize - (this.cx - this.game.ballRadius);
if ((collTop <= collLeft && collTop <= collRight) || (collBottom <= collLeft && collBottom <= collRight)) {
this.vy *= -1;
} else {
this.vx *= -1;
}
rect.hit(this);
}
}
checkHitPowerUp(powerUp) {
const distX = this.cx - powerUp.cx;
const distY = this.cy - powerUp.cy;
const distSquared = (distX ** 2) + (distY ** 2);
if (distSquared < (this.game.ballRadius + this.game.powerUpRadius) ** 2) {
powerUp.hit();
}
}
draw() {
this.game.ctx.fillStyle = '#fff';
this.game.ctx.beginPath();
this.game.ctx.arc(this.cx, this.cy, this.game.ballRadius, 0, 2 * Math.PI);
this.game.ctx.fill();
}
}

298
game.js Normal file
View File

@ -0,0 +1,298 @@
class Game {
constructor(container) {
this.elements = {
highscore: container.querySelector('#highscore'),
score: container.querySelector('#score'),
coinCount: container.querySelector('#coin-count'),
pauseBtn: container.querySelector('.play-btn'),
pauseMenu: container.querySelector('#pause-menu'),
gameOverMenu: container.querySelector('#game-over-menu'),
};
this.canvas = container.querySelector('#canvas');
this.ctx = this.canvas.getContext('2d');
this.width = this.canvas.width;
this.height = this.canvas.height;
this.colCount = 7;
this.rowCount = 7;
this.rectSize = this.width / this.colCount;
this.originX = this.width / 2;
this.originY = (this.rowCount + 2) * this.rectSize;
this.newOriginX = null;
this.ballRadius = 12;
this.ballVelocity = 15;
this.powerUpRadius = 15;
this.rects = [];
this.powerUps = [];
this.balls = [];
this.roundNumber = 0;
this.ballCount = 1;
this.remainingBalls = this.ballCount;
this.newBalls = 0;
this.isMoving = false;
this.isPaused = false;
this.isGameOver = false;
this.coinAngle = 0;
this.mousePos = null;
this.addEventListeners();
}
addEventListeners() {
this.canvas.addEventListener('mousedown', (e) => {
this.mousePos = cbRelMousePos(e);
});
this.canvas.addEventListener('mousemove', (e) => {
if (this.mousePos) {
this.mousePos = cbRelMousePos(e);
}
});
this.canvas.addEventListener('mouseup', (e) => {
this.mousePos = cbRelMousePos(e);
this.shoot().then(() => {
this.mousePos = null;
});
});
this.elements.pauseBtn.addEventListener('click', () => {
if (this.isPaused) {
this.play();
} else {
this.pause();
}
});
}
removeEventListeners() {
}
draw() {
this.ctx.clearRect(0, 0, this.width, this.height);
this.drawBase();
this.rects.forEach(rect => rect.draw());
this.powerUps.forEach(powerUp => powerUp.draw());
this.balls.forEach(ball => ball.draw());
this.drawOrigin();
if (!this.isPaused) {
requestAnimationFrame(() => this.draw());
}
}
drawBase() {
const y = this.originY + this.ballRadius;
const height = this.height - this.originY - this.ballRadius;
this.ctx.fillStyle = 'rgba(0,0,0,.15)';
this.ctx.fillRect(0, y, this.width, height);
}
drawOrigin() {
if (this.originX !== null) {
if (!this.isMoving && this.mousePos !== null) {
this.drawShootingLine();
}
this.ctx.fillStyle = '#fff';
this.ctx.fillCircle(this.originX, this.originY, this.ballRadius);
if (this.remainingBalls > 0) {
this.ctx.fillText(this.remainingBalls + 'x', this.originX, this.originY + 50);
}
}
if (this.newOriginX !== null) {
this.ctx.fillStyle = '#fff';
this.ctx.fillCircle(this.newOriginX, this.originY, this.ballRadius);
}
}
drawShootingLine() {
const dir = this.calcMouseAngle();
let curX = this.originX;
let curY = this.originY;
for (let i = 0; i < 10; i++) {
const alpha = 1 - (i / 20);
this.ctx.fillStyle = `rgba(255,255,255,${alpha})`;
this.ctx.beginPath();
this.ctx.arc(curX, curY, this.ballRadius, 0, 2 * Math.PI);
this.ctx.fill();
curX += Math.cos(dir) * this.ballRadius * 4;
curY -= Math.sin(dir) * this.ballRadius * 4;
}
}
calcMouseAngle() {
const dx = this.mousePos.x - this.originX,
dy = this.originY - this.mousePos.y;
let dir = Math.atan(dy / dx);
if (dx < 0)
dir += Math.PI;
dir = Math.min(Math.max(dir, .05), Math.PI - 0.05);
return dir;
}
async shoot() {
if (this.isMoving || this.mousePos >= this.originY) {
return;
}
this.isMoving = true;
const dir = this.calcMouseAngle();
const ballCount = this.ballCount;
for (let i = 0; i < ballCount; i++) {
const ball = new Ball(this, this.originX, this.originY, this.ballVelocity, dir);
this.balls.push(ball);
this.remainingBalls--;
if (this.remainingBalls === 0) {
this.originX = null;
}
await sleep(100);
}
}
roundOver() {
this.ballCount += this.newBalls;
this.newBalls = 0;
this.remainingBalls = this.ballCount;
this.originX = this.newOriginX;
this.newOriginX = null;
this.increaseRoundNumber();
this.addNewObjects();
this.shiftObjectsDown();
sounds.roundOver.play();
setTimeout(() => {
this.isMoving = false;
}, 500);
}
gameOver() {
this.isGameOver = true;
coinCount += Math.floor((this.roundNumber - this.rowCount) / 10);
this.elements.gameOverMenu.classList.add('shown');
onGameOver();
}
addNewObjects() {
const spawnBall = Math.random() < .5,
ballPos = spawnBall ? Math.round(Math.random() * (this.colCount - 1)) : null,
spawnCoin = Math.random() < .33,
coinPos = spawnCoin ? Math.round(Math.random() * (this.colCount - 1)) : null;
for (let i = 0; i < this.colCount; i++) {
if (spawnBall && ballPos === i) {
this.powerUps.push(new PowerUpBall(this, (i + .5) * this.rectSize, .5 * this.rectSize));
continue;
}
if (spawnCoin && coinPos === i) {
this.powerUps.push(new PowerUpCoin(this, (i + .5) * this.rectSize, .5 * this.rectSize));
continue;
}
if (Math.random() < .6) {
const number = Math.random() < .6 ? this.roundNumber : 2 * this.roundNumber;
this.rects.push(new Rect(this, i * this.rectSize, 0, number));
}
}
}
shiftObjectsDown() {
this.rects.forEach(rect => {
moveObject(rect, 0, this.rectSize, 500);
if (rect.y + 2 * this.rectSize >= this.originY) {
this.gameOver();
}
});
this.powerUps.forEach(powerUp => {
moveObject(powerUp, 0, this.rectSize, 500);
if (powerUp.cy + this.rectSize >= this.originY) {
setTimeout(() => {
powerUp.remove();
});
}
})
}
increaseRoundNumber() {
this.roundNumber++;
this.elements.score.innerText = this.roundNumber;
}
increaseCoinCount() {
coinCount++;
this.elements.coinCount.innerText = coinCount;
}
update() {
this.balls.forEach(ball => {
ball.update(10);
});
this.coinAngle += .05;
if (this.coinAngle >= 2 * Math.PI) {
this.coinAngle -= 2 * Math.PI;
}
if (!this.isPaused && !this.isGameOver) {
requestAnimationFrame(() => this.update());
}
}
start() {
this.increaseRoundNumber();
this.addNewObjects();
this.shiftObjectsDown();
this.play()
}
play() {
this.isPaused = false;
this.update();
this.draw();
this.elements.pauseBtn.classList.add('paused');
this.elements.pauseMenu.classList.remove('shown');
}
pause() {
this.isPaused = true;
this.elements.pauseBtn.classList.remove('paused');
this.elements.pauseMenu.classList.add('shown');
}
destroy() {
this.removeEventListeners();
}
}

120
helper-functions.js Normal file
View File

@ -0,0 +1,120 @@
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function moveObject(object, amountX, amountY, time) {
let counter = 0;
const step = time / 1000 * 60;
const stepX = amountX / step;
const stepY = amountY / step;
let goalX;
let goalY;
if (object.hasOwnProperty('cx') && object.hasOwnProperty('cy')) {
goalX = object.cx + amountX;
goalY = object.cy + amountY;
} else {
goalX = object.x + amountX;
goalY = object.y + amountY;
}
const interval = setInterval(() => {
if (object.hasOwnProperty('cx') && object.hasOwnProperty('cy')) {
object.cx += stepX;
object.cy += stepY;
} else {
object.x += stepX;
object.y += stepY;
}
counter++;
if (counter >= step) {
clearInterval(interval);
if (object.hasOwnProperty('cx') && object.hasOwnProperty('cy')) {
object.cx = goalX;
object.cy = goalY;
} else {
object.x = goalX;
object.y = goalY;
}
}
}, 1000 / 60);
}
function calculateColor(number) {
let minHue;
let maxHue;
let percentage;
if (number < 10) {
minHue = 25;
maxHue = 35;
percentage = number / 10;
} else if (number < 20) {
minHue = 60;
maxHue = 80;
percentage = (number - 10) / 10;
} else if (number < 50) {
minHue = 310;
maxHue = 340;
percentage = (number - 20) / 30;
} else if (number < 100) {
minHue = 35;
maxHue = 65;
percentage = (number - 50) / 50;
} else if (number < 200) {
minHue = 260;
maxHue = 280;
percentage = (number - 100) / 100;
}
const hue = (percentage * (maxHue - minHue)) + minHue;
return `hsl(${hue}, 100%, 50%)`;
}
function cbElementPos(event) {
event = event || window.event;
let obj = event.target || event.srcElement,
x = 0,
y = 0;
while (obj.offsetParent) {
x += obj.offsetLeft;
y += obj.offsetTop;
obj = obj.offsetParent;
}
return {x, y};
}
function cbMousePos(event) {
event = event || window.event;
return {
x: event.pageX || event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft,
y: event.pageY || event.clientY + document.body.scrollTop + document.documentElement.scrollTop,
}
}
function cbRelMousePos(event) {
const elem = cbElementPos(event),
mouse = cbMousePos(event);
return {
x: (mouse.x - elem.x),
y: (mouse.y - elem.y),
};
}
window.requestAnimationFrame = window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
return setTimeout(callback, 1000 / 60);
};
Math.clamp = function (a, b, c) {
return Math.max(b, Math.min(c, a));
};
CanvasRenderingContext2D.prototype.fillCircle = function (cx, cy, radius) {
this.beginPath();
this.arc(cx, cy, radius, 0, 2 * Math.PI);
this.fill();
};

63
index.html Normal file
View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ballz.js</title>
<link href="style.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="details">
<div class="menu-btn-container">
<div class="play-btn">
<div class="pause-btn"></div>
</div>
</div>
<div class="high-score-container">
<span class="details-title">Best:</span>
<h3 id="highscore">0</h3>
</div>
<div class="score-container">
<h1 id="score">0</h1>
</div>
<div class="coin-container">
<span class="details-title">Coins:</span>
<h3 id="coin-count">0</h3>
</div>
<div></div>
</div>
<div class="game-container">
<canvas id="canvas" width="500" height="800"></canvas>
<div class="game-overlay" id="pause-menu">
<h2 class="game-overlay-title">Pausing</h2>
<div class="game-overlay-btns">
<button class="btn primary">Resume</button>
<button class="btn danger">Main Menu</button>
</div>
</div>
<div class="game-overlay" id="game-over-menu">
</div>
</div>
</div>
<script src="helper-functions.js"></script>
<script src="rect.js"></script>
<script src="powerup-ball.js"></script>
<script src="powerup-coin.js"></script>
<script src="ball.js"></script>
<script src="game.js"></script>
<script src="main.js"></script>
</body>
</html>

17
main.js Normal file
View File

@ -0,0 +1,17 @@
const sounds = {
hitBlock: new Audio('assets/hit_block.wav'),
hitCoin: new Audio('assets/hit_coin.wav'),
hitBall: new Audio('assets/hit_ball.wav'),
roundOver: new Audio('assets/round_success.wav'),
gameOver: new Audio('assets/game_over.wav')
};
let coinCount = 0;
const container = document.getElementsByClassName('container')[0];
const game = new Game(container);
game.start();
function onGameOver() {
}

36
powerup-ball.js Normal file
View File

@ -0,0 +1,36 @@
class PowerUpBall {
constructor(game, cx, cy) {
this.game = game;
this.cx = cx;
this.cy = cy;
this.anim = 0.1;
this.animChange = 0.025;
}
draw() {
this.game.ctx.fillStyle = '#fff';
this.game.ctx.strokeStyle = '#fff';
this.game.ctx.beginPath();
this.game.ctx.arc(this.cx, this.cy, this.game.powerUpRadius * .6, 0, 2 * Math.PI);
this.game.ctx.fill();
this.game.ctx.beginPath();
this.game.ctx.arc(this.cx, this.cy, this.game.powerUpRadius * (.6 + this.anim), 0, 2 * Math.PI);
this.game.ctx.stroke();
this.anim = this.anim + this.animChange;
if (this.anim >= .4 || this.anim <= 0.1) {
this.animChange *= -1;
}
}
hit() {
sounds.hitBall.play();
this.game.newBalls++;
this.remove();
}
remove() {
this.game.powerUps.splice(this.game.powerUps.findIndex(powerUp => powerUp === this), 1);
}
}

24
powerup-coin.js Normal file
View File

@ -0,0 +1,24 @@
class PowerUpCoin {
constructor(game, cx, cy) {
this.game = game;
this.cx = cx;
this.cy = cy;
}
draw() {
this.game.ctx.fillStyle = '#fa0';
this.game.ctx.beginPath();
this.game.ctx.ellipse(this.cx, this.cy, Math.abs(this.game.powerUpRadius * Math.cos(this.game.coinAngle)), this.game.powerUpRadius, 0, 0, 2 * Math.PI);
this.game.ctx.fill();
}
hit() {
sounds.hitCoin.play();
this.game.increaseCoinCount();
this.remove();
}
remove() {
this.game.powerUps.splice(this.game.powerUps.findIndex(powerUp => powerUp === this), 1);
}
}

37
rect.js Normal file
View File

@ -0,0 +1,37 @@
class Rect {
constructor(game, x, y, number) {
this.game = game;
this.x = x;
this.y = y;
this.number = number;
this.color = calculateColor(number);
this.lastHits = {};
}
draw() {
this.game.ctx.fillStyle = this.color;
this.game.ctx.fillRect(this.x + 2, this.y + 2, this.game.rectSize - 4, this.game.rectSize - 4);
this.game.ctx.fillStyle = '#000';
this.game.ctx.font = '20px Roboto, Arial, sans-serif';
this.game.ctx.textAlign = 'center';
this.game.ctx.textBaseline = 'middle';
this.game.ctx.fillText(this.number, this.x + this.game.rectSize / 2, this.y + this.game.rectSize / 2);
}
hit(ball) {
if (this.lastHits[ball.id] && Date.now() - this.lastHits[ball.id] < 100) {
return;
}
this.number--;
this.color = calculateColor(this.number);
sounds.hitBlock.play();
if (this.number <= 0) {
this.game.rects.splice(this.game.rects.findIndex(rect => rect === this), 1);
}
this.lastHits[ball.id] = Date.now();
}
}

171
style.css Normal file
View File

@ -0,0 +1,171 @@
body {
width: 100%;
height: 100%;
margin: 0;
background: #333;
}
* {
color: #fff;
font-family: Roboto, Arial, sans-serif;
font-weight: 500;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 10px;
background: none;
color: #fff;
box-shadow: none;
font-size: 16px;
cursor: pointer;
}
.btn.primary {
background: #2272ff;
}
.btn.danger {
background: #F72754;
}
.container {
display: block;
width: 100%;
max-width: 500px;
margin: 10px auto;
}
.details {
display: grid;
grid-template-columns: 1fr 2fr 2fr 1fr 1fr;
}
.details h1, .details h3, .details .details-title {
display: block;
width: 100%;
margin: 0;
text-align: center;
}
.menu-btn-container {
display: block;
}
.menu-btn-container .play-btn {
position: relative;
display: block;
overflow: hidden;
width: 20px;
height: 20px;
margin: 10px auto;
cursor: pointer;
}
.menu-btn-container .play-btn::before, .menu-btn-container .play-btn::after {
position: absolute;
top: 0;
z-index: -1;
width: 51%;
height: 100%;
background: #ffffff;
content: "";
transition: .3s;
will-change: transform;
}
.menu-btn-container .play-btn::before {
left: 0;
}
.menu-btn-container .play-btn::after {
right: 0;
}
.menu-btn-container .play-btn.paused::before {
-moz-transform: translateX(-25%);
-ms-transform: translateX(-25%);
-o-transform: translateX(-25%);
-webkit-transform: translateX(-25%);
transform: translateX(-25%);
}
.menu-btn-container .play-btn.paused::after {
transform: translateX(25%)
}
.menu-btn-container .play-btn.paused .pause-btn::before, .menu-btn-container .play-btn.paused .pause-btn::after {
transform: rotate(0);
}
.menu-btn-container .play-btn .pause-btn::before, .menu-btn-container .play-btn .pause-btn::after {
position: absolute;
width: 150%;
height: 100%;
background: #333;
outline: 1px solid transparent;
content: "";
transition: .3s;
}
.menu-btn-container .play-btn .pause-btn::before {
top: -100%;
transform: rotate(26.5deg);
transform-origin: 0 100%;
}
.menu-btn-container .play-btn .pause-btn::after {
top: 100%;
transform: rotate(-26.5deg);
transform-origin: 0 0;
}
.game-container {
position: relative;
}
#canvas {
display: block;
margin: 20px auto;
border-radius: 10px;
background: rgba(255, 255, 255, .2);
}
.game-container .game-overlay {
position: absolute;
top: 0;
left: 0;
transform: translateY(100%);
width: 100%;
height: 100%;
border-radius: 10px;
background: #555;
opacity: 0;
pointer-events: none;
transition: .3s;
}
.game-container .game-overlay.shown {
transform: none;
opacity: 1;
pointer-events: all;
}
.game-container .game-overlay .game-overlay-title {
margin: 45% 25px 50px;
font-size: 25px;
text-align: center;
}
.game-container .game-overlay .game-overlay-btns {
display: block;
width: 50%;
margin: 25px auto;
}
.game-container .game-overlay .game-overlay-btns .btn {
display: block;
width: 100%;
margin: 16px 0 0;
}

205
temp.js Normal file
View File

@ -0,0 +1,205 @@
// const canvas = document.getElementById('canvas');
// const ctx = canvas.getContext('2d');
//
// let width = 500,
// height = 800;
//
// const rectSize = width / 7,
// ballRadius = 12,
// powerUpRadius = 15,
// ballVelocity = 15,
// frameRate = 60;
//
// const sounds = {
// };
//
// let roundNumber = 0;
//
// const rects = [];
// const powerUps = [];
// let balls = [];
// let ballCount = 1;
// let remainingBalls;
// let newBalls = 0;
// let originX;
// let newOriginX = 250;
// const originY = 9 * rectSize;
// let isMoving = false;
//
// let coinAngle = 0;
//
// let mousePos = null;
//
// function drawShootingLine() {
// const dx = mousePos.x - originX;
// const dy = originY - mousePos.y;
// let angle = Math.atan(dy / dx);
// let curX = originX;
// let curY = originY;
//
// if (dx < 0)
// angle += Math.PI;
//
// angle = Math.min(Math.max(angle, 0.05), Math.PI - 0.05);
//
// ctx.fillStyle = 'rgba(255, 255, 255, .5)';
// for (let i = 0; i < 10; i++) {
// ctx.beginPath();
// ctx.arc(curX, curY, ballRadius, 0, 2 * Math.PI);
// ctx.fill();
//
// curX += ballRadius * 4 * Math.cos(angle);
// curY -= ballRadius * 4 * Math.sin(angle);
// }
// }
//
// function update() {
// balls.forEach(ball => {
// ball.update(10);
// });
//
// coinAngle += .05;
//
// requestAnimationFrame(update);
// }
//
// function draw() {
// ctx.clearRect(0, 0, canvas.width, canvas.height);
//
// ctx.fillStyle = 'rgba(0,0,0,.15)';
// ctx.fillRect(0, originY + ballRadius, width, height - originX - ballRadius);
//
// rects.forEach(rect => rect.draw());
//
// powerUps.forEach(powerUp => powerUp.draw());
//
// if (originX !== null) {
// if (!isMoving && mousePos !== null) {
// drawShootingLine();
// }
//
// ctx.fillStyle = '#fff';
// ctx.beginPath();
// ctx.arc(originX, originY, ballRadius, 0, 2 * Math.PI);
// ctx.fill();
//
// if (remainingBalls > 0) {
// ctx.fillText(`${remainingBalls}x`, originX, originY + 50);
// } else {
// originX = null;
// }
// }
// if(newOriginX !== null) {
// ctx.fillStyle = '#fff';
// ctx.beginPath();
// ctx.arc(newOriginX, originY, ballRadius, 0, 2 * Math.PI);
// ctx.fill();
// }
//
// balls.forEach(ball => {
// ball.draw();
// });
//
// requestAnimationFrame(draw);
// }
//
// onRoundEnd();
// update();
// draw();
//
// canvas.addEventListener('mousemove', (e) => {
// mousePos = cbRelMousePos(e);
// });
//
// canvas.addEventListener('click', (e) => {
// mousePos = cbRelMousePos(e);
// shoot();
// });
//
// async function shoot() {
// if (isMoving)
// return;
//
// if (mousePos.y >= originY)
// return;
//
// isMoving = true;
// const dx = mousePos.x - originX,
// dy = originY - mousePos.y;
// let dir = Math.atan(dy / dx);
// if (dx < 0) {
// dir += Math.PI;
// }
//
// dir = Math.min(Math.max(dir, 0.05), Math.PI - 0.05);
//
// for (let i = 0; i < ballCount; i++) {
// balls.push(new Ball(originX, originY, ballVelocity, dir));
// remainingBalls--;
// await sleep(100);
// }
// }
//
// function onRoundEnd() {
// isMoving = false;
// roundNumber++;
// ballCount += newBalls;
// newBalls = 0;
// remainingBalls = ballCount;
// originX = newOriginX;
// newOriginX = null;
//
// rects.forEach(rect => {
// moveObject(rect, 0, rectSize, 500);
//
// if(rect.y + 2 * rectSize >= originY) {
// gameOver();
// }
// });
//
// powerUps.forEach(powerUp => {
// moveObject(powerUp, 0, rectSize, 500);
//
// if(powerUp.cy + rectSize >= originY) {
// setTimeout(() => {
// powerUp.remove();
// }, 500);
// }
// });
//
// const cols = width / rectSize;
// const spawnBall = Math.random() < .5;
// const ballPos = spawnBall ? Math.round(Math.random() * (width / rectSize - 1)) : null;
// const spawnCoin = Math.random() < .33;
// const coinPos = spawnCoin ? Math.round(Math.random() * (width / rectSize - 1)) : null;
// for (let i = 0; i < cols; i++) {
// if (spawnBall && i === ballPos) {
// const powerUp = new PowerUpBall((i + .5) * rectSize, .5 * rectSize);
// powerUps.push(powerUp);
//
// moveObject(powerUp, 0, rectSize, 500);
// } else if (spawnCoin && i === coinPos) {
// const powerUp = new PowerUpCoin((i + .5) * rectSize, .5 * rectSize);
// powerUps.push(powerUp);
//
// moveObject(powerUp, 0, rectSize, 500);
// } else {
// if (Math.random() < .6) {
// const number = Math.random() < .5 ? roundNumber : 2 * roundNumber;
// const rect = new Rect(i * rectSize, 0, number);
// rects.push(rect);
//
// moveObject(rect, 0, rectSize, 500);
// }
// }
// }
//
// sounds.roundOver.play();
// }
//
//
// function gameOver() {
// }
//
//