ballz.js/game.js
2019-04-06 19:19:26 +02:00

299 lines
7.9 KiB
JavaScript

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();
}
}