import * as THREE from 'three'; import type { Game } from './Game'; const SPEED = 8; const PLAYER_PROXIMITY = 1.4; export class InkBlob { body: THREE.Group; target: THREE.Vector3; private vel: THREE.Vector3; private life = 6; dead = false; nearPlayer = false; constructor(from: THREE.Vector3, target: THREE.Vector3) { this.target = target.clone(); this.body = new THREE.Group(); this.body.position.copy(from); this.vel = new THREE.Vector3().subVectors(target, from); this.vel.y = 0; const dist = this.vel.length(); if (dist > 0.01) { this.vel.normalize().multiplyScalar(SPEED); } const glowMat = new THREE.MeshBasicMaterial({ color: 0xaa44ff, transparent: true, opacity: 0.5, blending: THREE.AdditiveBlending, depthWrite: false, }); const glow = new THREE.Mesh(new THREE.SphereGeometry(0.5, 12, 12), glowMat); this.body.add(glow); const coreMat = new THREE.MeshToonMaterial({ color: 0x3a1060 }); const core = new THREE.Mesh(new THREE.SphereGeometry(0.28, 10, 10), coreMat); this.body.add(core); } update(dt: number, game: Game): boolean { this.life -= dt; this.body.position.x += this.vel.x * dt; this.body.position.z += this.vel.z * dt; this.body.position.y = 1.2 + Math.sin(this.life * 4) * 0.1; if (game.player.health > 0 && !this.nearPlayer) { const dist = this.body.position.distanceTo(game.player.body.position); if (dist < PLAYER_PROXIMITY) { this.nearPlayer = true; game.triggerInkVignette(); } } const dx = this.target.x - this.body.position.x; const dz = this.target.z - this.body.position.z; if (this.life <= 0 || dx * dx + dz * dz < 0.25) { this.dead = true; return false; } return true; } dispose() { this.body.removeFromParent(); for (const child of [...this.body.children]) { if (child instanceof THREE.Mesh) { child.geometry.dispose(); (child.material as THREE.Material).dispose(); } } } }