import * as THREE from 'three'; import { state } from '../state.js'; let cauldronParticles; let cauldronLight; const particleCount = 8; const particleVelocities = []; export function createCauldron(x, y, z) { const cauldronGroup = new THREE.Group(); const cauldronRadius = 0.2; const cauldronHeight = 0.25; // 1. Cauldron Body const cauldronMaterial = new THREE.MeshPhongMaterial({ color: 0x111111, shininess: 80 }); // Use a sphere geometry cut in half for the bowl shape const cauldronGeo = new THREE.SphereGeometry(cauldronRadius, 32, 16, 0, Math.PI * 2, 0, Math.PI / 2); const cauldronMesh = new THREE.Mesh(cauldronGeo, cauldronMaterial); cauldronMesh.castShadow = true; cauldronMesh.rotation.z = Math.PI; cauldronGroup.add(cauldronMesh); // 2. Glowing Liquid Surface const liquidColor = 0x00ff00; // Bright green const liquidMaterial = new THREE.MeshPhongMaterial({ color: liquidColor, emissive: liquidColor, emissiveIntensity: 0.6, shininess: 100 }); const liquidGeo = new THREE.CircleGeometry(cauldronRadius * 0.95, 32); const liquidSurface = new THREE.Mesh(liquidGeo, liquidMaterial); liquidSurface.rotation.x = -Math.PI / 2; liquidSurface.position.y = -0.01; // Slightly below the rim cauldronGroup.add(liquidSurface); // 3. Bubbling Particles const particleGeo = new THREE.BufferGeometry(); const positions = new Float32Array(particleCount * 3); for (let i = 0; i < particleCount; i++) { positions[i * 3] = (Math.random() - 0.5) * cauldronRadius * 1.5; positions[i * 3 + 1] = (Math.random() - 0.5) * 0.05; // Start near the surface positions[i * 3 + 2] = (Math.random() - 0.5) * cauldronRadius * 1.5; particleVelocities.push((0.05 + Math.random() * 0.1) / 60); // Random upward velocity } particleGeo.setAttribute('position', new THREE.BufferAttribute(positions, 3)); const particleMaterial = new THREE.PointsMaterial({ color: liquidColor, size: 0.015, transparent: true, blending: THREE.AdditiveBlending, depthWrite: false, opacity: 0.7 }); cauldronParticles = new THREE.Points(particleGeo, particleMaterial); cauldronGroup.add(cauldronParticles); // 4. Light from the cauldron cauldronLight = new THREE.PointLight(liquidColor, 0.8, 3); cauldronLight.position.y = 0.2; cauldronLight.castShadow = true; cauldronGroup.add(cauldronLight); // Position and add to scene cauldronGroup.position.set(x, y, z); state.scene.add(cauldronGroup); } export function updateCauldron() { if (!cauldronParticles || !cauldronLight) return; // Animate Bubbles const positions = cauldronParticles.geometry.attributes.position.array; const bubbleMaxHeight = 0.1; const overfly = Math.random() * bubbleMaxHeight; for (let i = 0; i < particleCount; i++) { positions[i * 3 + 1] += particleVelocities[i]; // Move bubble up // Reset bubble if it goes too high if (positions[i * 3 + 1] > bubbleMaxHeight + overfly) { positions[i * 3 + 1] = (Math.random() - 0.5) * 0.05; // Give it a new random X/Z position positions[i * 3] = (Math.random() - 0.5) * 0.2 * 1.5; positions[i * 3 + 2] = (Math.random() - 0.5) * 0.2 * 1.5; } } cauldronParticles.geometry.attributes.position.needsUpdate = true; // Flicker Light const flicker = Math.random() * 0.02; cauldronLight.intensity = 0.1 + flicker; }