fix: complete rewrite, simplified wave logic
This commit is contained in:
parent
9200d312dd
commit
39a95e7337
379
script.js
379
script.js
@ -21,7 +21,6 @@ const player = {
|
|||||||
weaponLevel: 1,
|
weaponLevel: 1,
|
||||||
score: 0,
|
score: 0,
|
||||||
invincible: 0,
|
invincible: 0,
|
||||||
level: 1,
|
|
||||||
wave: 1
|
wave: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,14 +43,14 @@ let keys = {};
|
|||||||
let paused = false;
|
let paused = false;
|
||||||
let gameTime = 0;
|
let gameTime = 0;
|
||||||
let waveTimer = 0;
|
let waveTimer = 0;
|
||||||
let waveActive = false;
|
let lastShot = 0;
|
||||||
let enemiesInWave = 0;
|
|
||||||
let enemiesToSpawn = 5;
|
|
||||||
let bossActive = false;
|
|
||||||
let boss = null;
|
|
||||||
let backgroundOffset = 0;
|
let backgroundOffset = 0;
|
||||||
let shake = 0;
|
let shake = 0;
|
||||||
let lastShot = 0;
|
let bossActive = false;
|
||||||
|
let boss = null;
|
||||||
|
let waveNum = 1;
|
||||||
|
let enemiesRemaining = 0;
|
||||||
|
let spawnTimer = 0;
|
||||||
|
|
||||||
const enemyTypes = [
|
const enemyTypes = [
|
||||||
{ name: 'soldier', width: 32, height: 48, hp: 50, speed: 1.5, color: '#8b0000', score: 100, weaponDrop: 0.25 },
|
{ name: 'soldier', width: 32, height: 48, hp: 50, speed: 1.5, color: '#8b0000', score: 100, weaponDrop: 0.25 },
|
||||||
@ -61,27 +60,29 @@ const enemyTypes = [
|
|||||||
{ name: 'mech', width: 70, height: 55, hp: 250, speed: 0.6, color: '#004a6a', score: 500, weaponDrop: 0.7 }
|
{ name: 'mech', width: 70, height: 55, hp: 250, speed: 0.6, color: '#004a6a', score: 500, weaponDrop: 0.7 }
|
||||||
];
|
];
|
||||||
|
|
||||||
function startWave() {
|
function startNextWave() {
|
||||||
if (bossActive) return;
|
if (bossActive) return;
|
||||||
waveActive = true;
|
waveNum++;
|
||||||
enemiesToSpawn = 5 + player.wave * 2;
|
enemiesRemaining = 5 + waveNum * 2;
|
||||||
enemiesInWave = 0;
|
spawnTimer = 0;
|
||||||
waveTimer = 0;
|
document.getElementById('wave-num').textContent = waveNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
function spawnEnemy() {
|
function spawnEnemy() {
|
||||||
const type = enemyTypes[Math.floor(Math.random() * Math.min(enemyTypes.length, 1 + player.wave))];
|
const typeIdx = Math.min(Math.floor(waveNum / 2), enemyTypes.length - 1);
|
||||||
|
const availableTypes = enemyTypes.slice(0, Math.max(1, typeIdx + 1));
|
||||||
|
const type = availableTypes[Math.floor(Math.random() * availableTypes.length)];
|
||||||
const isFly = type.fly;
|
const isFly = type.fly;
|
||||||
|
|
||||||
enemies.push({
|
enemies.push({
|
||||||
x: canvas.width + 50,
|
x: canvas.width + 50,
|
||||||
y: isFly ? 100 + Math.random() * 150 : 452 - type.height,
|
y: isFly ? 100 + Math.random() * 150 : 452 - type.height,
|
||||||
...type,
|
...type,
|
||||||
maxHp: type.hp + player.wave * 15,
|
maxHp: type.hp + waveNum * 15,
|
||||||
hp: type.hp + player.wave * 15,
|
hp: type.hp + waveNum * 15,
|
||||||
shootTimer: 0,
|
shootTimer: 0,
|
||||||
animFrame: 0,
|
animFrame: 0,
|
||||||
fly: isFly,
|
fly: isFly
|
||||||
isBoss: false
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,18 +93,16 @@ function spawnBoss() {
|
|||||||
y: 280,
|
y: 280,
|
||||||
width: 120,
|
width: 120,
|
||||||
height: 100,
|
height: 100,
|
||||||
hp: 3000 + player.wave * 500,
|
hp: 3000,
|
||||||
maxHp: 3000 + player.wave * 500,
|
maxHp: 3000,
|
||||||
speed: 0.5,
|
speed: 0.5,
|
||||||
color: '#8b0000',
|
color: '#8b0000',
|
||||||
score: 5000,
|
score: 5000,
|
||||||
animFrame: 0,
|
animFrame: 0,
|
||||||
shootTimer: 0,
|
moveTimer: 0
|
||||||
moveTimer: 0,
|
|
||||||
isBoss: true,
|
|
||||||
phase: 1
|
|
||||||
};
|
};
|
||||||
enemies = [];
|
enemies = [];
|
||||||
|
enemiesRemaining = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const powerupTypes = [
|
const powerupTypes = [
|
||||||
@ -181,15 +180,11 @@ document.addEventListener('keydown', e => {
|
|||||||
player.weapon = (player.weapon + 1) % weapons.length;
|
player.weapon = (player.weapon + 1) % weapons.length;
|
||||||
}
|
}
|
||||||
if (e.code === 'KeyP' || e.code === 'Escape') {
|
if (e.code === 'KeyP' || e.code === 'Escape') {
|
||||||
togglePause();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
document.addEventListener('keyup', e => keys[e.code] = false);
|
|
||||||
|
|
||||||
function togglePause() {
|
|
||||||
paused = !paused;
|
paused = !paused;
|
||||||
document.getElementById('pause-btn').textContent = paused ? '▶ Продолжить' : '⏸ Пауза';
|
document.getElementById('pause-btn').textContent = paused ? '▶ Продолжить' : '⏸ Пауза';
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
document.addEventListener('keyup', e => keys[e.code] = false);
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
if (paused) return;
|
if (paused) return;
|
||||||
@ -200,88 +195,31 @@ function update() {
|
|||||||
if (shake > 0) shake--;
|
if (shake > 0) shake--;
|
||||||
|
|
||||||
const wpn = weapons[player.weapon];
|
const wpn = weapons[player.weapon];
|
||||||
const damageMultiplier = 1 + (player.weaponLevel - 1) * 0.15;
|
const damageMult = 1 + (player.weaponLevel - 1) * 0.15;
|
||||||
|
|
||||||
if (keys['ArrowLeft']) { player.vx = -player.speed; player.facing = -1; }
|
// Спавн врагов
|
||||||
else if (keys['ArrowRight']) { player.vx = player.speed; player.facing = 1; }
|
if (!bossActive && enemiesRemaining > 0) {
|
||||||
else player.vx = 0;
|
spawnTimer++;
|
||||||
|
if (spawnTimer > 50) {
|
||||||
if (keys['ArrowUp'] && player.grounded) {
|
|
||||||
player.vy = player.jumpForce;
|
|
||||||
player.grounded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
player.vy += 0.7;
|
|
||||||
player.x += player.vx;
|
|
||||||
player.y += player.vy;
|
|
||||||
|
|
||||||
if (player.y > 452 - player.height) {
|
|
||||||
player.y = 452 - player.height;
|
|
||||||
player.vy = 0;
|
|
||||||
player.grounded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player.x < 0) player.x = 0;
|
|
||||||
if (player.x > canvas.width - player.width) player.x = canvas.width - player.width;
|
|
||||||
|
|
||||||
if (keys['KeyZ'] && Date.now() - lastShot > wpn.fireRate) {
|
|
||||||
const count = wpn.count || 1;
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
const angle = (count > 1) ? (i - (count-1)/2) * 0.15 : 0;
|
|
||||||
const spread = wpn.flame ? (Math.random() - 0.5) * 0.4 : 0;
|
|
||||||
const shootUp = keys['ArrowUp'] && !player.grounded ? -1 : 0;
|
|
||||||
bullets.push({
|
|
||||||
x: player.x + player.width/2,
|
|
||||||
y: player.y + 14,
|
|
||||||
vx: (Math.cos(angle + spread) * wpn.bulletSpeed * player.facing) + (wpn.flame ? (Math.random() - 0.5) * 2 : 0),
|
|
||||||
vy: (shootUp !== 0 ? -wpn.bulletSpeed * 0.7 : 0) + Math.sin(angle + spread) * wpn.bulletSpeed + (wpn.flame ? (Math.random() - 0.5) * 2 : 0),
|
|
||||||
damage: wpn.damage * damageMultiplier,
|
|
||||||
color: wpn.color,
|
|
||||||
size: wpn.bulletSize,
|
|
||||||
glow: wpn.glow,
|
|
||||||
explosive: wpn.explosive,
|
|
||||||
flame: wpn.flame,
|
|
||||||
plasma: wpn.plasma,
|
|
||||||
laser: wpn.laser,
|
|
||||||
life: wpn.flame ? 20 : 100
|
|
||||||
});
|
|
||||||
}
|
|
||||||
lastShot = Date.now();
|
|
||||||
}
|
|
||||||
|
|
||||||
bullets = bullets.filter(b => {
|
|
||||||
b.x += b.vx;
|
|
||||||
b.y += b.vy;
|
|
||||||
if (b.laser) {
|
|
||||||
b.life = 10;
|
|
||||||
b.x += b.vx * 3;
|
|
||||||
b.y += b.vy * 3;
|
|
||||||
}
|
|
||||||
if (b.flame) b.life--;
|
|
||||||
return b.x > -50 && b.x < canvas.width + 50 && b.y > -50 && b.y < canvas.height + 50 && (!b.flame || b.life > 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!bossActive && waveActive) {
|
|
||||||
if (enemiesToSpawn > 0 && waveTimer % 40 === 0) {
|
|
||||||
spawnEnemy();
|
spawnEnemy();
|
||||||
enemiesToSpawn--;
|
enemiesRemaining--;
|
||||||
enemiesInWave++;
|
spawnTimer = 0;
|
||||||
|
document.getElementById('wprogress').textContent = enemiesRemaining + '/' + (5 + waveNum * 2);
|
||||||
}
|
}
|
||||||
if (enemiesToSpawn <= 0 && enemies.length === 0) {
|
}
|
||||||
waveActive = false;
|
|
||||||
player.wave++;
|
// Проверка завершения волны
|
||||||
document.getElementById('wave').textContent = player.wave;
|
if (!bossActive && enemiesRemaining === 0 && enemies.length === 0) {
|
||||||
if (player.score >= 10000 * player.wave && !bossActive) {
|
if (player.score >= 10000 && waveNum >= 3) {
|
||||||
spawnBoss();
|
spawnBoss();
|
||||||
} else {
|
} else {
|
||||||
setTimeout(startWave, 2000);
|
startNextWave();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Босс
|
||||||
if (bossActive && boss) {
|
if (bossActive && boss) {
|
||||||
boss.moveTimer++;
|
boss.moveTimer++;
|
||||||
boss.shootTimer++;
|
|
||||||
boss.animFrame++;
|
boss.animFrame++;
|
||||||
boss.y += Math.sin(boss.moveTimer * 0.03) * 1.5;
|
boss.y += Math.sin(boss.moveTimer * 0.03) * 1.5;
|
||||||
if (boss.x > 500) boss.x -= boss.speed;
|
if (boss.x > 500) boss.x -= boss.speed;
|
||||||
@ -304,20 +242,76 @@ function update() {
|
|||||||
}
|
}
|
||||||
boss = null;
|
boss = null;
|
||||||
bossActive = false;
|
bossActive = false;
|
||||||
player.wave++;
|
startNextWave();
|
||||||
document.getElementById('wave').textContent = player.wave;
|
|
||||||
setTimeout(startWave, 3000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Игрок
|
||||||
|
if (keys['ArrowLeft']) { player.vx = -player.speed; player.facing = -1; }
|
||||||
|
else if (keys['ArrowRight']) { player.vx = player.speed; player.facing = 1; }
|
||||||
|
else player.vx = 0;
|
||||||
|
|
||||||
|
if (keys['ArrowUp'] && player.grounded) {
|
||||||
|
player.vy = player.jumpForce;
|
||||||
|
player.grounded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.vy += 0.7;
|
||||||
|
player.x += player.vx;
|
||||||
|
player.y += player.vy;
|
||||||
|
|
||||||
|
if (player.y > 452 - player.height) {
|
||||||
|
player.y = 452 - player.height;
|
||||||
|
player.vy = 0;
|
||||||
|
player.grounded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.x < 0) player.x = 0;
|
||||||
|
if (player.x > canvas.width - player.width) player.x = canvas.width - player.width;
|
||||||
|
|
||||||
|
// Стрельба
|
||||||
|
if (keys['KeyZ'] && Date.now() - lastShot > wpn.fireRate) {
|
||||||
|
const count = wpn.count || 1;
|
||||||
|
const shootUp = (keys['ArrowUp'] || (keys['ArrowUp'] && !player.grounded)) ? -1 : 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
const angle = (count > 1) ? (i - (count-1)/2) * 0.15 : 0;
|
||||||
|
const spread = wpn.flame ? (Math.random() - 0.5) * 0.4 : 0;
|
||||||
|
bullets.push({
|
||||||
|
x: player.x + player.width/2,
|
||||||
|
y: player.y + 14,
|
||||||
|
vx: Math.cos(angle + spread) * wpn.bulletSpeed * player.facing + (wpn.flame ? (Math.random() - 0.5) * 2 : 0),
|
||||||
|
vy: (shootUp ? -wpn.bulletSpeed * 0.7 : 0) + Math.sin(angle + spread) * wpn.bulletSpeed + (wpn.flame ? (Math.random() - 0.5) * 2 : 0),
|
||||||
|
damage: wpn.damage * damageMult,
|
||||||
|
color: wpn.color,
|
||||||
|
size: wpn.bulletSize,
|
||||||
|
glow: wpn.glow,
|
||||||
|
explosive: wpn.explosive,
|
||||||
|
flame: wpn.flame,
|
||||||
|
plasma: wpn.plasma,
|
||||||
|
laser: wpn.laser,
|
||||||
|
life: 100
|
||||||
|
});
|
||||||
|
}
|
||||||
|
lastShot = Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пули
|
||||||
|
bullets = bullets.filter(b => {
|
||||||
|
b.x += b.vx;
|
||||||
|
b.y += b.vy;
|
||||||
|
if (b.laser) b.life = 15;
|
||||||
|
if (b.flame) b.life--;
|
||||||
|
return b.life > 0 && b.x > -50 && b.x < canvas.width + 50 && b.y > -50 && b.y < canvas.height + 50;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Враги
|
||||||
enemies = enemies.filter(e => {
|
enemies = enemies.filter(e => {
|
||||||
e.x -= e.speed;
|
e.x -= e.speed;
|
||||||
e.shootTimer++;
|
e.shootTimer++;
|
||||||
e.animFrame++;
|
e.animFrame++;
|
||||||
|
|
||||||
if (e.fly) {
|
if (e.fly) e.y += Math.sin(e.animFrame * 0.05) * 2;
|
||||||
e.y += Math.sin(e.animFrame * 0.05) * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player.invincible <= 0 &&
|
if (player.invincible <= 0 &&
|
||||||
player.x < e.x + e.width && player.x + player.width > e.x &&
|
player.x < e.x + e.width && player.x + player.width > e.x &&
|
||||||
@ -331,50 +325,52 @@ function update() {
|
|||||||
return e.x > -100 && player.hp > 0;
|
return e.x > -100 && player.hp > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Попадания
|
||||||
bullets = bullets.filter(b => {
|
bullets = bullets.filter(b => {
|
||||||
let hit = false;
|
let hit = false;
|
||||||
|
|
||||||
|
// Босс
|
||||||
if (bossActive && boss &&
|
if (bossActive && boss &&
|
||||||
b.x > boss.x && b.x < boss.x + boss.width &&
|
b.x > boss.x && b.x < boss.x + boss.width &&
|
||||||
b.y > boss.y && b.y < boss.y + boss.height) {
|
b.y > boss.y && b.y < boss.y + boss.height) {
|
||||||
boss.hp -= b.damage;
|
boss.hp -= b.damage;
|
||||||
hit = true;
|
hit = true;
|
||||||
createExplosion(b.x, b.y, b.color, b.explosive ? 25 : b.laser ? 15 : 10);
|
createExplosion(b.x, b.y, b.color, b.laser ? 15 : 10);
|
||||||
createSparks(b.x, b.y, 5);
|
createSparks(b.x, b.y, 5);
|
||||||
if (boss.hp <= boss.maxHp * 0.5 && boss.phase === 1) {
|
|
||||||
boss.phase = 2;
|
|
||||||
boss.speed = 1.0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обычные враги
|
||||||
enemies.forEach((e, ei) => {
|
enemies.forEach((e, ei) => {
|
||||||
if (b.x > e.x && b.x < e.x + e.width && b.y > e.y && b.y < e.y + e.height) {
|
if (b.x > e.x && b.x < e.x + e.width && b.y > e.y && b.y < e.y + e.height) {
|
||||||
e.hp -= b.damage;
|
e.hp -= b.damage;
|
||||||
hit = true;
|
hit = true;
|
||||||
createExplosion(b.x, b.y, b.color, b.explosive ? 25 : b.laser ? 15 : 10);
|
createExplosion(b.x, b.y, b.color, 10);
|
||||||
|
|
||||||
if (e.hp <= 0) {
|
if (e.hp <= 0) {
|
||||||
player.score += e.score;
|
player.score += e.score;
|
||||||
createBlood(e.x + e.width/2, e.y + e.height/2, e.name === 'tank' || e.name === 'mech' ? 20 : 12);
|
createBlood(e.x + e.width/2, e.y + e.height/2, 12);
|
||||||
if (Math.random() < e.weaponDrop) spawnPowerup(e.x, e.y);
|
if (Math.random() < e.weaponDrop) spawnPowerup(e.x, e.y);
|
||||||
enemies.splice(ei, 1);
|
enemies.splice(ei, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (b.explosive) {
|
if (b.explosive) {
|
||||||
explosions.push({ x: b.x, y: b.y, radius: 0, maxRadius: 60, alpha: 1 });
|
explosions.push({ x: b.x, y: b.y, radius: 0, alpha: 1 });
|
||||||
shake = 3;
|
shake = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return !hit;
|
return !hit;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Взрывы
|
||||||
explosions = explosions.filter(e => {
|
explosions = explosions.filter(e => {
|
||||||
e.radius += 4;
|
e.radius += 4;
|
||||||
e.alpha -= 0.05;
|
e.alpha -= 0.05;
|
||||||
return e.alpha > 0;
|
return e.alpha > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Предметы
|
||||||
powerups = powerups.filter(p => {
|
powerups = powerups.filter(p => {
|
||||||
p.vy += 0.5;
|
p.vy += 0.5;
|
||||||
p.y += p.vy;
|
p.y += p.vy;
|
||||||
@ -400,19 +396,8 @@ function update() {
|
|||||||
if (p.shield) player.invincible = 300;
|
if (p.shield) player.invincible = 300;
|
||||||
if (p.bomb) {
|
if (p.bomb) {
|
||||||
enemies.forEach(e => { e.hp -= 150; createExplosion(e.x + e.width/2, e.y + e.height/2, '#ff6600', 10); });
|
enemies.forEach(e => { e.hp -= 150; createExplosion(e.x + e.width/2, e.y + e.height/2, '#ff6600', 10); });
|
||||||
enemies = enemies.filter(e => {
|
enemies = enemies.filter(e => e.hp > 0);
|
||||||
if (e.hp <= 0) {
|
if (bossActive && boss) { boss.hp -= 500; shake = 15; }
|
||||||
player.score += e.score;
|
|
||||||
createBlood(e.x + e.width/2, e.y + e.height/2, 15);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
if (bossActive && boss) {
|
|
||||||
boss.hp -= 500;
|
|
||||||
createExplosion(boss.x + boss.width/2, boss.y + boss.height/2, '#ff6600', 30);
|
|
||||||
shake = 15;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
player.score += 50;
|
player.score += 50;
|
||||||
createExplosion(p.x + p.width/2, p.y + p.height/2, p.color, 20);
|
createExplosion(p.x + p.width/2, p.y + p.height/2, p.color, 20);
|
||||||
@ -421,6 +406,7 @@ function update() {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Частицы
|
||||||
particles = particles.filter(p => {
|
particles = particles.filter(p => {
|
||||||
p.x += p.vx;
|
p.x += p.vx;
|
||||||
p.y += p.vy;
|
p.y += p.vy;
|
||||||
@ -434,26 +420,26 @@ function update() {
|
|||||||
|
|
||||||
backgroundOffset += player.vx * 0.3;
|
backgroundOffset += player.vx * 0.3;
|
||||||
|
|
||||||
|
// UI
|
||||||
document.getElementById('hp').textContent = player.hp;
|
document.getElementById('hp').textContent = player.hp;
|
||||||
document.getElementById('maxhp').textContent = player.maxHp;
|
document.getElementById('maxhp').textContent = player.maxHp;
|
||||||
document.getElementById('wpn').textContent = weapons[player.weapon].name + (player.weaponLevel > 1 ? ' +' + (player.weaponLevel - 1) : '');
|
document.getElementById('wpn').textContent = weapons[player.weapon].name + (player.weaponLevel > 1 ? ' +' + (player.weaponLevel - 1) : '');
|
||||||
document.getElementById('scr').textContent = player.score;
|
document.getElementById('scr').textContent = player.score;
|
||||||
document.getElementById('wave-num').textContent = player.wave;
|
|
||||||
document.getElementById('wprogress').textContent = (enemiesToSpawn > 0 ? enemiesToSpawn : 0) + '/' + enemiesInWave;
|
|
||||||
|
|
||||||
if (player.hp <= 0) {
|
if (player.hp <= 0) {
|
||||||
alert('GAME OVER! Счёт: ' + player.score + ', Волна: ' + player.wave);
|
alert('GAME OVER! Счёт: ' + player.score + ', Волна: ' + waveNum);
|
||||||
resetGame();
|
resetGame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetGame() {
|
function resetGame() {
|
||||||
player.x = 100; player.y = 350; player.hp = 100; player.maxHp = 100; player.score = 0;
|
player.x = 100; player.y = 350; player.hp = 100; player.maxHp = 100;
|
||||||
player.weapon = 0; player.weaponLevel = 1; player.level = 1; player.wave = 1;
|
player.score = 0; player.weapon = 0; player.weaponLevel = 1;
|
||||||
bullets = []; enemies = []; powerups = []; particles = []; explosions = []; boss = null;
|
waveNum = 1; enemiesRemaining = 5; spawnTimer = 0;
|
||||||
bossActive = false; waveActive = false;
|
bullets = []; enemies = []; powerups = []; particles = []; explosions = [];
|
||||||
|
boss = null; bossActive = false;
|
||||||
document.getElementById('wave-num').textContent = '1';
|
document.getElementById('wave-num').textContent = '1';
|
||||||
startWave();
|
document.getElementById('wprogress').textContent = '5/5';
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
@ -462,6 +448,7 @@ function draw() {
|
|||||||
ctx.translate((Math.random() - 0.5) * shake * 2, (Math.random() - 0.5) * shake * 2);
|
ctx.translate((Math.random() - 0.5) * shake * 2, (Math.random() - 0.5) * shake * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Небо
|
||||||
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
|
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
|
||||||
gradient.addColorStop(0, '#0a0a1a');
|
gradient.addColorStop(0, '#0a0a1a');
|
||||||
gradient.addColorStop(0.5, '#16213e');
|
gradient.addColorStop(0.5, '#16213e');
|
||||||
@ -469,6 +456,7 @@ function draw() {
|
|||||||
ctx.fillStyle = gradient;
|
ctx.fillStyle = gradient;
|
||||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
// Звёзды
|
||||||
ctx.fillStyle = '#fff';
|
ctx.fillStyle = '#fff';
|
||||||
for (let i = 0; i < 80; i++) {
|
for (let i = 0; i < 80; i++) {
|
||||||
const x = ((i * 73 + backgroundOffset * 0.1) % canvas.width + canvas.width) % canvas.width;
|
const x = ((i * 73 + backgroundOffset * 0.1) % canvas.width + canvas.width) % canvas.width;
|
||||||
@ -479,6 +467,7 @@ function draw() {
|
|||||||
}
|
}
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
|
|
||||||
|
// Горы
|
||||||
ctx.fillStyle = '#1a1a2e';
|
ctx.fillStyle = '#1a1a2e';
|
||||||
for (let i = 0; i < 8; i++) {
|
for (let i = 0; i < 8; i++) {
|
||||||
const x = ((i * 120 - backgroundOffset * 0.2) % 1200) - 200;
|
const x = ((i * 120 - backgroundOffset * 0.2) % 1200) - 200;
|
||||||
@ -489,30 +478,31 @@ function draw() {
|
|||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Город
|
||||||
ctx.fillStyle = '#2a2a3e';
|
ctx.fillStyle = '#2a2a3e';
|
||||||
for (let i = 0; i < canvas.width; i += 60) {
|
for (let i = 0; i < canvas.width; i += 60) {
|
||||||
const buildingH = 80 + Math.sin(i) * 30;
|
const h = 80 + Math.sin(i) * 30;
|
||||||
ctx.fillRect(i, 452 - buildingH, 50, buildingH);
|
ctx.fillRect(i, 452 - h, 50, h);
|
||||||
const windows = Math.floor(buildingH / 20);
|
for (let w = 0; w < Math.floor(h / 20); w++) {
|
||||||
for (let w = 0; w < windows; w++) {
|
for (let c = 0; c < 3; c++) {
|
||||||
for (let col = 0; col < 3; col++) {
|
if (Math.sin(gameTime * 0.02 + i + w + c) > 0.3) {
|
||||||
if (Math.sin(gameTime * 0.02 + i + w + col) > 0.3) {
|
|
||||||
ctx.fillStyle = '#ffff88';
|
ctx.fillStyle = '#ffff88';
|
||||||
ctx.fillRect(i + 8 + col * 14, 452 - buildingH + 10 + w * 20, 8, 12);
|
ctx.fillRect(i + 8 + c * 14, 452 - h + 10 + w * 20, 8, 12);
|
||||||
ctx.fillStyle = '#2a2a3e';
|
ctx.fillStyle = '#2a2a3e';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Земля
|
||||||
ctx.fillStyle = '#3a2e1a';
|
ctx.fillStyle = '#3a2e1a';
|
||||||
ctx.fillRect(0, 452, canvas.width, 48);
|
ctx.fillRect(0, 452, canvas.width, 48);
|
||||||
ctx.fillStyle = '#4a3a2a';
|
ctx.fillStyle = '#4a3a2a';
|
||||||
ctx.fillRect(0, 452, canvas.width, 8);
|
ctx.fillRect(0, 452, canvas.width, 8);
|
||||||
ctx.fillStyle = '#2d4a1e';
|
ctx.fillStyle = '#2d4a1e';
|
||||||
for (let i = 0; i < canvas.width; i += 20) {
|
for (let i = 0; i < canvas.width; i += 20) {
|
||||||
const grassH = 4 + Math.sin(gameTime * 0.05 + i) * 2;
|
const gh = 4 + Math.sin(gameTime * 0.05 + i) * 2;
|
||||||
ctx.fillRect(i, 452 - grassH, 8, grassH);
|
ctx.fillRect(i, 452 - gh, 8, gh);
|
||||||
}
|
}
|
||||||
ctx.fillStyle = '#5a4a3a';
|
ctx.fillStyle = '#5a4a3a';
|
||||||
ctx.fillRect(120, 456, 16, 10);
|
ctx.fillRect(120, 456, 16, 10);
|
||||||
@ -520,11 +510,12 @@ function draw() {
|
|||||||
ctx.fillRect(580, 454, 14, 12);
|
ctx.fillRect(580, 454, 14, 12);
|
||||||
ctx.fillRect(720, 458, 18, 8);
|
ctx.fillRect(720, 458, 18, 8);
|
||||||
|
|
||||||
|
// Игрок
|
||||||
if (player.invincible % 4 < 2) {
|
if (player.invincible % 4 < 2) {
|
||||||
const legOffset = player.vx !== 0 ? Math.sin(Date.now() * 0.02) * 6 : 0;
|
const lo = player.vx !== 0 ? Math.sin(Date.now() * 0.02) * 6 : 0;
|
||||||
ctx.fillStyle = '#2a3a5a';
|
ctx.fillStyle = '#2a3a5a';
|
||||||
ctx.fillRect(player.x + 6, player.y + 36, 8, 12 + legOffset);
|
ctx.fillRect(player.x + 6, player.y + 36, 8, 12 + lo);
|
||||||
ctx.fillRect(player.x + 18, player.y + 36, 8, 12 - legOffset);
|
ctx.fillRect(player.x + 18, player.y + 36, 8, 12 - lo);
|
||||||
ctx.fillStyle = '#1a1a2e';
|
ctx.fillStyle = '#1a1a2e';
|
||||||
ctx.fillRect(player.x + 4, player.y + 44, 10, 4);
|
ctx.fillRect(player.x + 4, player.y + 44, 10, 4);
|
||||||
ctx.fillRect(player.x + 18, player.y + 44, 10, 4);
|
ctx.fillRect(player.x + 18, player.y + 44, 10, 4);
|
||||||
@ -541,10 +532,10 @@ function draw() {
|
|||||||
ctx.fillStyle = '#2a3a4a';
|
ctx.fillStyle = '#2a3a4a';
|
||||||
ctx.fillRect(player.x + 8, player.y - 6, 14, 6);
|
ctx.fillRect(player.x + 8, player.y - 6, 14, 6);
|
||||||
ctx.fillStyle = '#fff';
|
ctx.fillStyle = '#fff';
|
||||||
const eyeX = player.facing === 1 ? player.x + 18 : player.x + 10;
|
const ex = player.facing === 1 ? player.x + 18 : player.x + 10;
|
||||||
ctx.fillRect(eyeX, player.y + 2, 6, 4);
|
ctx.fillRect(ex, player.y + 2, 6, 4);
|
||||||
ctx.fillStyle = '#000';
|
ctx.fillStyle = '#000';
|
||||||
ctx.fillRect(player.facing === 1 ? eyeX + 2 : eyeX, player.y + 3, 3, 2);
|
ctx.fillRect(player.facing === 1 ? ex + 2 : ex, player.y + 3, 3, 2);
|
||||||
ctx.fillStyle = '#ffccaa';
|
ctx.fillStyle = '#ffccaa';
|
||||||
ctx.fillRect(player.x + 14, player.y + 16, 10, 10);
|
ctx.fillRect(player.x + 14, player.y + 16, 10, 10);
|
||||||
ctx.fillStyle = '#4a90d9';
|
ctx.fillStyle = '#4a90d9';
|
||||||
@ -562,6 +553,7 @@ function draw() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Пули
|
||||||
bullets.forEach(b => {
|
bullets.forEach(b => {
|
||||||
if (b.laser) {
|
if (b.laser) {
|
||||||
ctx.strokeStyle = b.glow;
|
ctx.strokeStyle = b.glow;
|
||||||
@ -578,10 +570,10 @@ function draw() {
|
|||||||
ctx.arc(b.x, b.y, b.size, 0, Math.PI * 2);
|
ctx.arc(b.x, b.y, b.size, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
} else {
|
} else {
|
||||||
const glowGradient = ctx.createRadialGradient(b.x, b.y, 0, b.x, b.y, b.size * 3);
|
const gg = ctx.createRadialGradient(b.x, b.y, 0, b.x, b.y, b.size * 3);
|
||||||
glowGradient.addColorStop(0, b.glow);
|
gg.addColorStop(0, b.glow);
|
||||||
glowGradient.addColorStop(1, 'transparent');
|
gg.addColorStop(1, 'transparent');
|
||||||
ctx.fillStyle = glowGradient;
|
ctx.fillStyle = gg;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(b.x, b.y, b.size * 3, 0, Math.PI * 2);
|
ctx.arc(b.x, b.y, b.size * 3, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
@ -589,28 +581,18 @@ function draw() {
|
|||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(b.x, b.y, b.size, 0, Math.PI * 2);
|
ctx.arc(b.x, b.y, b.size, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
if (b.plasma) {
|
|
||||||
ctx.strokeStyle = b.glow;
|
|
||||||
ctx.lineWidth = b.size;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(b.x, b.y);
|
|
||||||
ctx.lineTo(b.x - b.vx * 2, b.y - b.vy * 2);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Взрывы
|
||||||
explosions.forEach(e => {
|
explosions.forEach(e => {
|
||||||
ctx.fillStyle = `rgba(255, 100, 0, ${e.alpha})`;
|
ctx.fillStyle = `rgba(255, 100, 0, ${e.alpha})`;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(e.x, e.y, e.radius, 0, Math.PI * 2);
|
ctx.arc(e.x, e.y, e.radius, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.fillStyle = `rgba(255, 200, 50, ${e.alpha * 0.7})`;
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(e.x, e.y, e.radius * 0.7, 0, Math.PI * 2);
|
|
||||||
ctx.fill();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Частицы
|
||||||
particles.forEach(p => {
|
particles.forEach(p => {
|
||||||
ctx.globalAlpha = p.life / 50;
|
ctx.globalAlpha = p.life / 50;
|
||||||
ctx.fillStyle = p.color;
|
ctx.fillStyle = p.color;
|
||||||
@ -620,6 +602,7 @@ function draw() {
|
|||||||
});
|
});
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
|
|
||||||
|
// Босс
|
||||||
if (bossActive && boss) {
|
if (bossActive && boss) {
|
||||||
boss.animFrame++;
|
boss.animFrame++;
|
||||||
ctx.fillStyle = '#3a1a1a';
|
ctx.fillStyle = '#3a1a1a';
|
||||||
@ -638,6 +621,7 @@ function draw() {
|
|||||||
ctx.fillStyle = '#5a2a2a';
|
ctx.fillStyle = '#5a2a2a';
|
||||||
ctx.fillRect(boss.x, boss.y + 60, 20, 40);
|
ctx.fillRect(boss.x, boss.y + 60, 20, 40);
|
||||||
ctx.fillRect(boss.x + 100, boss.y + 60, 20, 40);
|
ctx.fillRect(boss.x + 100, boss.y + 60, 20, 40);
|
||||||
|
|
||||||
const hpPct = boss.hp / boss.maxHp;
|
const hpPct = boss.hp / boss.maxHp;
|
||||||
ctx.fillStyle = '#333';
|
ctx.fillStyle = '#333';
|
||||||
ctx.fillRect(boss.x, boss.y - 25, boss.width, 12);
|
ctx.fillRect(boss.x, boss.y - 25, boss.width, 12);
|
||||||
@ -650,42 +634,37 @@ function draw() {
|
|||||||
ctx.textAlign = 'left';
|
ctx.textAlign = 'left';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Враги
|
||||||
enemies.forEach(e => {
|
enemies.forEach(e => {
|
||||||
e.animFrame++;
|
e.animFrame++;
|
||||||
if (e.name === 'soldier') {
|
if (e.name === 'soldier') {
|
||||||
const legOffset = Math.sin(e.animFrame * 0.3) * 6;
|
const lo = Math.sin(e.animFrame * 0.3) * 6;
|
||||||
ctx.fillStyle = '#1a1a2e';
|
ctx.fillStyle = '#1a1a2e';
|
||||||
ctx.fillRect(e.x + 6, e.y + 36, 8, 12 + legOffset);
|
ctx.fillRect(e.x + 6, e.y + 36, 8, 12 + lo);
|
||||||
ctx.fillRect(e.x + 18, e.y + 36, 8, 12 - legOffset);
|
ctx.fillRect(e.x + 18, e.y + 36, 8, 12 - lo);
|
||||||
ctx.fillStyle = e.color;
|
ctx.fillStyle = e.color;
|
||||||
ctx.fillRect(e.x + 4, e.y + 14, 24, 24);
|
ctx.fillRect(e.x + 4, e.y + 14, 24, 24);
|
||||||
ctx.fillStyle = '#ffccaa';
|
ctx.fillStyle = '#ffccaa';
|
||||||
ctx.fillRect(e.x + 6, e.y, 20, 16);
|
ctx.fillRect(e.x + 6, e.y, 20, 16);
|
||||||
ctx.fillStyle = '#3a4a3a';
|
ctx.fillStyle = '#3a4a3a';
|
||||||
ctx.fillRect(e.x + 4, e.y - 4, 24, 8);
|
ctx.fillRect(e.x + 4, e.y - 4, 24, 8);
|
||||||
ctx.fillStyle = '#4a2a2a';
|
|
||||||
ctx.fillRect(e.x + 16, e.y + 18, 14, 8);
|
|
||||||
ctx.fillStyle = '#444';
|
ctx.fillStyle = '#444';
|
||||||
ctx.fillRect(e.x + 28, e.y + 20, 12, 6);
|
ctx.fillRect(e.x + 28, e.y + 20, 12, 6);
|
||||||
} else if (e.name === 'tank') {
|
} else if (e.name === 'tank') {
|
||||||
ctx.fillStyle = '#1a1a1a';
|
ctx.fillStyle = '#1a1a1a';
|
||||||
const trackOffset = Math.sin(e.animFrame * 0.1) * 2;
|
const to = Math.sin(e.animFrame * 0.1) * 2;
|
||||||
ctx.fillRect(e.x, e.y + 24 + trackOffset, e.width, 11);
|
ctx.fillRect(e.x, e.y + 24 + to, e.width, 11);
|
||||||
ctx.fillStyle = e.color;
|
ctx.fillStyle = e.color;
|
||||||
ctx.fillRect(e.x + 4, e.y + 8, e.width - 8, 20);
|
ctx.fillRect(e.x + 4, e.y + 8, e.width - 8, 20);
|
||||||
ctx.fillStyle = '#3d6016';
|
ctx.fillStyle = '#3d6016';
|
||||||
ctx.fillRect(e.x + 18, e.y, 24, 14);
|
ctx.fillRect(e.x + 18, e.y, 24, 14);
|
||||||
ctx.fillStyle = '#2d4010';
|
ctx.fillStyle = '#2d4010';
|
||||||
ctx.fillRect(e.x + 36, e.y + 4, 28, 8);
|
ctx.fillRect(e.x + 36, e.y + 4, 28, 8);
|
||||||
ctx.fillStyle = '#1a1a1a';
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(e.x + 30, e.y + 7, 5, 0, Math.PI * 2);
|
|
||||||
ctx.fill();
|
|
||||||
} else if (e.name === 'helicopter') {
|
} else if (e.name === 'helicopter') {
|
||||||
ctx.fillStyle = '#333';
|
ctx.fillStyle = '#333';
|
||||||
const rotorOffset = Math.sin(e.animFrame * 0.8) * 20;
|
const ro = Math.sin(e.animFrame * 0.8) * 20;
|
||||||
ctx.fillRect(e.x - 15, e.y - 8, 85, 4);
|
ctx.fillRect(e.x - 15, e.y - 8, 85, 4);
|
||||||
ctx.fillRect(e.x + 25 + rotorOffset, e.y - 12, 6, 8);
|
ctx.fillRect(e.x + 25 + ro, e.y - 12, 6, 8);
|
||||||
ctx.fillStyle = e.color;
|
ctx.fillStyle = e.color;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.ellipse(e.x + 25, e.y + 8, 30, 15, 0, 0, Math.PI * 2);
|
ctx.ellipse(e.x + 25, e.y + 8, 30, 15, 0, 0, Math.PI * 2);
|
||||||
@ -694,29 +673,24 @@ function draw() {
|
|||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(e.x + 40, e.y + 5, 12, 0, Math.PI * 2);
|
ctx.arc(e.x + 40, e.y + 5, 12, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.fillStyle = e.color;
|
|
||||||
ctx.fillRect(e.x - 20, e.y + 4, 20, 8);
|
|
||||||
ctx.fillRect(e.x - 25, e.y, 8, 16);
|
|
||||||
} else if (e.name === 'commando') {
|
} else if (e.name === 'commando') {
|
||||||
const legOffset = Math.sin(e.animFrame * 0.4) * 8;
|
const lo = Math.sin(e.animFrame * 0.4) * 8;
|
||||||
ctx.fillStyle = '#2a1a2a';
|
ctx.fillStyle = '#2a1a2a';
|
||||||
ctx.fillRect(e.x + 8, e.y + 38, 7, 12 + legOffset);
|
ctx.fillRect(e.x + 8, e.y + 38, 7, 12 + lo);
|
||||||
ctx.fillRect(e.x + 19, e.y + 38, 7, 12 - legOffset);
|
ctx.fillRect(e.x + 19, e.y + 38, 7, 12 - lo);
|
||||||
ctx.fillStyle = e.color;
|
ctx.fillStyle = e.color;
|
||||||
ctx.fillRect(e.x + 6, e.y + 16, 22, 24);
|
ctx.fillRect(e.x + 6, e.y + 16, 22, 24);
|
||||||
ctx.fillStyle = '#ffccaa';
|
ctx.fillStyle = '#ffccaa';
|
||||||
ctx.fillRect(e.x + 8, e.y + 2, 18, 16);
|
ctx.fillRect(e.x + 8, e.y + 2, 18, 16);
|
||||||
ctx.fillStyle = '#4a1a4a';
|
|
||||||
ctx.fillRect(e.x + 6, e.y - 2, 22, 8);
|
|
||||||
ctx.fillStyle = '#6a2a6a';
|
ctx.fillStyle = '#6a2a6a';
|
||||||
ctx.fillRect(e.x + 18, e.y + 20, 16, 8);
|
ctx.fillRect(e.x + 18, e.y + 20, 16, 8);
|
||||||
ctx.fillStyle = '#333';
|
ctx.fillStyle = '#333';
|
||||||
ctx.fillRect(e.x + 32, e.y + 22, 14, 6);
|
ctx.fillRect(e.x + 32, e.y + 22, 14, 6);
|
||||||
} else if (e.name === 'mech') {
|
} else if (e.name === 'mech') {
|
||||||
const legOffset = Math.sin(e.animFrame * 0.15) * 4;
|
const lo = Math.sin(e.animFrame * 0.15) * 4;
|
||||||
ctx.fillStyle = '#1a2a3a';
|
ctx.fillStyle = '#1a2a3a';
|
||||||
ctx.fillRect(e.x + 10, e.y + 40, 14, 15 + legOffset);
|
ctx.fillRect(e.x + 10, e.y + 40, 14, 15 + lo);
|
||||||
ctx.fillRect(e.x + 46, e.y + 40, 14, 15 - legOffset);
|
ctx.fillRect(e.x + 46, e.y + 40, 14, 15 - lo);
|
||||||
ctx.fillStyle = e.color;
|
ctx.fillStyle = e.color;
|
||||||
ctx.fillRect(e.x + 8, e.y + 20, 54, 28);
|
ctx.fillRect(e.x + 8, e.y + 20, 54, 28);
|
||||||
ctx.fillRect(e.x + 20, e.y + 8, 30, 20);
|
ctx.fillRect(e.x + 20, e.y + 8, 30, 20);
|
||||||
@ -724,8 +698,6 @@ function draw() {
|
|||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(e.x + 35, e.y + 18, 10, 0, Math.PI * 2);
|
ctx.arc(e.x + 35, e.y + 18, 10, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.fillStyle = '#003344';
|
|
||||||
ctx.fillRect(e.x + 50, e.y + 24, 24, 10);
|
|
||||||
ctx.fillStyle = '#ff4400';
|
ctx.fillStyle = '#ff4400';
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(e.x + 35, e.y + 18, 4, 0, Math.PI * 2);
|
ctx.arc(e.x + 35, e.y + 18, 4, 0, Math.PI * 2);
|
||||||
@ -739,24 +711,25 @@ function draw() {
|
|||||||
ctx.fillRect(e.x + 1, e.y - 15, (e.width - 2) * hpPct, 6);
|
ctx.fillRect(e.x + 1, e.y - 15, (e.width - 2) * hpPct, 6);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Предметы
|
||||||
powerups.forEach(p => {
|
powerups.forEach(p => {
|
||||||
const bobY = p.grounded ? Math.sin(p.bobOffset) * 4 : 0;
|
const by = p.grounded ? Math.sin(p.bobOffset) * 4 : 0;
|
||||||
const glowGradient = ctx.createRadialGradient(p.x + p.width/2, p.y + p.height/2 + bobY, 0, p.x + p.width/2, p.y + p.height/2 + bobY, 20);
|
const gg = ctx.createRadialGradient(p.x + p.width/2, p.y + p.height/2 + by, 0, p.x + p.width/2, p.y + p.height/2 + by, 20);
|
||||||
glowGradient.addColorStop(0, p.color);
|
gg.addColorStop(0, p.color);
|
||||||
glowGradient.addColorStop(1, 'transparent');
|
gg.addColorStop(1, 'transparent');
|
||||||
ctx.fillStyle = glowGradient;
|
ctx.fillStyle = gg;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(p.x + p.width/2, p.y + p.height/2 + bobY, 20, 0, Math.PI * 2);
|
ctx.arc(p.x + p.width/2, p.y + p.height/2 + by, 20, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.fillStyle = p.color;
|
ctx.fillStyle = p.color;
|
||||||
ctx.fillRect(p.x + 2, p.y + 4 + bobY, p.width - 4, p.height - 4);
|
ctx.fillRect(p.x + 2, p.y + 4 + by, p.width - 4, p.height - 4);
|
||||||
ctx.strokeStyle = '#fff';
|
ctx.strokeStyle = '#fff';
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 2;
|
||||||
ctx.strokeRect(p.x + 2, p.y + 4 + bobY, p.width - 4, p.height - 4);
|
ctx.strokeRect(p.x + 2, p.y + 4 + by, p.width - 4, p.height - 4);
|
||||||
ctx.fillStyle = '#fff';
|
ctx.fillStyle = '#fff';
|
||||||
ctx.font = 'bold 18px monospace';
|
ctx.font = 'bold 18px monospace';
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.fillText(p.label, p.x + p.width/2, p.y + 20 + bobY);
|
ctx.fillText(p.label, p.x + p.width/2, p.y + 20 + by);
|
||||||
ctx.textAlign = 'left';
|
ctx.textAlign = 'left';
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -769,5 +742,5 @@ function loop() {
|
|||||||
requestAnimationFrame(loop);
|
requestAnimationFrame(loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
startWave();
|
resetGame();
|
||||||
loop();
|
loop();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user