class IncomingAtom { constructor(anchorIndex) { this.anchorIndex = anchorIndex; const ap = anchorPoints[anchorIndex]; const far = Math.max(canvas.width, canvas.height) * 0.9; this.x = CX + Math.cos(ap.angle) * far; this.y = CY + Math.sin(ap.angle) * far; this.tx = ap.x; this.ty = ap.y; this.speed = ATOM_SPEED_MIN + Math.random() * (ATOM_SPEED_MAX - ATOM_SPEED_MIN); this.radius = ATOM_RADIUS_MIN + Math.random() * (ATOM_RADIUS_MAX - ATOM_RADIUS_MIN); this.hue = ATOM_HUE_MIN + Math.random() * ATOM_HUE_RANGE; this.dead = false; this.eAngle = Math.random() * Math.PI * 2; this.eSpeed = ATOM_ELECTRON_SPEED_MIN + Math.random() * (ATOM_ELECTRON_SPEED_MAX - ATOM_ELECTRON_SPEED_MIN); // ap.active is set once the atom enters the visible screen (see update) } update() { if (this.dead || gameOver) return; // Activate the anchor the moment the atom becomes visible const ap = anchorPoints[this.anchorIndex]; if (!ap.active && this.x >= 0 && this.x <= canvas.width && this.y >= 0 && this.y <= canvas.height) { ap.active = true; } const dx = this.tx - this.x; const dy = this.ty - this.y; const dist = Math.hypot(dx, dy); if (dist < this.speed) { this.dead = true; anchorPoints[this.anchorIndex].active = false; docks++; document.getElementById('docks-count').textContent = `${docks} / ${MAX_DOCKS} docks`; updateCystinstein(); burst(this.tx, this.ty, '#ff4444'); if (docks >= MAX_DOCKS) triggerGameOver(); } else { this.x += (dx / dist) * this.speed; this.y += (dy / dist) * this.speed; this.eAngle += this.eSpeed; } } draw() { if (this.dead) return; const c = `hsl(${this.hue}, 80%, 60%)`; const g = ctx.createRadialGradient(this.x, this.y, 0, this.x, this.y, this.radius * 2.5); g.addColorStop(0, `hsla(${this.hue}, 80%, 60%, 0.4)`); g.addColorStop(1, 'transparent'); ctx.fillStyle = g; ctx.beginPath(); ctx.arc(this.x, this.y, this.radius * 2.5, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = c; ctx.beginPath(); ctx.arc(this.x, this.y, this.radius * 0.55, 0, Math.PI * 2); ctx.fill(); ctx.save(); ctx.translate(this.x, this.y); ctx.strokeStyle = `hsla(${this.hue}, 80%, 70%, 0.35)`; ctx.lineWidth = 1; ctx.beginPath(); ctx.ellipse(0, 0, this.radius, this.radius * 0.4, this.eAngle * 0.25, 0, Math.PI * 2); ctx.stroke(); ctx.fillStyle = '#fff'; ctx.beginPath(); ctx.arc( Math.cos(this.eAngle) * this.radius, Math.sin(this.eAngle) * (this.radius * 0.4), 3, 0, Math.PI * 2 ); ctx.fill(); ctx.restore(); } }