Files
kaltaquise-gamification/sys_digger/render.js
2026-04-20 14:44:51 +02:00

211 lines
6.9 KiB
JavaScript

function burst(x, y, color) {
for (let i = 0; i < BURST_COUNT; i++) {
const a = Math.random() * Math.PI * 2;
const s = BURST_SPEED_MIN + Math.random() * (BURST_SPEED_MAX - BURST_SPEED_MIN);
particles.push({
x, y,
vx: Math.cos(a) * s, vy: Math.sin(a) * s,
life: 1,
decay: PARTICLE_DECAY_MIN + Math.random() * (PARTICLE_DECAY_MAX - PARTICLE_DECAY_MIN),
r: PARTICLE_RADIUS_MIN + Math.random() * (PARTICLE_RADIUS_MAX - PARTICLE_RADIUS_MIN),
color
});
}
}
function drawBackground() {
ctx.fillStyle = '#0a0a1a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
stars.forEach(s => {
ctx.fillStyle = `rgba(255,255,255,${s.a})`;
ctx.beginPath();
ctx.arc(s.x, s.y, s.r, 0, Math.PI * 2);
ctx.fill();
});
}
function drawCentralMolecule() {
const t = Date.now() / 1000;
const amb = ctx.createRadialGradient(CX, CY, 10, CX, CY, ANCHOR_RADIUS + 50);
amb.addColorStop(0, 'rgba(0,180,255,0.12)');
amb.addColorStop(1, 'transparent');
ctx.fillStyle = amb;
ctx.beginPath();
ctx.arc(CX, CY, ANCHOR_RADIUS + 50, 0, Math.PI * 2);
ctx.fill();
anchorPoints.forEach(ap => {
ctx.beginPath();
ctx.moveTo(CX, CY);
ctx.lineTo(ap.x, ap.y);
ctx.strokeStyle = ap.active ? 'rgba(255,80,80,0.45)' : 'rgba(0,200,255,0.18)';
ctx.lineWidth = ap.active ? 2 : 1;
ctx.stroke();
});
const ng = ctx.createRadialGradient(CX, CY, 0, CX, CY, NUCLEUS_RADIUS);
ng.addColorStop(0, '#00ffcc');
ng.addColorStop(0.55, '#0088ff');
ng.addColorStop(1, 'rgba(0,80,220,0.3)');
ctx.fillStyle = ng;
ctx.beginPath();
ctx.arc(CX, CY, NUCLEUS_RADIUS, 0, Math.PI * 2);
ctx.fill();
const pulse = Math.sin(t * 2) * 0.3 + 0.7;
ctx.strokeStyle = `rgba(0,255,200,${pulse * 0.6})`;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(CX, CY, NUCLEUS_RADIUS + Math.sin(t * 2) * 5, 0, Math.PI * 2);
ctx.stroke();
ctx.fillStyle = 'rgba(255,255,255,0.55)';
ctx.beginPath();
ctx.arc(CX - 16, CY - 16, 8, 0, Math.PI * 2);
ctx.fill();
for (let i = 0; i < NUCLEUS_ORBITS; i++) {
const orbitAngle = t * (0.45 + i * 0.3) + (i * Math.PI * 2 / 3);
const rx = 72 + i * 10;
const ry = 28 + i * 8;
const tilt = i * Math.PI / 3;
ctx.save();
ctx.translate(CX, CY);
ctx.rotate(tilt);
ctx.strokeStyle = 'rgba(0,200,255,0.13)';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.ellipse(0, 0, rx, ry, 0, 0, Math.PI * 2);
ctx.stroke();
ctx.fillStyle = '#88eeff';
ctx.beginPath();
ctx.arc(Math.cos(orbitAngle) * rx, Math.sin(orbitAngle) * ry, 4, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
function drawAnchorPoints() {
const t = Date.now() / 1000;
anchorPoints.forEach(ap => {
const pulse = Math.sin(t * 3 + ap.pulsePhase) * 0.5 + 0.5;
if (ap.blockTimer > 0) {
ap.blockTimer--;
ctx.strokeStyle = `rgba(0,255,100,${ap.blockTimer / BLOCK_TIMER_FRAMES})`;
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(ap.x, ap.y, 20 + (BLOCK_TIMER_FRAMES - ap.blockTimer), 0, Math.PI * 2);
ctx.stroke();
ctx.fillStyle = 'rgba(0,255,100,0.55)';
ctx.beginPath();
ctx.arc(ap.x, ap.y, 13, 0, Math.PI * 2);
ctx.fill();
} else if (ap.active) {
ctx.strokeStyle = `rgba(255,70,70,${0.5 + pulse * 0.5})`;
ctx.lineWidth = 2.5;
ctx.beginPath();
ctx.arc(ap.x, ap.y, 18 + pulse * 6, 0, Math.PI * 2);
ctx.stroke();
ctx.fillStyle = 'rgba(255,70,70,0.35)';
ctx.beginPath();
ctx.arc(ap.x, ap.y, 15, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = 'rgba(255,200,200,0.85)';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(ap.x - 9, ap.y); ctx.lineTo(ap.x + 9, ap.y);
ctx.moveTo(ap.x, ap.y - 9); ctx.lineTo(ap.x, ap.y + 9);
ctx.stroke();
} else {
ctx.strokeStyle = `rgba(0,200,255,${0.18 + pulse * 0.28})`;
ctx.lineWidth = 1.5;
ctx.beginPath();
ctx.arc(ap.x, ap.y, 13, 0, Math.PI * 2);
ctx.stroke();
ctx.fillStyle = 'rgba(0,200,255,0.08)';
ctx.beginPath();
ctx.arc(ap.x, ap.y, 13, 0, Math.PI * 2);
ctx.fill();
}
});
}
function drawParticles() {
particles = particles.filter(p => p.life > 0);
particles.forEach(p => {
p.x += p.vx; p.y += p.vy;
p.vx *= 0.94; p.vy *= 0.94;
p.life -= p.decay;
ctx.globalAlpha = p.life;
ctx.fillStyle = p.color;
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
ctx.fill();
ctx.globalAlpha = 1;
});
}
function drawClickEffects() {
clickEffects = clickEffects.filter(e => e.life > 0);
clickEffects.forEach(e => {
e.radius += 3;
e.life -= 0.06;
ctx.globalAlpha = e.life;
ctx.strokeStyle = '#00ff88';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(e.x, e.y, e.radius, 0, Math.PI * 2);
ctx.stroke();
ctx.globalAlpha = 1;
});
}
function updateCystinstein() {
cysCtx.clearRect(0, 0, cysCanvas.width, cysCanvas.height);
cysCtx.fillStyle = '#00aaff';
cysCtx.shadowColor = '#00aaff';
cysCtx.shadowBlur = docks > 0 ? 6 : 0;
cysCtx.beginPath();
cysCtx.arc(CYS_CENTER_X, CYS_CENTER_Y, CYS_CORE_RADIUS, 0, Math.PI * 2);
cysCtx.fill();
cysCtx.shadowBlur = 0;
for (let i = 0; i < docks && i < MAX_DOCKS; i++) {
const angle = (i / MAX_DOCKS) * Math.PI * 2 - Math.PI / 2;
const ax = CYS_CENTER_X + Math.cos(angle) * CYS_DOCK_RADIUS;
const ay = CYS_CENTER_Y + Math.sin(angle) * CYS_DOCK_RADIUS;
const danger = i / (MAX_DOCKS - 1);
cysCtx.strokeStyle = `rgba(255,${Math.floor(150 - danger * 150)},50,0.8)`;
cysCtx.lineWidth = 2;
cysCtx.beginPath();
cysCtx.moveTo(CYS_CENTER_X, CYS_CENTER_Y);
cysCtx.lineTo(ax, ay);
cysCtx.stroke();
cysCtx.fillStyle = `rgb(${Math.floor(200 + danger * 55)},${Math.floor(100 - danger * 100)},50)`;
cysCtx.shadowColor = '#ff4444';
cysCtx.shadowBlur = danger > 0.7 ? 8 : 0;
cysCtx.beginPath();
cysCtx.arc(ax, ay, CYS_ATOM_RADIUS, 0, Math.PI * 2);
cysCtx.fill();
cysCtx.shadowBlur = 0;
}
if (docks >= MAX_DOCKS) {
cysCtx.strokeStyle = 'rgba(255,0,0,0.9)';
cysCtx.lineWidth = 3;
cysCtx.shadowColor = '#ff0000';
cysCtx.shadowBlur = 15;
cysCtx.beginPath();
cysCtx.arc(CYS_CENTER_X, CYS_CENTER_Y, CYS_RING_RADIUS, 0, Math.PI * 2);
cysCtx.stroke();
cysCtx.shadowBlur = 0;
}
}