Мэлон
by TurboGamer88519 lines21.6 KB
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Мэлон</title>
<style>
body { margin: 0; background: #0a0a12; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; font-family: Arial; overflow: hidden; touch-action: none; }
canvas { background: #1a1a2e; border: 2px solid #4CAF50; border-radius: 8px; touch-action: none; }
#hud { font-size: 13px; margin-bottom: 4px; color: #4CAF50; font-weight: bold; text-align: center; }
#toolbar { position: absolute; bottom: 15px; left: 0; width: 100%; display: flex; justify-content: center; gap: 6px; pointer-events: none; flex-wrap: wrap; padding: 0 5px; box-sizing: border-box; }
.tool { width: 45px; height: 45px; border-radius: 10px; background: rgba(76,175,80,0.25); color: #fff; font-size: 18px; border: 2px solid #4CAF50; display: flex; align-items: center; justify-content: center; user-select: none; pointer-events: auto; touch-action: none; }
.tool.active { background: rgba(76,175,80,0.6); box-shadow: 0 0 10px #4CAF50; }
#clearBtn { background: rgba(255,0,102,0.25); border-color: #ff0066; }
</style>
</head>
<body>
<div id="hud">🍉 Мэлон | Объектов: 0 | 💥 Взрывов: 0 | 🩸 Ранений: 0</div>
<canvas id="game" width="420" height="600"></canvas>
<div id="toolbar">
<div class="tool active" data-tool="melon">🍉</div>
<div class="tool" data-tool="pumpkin">🎃</div>
<div class="tool" data-tool="box">📦</div>
<div class="tool" data-tool="spike">🔺</div>
<div class="tool" data-tool="gun">🔫</div>
<div class="tool" data-tool="sword">⚔️</div>
<div class="tool" data-tool="bomb">💣</div>
<div class="tool" data-tool="cannon">💥</div>
<div class="tool" id="clearBtn">🗑</div>
</div>
<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const hud = document.getElementById('hud');
let objects = [];
let bullets = [];
let explosions = [];
const groundY = 560;
let selectedTool = 'melon';
let dragged = null;
let dragOffsetX = 0, dragOffsetY = 0;
let mouseX = 0, mouseY = 0;
let explosionCount = 0;
let woundCount = 0;
// --- МУЗЫКА ---
let audioCtx = null, musicOn = true, musicNodes = [], beatTimer = null, melodyTimer = null;
let soundOn = true;
function initAudio() {
if (audioCtx) return;
try { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); startMusic(); } catch(e) {}
}
function startMusic() {
if (!audioCtx || !musicOn) return;
stopMusic();
try {
const bass = audioCtx.createOscillator();
const bg = audioCtx.createGain();
bass.type = 'sine'; bass.frequency.value = 65; bg.gain.value = 0.03;
bass.connect(bg); bg.connect(audioCtx.destination); bass.start();
musicNodes.push(bass, bg);
function beat() {
if (!audioCtx || !musicOn) return;
try {
const o = audioCtx.createOscillator(); const g = audioCtx.createGain();
o.type = 'square'; o.frequency.value = 80;
g.gain.setValueAtTime(0.05, audioCtx.currentTime);
g.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.1);
o.connect(g); g.connect(audioCtx.destination); o.start(); o.stop(audioCtx.currentTime + 0.1);
beatTimer = setTimeout(beat, 500);
} catch(e) {}
}
beat();
const mel = [262,330,392,330,262,294,349,294]; let mi = 0;
function melo() {
if (!audioCtx || !musicOn) return;
try {
const o = audioCtx.createOscillator(); const g = audioCtx.createGain();
o.type = 'triangle'; o.frequency.value = mel[mi % mel.length];
g.gain.setValueAtTime(0.03, audioCtx.currentTime);
g.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.3);
o.connect(g); g.connect(audioCtx.destination); o.start(); o.stop(audioCtx.currentTime + 0.3);
mi++; melodyTimer = setTimeout(melo, 600);
} catch(e) {}
}
melo();
} catch(e) {}
}
function stopMusic() {
if (beatTimer) { clearTimeout(beatTimer); beatTimer = null; }
if (melodyTimer) { clearTimeout(melodyTimer); melodyTimer = null; }
musicNodes.forEach(n => { try { n.stop(); } catch(e) {} try { n.disconnect(); } catch(e) {} });
musicNodes = [];
}
function playSound(freq, dur, type, vol) {
if (!audioCtx || !soundOn) return;
try {
const o = audioCtx.createOscillator(); const g = audioCtx.createGain();
o.type = type || 'sine'; o.frequency.value = freq;
g.gain.setValueAtTime(vol || 0.08, audioCtx.currentTime);
g.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + dur);
o.connect(g); g.connect(audioCtx.destination); o.start(); o.stop(audioCtx.currentTime + dur);
} catch(e) {}
}
function sfxSpawn() { playSound(600, 0.08, 'sine', 0.06); }
function sfxThud() { playSound(80, 0.12, 'sawtooth', 0.1); }
function sfxShoot() { playSound(900, 0.06, 'square', 0.1); }
function sfxSlash() { playSound(400, 0.08, 'sawtooth', 0.08); }
function sfxBoom() { playSound(60, 0.4, 'sawtooth', 0.2); setTimeout(() => playSound(40, 0.3, 'sawtooth', 0.15), 100); }
function sfxPain() { playSound(300, 0.15, 'sawtooth', 0.1); setTimeout(() => playSound(200, 0.15, 'sawtooth', 0.08), 80); }
function sfxClick() { playSound(800, 0.05, 'square', 0.06); }
// --- СОЗДАНИЕ ---
function createMelon(x, y) {
const bw = 20, bh = 28, hr = 10;
return { type: 'melon', alive: true, parts: [
{ x: x, y: y, w: bw, h: bh, vx: 0, vy: 0, angle: 0, va: 0, type: 'body', hitTimer: 0 },
{ x: x + bw/2, y: y - hr, r: hr, vx: 0, vy: 0, angle: 0, va: 0, type: 'head', hitTimer: 0 },
{ x: x - 5, y: y + 5, w: 6, h: 16, vx: 0, vy: 0, angle: 0, va: 0, type: 'armL', hitTimer: 0 },
{ x: x + bw - 1, y: y + 5, w: 6, h: 16, vx: 0, vy: 0, angle: 0, va: 0, type: 'armR', hitTimer: 0 },
{ x: x + 2, y: y + bh - 2, w: 7, h: 14, vx: 0, vy: 0, angle: 0, va: 0, type: 'legL', hitTimer: 0 },
{ x: x + bw - 9, y: y + bh - 2, w: 7, h: 14, vx: 0, vy: 0, angle: 0, va: 0, type: 'legR', hitTimer: 0 }
]};
}
function createPumpkin(x, y) {
return { type: 'pumpkin', alive: true, x: x, y: y, w: 34, h: 34, vx: 0, vy: 0, angle: 0, va: 0, hitTimer: 0 };
}
function createBox(x, y) { return { type: 'box', alive: true, x, y, w: 40, h: 40, vx: 0, vy: 0, angle: 0, va: 0, fallen: false }; }
function createSpike(x, y) { return { type: 'spike', alive: true, x, y, w: 30, h: 30, vx: 0, vy: 0, angle: 0, va: 0, fallen: false }; }
function createGun(x, y) { return { type: 'gun', alive: true, x, y, w: 30, h: 14, vx: 0, vy: 0, angle: 0, va: 0, cooldown: 0, fallen: false }; }
function createSword(x, y) { return { type: 'sword', alive: true, x, y, w: 40, h: 8, vx: 0, vy: 0, angle: 0, va: 0, slashTimer: 0, fallen: false }; }
function createBomb(x, y) { return { type: 'bomb', alive: true, x, y, w: 24, h: 24, vx: 0, vy: 0, angle: 0, va: 0, fuse: 120, fallen: false }; }
function createCannon(x, y) { return { type: 'cannon', alive: true, x, y, w: 40, h: 24, vx: 0, vy: 0, angle: 0, va: 0, cooldown: 0, fallen: false }; }
function spawnAt(x, y) {
initAudio();
if (y > groundY - 40) y = groundY - 40;
if (x < 50) x = 50;
if (x > canvas.width - 50) x = canvas.width - 50;
if (selectedTool === 'melon') objects.push(createMelon(x - 15, y - 30));
else if (selectedTool === 'pumpkin') objects.push(createPumpkin(x - 17, y - 17));
else if (selectedTool === 'box') objects.push(createBox(x - 20, y - 20));
else if (selectedTool === 'spike') objects.push(createSpike(x - 15, y - 15));
else if (selectedTool === 'gun') objects.push(createGun(x - 15, y - 7));
else if (selectedTool === 'sword') objects.push(createSword(x - 20, y - 4));
else if (selectedTool === 'bomb') objects.push(createBomb(x - 12, y - 12));
else if (selectedTool === 'cannon') objects.push(createCannon(x - 20, y - 12));
sfxSpawn();
updateHUD();
}
function updateHUD() { hud.textContent = `🍉 Мэлон | Объектов: ${objects.length} | 💥 Взрывов: ${explosionCount} | 🩸 Ранений: ${woundCount}`; }
// --- ФИЗИКА (ИСПРАВЛЕНА) ---
function updatePhysics() {
const G = 0.6, FR = 0.95, BOUNCE = 0.2;
const W = canvas.width;
objects.forEach(obj => {
if (!obj.alive) return;
if (obj.type === 'melon') {
// Гравитация для всех частей
obj.parts.forEach(p => {
if (p.hitTimer > 0) p.hitTimer--;
if (dragged && dragged.parts && dragged.parts.includes(p)) return;
p.vy += G;
p.vx *= FR;
p.x += p.vx;
p.y += p.vy;
p.angle += p.va;
p.va *= 0.95;
});
// Коллизии с землёй и стенами
obj.parts.forEach(p => {
if (dragged && dragged.parts && dragged.parts.includes(p)) return;
const bottom = p.type === 'head' ? p.y + p.r : p.y + p.h;
if (bottom > groundY) {
if (p.type === 'head') p.y = groundY - p.r; else p.y = groundY - p.h;
p.vy = -p.vy * BOUNCE;
if (Math.abs(p.vy) > 1) sfxThud();
}
if (p.x < 0) { p.x = 0; p.vx = -p.vx * BOUNCE; }
const pw = p.w || p.r*2;
if (p.x + pw > W) { p.x = W - pw; p.vx = -p.vx * BOUNCE; }
});
// Связи между частями (арбуз стоит как человечек)
const body = obj.parts[0];
const head = obj.parts[1];
const armL = obj.parts[2];
const armR = obj.parts[3];
const legL = obj.parts[4];
const legR = obj.parts[5];
if (!dragged || !dragged.parts.includes(head)) {
head.x += (body.x + body.w/2 - head.r - head.x) * 0.2;
head.y += (body.y - head.r * 2 - head.y) * 0.2;
}
if (!dragged || !dragged.parts.includes(armL)) {
armL.x += (body.x - 5 - armL.x) * 0.15;
armL.y += (body.y + 5 - armL.y) * 0.15;
}
if (!dragged || !dragged.parts.includes(armR)) {
armR.x += (body.x + body.w - 1 - armR.x) * 0.15;
armR.y += (body.y + 5 - armR.y) * 0.15;
}
if (!dragged || !dragged.parts.includes(legL)) {
legL.x += (body.x + 2 - legL.x) * 0.15;
legL.y += (body.y + body.h - 2 - legL.y) * 0.15;
}
if (!dragged || !dragged.parts.includes(legR)) {
legR.x += (body.x + body.w - 9 - legR.x) * 0.15;
legR.y += (body.y + body.h - 2 - legR.y) * 0.15;
}
} else {
// Оружие и предметы падают
if (dragged === obj) return;
obj.vy += G;
obj.vx *= FR;
obj.x += obj.vx;
obj.y += obj.vy;
obj.angle += obj.va;
obj.va *= 0.95;
if (obj.y + obj.h > groundY) {
obj.y = groundY - obj.h;
obj.vy = -obj.vy * BOUNCE;
obj.fallen = true;
}
if (obj.x < 0) { obj.x = 0; obj.vx = -obj.vx * BOUNCE; }
if (obj.x + obj.w > W) { obj.x = W - obj.w; obj.vx = -obj.vx * BOUNCE; }
// Оружие стреляет
if (obj.type === 'gun') {
if (obj.cooldown > 0) obj.cooldown--;
if (obj.cooldown === 0 && obj.fallen && Math.random() < 0.01) {
bullets.push({ x: obj.x + obj.w/2, y: obj.y + obj.h/2, vx: (Math.random() - 0.5) * 8, vy: -2, life: 120 });
obj.cooldown = 60;
sfxShoot();
}
}
if (obj.type === 'cannon') {
if (obj.cooldown > 0) obj.cooldown--;
if (obj.cooldown === 0 && obj.fallen) {
// Пушка стреляет в сторону ближайшего арбуза
let target = null, minDist = Infinity;
objects.forEach(o => {
if (o.type === 'melon' && o.alive) {
const d = Math.hypot(o.parts[0].x - obj.x, o.parts[0].y - obj.y);
if (d < minDist) { minDist = d; target = o; }
}
});
if (target) {
const dx = target.parts[0].x - obj.x;
const dy = target.parts[0].y - obj.y;
const len = Math.hypot(dx, dy);
bullets.push({ x: obj.x + obj.w/2, y: obj.y + obj.h/2, vx: dx/len * 8, vy: dy/len * 8, life: 120, isCannon: true });
obj.cooldown = 90;
sfxShoot();
} else {
obj.cooldown = 60;
}
}
}
if (obj.type === 'sword') {
if (obj.slashTimer > 0) obj.slashTimer--;
}
if (obj.type === 'bomb') {
obj.fuse--;
if (obj.fuse <= 0) {
explode(obj.x + obj.w/2, obj.y + obj.h/2);
obj.alive = false;
}
}
}
});
// Пули
bullets.forEach((b, i) => {
b.x += b.vx; b.y += b.vy; b.vy += 0.2; b.life--;
if (b.life <= 0 || b.y > groundY) { bullets.splice(i, 1); return; }
objects.forEach(obj => {
if (obj.type === 'melon' && obj.alive) {
obj.parts.forEach(p => {
const cx = p.x + (p.w || p.r*2)/2;
const cy = p.y + (p.h || p.r*2)/2;
const r = (p.w || p.r*2)/2;
if (Math.hypot(b.x - cx, b.y - cy) < r + 5) {
p.vx += b.vx * 0.5; p.vy -= 2;
p.hitTimer = 20;
woundCount++;
sfxPain();
if (b.isCannon) explosionCount++;
bullets.splice(i, 1);
updateHUD();
}
});
}
});
});
bullets = bullets.filter(b => b.life > 0);
// Взрывы
explosions.forEach((ex, i) => {
if (ex.isPain) {
ex.x += ex.vx; ex.y += ex.vy; ex.alpha -= 0.05;
} else {
ex.r += 8; ex.alpha -= 0.05;
}
if (ex.alpha <= 0) explosions.splice(i, 1);
});
}
function explode(x, y) {
explosions.push({ x, y, r: 10, alpha: 1, isPain: false });
explosionCount++;
sfxBoom();
updateHUD();
objects.forEach(obj => {
if (obj.type === 'melon' && obj.alive) {
obj.parts.forEach(p => {
const dx = (p.x + (p.w||p.r*2)/2) - x;
const dy = (p.y + (p.h||p.r*2)/2) - y;
const dist = Math.hypot(dx, dy);
if (dist < 150) {
const force = (150 - dist) / 150 * 15;
p.vx += (dx / (dist || 1)) * force;
p.vy += (dy / (dist || 1)) * force - 3;
p.hitTimer = 30;
}
});
}
});
}
// --- РИСОВАНИЕ ---
function drawGround() {
ctx.fillStyle = '#2d1f0d';
ctx.fillRect(0, groundY, canvas.width, canvas.height - groundY);
ctx.fillStyle = '#4CAF50';
ctx.fillRect(0, groundY, canvas.width, 8);
ctx.fillStyle = '#388E3C';
for (let x = 0; x < canvas.width; x += 12) ctx.fillRect(x, groundY - 5, 3, 8);
}
function drawMelon(obj) {
obj.parts.forEach(p => {
ctx.save();
ctx.translate(p.x + (p.w || p.r*2)/2, p.y + (p.h || p.r*2)/2);
ctx.rotate(p.angle);
const hurt = p.hitTimer > 0;
if (p.type === 'body') {
ctx.fillStyle = hurt ? '#ff4444' : '#4CAF50';
ctx.beginPath(); ctx.ellipse(0, 0, p.w/2, p.h/2, 0, 0, Math.PI * 2); ctx.fill();
ctx.strokeStyle = '#2E7D32'; ctx.lineWidth = 2; ctx.stroke();
if (!hurt) {
ctx.strokeStyle = '#1B5E20'; ctx.lineWidth = 1;
for (let i = -2; i <= 2; i++) { ctx.beginPath(); ctx.moveTo(i * 5, -p.h/2 + 3); ctx.lineTo(i * 5, p.h/2 - 3); ctx.stroke(); }
}
} else if (p.type === 'head') {
ctx.fillStyle = hurt ? '#ff4444' : '#66BB6A';
ctx.beginPath(); ctx.arc(0, 0, p.r, 0, Math.PI * 2); ctx.fill();
ctx.strokeStyle = '#2E7D32'; ctx.lineWidth = 2; ctx.stroke();
if (hurt) {
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2;
ctx.beginPath(); ctx.moveTo(-5, -4); ctx.lineTo(-1, 0); ctx.moveTo(-1, -4); ctx.lineTo(-5, 0); ctx.stroke();
ctx.beginPath(); ctx.moveTo(3, -4); ctx.lineTo(7, 0); ctx.moveTo(7, -4); ctx.lineTo(3, 0); ctx.stroke();
} else {
ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc(-3, -2, 2.5, 0, Math.PI * 2); ctx.arc(3, -2, 2.5, 0, Math.PI * 2); ctx.fill();
ctx.fillStyle = '#000'; ctx.beginPath(); ctx.arc(-2.5, -2, 1.2, 0, Math.PI * 2); ctx.arc(3.5, -2, 1.2, 0, Math.PI * 2); ctx.fill();
ctx.strokeStyle = '#000'; ctx.lineWidth = 1; ctx.beginPath(); ctx.arc(0, 2, 3, 0.1, Math.PI - 0.1); ctx.stroke();
}
} else {
ctx.fillStyle = hurt ? '#ff4444' : '#4CAF50';
ctx.fillRect(-p.w/2, -p.h/2, p.w, p.h);
ctx.strokeStyle = '#2E7D32'; ctx.lineWidth = 1; ctx.strokeRect(-p.w/2, -p.h/2, p.w, p.h);
}
ctx.restore();
});
}
function drawPumpkin(obj) {
ctx.save();
ctx.translate(obj.x + obj.w/2, obj.y + obj.h/2);
ctx.rotate(obj.angle);
const hurt = obj.hitTimer > 0;
// Тыква
ctx.fillStyle = hurt ? '#ff4444' : '#ff8c00';
ctx.beginPath(); ctx.ellipse(0, 0, obj.w/2, obj.h/2, 0, 0, Math.PI * 2); ctx.fill();
ctx.strokeStyle = '#cc6600'; ctx.lineWidth = 2; ctx.stroke();
// Полоски
ctx.strokeStyle = '#cc6600'; ctx.lineWidth = 1;
for (let i = -2; i <= 2; i++) {
ctx.beginPath(); ctx.moveTo(i * 7, -obj.h/2 + 5); ctx.lineTo(i * 7, obj.h/2 - 5); ctx.stroke();
}
// Глаза
if (hurt) {
ctx.strokeStyle = '#fff'; ctx.lineWidth = 2;
ctx.beginPath(); ctx.moveTo(-8, -5); ctx.lineTo(-3, 0); ctx.moveTo(-3, -5); ctx.lineTo(-8, 0); ctx.stroke();
ctx.beginPath(); ctx.moveTo(3, -5); ctx.lineTo(8, 0); ctx.moveTo(8, -5); ctx.lineTo(3, 0); ctx.stroke();
} else {
ctx.fillStyle = '#fff';
ctx.beginPath(); ctx.arc(-5, -3, 4, 0, Math.PI * 2); ctx.arc(5, -3, 4, 0, Math.PI * 2); ctx.fill();
ctx.fillStyle = '#000';
ctx.beginPath(); ctx.arc(-4, -3, 2, 0, Math.PI * 2); ctx.arc(6, -3, 2, 0, Math.PI * 2); ctx.fill();
}
// Рот
ctx.strokeStyle = '#000'; ctx.lineWidth = 2;
ctx.beginPath(); ctx.arc(0, 3, 5, 0.1, Math.PI - 0.1); ctx.stroke();
ctx.restore();
}
function drawBox(obj) {
ctx.save(); ctx.translate(obj.x + obj.w/2, obj.y + obj.h/2); ctx.rotate(obj.angle);
ctx.fillStyle = '#d4a017'; ctx.fillRect(-obj.w/2, -obj.h/2, obj.w, obj.h);
ctx.strokeStyle = '#8B4513'; ctx.lineWidth = 3; ctx.strokeRect(-obj.w/2, -obj.h/2, obj.w, obj.h);
ctx.strokeStyle = '#8B4513'; ctx.lineWidth = 2;
ctx.beginPath(); ctx.moveTo(-obj.w/2, -obj.h/2); ctx.lineTo(obj.w/2, obj.h/2); ctx.stroke();
ctx.beginPath(); ctx.moveTo(obj.w/2, -obj.h/2); ctx.lineTo(-obj.w/2, obj.h/2); ctx.stroke();
ctx.restore();
}
function drawSpike(obj) {
ctx.save(); ctx.translate(obj.x + obj.w/2, obj.y + obj.h/2); ctx.rotate(obj.angle);
ctx.fillStyle = '#e94560'; ctx.beginPath();
ctx.moveTo(0, -obj.h/2); ctx.lineTo(obj.w/2, obj.h/2); ctx.lineTo(-obj.w/2, obj.h/2); ctx.closePath(); ctx.fill();
ctx.strokeStyle = '#aa0044'; ctx.lineWidth = 2; ctx.stroke();
ctx.restore();
}
function drawGun(obj) {
ctx.save(); ctx.translate(obj.x + obj.w/2, obj.y + obj.h/2); ctx.rotate(obj.angle);
ctx.fillStyle = '#333'; ctx.fillRect(-obj.w/2, -obj.h/2, obj.w, obj.h);
ctx.fillStyle = '#666'; ctx.fillRect(-obj.w/2, -obj.h/2 - 3, obj.w * 0.6, 4);
ctx.strokeStyle = '#000'; ctx.lineWidth = 1; ctx.strokeRect(-obj.w/2, -obj.h/2, obj.w, obj.h);
ctx.restore();
}
function drawSword(obj) {
ctx.save(); ctx.translate(obj.x + obj.w/2, obj.y + obj.h/2); ctx.rotate(obj.angle);
ctx.fillStyle = '#ccc'; ctx.fillRect(-obj.w/2, -2, obj.w * 0.7, 4);
ctx.fillStyle = '#8B4513'; ctx.fillRect(obj.w/2 - obj.w * 0.3, -4, obj.w * 0.3, 8);
if (obj.slashTimer > 0) {
ctx.strokeStyle = 'rgba(255,255,255,0.8)'; ctx.lineWidth = 3;
ctx.beginPath(); ctx.arc(0, 0, 30, -0.5, 0.5); ctx.stroke();
}
ctx.restore();
}
function drawBomb(obj) {
ctx.save(); ctx.translate(obj.x + obj.w/2, obj.y + obj.h/2); ctx.rotate(obj.angle);
ctx.fillStyle = '#222'; ctx.beginPath(); ctx.arc(0, 0, obj.w/2, 0, Math.PI * 2); ctx.fill();
ctx.strokeStyle = '#000'; ctx.lineWidth = 2; ctx.stroke();
ctx.strokeStyle = '#ff6600'; ctx.lineWidth = 2;
ctx.beginPath(); ctx.moveTo(0, -obj.w/2); ctx.lineTo(5, -obj.w/2 - 8); ctx.stroke();
if (Math.floor(Date.now() / 100) % 2 === 0) {
ctx.fillStyle = '#ffdd00'; ctx.beginPath(); ctx.arc(5, -obj.w/2 - 8, 3, 0, Math.PI * 2); ctx.fill();
}
ctx.fillStyle = '#fff'; ctx.font = 'bold 10px Arial'; ctx.textAlign = 'center';
ctx.fillText(Math.ceil(obj.fuse / 60), 0, 3); ctx.textAlign = 'left';
ctx.restore();
}
function drawCannon(obj) {
ctx.save(); ctx.translate(obj.x + obj.w/2, obj.y + obj.h/2); ctx.rotate(obj.angle);
// Основание
ctx.fillStyle = '#555';
ctx.fillRect(-obj.w/2, 0, obj.w, obj.h/2);
// Ствол
ctx.fillStyle = '#333';
ctx.fillRect(-obj.w/2 + 5, -obj.h/2, obj.w - 10, obj.h/2);
ctx.strokeStyle = '#000'; ctx.lineWidth = 2;
ctx.strokeRect(-obj.w/2 + 5, -obj.h/2, obj.w - 10, obj.h/2);
// Дуло
ctx.fillStyle = '#ff6600';
ctx.beginPath(); ctx.arc(-obj.w/2 + 5, 0, 4, 0, Math.PI * 2); ctx.fill();
ctx.restore();
}
function draw() {
const grad = ctx.createLinearGradient(0, 0, 0, canvas.height);
grad.addColorStop(0, '#1a1a2e'); grad.addColorStop(1, '#2a2a4e');
ctx.fillStyle = grad; ctx.fillRect(0, 0, canvas.width, canvas.height);
drawGround();
objects.forEach(obj => {
if (!obj.alive) return;
if (obj.type === 'melon') drawMelon(obj);
else if (obj.type === 'pumpkin') drawPumpkin(obj);
else if (obj.type === 'box') drawBox(obj);
else if (obj.type === 'spike') drawSpike(obj);
else if (obj.type === 'gun') drawGun(obj);
else if (obj.type === 'sword') drawSword(obj);
else if (obj.type === 'bomb') drawBomb(obj);
else if (obj.type === 'cannon') drawCannon(obj);
});
bullets.forEach(b => {
ctx.fillStyle = b.isCannon ? '#ff6600' : '#ffdd00';
ctx.beginPath(); ctx.arc(b.x, b.y, b.isCannon ? 6 : 4, 0, Math.PI * 2); ctx.fill();
ctx.fillStyle = b.isCannon ? 'rgba(255,102,0,0.3)' : 'rgba(255,221,0,0.3)';
ctx.beginPath(); ctx.arc(b.x, b.y, b.isCannon ? 10 : 8, 0, Math.PI * 2); ctx.fill();
});
explosions.forEach(ex => {
ctx.globalAlpha = ex.alpha;
if (ex.isPain) {
ctx.fillStyle = '#ff4444';
ctx.beginPath(); ctx.arc(ex.x, ex.y, ex.r, 0, Math.PI * 2); ctx.fill();
} else {
ctx.fillStyle = '#ff6600';
ctx.beginPath(); ctx.arc(ex.x, ex.y, ex.r, 0, Math.PI * 2); ctx.fill();
ctx.fillStyle = '#ffdd00';
ctx.beginPath(); ctx.arc(exGame Source: Мэлон
Creator: TurboGamer88
Libraries: none
Complexity: complex (519 lines, 21.6 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-turbogamer88" to link back to the original. Then publish at arcadelab.ai/publish.