/** * Coin class - manages collectible coins with enhanced visuals */ class Coin { constructor(scene, lane) { this.scene = scene; this.mesh = null; this.glowMesh = null; this.createMesh(lane); } createMesh(lane) { // Create procedural texture for coin const canvas = document.createElement('canvas'); canvas.width = 128; canvas.height = 64; const ctx = canvas.getContext('2d'); // Gold gradient background const gradient = ctx.createLinearGradient(0, 0, 128, 64); gradient.addColorStop(0, '#ffd700'); gradient.addColorStop(0.3, '#ffec8b'); gradient.addColorStop(0.5, '#ffd700'); gradient.addColorStop(0.7, '#ffec8b'); gradient.addColorStop(1, '#daa520'); ctx.fillStyle = gradient; ctx.fillRect(0, 0, 128, 64); // Inner ring ctx.strokeStyle = '#b8860b'; ctx.lineWidth = 3; ctx.beginPath(); ctx.ellipse(64, 32, 45, 20, 0, 0, Math.PI * 2); ctx.stroke(); // Star symbol in center ctx.fillStyle = '#b8860b'; ctx.beginPath(); const cx = 64, cy = 32; for (let i = 0; i < 5; i++) { const angle = (i * 4 * Math.PI / 5) - Math.PI / 2; const x = cx + Math.cos(angle) * 15; const y = cy + Math.sin(angle) * 15; if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.closePath(); ctx.fill(); const texture = new THREE.CanvasTexture(canvas); const geometry = new THREE.CylinderGeometry(0.5, 0.5, 0.15, 24); const material = new THREE.MeshStandardMaterial({ map: texture, color: 0xffd700, emissive: 0xffd700, emissiveIntensity: 0.6, metalness: 0.9, roughness: 0.1 }); const LANE_WIDTH = 3.5; this.mesh = new THREE.Mesh(geometry, material); this.mesh.position.set(lane * LANE_WIDTH, 1, -60); this.mesh.rotation.x = Math.PI / 2; // Outer glow ring const glowGeometry = new THREE.TorusGeometry(0.6, 0.12, 8, 32); const glowMaterial = new THREE.MeshBasicMaterial({ color: 0xffd700, transparent: true, opacity: 0.4 }); this.glowMesh = new THREE.Mesh(glowGeometry, glowMaterial); this.glowMesh.rotation.x = Math.PI / 2; this.mesh.add(this.glowMesh); // Add point light for glow const coinLight = new THREE.PointLight(0xffd700, 0.4, 3); this.mesh.add(coinLight); this.scene.add(this.mesh); } update(gameSpeed) { this.mesh.position.z += gameSpeed; // Rotate coin this.mesh.rotation.z += 0.05; this.glowMesh.rotation.z -= 0.03; // Bobbing motion this.mesh.position.y = 1 + Math.sin(Date.now() * 0.005) * 0.15; } isPastCamera() { return this.mesh.position.z > 15; } checkCollection(playerPosition) { const dx = Math.abs(this.mesh.position.x - playerPosition.x); const dz = Math.abs(this.mesh.position.z - playerPosition.z); return dx < 1 && dz < 1; } dispose() { this.scene.remove(this.mesh); } }