2026-07-19 18:02:33 +03:00
|
|
|
import {
|
|
|
|
|
BALL_SPLIT_ANGLE,
|
|
|
|
|
CONTAINER_WIDTH,
|
|
|
|
|
PADDLE_WIDE_WIDTH,
|
|
|
|
|
PADDLE_WIDTH,
|
|
|
|
|
PERK_BALL_SPEED_DECREASE,
|
|
|
|
|
PERK_DURATION_SEC,
|
|
|
|
|
} from '../../config';
|
2026-07-19 17:47:01 +03:00
|
|
|
import { clone } from '../../entities/ball/clone/clone';
|
2026-07-19 15:09:56 +03:00
|
|
|
import { Game } from '../../game';
|
2026-07-19 17:00:23 +03:00
|
|
|
import { calculateAABBCollision } from '../calculateAABBCollision/calculateAABBCollision';
|
2026-07-19 15:09:56 +03:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Обновляет состояние бонусов, меняет положение, проверяет столкновение с ракеткой и выход за границу поля
|
|
|
|
|
* @param {Game} game экземпляр класса игры
|
|
|
|
|
* @param {number} containerHeight высота игрового контейнера в пикселях
|
|
|
|
|
* @param {number} deltaTime изменение времени из Ticker
|
|
|
|
|
*/
|
|
|
|
|
export function updatePerks(game, containerHeight, deltaTime) {
|
|
|
|
|
try {
|
|
|
|
|
if (!(game instanceof Game)) {
|
|
|
|
|
throw new Error('Аргумент game должен быть экземпляром класса игры');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof containerHeight !== 'number' || containerHeight <= 0) {
|
|
|
|
|
throw new Error('Высота игрового контейнера должна быть положительным числом');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof deltaTime !== 'number' || deltaTime <= 0) {
|
|
|
|
|
throw new Error('Значение изменения времени должно быть положительным числом');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { paddle } = game;
|
|
|
|
|
|
|
|
|
|
for (const perk of game.perks) {
|
|
|
|
|
perk.moveForward(deltaTime);
|
|
|
|
|
|
|
|
|
|
const perkObject = {
|
|
|
|
|
left: perk.x,
|
|
|
|
|
right: perk.x + perk.width,
|
|
|
|
|
top: perk.y,
|
|
|
|
|
bottom: perk.y + perk.height,
|
|
|
|
|
};
|
|
|
|
|
const paddleObject = {
|
|
|
|
|
left: paddle.x,
|
|
|
|
|
right: paddle.x + paddle.width,
|
|
|
|
|
top: paddle.y,
|
|
|
|
|
bottom: paddle.y + paddle.height,
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-19 17:00:23 +03:00
|
|
|
const isCollided = calculateAABBCollision(perkObject, paddleObject);
|
2026-07-19 15:09:56 +03:00
|
|
|
|
|
|
|
|
if (isCollided) {
|
|
|
|
|
switch (perk.type) {
|
|
|
|
|
case 'slow':
|
2026-07-19 17:47:01 +03:00
|
|
|
for (const ball of game.balls) {
|
|
|
|
|
if (ball.speed - PERK_BALL_SPEED_DECREASE > 0) {
|
|
|
|
|
ball.increaseSpeed(-1 * PERK_BALL_SPEED_DECREASE);
|
|
|
|
|
}
|
2026-07-19 15:09:56 +03:00
|
|
|
}
|
2026-07-19 18:02:33 +03:00
|
|
|
setTimeout(() => {
|
|
|
|
|
for (const ball of game.balls) {
|
|
|
|
|
ball.increaseSpeed(PERK_BALL_SPEED_DECREASE);
|
|
|
|
|
}
|
|
|
|
|
}, PERK_DURATION_SEC * 1000);
|
2026-07-19 15:09:56 +03:00
|
|
|
break;
|
2026-07-19 15:32:09 +03:00
|
|
|
case 'wide':
|
|
|
|
|
paddle.width = PADDLE_WIDE_WIDTH;
|
|
|
|
|
paddle.moveTo(paddle.x, 0, CONTAINER_WIDTH);
|
2026-07-19 18:02:33 +03:00
|
|
|
setTimeout(() => {
|
|
|
|
|
paddle.width = PADDLE_WIDTH;
|
|
|
|
|
}, PERK_DURATION_SEC * 1000);
|
2026-07-19 15:32:09 +03:00
|
|
|
break;
|
2026-07-19 15:37:42 +03:00
|
|
|
case 'life':
|
|
|
|
|
game.livesAmount += 1;
|
|
|
|
|
break;
|
2026-07-19 17:47:01 +03:00
|
|
|
case 'clone':
|
|
|
|
|
// Основной мяч делится на два клона, разлетающихся под углом
|
|
|
|
|
game.balls.push(clone(game.ball, BALL_SPLIT_ANGLE), clone(game.ball, -BALL_SPLIT_ANGLE));
|
|
|
|
|
break;
|
2026-07-19 15:09:56 +03:00
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
perk.alive = false;
|
|
|
|
|
} else if (perk.y > containerHeight) {
|
|
|
|
|
perk.alive = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
game.perks = game.perks.filter((perk) => perk.alive);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error(err);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|